From 4cea71fe65e2a03194654b7087a30d393d4917c4 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Wed, 24 Feb 2016 20:05:06 -0800 Subject: [PATCH] keep all pending blocks off the heap. --- lib/bcoin.js | 1 + lib/bcoin/chain.js | 57 +++++++++++++++++++++--------------- lib/bcoin/compactblock.js | 51 ++++++++++++++++++++++++++++++++ lib/bcoin/peer.js | 2 +- lib/bcoin/protocol/parser.js | 55 +++++++++++++++++++++++++++++++++- 5 files changed, 141 insertions(+), 25 deletions(-) create mode 100644 lib/bcoin/compactblock.js diff --git a/lib/bcoin.js b/lib/bcoin.js index 4db9177b..17c0312c 100644 --- a/lib/bcoin.js +++ b/lib/bcoin.js @@ -68,6 +68,7 @@ bcoin.tx = require('./bcoin/tx'); bcoin.mtx = require('./bcoin/mtx'); bcoin.txpool = require('./bcoin/tx-pool'); bcoin.abstractblock = require('./bcoin/abstractblock'); +bcoin.compactblock = require('./bcoin/compactblock'); bcoin.block = require('./bcoin/block'); bcoin.merkleblock = require('./bcoin/merkleblock'); bcoin.headers = require('./bcoin/headers'); diff --git a/lib/bcoin/chain.js b/lib/bcoin/chain.js index 8095a643..5e9ca44b 100644 --- a/lib/bcoin/chain.js +++ b/lib/bcoin/chain.js @@ -456,6 +456,9 @@ Chain.prototype._verify = function _verify(block, prev) { var flags = constants.flags.MANDATORY_VERIFY_FLAGS; var height, ts, i, tx, cb, coinbaseHeight, medianTime, locktimeMedian; + if (!block.verify()) + return flags; + // Skip the genesis block if (block.isGenesis()) return flags; @@ -1018,7 +1021,7 @@ Chain.prototype.add = function add(initial, peer, callback, force) { (function next(block) { var hash = block.hash('hex'); var prevHash = block.prevBlock; - var prevHeight, height, entry, checkpoint, prev, orphan; + var prevHeight, height, checkpoint, prev, orphan; // Find the previous block height/index. prevHeight = self.db.getHeight(prevHash); @@ -1115,24 +1118,12 @@ Chain.prototype.add = function add(initial, peer, callback, force) { return done(); } - // Create a new chain entry. - entry = new bcoin.chainblock(self, { - hash: hash, - version: block.version, - prevBlock: prevHash, - merkleRoot: block.merkleRoot, - ts: block.ts, - bits: block.bits, - nonce: block.nonce, - height: prevHeight + 1 - }); - // Verify the checkpoint. - checkpoint = network.checkpoints[entry.height]; + checkpoint = network.checkpoints[height]; if (checkpoint) { self.emit('checkpoint', block, { - height: entry.height, - hash: entry.hash, + height: height, + hash: hash, checkpoint: checkpoint }, peer); @@ -1142,14 +1133,14 @@ Chain.prototype.add = function add(initial, peer, callback, force) { // so we don't do it. The misbehaving peer has // been killed and hopefully we find a peer // who isn't trying to fool us. - if (entry.hash !== checkpoint) { + if (hash !== checkpoint) { self.purgeOrphans(); self.purgePending(); self.emit('fork', block, { - height: entry.height, + height: height, expected: checkpoint, - received: entry.hash, + received: hash, checkpoint: true }, peer); @@ -1171,26 +1162,46 @@ Chain.prototype.add = function add(initial, peer, callback, force) { assert(prev); + if (block.type === 'compactblock') { + block = block.toBlock(peer); + if (!block) + return done(new Error('Failed to parse block.')); + if (block._raw === initial._raw) + initial = block; + } + // Do "contextual" verification on our block // now that we're certain its previous // block is in the chain. self._verifyContext(block, prev, function(err, verified) { - var existing; + var entry, existing; if (err) return done(err); if (!verified) { - self.invalid[entry.hash] = true; + self.invalid[hash] = true; self.emit('invalid', block, { - height: entry.height, - hash: entry.hash, + height: height, + hash: hash, seen: false, chain: false }, peer); return done(); } + // Create a new chain entry. + entry = new bcoin.chainblock(self, { + hash: hash, + version: block.version, + prevBlock: block.prevBlock, + merkleRoot: block.merkleRoot, + ts: block.ts, + bits: block.bits, + nonce: block.nonce, + height: height + }); + // Real fork resolution would just be this. // if (entry.chainwork.cmp(self.tip.chainwork) > 0) // return self.setBestChain(entry); diff --git a/lib/bcoin/compactblock.js b/lib/bcoin/compactblock.js new file mode 100644 index 00000000..bb2c2dc0 --- /dev/null +++ b/lib/bcoin/compactblock.js @@ -0,0 +1,51 @@ +/** + * compactblock.js - compact block object for bcoin + * Copyright (c) 2014-2015, Fedor Indutny (MIT License) + * https://github.com/indutny/bcoin + */ + +var bcoin = require('../bcoin'); +var bn = require('bn.js'); +var utils = bcoin.utils; +var assert = utils.assert; +var constants = bcoin.protocol.constants; +var network = bcoin.protocol.network; + +/** + * CompactBlock + */ + +function CompactBlock(data) { + var self = this; + + if (!(this instanceof CompactBlock)) + return new CompactBlock(data); + + bcoin.abstractblock.call(this, data); + + this.type = 'compactblock'; + this.coinbaseHeight = data.coinbaseHeight; +} + +utils.inherits(CompactBlock, bcoin.abstractblock); + +CompactBlock.prototype._verify = function _verify() { + return this.verifyHeaders(); +}; + +CompactBlock.prototype.getCoinbaseHeight = function getCoinbaseHeight() { + return this.coinbaseHeight; +}; + +CompactBlock.prototype.toBlock = function toBlock(peer) { + var block = peer.parser.parseBlock(this._raw); + if (!block) + return; + return new bcoin.block(block); +}; + +/** + * Expose + */ + +module.exports = CompactBlock; diff --git a/lib/bcoin/peer.js b/lib/bcoin/peer.js index ed25cb0f..db99bbbf 100644 --- a/lib/bcoin/peer.js +++ b/lib/bcoin/peer.js @@ -415,7 +415,7 @@ Peer.prototype._onPacket = function onPacket(packet) { return this._handleReject(payload); if (cmd === 'block') { - payload = bcoin.block(payload); + payload = bcoin.compactblock(payload); } else if (cmd === 'merkleblock') { payload = bcoin.merkleblock(payload); this.lastBlock = payload; diff --git a/lib/bcoin/protocol/parser.js b/lib/bcoin/protocol/parser.js index d9c09802..cd12258a 100644 --- a/lib/bcoin/protocol/parser.js +++ b/lib/bcoin/protocol/parser.js @@ -123,8 +123,11 @@ Parser.prototype.parsePayload = function parsePayload(cmd, p) { if (cmd === 'headers') return this.parseHeaders(p); + // if (cmd === 'block') + // return this.parseBlock(p); + if (cmd === 'block') - return this.parseBlock(p); + return this.parseBlockCompact(p); if (cmd === 'tx') return this.parseTX(p); @@ -358,6 +361,56 @@ Parser.prototype.parseBlock = function parseBlock(p) { }; }; +Parser.prototype.parseBlockCompact = function parseBlockCompact(p) { + var height = -1; + var i, result, off, totalTX, tx; + var inCount, input, s, version; + + if (p.length < 81) + return this._error('Invalid block size'); + + version = utils.read32(p, 0); + + result = utils.readIntv(p, 80); + off = result.off; + totalTX = result.r; + + if (version > 1 && totalTX > 0) { + if (p.length < off + 10) + return this._error('Invalid tx size'); + + inCount = utils.readIntv(p, off + 4); + off = inCount.off; + inCount = inCount.r; + + if (inCount > 0) { + input = this.parseInput(p.slice(off)); + if (!input) + return this._error('Invalid tx count for block'); + } + } + + if (input) { + s = bcoin.script.decode(input.script); + if (Buffer.isBuffer(s[0])) + height = bcoin.script.num(s[0], true); + } + + return { + version: version, + prevBlock: p.slice(4, 36), + merkleRoot: p.slice(36, 68), + ts: utils.readU32(p, 68), + bits: utils.readU32(p, 72), + nonce: utils.readU32(p, 76), + totalTX: totalTX, + coinbaseHeight: height, + txs: [], + _raw: p, + _size: p.length + }; +}; + Parser.prototype.parseInput = function parseInput(p) { var scriptLen, off;