From 8c212d797f45b320390ff980cf5bf3183c03bc6a Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Tue, 25 Jul 2017 01:06:55 -0700 Subject: [PATCH] block: rename some more methods. refactor bip152 ptx vector. --- lib/blockchain/chain.js | 5 ++- lib/http/rpc.js | 6 ++- lib/net/bip152.js | 74 +++++++++++---------------------- lib/net/pool.js | 7 +++- lib/primitives/abstractblock.js | 56 +++++++++++-------------- lib/primitives/block.js | 13 +++++- lib/primitives/headers.js | 22 ++++------ lib/primitives/memblock.js | 14 ++++--- lib/primitives/merkleblock.js | 35 ++++++++++------ 9 files changed, 112 insertions(+), 120 deletions(-) diff --git a/lib/blockchain/chain.js b/lib/blockchain/chain.js index 8a3f6cb4..f4810acf 100644 --- a/lib/blockchain/chain.js +++ b/lib/blockchain/chain.js @@ -1334,7 +1334,7 @@ Chain.prototype.connect = async function connect(prev, block, flags) { // validated, and connected. Hopefully the // deserialized blocks get cleaned up by the // GC quickly. - if (block.memory) { + if (block.isMemory()) { try { block = block.toBlock(); } catch (e) { @@ -1342,7 +1342,8 @@ Chain.prototype.connect = async function connect(prev, block, flags) { throw new VerifyError(block, 'malformed', 'error parsing message', - 10); + 10, + true); } } diff --git a/lib/http/rpc.js b/lib/http/rpc.js index 9806cf5d..eaa7abb1 100644 --- a/lib/http/rpc.js +++ b/lib/http/rpc.js @@ -921,7 +921,7 @@ RPC.prototype.verifyTXOutProof = async function verifyTXOutProof(args, help) { let valid = new Validator([args]); let data = valid.buf(0); let out = []; - let block, entry; + let block, entry, tree; if (help || args.length !== 1) throw new RPCError(errs.MISC_ERROR, 'verifytxoutproof "proof"'); @@ -939,7 +939,9 @@ RPC.prototype.verifyTXOutProof = async function verifyTXOutProof(args, help) { if (!entry) throw new RPCError(errs.MISC_ERROR, 'Block not found in chain.'); - for (let hash of block.tree.matches) + tree = block.getTree(); + + for (let hash of tree.matches) out.push(util.revHex(hash)); return out; diff --git a/lib/net/bip152.js b/lib/net/bip152.js index 94a176f9..3b48034c 100644 --- a/lib/net/bip152.js +++ b/lib/net/bip152.js @@ -124,12 +124,7 @@ CompactBlock.prototype.fromRaw = function fromRaw(data) { let br = new BufferReader(data); let count; - this.version = br.readU32(); - this.prevBlock = br.readHash('hex'); - this.merkleRoot = br.readHash('hex'); - this.ts = br.readU32(); - this.bits = br.readU32(); - this.nonce = br.readU32(); + this.readHead(br); this.keyNonce = br.readBytes(8); this.sipKey = this.getKey(); @@ -154,7 +149,7 @@ CompactBlock.prototype.fromRaw = function fromRaw(data) { tx = TX.fromReader(br); - this.ptx.push(new PrefilledTX(index, tx)); + this.ptx.push([index, tx]); } return this; @@ -238,12 +233,13 @@ CompactBlock.prototype.getSize = function getSize(witness) { size += this.ids.length * 6; size += encoding.sizeVarint(this.ptx.length); - for (let ptx of this.ptx) { - size += encoding.sizeVarint(ptx.index); + for (let [index, tx] of this.ptx) { + size += encoding.sizeVarint(index); + if (witness) - size += ptx.tx.getSize(); + size += tx.getSize(); else - size += ptx.tx.getBaseSize(); + size += tx.getBaseSize(); } return size; @@ -273,12 +269,13 @@ CompactBlock.prototype.writeRaw = function writeRaw(bw, witness) { bw.writeVarint(this.ptx.length); - for (let ptx of this.ptx) { - bw.writeVarint(ptx.index); + for (let [index, tx] of this.ptx) { + bw.writeVarint(index); + if (witness) - ptx.tx.toWriter(bw); + tx.toWriter(bw); else - ptx.tx.toNormalWriter(bw); + tx.toNormalWriter(bw); } return bw; @@ -302,13 +299,14 @@ CompactBlock.prototype.toRequest = function toRequest() { */ CompactBlock.prototype.fillMempool = function fillMempool(witness, mempool) { - let have = {}; + let have; if (this.count === this.totalTX) return true; - for (let entry of mempool.map.values()) { - let tx = entry.tx; + have = new Set(); + + for (let {tx} of mempool.map.values()) { let hash = tx.hash(); let id, index; @@ -321,7 +319,7 @@ CompactBlock.prototype.fillMempool = function fillMempool(witness, mempool) { if (index == null) continue; - if (have[index]) { + if (have.has(index)) { // Siphash collision, just request it. this.available[index] = null; this.count--; @@ -329,7 +327,7 @@ CompactBlock.prototype.fillMempool = function fillMempool(witness, mempool) { } this.available[index] = tx; - have[index] = true; + have.add(index); this.count++; // We actually may have a siphash collision @@ -417,7 +415,7 @@ CompactBlock.prototype.init = function init() { if (this.totalTX > consensus.MAX_BLOCK_SIZE / 10) throw new Error('Compact block too big.'); - // Custom limit to avoid hashdos: + // Custom limit to avoid a hashdos. // Min valid tx size: (4 + 1 + 41 + 1 + 9 + 4) = 60 // Min block header size: 81 // Max number of transactions: (1000000 - 81) / 60 = 16665 @@ -429,33 +427,25 @@ CompactBlock.prototype.init = function init() { this.available.push(null); for (let i = 0; i < this.ptx.length; i++) { - let ptx = this.ptx[i]; - assert(ptx); - last += ptx.index + 1; + let [index, tx] = this.ptx[i]; + last += index + 1; assert(last <= 0xffff); assert(last <= this.ids.length + i); - this.available[last] = ptx.tx; + this.available[last] = tx; this.count++; } for (let i = 0; i < this.ids.length; i++) { - let id; + let id = this.ids[i]; while (this.available[i + offset]) offset++; - id = this.ids[i]; - - // Fails on siphash collision + // Fails on siphash collision. if (this.idMap.has(id)) return false; this.idMap.set(id, i + offset); - - // We're supposed to fail here if there's - // more than 12 hash collisions, but we - // don't have lowlevel access to our hash - // table. Hopefully we don't get hashdos'd. } return true; @@ -526,7 +516,7 @@ CompactBlock.prototype.fromBlock = function fromBlock(block, witness, nonce) { this.ids.push(id); } - this.ptx.push(new PrefilledTX(0, block.txs[0])); + this.ptx.push([0, block.txs[0]]); return this; }; @@ -964,20 +954,6 @@ TXResponse.prototype.frameRaw = function frameRaw(witness) { return this.writeRaw(new StaticWriter(size), witness).render(); }; -/** - * Represents a prefilled TX. - * @constructor - * @param {Number} index - * @param {TX} tx - * @property {Number} index - * @property {TX} tx - */ - -function PrefilledTX(index, tx) { - this.index = index; - this.tx = tx; -} - /* * Expose */ diff --git a/lib/net/pool.js b/lib/net/pool.js index e03a6e12..58ccf241 100644 --- a/lib/net/pool.js +++ b/lib/net/pool.js @@ -2687,6 +2687,7 @@ Pool.prototype._handleMerkleBlock = async function handleMerkleBlock(peer, packe let block = packet.block; let hash = block.hash('hex'); let flags = chainCommon.flags.VERIFY_NONE; + let tree; if (!this.syncing) return; @@ -2724,14 +2725,16 @@ Pool.prototype._handleMerkleBlock = async function handleMerkleBlock(peer, packe return; } - if (block.tree.matches.length === 0) { + tree = block.getTree(); + + if (tree.matches.length === 0) { await this._addBlock(peer, block, flags); return; } peer.merkleBlock = block; peer.merkleTime = util.ms(); - peer.merkleMatches = block.tree.matches.length; + peer.merkleMatches = tree.matches.length; peer.merkleMap = new Set(); }; diff --git a/lib/primitives/abstractblock.js b/lib/primitives/abstractblock.js index dd4f97b9..199ddb60 100644 --- a/lib/primitives/abstractblock.js +++ b/lib/primitives/abstractblock.js @@ -29,8 +29,6 @@ const consensus = require('../protocol/consensus'); * @property {Number} ts - Timestamp. * @property {Number} bits * @property {Number} nonce - * @property {TX[]} txs - Transaction vector. - * @property {ReversedHash} rhash - Reversed block hash (uint256le). */ function AbstractBlock() { @@ -44,24 +42,12 @@ function AbstractBlock() { this.bits = 0; this.nonce = 0; - this.txs = null; this.mutable = false; this._hash = null; this._hhash = null; - this._size = -1; - this._witness = -1; } -/** - * Memory flag. - * @const {Boolean} - * @default - * @memberof AbstractBlock# - */ - -AbstractBlock.prototype.memory = false; - /** * Inject properties from options object. * @private @@ -116,33 +102,29 @@ AbstractBlock.prototype.parseJSON = function parseJSON(json) { }; /** - * Clear any cached values (abstract). - * @param {Boolean?} all - Clear transactions. + * Test whether the block is a memblock. + * @returns {Boolean} */ -AbstractBlock.prototype._refresh = function refresh(all) { +AbstractBlock.prototype.isMemory = function isMemory() { + return false; +}; + +/** + * Clear any cached values (abstract). + */ + +AbstractBlock.prototype._refresh = function refresh() { this._hash = null; this._hhash = null; - this._size = -1; - this._witness = -1; - - if (!all) - return; - - if (!this.txs) - return; - - for (let tx of this.txs) - tx.refresh(); }; /** * Clear any cached values. - * @param {Boolean?} all - Clear transactions. */ -AbstractBlock.prototype.refresh = function refresh(all) { - return this._refresh(all); +AbstractBlock.prototype.refresh = function refresh() { + return this._refresh(); }; /** @@ -182,6 +164,16 @@ AbstractBlock.prototype.toHead = function toHead() { return this.writeHead(new StaticWriter(80)).render(); }; +/** + * Inject properties from serialized data. + * @private + * @param {Buffer} data + */ + +AbstractBlock.prototype.fromHead = function fromHead(data) { + return this.readHead(new BufferReader(data)); +}; + /** * Serialize the block headers. * @param {BufferWriter} bw @@ -202,7 +194,7 @@ AbstractBlock.prototype.writeHead = function writeHead(bw) { * @param {BufferReader} br */ -AbstractBlock.prototype.parseHead = function parseHead(br) { +AbstractBlock.prototype.readHead = function readHead(br) { this.version = br.readU32(); this.prevBlock = br.readHash('hex'); this.merkleRoot = br.readHash('hex'); diff --git a/lib/primitives/block.js b/lib/primitives/block.js index 08ec67c7..bd225bf6 100644 --- a/lib/primitives/block.js +++ b/lib/primitives/block.js @@ -79,8 +79,17 @@ Block.fromOptions = function fromOptions(options) { */ Block.prototype.refresh = function refresh(all) { + this._refresh(); + this._raw = null; - this._refresh(all); + this._size = -1; + this._witness = -1; + + if (!all) + return; + + for (let tx of this.txs) + tx.refresh(); }; /** @@ -644,7 +653,7 @@ Block.prototype.fromReader = function fromReader(br) { br.start(); - this.parseHead(br); + this.readHead(br); count = br.readVarint(); diff --git a/lib/primitives/headers.js b/lib/primitives/headers.js index 96cd92c1..0f4a9da5 100644 --- a/lib/primitives/headers.js +++ b/lib/primitives/headers.js @@ -80,7 +80,7 @@ Headers.prototype.toRaw = function toRaw() { */ Headers.prototype.fromReader = function fromReader(br) { - this.parseHead(br); + this.readHead(br); br.readVarint(); return this; }; @@ -118,17 +118,6 @@ Headers.fromRaw = function fromRaw(data, enc) { return new Headers().fromRaw(data); }; -/** - * Inject properties from serialized data. - * TODO: MOVE. - * @private - * @param {Buffer} data - */ - -Headers.prototype.fromHead = function fromHead(data) { - return this.parseHead(new BufferReader(data)); -}; - /** * Instantiate headers from serialized data. * @param {Buffer} data @@ -149,8 +138,15 @@ Headers.fromHead = function fromHead(data, enc) { */ Headers.fromEntry = function fromEntry(entry) { - let headers = new Headers(entry); + let headers = new Headers(); + headers.version = entry.version; + headers.prevBlock = entry.prevBlock; + headers.merkleRoot = entry.merkleRoot; + headers.ts = entry.ts; + headers.bits = entry.bits; + headers.nonce = entry.nonce; headers._hash = Buffer.from(entry.hash, 'hex'); + headers._hhash = entry.hash; return headers; }; diff --git a/lib/primitives/memblock.js b/lib/primitives/memblock.js index 475237c5..0f3c217c 100644 --- a/lib/primitives/memblock.js +++ b/lib/primitives/memblock.js @@ -40,19 +40,21 @@ function MemBlock() { if (!(this instanceof MemBlock)) return new MemBlock(); + AbstractBlock.call(this); + this._raw = DUMMY; } util.inherits(MemBlock, AbstractBlock); /** - * Memory flag. - * @const {Boolean} - * @default - * @memberof MemBlock# + * Test whether the block is a memblock. + * @returns {Boolean} */ -MemBlock.prototype.memory = true; +MemBlock.prototype.isMemory = function isMemory() { + return true; +}; /** * Serialize the block headers. @@ -144,7 +146,7 @@ MemBlock.prototype.parseCoinbaseHeight = function parseCoinbaseHeight() { MemBlock.prototype.fromRaw = function fromRaw(data) { let br = new BufferReader(data, true); - this.parseHead(br); + this.readHead(br); this._raw = br.data; diff --git a/lib/primitives/merkleblock.js b/lib/primitives/merkleblock.js index 42f2d03d..b5c628ed 100644 --- a/lib/primitives/merkleblock.js +++ b/lib/primitives/merkleblock.js @@ -32,12 +32,12 @@ function MerkleBlock(options) { AbstractBlock.call(this); + this.txs = []; this.hashes = []; this.flags = DUMMY; this.totalTX = 0; - this.tree = null; - this.txs = []; + this._tree = null; if (options) this.fromOptions(options); @@ -63,15 +63,20 @@ MerkleBlock.prototype.fromOptions = function fromOptions(options) { for (let hash of options.hashes) { if (typeof hash === 'string') hash = Buffer.from(hash, 'hex'); + assert(Buffer.isBuffer(hash)); this.hashes.push(hash); } } - if (options.flags) + if (options.flags) { + assert(Buffer.isBuffer(options.flags)); this.flags = options.flags; + } - if (options.totalTX != null) + if (options.totalTX != null) { + assert(util.isUInt32(options.totalTX)); this.totalTX = options.totalTX; + } return this; }; @@ -92,8 +97,14 @@ MerkleBlock.fromOptions = function fromOptions(data) { */ MerkleBlock.prototype.refresh = function refresh(all) { - this.tree = null; - this._refresh(all); + this._refresh(); + this._tree = null; + + if (!all) + return; + + for (let tx of this.txs) + tx.refresh(); }; /** @@ -161,7 +172,7 @@ MerkleBlock.prototype.checkBody = function checkBody() { if (tree.root !== this.merkleRoot) return [false, 'bad-txnmrklroot', 100]; - return [true, 'valid', 100]; + return [true, 'valid', 0]; }; /** @@ -171,14 +182,14 @@ MerkleBlock.prototype.checkBody = function checkBody() { */ MerkleBlock.prototype.getTree = function getTree() { - if (!this.tree) { + if (!this._tree) { try { - this.tree = this.extractTree(); + this._tree = this.extractTree(); } catch (e) { - this.tree = new PartialTree(); + this._tree = new PartialTree(); } } - return this.tree; + return this._tree; }; /** @@ -380,7 +391,7 @@ MerkleBlock.prototype.toRaw = function toRaw() { MerkleBlock.prototype.fromReader = function fromReader(br) { let count; - this.parseHead(br); + this.readHead(br); this.totalTX = br.readU32();