From 62ef5ea7f4843db3ab335bc92822801fd640dae0 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Sun, 6 Nov 2016 22:34:05 -0800 Subject: [PATCH] bip152: implement segwit compact blocks. --- lib/chain/chain.js | 2 +- lib/net/bip152.js | 27 ++++++++++++++++----------- lib/net/peer.js | 29 ++++++++++++++++------------- test/block-test.js | 16 ++++++++-------- 4 files changed, 41 insertions(+), 33 deletions(-) diff --git a/lib/chain/chain.js b/lib/chain/chain.js index 35c3ba49..cf447400 100644 --- a/lib/chain/chain.js +++ b/lib/chain/chain.js @@ -1317,7 +1317,7 @@ Chain.prototype.finish = function finish(block, entry) { time = elapsed[0] * 1000 + elapsed[1] / 1e6; this.logger.info( - 'Block %s (%d) added to chain (size=%d, txs=%d time=%d).', + 'Block %s (%d) added to chain (size=%d txs=%d time=%d).', entry.rhash, entry.height, block.getSize(), diff --git a/lib/net/bip152.js b/lib/net/bip152.js index 3eb866fe..e015ab32 100644 --- a/lib/net/bip152.js +++ b/lib/net/bip152.js @@ -183,13 +183,20 @@ CompactBlock.prototype.toRequest = function toRequest() { return TXRequest.fromCompact(this); }; -CompactBlock.prototype.fillMempool = function fillMempool(mempool) { +CompactBlock.prototype.fillMempool = function fillMempool(witness, mempool) { var have = {}; var hashes = mempool.getSnapshot(); var i, id, index, hash, tx; for (i = 0; i < hashes.length; i++) { hash = hashes[i]; + tx = mempool.getTX(hash); + assert(tx); + hash = tx.hash(); + + if (witness) + hash = tx.witnessHash(); + id = this.sid(hash); index = this.idMap[id]; @@ -203,11 +210,6 @@ CompactBlock.prototype.fillMempool = function fillMempool(mempool) { continue; } - tx = mempool.getTX(hash); - - if (!tx) - continue; - this.available[index] = tx; have[index] = true; this.count++; @@ -334,8 +336,8 @@ CompactBlock.prototype.toBlock = function toBlock() { return block; }; -CompactBlock.prototype.fromBlock = function fromBlock(block, nonce) { - var i, tx, id; +CompactBlock.prototype.fromBlock = function fromBlock(block, witness, nonce) { + var i, tx, hash, id; this.version = block.version; this.prevBlock = block.prevBlock; @@ -354,7 +356,10 @@ CompactBlock.prototype.fromBlock = function fromBlock(block, nonce) { for (i = 1; i < block.txs.length; i++) { tx = block.txs[i]; - id = this.sid(tx.hash()); + hash = tx.hash(); + if (witness) + hash = tx.witnessHash(); + id = this.sid(hash); this.ids.push(id); } @@ -363,8 +368,8 @@ CompactBlock.prototype.fromBlock = function fromBlock(block, nonce) { return this; }; -CompactBlock.fromBlock = function fromBlock(block, nonce) { - return new CompactBlock().fromBlock(block, nonce); +CompactBlock.fromBlock = function fromBlock(block, witness, nonce) { + return new CompactBlock().fromBlock(block, witness, nonce); }; CompactBlock.prototype.wait = function wait(time) { diff --git a/lib/net/peer.js b/lib/net/peer.js index 501928a1..ec5da013 100644 --- a/lib/net/peer.js +++ b/lib/net/peer.js @@ -106,6 +106,7 @@ function Peer(pool, addr, socket) { this.syncSent = false; this.connectTimeout = null; this.compactMode = null; + this.compactWitness = false; this.compactBlocks = {}; this.sentAddr = false; this.bip151 = null; @@ -523,7 +524,7 @@ Peer.prototype.announce = co(function* announce(items) { if (item instanceof Block) { if (!this.invFilter.added(item.hash())) continue; - yield this._sendCompactBlock(item, false); + yield this._sendCompactBlock(item, this.compactWitness); continue; } } @@ -1543,12 +1544,12 @@ Peer.prototype._getItem = co(function* _getItem(item) { * @returns {Boolean} */ -Peer.prototype._sendBlock = co(function* _sendBlock(item) { +Peer.prototype._sendBlock = co(function* _sendBlock(item, witness) { var block = this._getBroadcasted(item); // Check for a broadcasted item first. if (block) { - yield this.send(new packets.BlockPacket(block, item.hasWitness())); + yield this.send(new packets.BlockPacket(block, witness)); return true; } @@ -1560,7 +1561,7 @@ Peer.prototype._sendBlock = co(function* _sendBlock(item) { // If we have the same serialization, we // can write the raw binary to the socket. - if (item.hasWitness() === !!this.options.witness) { + if (witness === !!this.options.witness) { block = yield this.chain.db.getRawBlock(item.hash); if (!block) @@ -1573,7 +1574,7 @@ Peer.prototype._sendBlock = co(function* _sendBlock(item) { if (!block) return false; - yield this.send(new packets.BlockPacket(block, item.hasWitness())); + yield this.send(new packets.BlockPacket(block, witness)); } return true; @@ -1581,7 +1582,8 @@ Peer.prototype._sendBlock = co(function* _sendBlock(item) { /** * Send a compact block. - * @param {InvItem} item + * @param {Block} block + * @param {Boolean} witness * @returns {Boolean} */ @@ -1590,7 +1592,7 @@ Peer.prototype._sendCompactBlock = function _sendCompactBlock(block, witness) { // if we get a siphash collision. for (;;) { try { - block = BIP152.CompactBlock.fromBlock(block); + block = BIP152.CompactBlock.fromBlock(block, witness); } catch (e) { continue; } @@ -1643,7 +1645,7 @@ Peer.prototype._handleGetData = co(function* _handleGetData(packet) { switch (item.type) { case constants.inv.BLOCK: case constants.inv.WITNESS_BLOCK: - result = yield this._sendBlock(item); + result = yield this._sendBlock(item, item.hasWitness()); if (!result) { notFound.push(item); continue; @@ -1676,7 +1678,7 @@ Peer.prototype._handleGetData = co(function* _handleGetData(packet) { case constants.inv.CMPCT_BLOCK: // Fallback to full block. if (block.height < this.chain.tip.height - 10) { - result = yield this._sendBlock(item); + result = yield this._sendBlock(item, this.compactWitness); if (!result) { notFound.push(item); continue; @@ -1691,7 +1693,7 @@ Peer.prototype._handleGetData = co(function* _handleGetData(packet) { continue; } - yield this._sendCompactBlock(block, false); + yield this._sendCompactBlock(block, this.compactWitness); break; default: @@ -2099,7 +2101,7 @@ Peer.prototype._handleUnknown = function _handleUnknown(packet) { */ Peer.prototype._handleSendCmpct = function _handleSendCmpct(packet) { - if (packet.version !== 1) { + if (packet.version > 2) { // Ignore this.logger.info('Peer request compact blocks version %d (%s).', packet.version, this.hostname); @@ -2115,6 +2117,7 @@ Peer.prototype._handleSendCmpct = function _handleSendCmpct(packet) { this.logger.info('Peer initialized compact blocks (%s).', this.hostname); this.compactMode = packet; + this.compactWitness = packet.version === 2; this.fire('sendcmpct', packet); }; @@ -2148,7 +2151,7 @@ Peer.prototype._handleCmpctBlock = co(function* _handleCmpctBlock(packet) { this.compactBlocks[hash] = block; - result = block.fillMempool(this.mempool); + result = block.fillMempool(this.options.witness, this.mempool); if (result) { delete this.compactBlocks[hash]; @@ -2216,7 +2219,7 @@ Peer.prototype._handleGetBlockTxn = co(function* _handleGetBlockTxn(packet) { res = BIP152.TXResponse.fromBlock(block, req); - yield this.send(new packets.BlockTxnPacket(res, false)); + yield this.send(new packets.BlockTxnPacket(res, this.compactWitness)); this.fire('blocktxn', req); }); diff --git a/test/block-test.js b/test/block-test.js index 5e4f2111..a3bc565e 100644 --- a/test/block-test.js +++ b/test/block-test.js @@ -215,7 +215,7 @@ describe('Block', function() { it('should handle compact block', function(cb) { var cblock = bip152.CompactBlock.fromRaw(cmpct[0], 'hex'); var block = bcoin.block.fromRaw(cmpct[1], 'hex'); - var cblock2 = bip152.CompactBlock.fromBlock(block, cblock.keyNonce); + var cblock2 = bip152.CompactBlock.fromBlock(block, false, cblock.keyNonce); var map = {}; assert.equal(cblock.toRaw().toString('hex'), cmpct[0]); @@ -237,7 +237,7 @@ describe('Block', function() { assert.equal(cblock.sid(block.txs[1].hash()), 125673511480291); - var result = cblock.fillMempool(fakeMempool); + var result = cblock.fillMempool(false, fakeMempool); assert(result); for (var i = 0; i < cblock.available.length; i++) assert(cblock.available[i]); @@ -248,7 +248,7 @@ describe('Block', function() { it('should handle half-full compact block', function(cb) { var cblock = bip152.CompactBlock.fromRaw(cmpct[0], 'hex'); var block = bcoin.block.fromRaw(cmpct[1], 'hex'); - var cblock2 = bip152.CompactBlock.fromBlock(block, cblock.keyNonce); + var cblock2 = bip152.CompactBlock.fromBlock(block, false, cblock.keyNonce); var map = {}; assert.equal(cblock.toRaw().toString('hex'), cmpct[0]); @@ -273,7 +273,7 @@ describe('Block', function() { assert.equal(cblock.sid(block.txs[1].hash()), 125673511480291); - var result = cblock.fillMempool(fakeMempool); + var result = cblock.fillMempool(false, fakeMempool); assert(!result); var req = cblock.toRequest(); @@ -301,7 +301,7 @@ describe('Block', function() { it('should handle compact block', function(cb) { var cblock = bip152.CompactBlock.fromRaw(cmpct2, 'hex'); var block = bcoin.block.fromRaw(cmpct2block); - var cblock2 = bip152.CompactBlock.fromBlock(block, cblock.keyNonce); + var cblock2 = bip152.CompactBlock.fromBlock(block, false, cblock.keyNonce); var map = {}; assert.equal(cblock.toRaw().toString('hex'), cmpct2); @@ -323,7 +323,7 @@ describe('Block', function() { //assert.equal(cblock.sid(block.txs[1].hash()), 125673511480291); - var result = cblock.fillMempool(fakeMempool); + var result = cblock.fillMempool(false, fakeMempool); assert(result); for (var i = 0; i < cblock.available.length; i++) assert(cblock.available[i]); @@ -334,7 +334,7 @@ describe('Block', function() { it('should handle half-full compact block', function(cb) { var cblock = bip152.CompactBlock.fromRaw(cmpct2, 'hex'); var block = bcoin.block.fromRaw(cmpct2block); - var cblock2 = bip152.CompactBlock.fromBlock(block, cblock.keyNonce); + var cblock2 = bip152.CompactBlock.fromBlock(block, false, cblock.keyNonce); var map = {}; assert.equal(cblock.toRaw().toString('hex'), cmpct2); @@ -359,7 +359,7 @@ describe('Block', function() { //assert.equal(cblock.sid(block.txs[1].hash()), 125673511480291); - var result = cblock.fillMempool(fakeMempool); + var result = cblock.fillMempool(false, fakeMempool); assert(!result); var req = cblock.toRequest();