UNPKG

hsd

Version:
370 lines (292 loc) 6.55 kB
/*! * records.js - chaindb records * Copyright (c) 2024 The Handshake Developers (MIT License). * https://github.com/handshake-org/hsd */ 'use strict'; const assert = require('bsert'); const bio = require('bufio'); const {BufferMap} = require('buffer-map'); const consensus = require('../protocol/consensus'); const Network = require('../protocol/network'); /** * ChainFlags */ class ChainFlags extends bio.Struct { /** * Create chain flags. * @alias module:blockchain.ChainFlags * @constructor */ constructor(options) { super(); this.network = Network.primary; this.spv = false; this.prune = false; this.indexTX = false; this.indexAddress = false; if (options) this.fromOptions(options); } fromOptions(options) { this.network = Network.get(options.network); if (options.spv != null) { assert(typeof options.spv === 'boolean'); this.spv = options.spv; } if (options.prune != null) { assert(typeof options.prune === 'boolean'); this.prune = options.prune; } if (options.indexTX != null) { assert(typeof options.indexTX === 'boolean'); this.indexTX = options.indexTX; } if (options.indexAddress != null) { assert(typeof options.indexAddress === 'boolean'); this.indexAddress = options.indexAddress; } return this; } getSize() { return 12; } write(bw) { let flags = 0; if (this.spv) flags |= 1 << 0; if (this.prune) flags |= 1 << 1; if (this.indexTX) flags |= 1 << 2; if (this.indexAddress) flags |= 1 << 3; bw.writeU32(this.network.magic); bw.writeU32(flags); bw.writeU32(0); return bw; } read(br) { this.network = Network.fromMagic(br.readU32()); const flags = br.readU32(); this.spv = (flags & 1) !== 0; this.prune = (flags & 2) !== 0; this.indexTX = (flags & 4) !== 0; this.indexAddress = (flags & 8) !== 0; return this; } } /** * Chain State */ class ChainState extends bio.Struct { /** * Create chain state. * @alias module:blockchain.ChainState * @constructor */ constructor() { super(); this.tip = consensus.ZERO_HASH; this.tx = 0; this.coin = 0; this.value = 0; this.burned = 0; this.committed = false; } inject(state) { this.tip = state.tip; this.tx = state.tx; this.coin = state.coin; this.value = state.value; this.burned = state.burned; return this; } connect(block) { this.tx += block.txs.length; } disconnect(block) { this.tx -= block.txs.length; } add(coin) { this.coin += 1; this.value += coin.value; } spend(coin) { this.coin -= 1; this.value -= coin.value; } burn(coin) { this.coin += 1; this.burned += coin.value; } unburn(coin) { this.coin -= 1; this.burned -= coin.value; } commit(hash) { assert(Buffer.isBuffer(hash)); this.tip = hash; this.committed = true; return this.encode(); } getSize() { return 64; } write(bw) { bw.writeHash(this.tip); bw.writeU64(this.tx); bw.writeU64(this.coin); bw.writeU64(this.value); bw.writeU64(this.burned); return bw; } read(br) { this.tip = br.readHash(); this.tx = br.readU64(); this.coin = br.readU64(); this.value = br.readU64(); this.burned = br.readU64(); return this; } } /** * State Cache */ class StateCache { /** * Create state cache. * @alias module:blockchain.StateCache * @constructor */ constructor(network) { this.network = network; this.bits = []; this.updates = []; this.init(); } init() { for (let i = 0; i < 32; i++) this.bits.push(null); for (const {bit} of this.network.deploys) { assert(!this.bits[bit]); this.bits[bit] = new BufferMap(); } } set(bit, entry, state) { const cache = this.bits[bit]; assert(cache); if (cache.get(entry.hash) !== state) { cache.set(entry.hash, state); this.updates.push(new CacheUpdate(bit, entry.hash, state)); } } get(bit, entry) { const cache = this.bits[bit]; assert(cache); const state = cache.get(entry.hash); if (state == null) return -1; return state; } commit() { this.updates.length = 0; } drop() { for (const {bit, hash} of this.updates) { const cache = this.bits[bit]; assert(cache); cache.delete(hash); } this.updates.length = 0; } insert(bit, hash, state) { const cache = this.bits[bit]; assert(cache); cache.set(hash, state); } } /** * Cache Update */ class CacheUpdate { /** * Create cache update. * @constructor * @ignore */ constructor(bit, hash, state) { this.bit = bit; this.hash = hash; this.state = state; } encode() { const data = Buffer.allocUnsafe(1); data[0] = this.state; return data; } } /** * Tree related state. */ class TreeState extends bio.Struct { /** * Create tree state. * @constructor * @ignore */ constructor() { super(); this.treeRoot = consensus.ZERO_HASH; this.commitHeight = 0; this.compactionRoot = consensus.ZERO_HASH; this.compactionHeight = 0; this.committed = false; } inject(state) { this.treeRoot = state.treeRoot; this.commitHeight = state.treeHeight; this.compactionHeight = state.compactionHeight; this.compactionRoot = state.compactionRoot; return this; } compact(hash, height) { assert(Buffer.isBuffer(hash)); assert((height >>> 0) === height); this.compactionRoot = hash; this.compactionHeight = height; }; commit(hash, height) { assert(Buffer.isBuffer(hash)); assert((height >>> 0) === height); this.treeRoot = hash; this.commitHeight = height; this.committed = true; return this.encode(); } getSize() { return 72; } write(bw) { bw.writeHash(this.treeRoot); bw.writeU32(this.commitHeight); bw.writeHash(this.compactionRoot); bw.writeU32(this.compactionHeight); return bw; } read(br) { this.treeRoot = br.readHash(); this.commitHeight = br.readU32(); this.compactionRoot = br.readHash(); this.compactionHeight = br.readU32(); return this; } } /* * Expose */ exports.ChainFlags = ChainFlags; exports.ChainState = ChainState; exports.StateCache = StateCache; exports.TreeState = TreeState; exports.CacheUpdate = CacheUpdate;