From a3720bd42d1dceba5b81c37a9cf3d78c5c5807a7 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Thu, 7 Apr 2016 02:03:38 -0700 Subject: [PATCH] more accurate segwit4 verification. misc. --- bin/node | 2 +- bin/spvnode | 2 +- lib/bcoin/block.js | 28 +++++++++++++++++++++++---- lib/bcoin/chain.js | 39 ++++++++++++++++++++++++------------- lib/bcoin/env.js | 45 +++++++++++++++---------------------------- lib/bcoin/miner.js | 30 +++++++++++++++++++++-------- lib/bcoin/profiler.js | 6 ------ lib/bcoin/utils.js | 13 ++++++++++++- 8 files changed, 102 insertions(+), 63 deletions(-) diff --git a/bin/node b/bin/node index 0ddd1e39..56a4192a 100755 --- a/bin/node +++ b/bin/node @@ -1,6 +1,6 @@ #!/usr/bin/env node -var bcoin = require('bcoin')({ debug: true }); +var bcoin = require('bcoin')({ debug: true, debugFile: true }); var utils = bcoin.utils; var assert = utils.assert; diff --git a/bin/spvnode b/bin/spvnode index 3abfc9e1..d3c00979 100755 --- a/bin/spvnode +++ b/bin/spvnode @@ -1,6 +1,6 @@ #!/usr/bin/env node -var bcoin = require('bcoin')({ debug: true }); +var bcoin = require('bcoin')({ debug: true, debugFile: true }); var utils = bcoin.utils; var assert = utils.assert; diff --git a/lib/bcoin/block.js b/lib/bcoin/block.js index 0efbc38f..7b612b12 100644 --- a/lib/bcoin/block.js +++ b/lib/bcoin/block.js @@ -148,9 +148,8 @@ Block.prototype.getMerkleRoot = function getMerkleRoot(enc) { Block.prototype.getCommitmentHash = function getCommitmentHash(enc) { var leaves = []; - var i, witnessNonce, witnessRoot, commitmentHash; - - witnessNonce = this.txs[0].inputs[0].witness.items[0]; + var witnessNonce = this.witnessNonce; + var i, witnessRoot, commitmentHash; if (!witnessNonce) return; @@ -170,6 +169,24 @@ Block.prototype.getCommitmentHash = function getCommitmentHash(enc) { : commitmentHash; }; +Block.prototype.__defineGetter__('witnessNonce', function() { + var coinbase = this.txs[0]; + + if (!coinbase) + return; + + if (coinbase.inputs.length !== 1) + return; + + if (coinbase.inputs[0].witness.items.length !== 1) + return; + + if (coinbase.inputs[0].witness.items[0].length !== 32) + return; + + return coinbase.inputs[0].witness.items[0]; +}); + Block.prototype.__defineGetter__('commitmentHash', function() { var coinbase, i, commitment, commitmentHash; @@ -178,6 +195,9 @@ Block.prototype.__defineGetter__('commitmentHash', function() { coinbase = this.txs[0]; + if (!coinbase) + return; + for (i = 0; i < coinbase.outputs.length; i++) { commitment = coinbase.outputs[i].script; if (commitment.isCommitment()) { @@ -211,7 +231,7 @@ Block.prototype._verify = function _verify(ret) { } // First TX must be a coinbase - if (!this.txs.length || !this.txs[0].isCoinbase()) { + if (this.txs.length === 0 || !this.txs[0].isCoinbase()) { ret.reason = 'bad-cb-missing'; ret.score = 100; return false; diff --git a/lib/bcoin/chain.js b/lib/bcoin/chain.js index 68d6e672..8daf42a7 100644 --- a/lib/bcoin/chain.js +++ b/lib/bcoin/chain.js @@ -405,7 +405,7 @@ Chain.prototype._verify = function _verify(block, prev, callback) { var flags = constants.flags.MANDATORY_VERIFY_FLAGS; var lockFlags = constants.flags.MANDATORY_LOCKTIME_FLAGS; var height, ts, i, tx, coinbaseHeight; - var medianTime, segwit; + var medianTime, segwit, commitmentHash; var ret = {}; function done(err, result) { @@ -492,12 +492,14 @@ Chain.prototype._verify = function _verify(block, prev, callback) { // Segregrated witness is now usable if (network.type === 'segnet3' && height >= network.segwitHeight) { - if (block.version >= 5 && prev.isUpgraded(5)) { - flags |= constants.flags.VERIFY_WITNESS; - segwit = true; - self.segwitActive = true; - } else if (block.version >= 5) { - self.segwitActive = false; + if (block.version >= 5) { + if (prev.isUpgraded(5)) { + flags |= constants.flags.VERIFY_WITNESS; + segwit = true; + self.segwitActive = true; + } else { + self.segwitActive = false; + } } } @@ -536,13 +538,24 @@ Chain.prototype._verify = function _verify(block, prev, callback) { } if (segwit) { - if (block.commitmentHash !== block.getCommitmentHash('hex')) { - return done(new VerifyError(block, - 'invalid', - 'bad-blk-wit-length', - 100)); + commitmentHash = block.commitmentHash; + if (commitmentHash) { + if (!block.witnessNonce) { + return done(new VerifyError(block, + 'invalid', + 'bad-witness-merkle-size', + 100)); + } + if (commitmentHash !== block.getCommitmentHash('hex')) { + return done(new VerifyError(block, + 'invalid', + 'bad-witness-merkle-match', + 100)); + } } - } else { + } + + if (!commitmentHash) { if (block.hasWitness()) { return done(new VerifyError(block, 'invalid', diff --git a/lib/bcoin/env.js b/lib/bcoin/env.js index ca8865c2..af184939 100644 --- a/lib/bcoin/env.js +++ b/lib/bcoin/env.js @@ -31,6 +31,9 @@ function Environment(options) { this.options = options; + this._ensured = false; + this._debug = null; + this.isBrowser = (typeof process !== 'undefined' && process.browser) || typeof window !== 'undefined'; @@ -46,9 +49,6 @@ function Environment(options) { this.debugFile = options.debugFile; - if (options.debugFile == null) - this.debugFile = true; - if (process.env.BCOIN_DEBUGFILE != null) { if (process.env.BCOIN_DEBUGFILE === '0' || process.env.BCOIN_DEBUGFILE === '1') { @@ -66,11 +66,6 @@ function Environment(options) { if (process.env.BCOIN_PROFILE != null) this.profile = +process.env.BCOIN_PROFILE === 1; - this.fresh = options.fresh; - - if (process.env.BCOIN_FRESH != null) - this.fresh = +process.env.BCOIN_FRESH === 1; - this.useWorkers = options.useWorkers; if (process.env.BCOIN_USE_WORKERS != null) @@ -127,13 +122,13 @@ function Environment(options) { this.chain = require('./chain')(this); this.mempool = require('./mempool')(this); this.keypair = require('./keypair')(this); + this.hd = require('./hd')(this); this.address = require('./address')(this); + this.wallet = require('./wallet')(this); this.walletdb = require('./walletdb')(this); this.provider = this.walletdb.provider; - this.wallet = require('./wallet')(this); this.peer = require('./peer')(this); this.pool = require('./pool')(this); - this.hd = require('./hd')(this); this.miner = require('./miner')(this); this.minerblock = this.miner.minerblock; this.http = require('./http')(this); @@ -151,9 +146,6 @@ Environment.prototype.ensurePrefix = function ensurePrefix() { this._ensured = true; - if (this.fresh && this.prefix.indexOf('bcoin') !== -1) - this.rimraf(this.prefix); - try { fs.statSync(this.prefix); } catch (e) { @@ -161,31 +153,26 @@ Environment.prototype.ensurePrefix = function ensurePrefix() { } }; -Environment.prototype.rimraf = function rimraf(file) { - var cp; - - if (this.isBrowser) - return; - - cp = require('child_' + 'process'); - - assert(typeof file === 'string'); - assert(file !== '/'); - assert(file !== process.env.HOME); - - cp.execFileSync('rm', ['-rf', file], { stdio: 'ignore' }); -}; - Environment.prototype.debug = function debug() { var args = Array.prototype.slice.call(arguments); var msg; + if (this.isBrowser) { + if (this.debugLogs) { + msg = typeof args[0] === 'object' + ? args[0] + : utils.format(args, false).slice(0, -1); + console.error(msg); + } + return; + } + if (this.debugLogs) { msg = utils.format(args, true); process.stderr.write(msg); } - if (this.debugFile && !this.isBrowser) { + if (this.debugFile) { if (!this._debug) { this.ensurePrefix(); this._debug = fs.createWriteStream(this.debugFile, { flags: 'a' }); diff --git a/lib/bcoin/miner.js b/lib/bcoin/miner.js index 7cb863df..540a163f 100644 --- a/lib/bcoin/miner.js +++ b/lib/bcoin/miner.js @@ -44,6 +44,7 @@ function Miner(options) { this.running = false; this.timeout = null; + this.loaded = false; this.block = null; @@ -53,7 +54,10 @@ function Miner(options) { utils.inherits(Miner, EventEmitter); Miner.prototype.open = function open(callback) { - return utils.nextTick(callback); + if (this.loaded) + return utils.nextTick(callback); + + return this.once('open', callback); }; Miner.prototype.close = @@ -105,6 +109,13 @@ Miner.prototype._init = function _init() { stat.height, stat.best); }); + + this.chain.open(function(err) { + if (err) + return self.emit('error', err); + self.loaded = true; + self.emit('open'); + }); }; Miner.prototype.start = function start() { @@ -263,7 +274,7 @@ function MinerBlock(options) { this.coinbase.addInput({ prevout: { - hash: utils.toHex(constants.zeroHash), + hash: constants.nullHash, index: 0xffffffff }, coin: null, @@ -283,7 +294,7 @@ function MinerBlock(options) { // miner succeeded. new Buffer(options.coinbaseFlags, 'ascii') ]), - witness: new bcoin.script.witness([]), + witness: new bcoin.script.witness(), sequence: 0xffffffff }); @@ -296,10 +307,11 @@ function MinerBlock(options) { this.block = new bcoin.block({ version: options.version, prevBlock: this.tip.hash, - merkleRoot: constants.zeroHash, + merkleRoot: constants.nullHash, ts: Math.max(utils.now(), this.tip.ts + 1), bits: options.target, - nonce: 0 + nonce: 0, + height: this.height }); this.block.txs.push(this.coinbase); @@ -311,7 +323,7 @@ function MinerBlock(options) { this.witnessNonce = utils.dsha256(new Buffer(this.tip.hash, 'hex')); this.coinbase.inputs[0].witness.items[0] = this.witnessNonce; this.coinbase.addOutput({ - script: new bcoin.script([]), + script: new bcoin.script(), value: new bn(0) }); } @@ -478,8 +490,10 @@ MinerBlock.prototype.mineAsync = function mine(callback) { }; MinerBlock.prototype.destroy = function destroy() { - clearTimeout(this.timeout); - this.timeout = null; + if (this.timeout) { + clearTimeout(this.timeout); + this.timeout = null; + } this.block = null; }; diff --git a/lib/bcoin/profiler.js b/lib/bcoin/profiler.js index 35088b82..da6c089d 100644 --- a/lib/bcoin/profiler.js +++ b/lib/bcoin/profiler.js @@ -18,12 +18,6 @@ if (bcoin.profile && !bcoin.isBrowser) { fs = require('f' + 's'); } -if (profiler) { - utils.nextTick(function() { - bcoin.debug('Starting node with profiler enabled.'); - }); -} - /** * Profile */ diff --git a/lib/bcoin/utils.js b/lib/bcoin/utils.js index 370d0e42..c01b01b0 100644 --- a/lib/bcoin/utils.js +++ b/lib/bcoin/utils.js @@ -708,7 +708,18 @@ utils.format = function format(args, color) { utils.print = function print() { var args = Array.prototype.slice.call(arguments); - return process.stdout.write(utils.format(args, true)); + var msg; + + if (utils.isBrowser) { + msg = typeof args[0] === 'object' + ? args[0] + : utils.format(args, false).slice(0, -1); + console.log(msg); + return; + } + + msg = utils.format(args, true); + process.stdout.write(msg); }; utils.merge = function merge(target) {