diff --git a/lib/bcoin/block.js b/lib/bcoin/block.js index 8e82aa34..3f123108 100644 --- a/lib/bcoin/block.js +++ b/lib/bcoin/block.js @@ -419,7 +419,7 @@ Block.prototype.toCompact = function toCompact() { p.writeVarint(this.txs.length); this.txs.forEach(function(tx) { - p.writeBytes(tx.hash()); + p.writeHash(tx.hash()); }); return p.render(); diff --git a/lib/bcoin/chain.js b/lib/bcoin/chain.js index c3f22fe7..51558ec5 100644 --- a/lib/bcoin/chain.js +++ b/lib/bcoin/chain.js @@ -43,6 +43,7 @@ function Chain(node, options) { this.tip = null; this.height = -1; this.segwitActive = null; + this.spv = !!options.spv; this.orphan = { map: {}, @@ -241,7 +242,7 @@ Chain.prototype._preload = function _preload(callback) { if (!this.options.preload) return callback(); - if (!this.options.spv) + if (!this.spv) return callback(); if (network.type !== 'main') @@ -478,7 +479,7 @@ Chain.prototype._verify = function _verify(block, prev, callback) { // Only allow version 5 blocks (segwit) // once the majority of blocks are using it. - if (network.segwitHeight !== -1 && height >= network.segwitHeight) { + if (network.type === 'segnet3' && height >= network.segwitHeight) { if (block.version < 5 && prev.isOutdated(5)) return done(new VerifyError(block, 'obsolete', 'bad-version', 0)); } @@ -591,7 +592,7 @@ Chain.prototype._checkDuplicates = function _checkDuplicates(block, prev, callba var self = this; var height = prev.height + 1; - if (this.options.spv || block.type !== 'block') + if (this.spv || block.type !== 'block') return callback(); if (block.isGenesis()) @@ -628,7 +629,7 @@ Chain.prototype._checkInputs = function _checkInputs(block, prev, flags, callbac var scriptCheck = true; var historical = false; - if (this.options.spv || block.type !== 'block') + if (this.spv || block.type !== 'block') return callback(); if (block.isGenesis()) @@ -1551,31 +1552,7 @@ Chain.prototype.getLocator = function getLocator(start, callback, force) { start = start.hash('hex'); } - function getTop(callback) { - if (typeof start === 'string') { - return self.db.getHeight(start, function(err, top) { - if (err) - return callback(err); - - if (top === -1) { - // We could simply `return [start]` here, - // but there is no standardized "spacing" - // for locator hashes. Pretend this hash - // is our tip. This is useful for getheaders - // when not using headers-first. - hashes.push(start); - top = self.height; - } - - return callback(null, top); - }); - } else if (typeof start === 'number') { - top = start; - } - return callback(null, top); - } - - return getTop(function(err, top) { + function build(err, top) { if (err) return callback(err); @@ -1611,7 +1588,31 @@ Chain.prototype.getLocator = function getLocator(start, callback, force) { return callback(err); return callback(null, hashes); }); - }); + } + + if (typeof start === 'string') { + return self.db.getHeight(start, function(err, top) { + if (err) + return build(err); + + if (top === -1) { + // We could simply `return [start]` here, + // but there is no standardized "spacing" + // for locator hashes. Pretend this hash + // is our tip. This is useful for getheaders + // when not using headers-first. + hashes.push(start); + top = self.height; + } + + return build(null, top); + }); + } + + if (typeof start === 'number') + top = start; + + return build(null, top); }; Chain.prototype.getOrphanRoot = function getOrphanRoot(hash) { @@ -1662,7 +1663,7 @@ Chain.prototype.getTargetAsync = function getTargetAsync(last, block, callback) Chain.prototype.getTarget = function getTarget(last, block, ancestors) { var powLimit = utils.toCompact(network.powLimit); - var ts, first, i, prev; + var ts, first, i; // Genesis if (!last) @@ -1680,14 +1681,11 @@ Chain.prototype.getTarget = function getTarget(last, block, ancestors) { return powLimit; i = 1; - prev = ancestors; - while (prev[i] + while (ancestors[i] && last.height % network.powDiffInterval !== 0 && last.bits === powLimit) { - last = prev[i++]; + last = ancestors[i++]; } - - return last.bits; } return last.bits; } @@ -1744,7 +1742,6 @@ Chain.prototype.findLocator = function findLocator(locator, callback) { }, callback); }; -// https://github.com/bitcoin/bitcoin/pull/7648/files Chain.prototype.getState = function getState(prev, id, callback) { var self = this; var period = network.minerConfirmationWindow; @@ -1957,15 +1954,15 @@ Chain.prototype.isSegwitActive = function isSegwitActive(callback, force) { if (err) return callback(err); - if (!prev.isUpgraded(5)) { + if (self.tip.version >= 5 && prev.isUpgraded(5)) { prev.free(); - self.segwitActive = false; - return callback(null, false); + self.segwitActive = true; + return callback(null, true); } prev.free(); - self.segwitActive = true; - return callback(null, true); + self.segwitActive = false; + return callback(null, false); }); }); }; diff --git a/lib/bcoin/chaindb.js b/lib/bcoin/chaindb.js index a46b1121..0d473bd6 100644 --- a/lib/bcoin/chaindb.js +++ b/lib/bcoin/chaindb.js @@ -39,6 +39,7 @@ function ChainDB(chain, options) { this.keepBlocks = options.keepBlocks || 288; this.prune = !!options.prune; + this.spv = !!options.spv; // Need to cache up to the retarget interval // if we're going to be checking the damn @@ -65,8 +66,8 @@ ChainDB.prototype._init = function _init() { if (this.loaded) return; - this.db = bcoin.ldb((this.options.spv ? 'spv' : '') + 'chain', { - compression: false, + this.db = bcoin.ldb(this.spv ? 'spvchain' : 'chain', { + compression: true, cacheSize: 16 << 20, writeBufferSize: 8 << 20 }); @@ -577,7 +578,7 @@ ChainDB.prototype.has = function has(height, callback) { }; ChainDB.prototype.saveBlock = function saveBlock(block, batch, connect, callback) { - if (this.options.spv) + if (this.spv) return utils.nextTick(callback); batch.put('b/b/' + block.hash('hex'), block.toCompact()); @@ -595,7 +596,7 @@ ChainDB.prototype.saveBlock = function saveBlock(block, batch, connect, callback ChainDB.prototype.removeBlock = function removeBlock(hash, batch, callback) { var self = this; - if (this.options.spv) + if (this.spv) return utils.nextTick(callback); this._ensureHistory(hash, function(err, block) { @@ -618,7 +619,7 @@ ChainDB.prototype.removeBlock = function removeBlock(hash, batch, callback) { ChainDB.prototype.connectBlock = function connectBlock(block, batch, callback) { var self = this; - if (this.options.spv) { + if (this.spv) { self.emit('add block', block); return utils.nextTick(callback); } @@ -689,7 +690,7 @@ ChainDB.prototype.connectBlock = function connectBlock(block, batch, callback) { ChainDB.prototype.disconnectBlock = function disconnectBlock(hash, batch, callback) { var self = this; - if (this.options.spv) + if (this.spv) return utils.nextTick(callback); this._ensureHistory(hash, function(err, block) { @@ -699,8 +700,10 @@ ChainDB.prototype.disconnectBlock = function disconnectBlock(hash, batch, callba if (!block) return callback(); - if (typeof hash === 'string') - assert(block.hash('hex') === hash); + if (self.options.paranoid) { + if (typeof hash === 'string') + assert(block.hash('hex') === hash, 'Database is corrupt.'); + } block.txs.forEach(function(tx) { var hash = tx.hash('hex'); @@ -1040,8 +1043,8 @@ ChainDB.prototype.getTX = function getTX(hash, callback) { return callback(e); } - if (self.options.paranoid && tx.hash('hex') !== hash) - return callback(new Error('ChainDB is corrupt. All is lost.')); + if (self.options.paranoid) + assert(tx.hash('hex') === hash, 'Database is corrupt.'); return callback(null, tx); }); @@ -1234,6 +1237,10 @@ ChainDB.prototype.getBlock = function getBlock(hash, callback) { delete block.hashes; block = new bcoin.block(block); + + if (self.options.paranoid) + assert(block.hash('hex') === hash, 'Database is corrupt.'); + return callback(null, block); }); }); @@ -1251,8 +1258,9 @@ ChainDB.prototype._getTX = function _getTX(hash, callback) { ChainDB.prototype.isUnspentTX = function isUnspentTX(hash, callback) { return callback(null, false); - if (this.options.spv) + if (this.spv) return callback(null, false); + return this.isSpentTX(hash, function(err, spent) { if (err) return callback(err); @@ -1292,7 +1300,7 @@ ChainDB.prototype.isSpentTX = function isSpentTX(hash, callback) { ChainDB.prototype._pruneBlock = function _pruneBlock(block, batch, callback) { var futureHeight; - if (this.options.spv) + if (this.spv) return callback(); if (!this.prune) diff --git a/lib/bcoin/miner.js b/lib/bcoin/miner.js index a2b99f3b..43e641ec 100644 --- a/lib/bcoin/miner.js +++ b/lib/bcoin/miner.js @@ -268,7 +268,7 @@ Miner.prototype.createBlock = function createBlock(callback) { // Set up the witness nonce and // commitment output for segwit. block.witness = true; - block.witnessNonce = utils.nonce().toBuffer(); + block.witnessNonce = utils.nonce().toBuffer('le', 8); coinbase.inputs[0].witness.items[0] = block.witnessNonce; coinbase.addOutput({ script: new bcoin.script([]), diff --git a/lib/bcoin/peer.js b/lib/bcoin/peer.js index f5454f67..9c927f30 100644 --- a/lib/bcoin/peer.js +++ b/lib/bcoin/peer.js @@ -47,6 +47,7 @@ function Peer(pool, options) { this.haveWitness = false; this.hashContinue = null; this.filter = null; + this.relay = true; this.challenge = null; this.lastPong = 0; @@ -229,7 +230,7 @@ Peer.prototype.broadcast = function broadcast(items) { var result = []; var payload = []; - if (this.version && this.version.relay === false) + if (!this.relay) return; if (this.destroyed) @@ -522,24 +523,19 @@ Peer.prototype._handleFilterLoad = function _handleFilterLoad(payload) { this.filter = new bcoin.bloom(size, payload.n, payload.tweak); this.filter.filter = payload.filter; this.filter.update = payload.update; - if (this.version) - this.version.relay = true; + this.relay = true; }; Peer.prototype._handleFilterAdd = function _handleFilterAdd(payload) { if (this.filter) this.filter.add(payload.data); - - if (this.version) - this.version.relay = true; + this.relay = true; }; Peer.prototype._handleFilterClear = function _handleFilterClear(payload) { if (this.filter) this.filter.reset(); - - if (this.version) - this.version.relay = true; + this.relay = true; }; Peer.prototype._handleUTXOs = function _handleUTXOs(payload) { @@ -795,6 +791,9 @@ Peer.prototype._handleVersion = function handleVersion(payload) { if (payload.witness) this.haveWitness = true; + if (payload.relay === false) + this.relay = false; + // ACK this._write(this.framer.verack()); this.version = payload; diff --git a/lib/bcoin/protocol/framer.js b/lib/bcoin/protocol/framer.js index 9606ee9a..9a9a0823 100644 --- a/lib/bcoin/protocol/framer.js +++ b/lib/bcoin/protocol/framer.js @@ -642,7 +642,7 @@ Framer.merkleBlock = function _merkleBlock(block, writer) { var p = new BufferWriter(writer); var i; - p.write32(block.version); + p.writeU32(block.version); p.writeHash(block.prevBlock); p.writeHash(block.merkleRoot); p.writeU32(block.ts); diff --git a/lib/bcoin/protocol/parser.js b/lib/bcoin/protocol/parser.js index 7fe11f41..0f14b70f 100644 --- a/lib/bcoin/protocol/parser.js +++ b/lib/bcoin/protocol/parser.js @@ -375,8 +375,6 @@ Parser.parseVersion = function parseVersion(p) { assert(ts >= 0, 'Timestamp is negative.'); assert(height >= 0, 'Height is negative.'); - this.version = version; - return { version: version, services: services, @@ -476,7 +474,7 @@ Parser.parseMerkleBlock = function parseMerkleBlock(p) { p = new BufferReader(p); p.start(); - version = p.readU32(); // Technically signed + version = p.readU32(); prevBlock = p.readHash(); merkleRoot = p.readHash(); ts = p.readU32(); @@ -735,8 +733,8 @@ Parser.parseCoin = function parseCoin(p, extended) { }; Parser.parseTX = function parseTX(p) { - var inCount, txIn, tx; - var outCount, txOut; + var inCount, inputs; + var outCount, outputs; var version, locktime, i; var raw; @@ -749,20 +747,16 @@ Parser.parseTX = function parseTX(p) { version = p.readU32(); // Technically signed inCount = p.readVarint(); - txIn = new Array(inCount); + inputs = new Array(inCount); for (i = 0; i < inCount; i++) { - tx = Parser.parseInput(p); - - txIn[i] = tx; - txIn[i].witness = { items: [] }; + inputs[i] = Parser.parseInput(p); + inputs[i].witness = { items: [] }; } outCount = p.readVarint(); - txOut = new Array(outCount); - for (i = 0; i < outCount; i++) { - tx = Parser.parseOutput(p); - txOut[i] = tx; - } + outputs = new Array(outCount); + for (i = 0; i < outCount; i++) + outputs[i] = Parser.parseOutput(p); locktime = p.readU32(); @@ -771,8 +765,8 @@ Parser.parseTX = function parseTX(p) { return { version: version, flag: 1, - inputs: txIn, - outputs: txOut, + inputs: inputs, + outputs: outputs, locktime: locktime, _witnessSize: 0, _raw: raw, @@ -796,8 +790,8 @@ Parser.isWitnessTX = function isWitnessTX(p) { }; Parser.parseWitnessTX = function parseWitnessTX(p) { - var inCount, txIn, tx; - var outCount, txOut; + var inCount, inputs, witness; + var outCount, outputs; var marker, flag; var version, locktime, i; var witnessSize = 0; @@ -819,24 +813,20 @@ Parser.parseWitnessTX = function parseWitnessTX(p) { inCount = p.readVarint(); - txIn = new Array(inCount); - for (i = 0; i < inCount; i++) { - tx = Parser.parseInput(p); - txIn[i] = tx; - } + inputs = new Array(inCount); + for (i = 0; i < inCount; i++) + inputs[i] = Parser.parseInput(p); outCount = p.readVarint(); - txOut = new Array(outCount); - for (i = 0; i < outCount; i++) { - tx = Parser.parseOutput(p); - txOut[i] = tx; - } + outputs = new Array(outCount); + for (i = 0; i < outCount; i++) + outputs[i] = Parser.parseOutput(p); for (i = 0; i < inCount; i++) { - tx = Parser.parseWitness(p); - txIn[i].witness = tx; - witnessSize += tx._size; + witness = Parser.parseWitness(p); + inputs[i].witness = witness; + witnessSize += witness._size; } locktime = p.readU32(); @@ -846,8 +836,8 @@ Parser.parseWitnessTX = function parseWitnessTX(p) { return { version: version, flag: flag, - inputs: txIn, - outputs: txOut, + inputs: inputs, + outputs: outputs, locktime: locktime, _raw: raw, _size: raw.length, @@ -898,11 +888,10 @@ Parser.parseReject = function parseReject(p) { ccode = p.readU8(); reason = p.readVarString('ascii'); - try { + if (p.left() >= 32) data = p.readHash(); - } catch (e) { + else data = null; - } return { message: message, @@ -919,7 +908,7 @@ Parser.parseAddress = function parseAddress(p, full) { p = new BufferReader(p); p.start(); - if (full && this.version >= 31402) + if (full) // only version >= 31402 ts = p.readU32(); else ts = 0;