diff --git a/lib/bcoin/block.js b/lib/bcoin/block.js index 62649e66..7c010f61 100644 --- a/lib/bcoin/block.js +++ b/lib/bcoin/block.js @@ -100,47 +100,54 @@ Block.prototype.getMerkleRoot = function getMerkleRoot() { root = utils.getMerkleRoot(leaves); if (!root) - return utils.toHex(constants.zeroHash); + return; return utils.toHex(root); }; -Block.prototype.getWitnessRoot = function getWitnessRoot() { +Block.prototype.getCommitmentHash = function getCommitmentHash() { var leaves = []; - var i, root; + var i, witnessNonce, witnessRoot, commitmentHash; + + witnessNonce = this.txs[0].inputs[0].witness[0]; + + if (!witnessNonce) + return; for (i = 0; i < this.txs.length; i++) leaves.push(this.txs[i].witnessHash()); - root = utils.getMerkleRoot(leaves); + witnessRoot = utils.getMerkleRoot(leaves); - if (!root) - return utils.toHex(constants.zeroHash); + if (!witnessRoot) + return; - return utils.toHex(root); + commitmentHash = utils.dsha256(Buffer.concat([witnessRoot, witnessNonce])); + + return utils.toHex(commitmentHash); }; -Block.prototype.__defineGetter__('witnessRoot', function() { - var coinbase, i, commitment, witnessRoot; +Block.prototype.__defineGetter__('commitmentHash', function() { + var coinbase, i, commitment, witnessNonce, commitmentHash; - if (this._witnessRoot) - return this._witnessRoot; + if (this._commitmentHash) + return this._commitmentHash; - coinbase = block.txs[0]; + coinbase = this.txs[0]; // Find the fucking commitment for segregated shitness for (i = 0; i < coinbase.outputs.length; i++) { commitment = coinbase.outputs[i].script; if (bcoin.script.isCommitment(commitment)) { - witnessRoot = bcoin.script.getWitnessRoot(commitment); + commitmentHash = bcoin.script.getCommitmentHash(commitment); break; } } - if (witnessRoot) - this._witnessRoot = utils.toHex(witnessRoot); + if (commitmentHash) + this._commitmentHash = utils.toHex(commitmentHash); - return this._witnessRoot; + return this._commitmentHash; }); Block.prototype._verify = function _verify() { @@ -296,7 +303,7 @@ Block.prototype.inspect = function inspect() { version: this.version, prevBlock: utils.revHex(this.prevBlock), merkleRoot: utils.revHex(this.merkleRoot), - witnessRoot: utils.revHex(this.witnessRoot), + commitmentHash: this.commitmentHash ? utils.revHex(this.commitmentHash) : null, ts: this.ts, bits: this.bits, nonce: this.nonce, @@ -313,7 +320,7 @@ Block.prototype.toJSON = function toJSON() { version: this.version, prevBlock: utils.revHex(this.prevBlock), merkleRoot: utils.revHex(this.merkleRoot), - witnessRoot: utils.revHex(this.witnessRoot), + commitmentHash: this.commitmentHash ? utils.revHex(this.commitmentHash) : null, ts: this.ts, bits: this.bits, nonce: this.nonce, diff --git a/lib/bcoin/chain.js b/lib/bcoin/chain.js index 894f8e9e..7e6d2f71 100644 --- a/lib/bcoin/chain.js +++ b/lib/bcoin/chain.js @@ -455,7 +455,7 @@ Chain.prototype._verifyContext = function _verifyContext(block, prev, callback) Chain.prototype._verify = function _verify(block, prev) { var flags = constants.flags.MANDATORY_VERIFY_FLAGS; var height, ts, i, tx, cb, coinbaseHeight, medianTime; - var locktimeMedian, coinbase, commitment, segwit, witnessRoot; + var locktimeMedian, coinbase, commitment, segwit; if (!block.verify()) return flags; @@ -534,8 +534,10 @@ Chain.prototype._verify = function _verify(block, prev) { // } // Make sure the height contained in the coinbase is correct. - if (block.version >= 2 && prev.isUpgraded(2)) - coinbaseHeight = true; + if (network.block.bip34height !== -1 && height >= network.block.bip34height) { + if (block.version >= 2 && prev.isUpgraded(2)) + coinbaseHeight = true; + } // Signature validation is now enforced (bip66) if (block.version >= 3 && prev.isUpgraded(3)) @@ -546,8 +548,8 @@ Chain.prototype._verify = function _verify(block, prev) { flags |= constants.flags.VERIFY_CHECKLOCKTIMEVERIFY; // Segregrated witness is now usable (the-bip-that-really-needs-to-be-rewritten) - if (height >= network.segwitHeight) { - if (block.witness && block.version >= 5 && prev.isUpgraded(5) ) { + if (network.segwitHeight !== -1 && height >= network.segwitHeight) { + if (block.version >= 5 && prev.isUpgraded(5) ) { flags |= constants.flags.VERIFY_WITNESS; segwit = true; } @@ -573,8 +575,8 @@ Chain.prototype._verify = function _verify(block, prev) { } } - if (segwit && block.witness) { - if (block.witnessRoot !== block.getWitnessRoot()) { + if (segwit) { + if (block._witness && block.commitmentHash !== block.getCommitmentHash()) { utils.debug('Block failed witnessroot test: %s', block.rhash); return false; } diff --git a/lib/bcoin/hd.js b/lib/bcoin/hd.js index 93401e1e..dd45e04d 100644 --- a/lib/bcoin/hd.js +++ b/lib/bcoin/hd.js @@ -505,7 +505,7 @@ HDPrivateKey.isExtended = function isExtended(data) { if (typeof data !== 'string') return false; - return data.indexOf('xprv') === 0 || data.indexOf('tprv') === 0; + return network.xprivkeys[data.slice(0, 4)]; }; HDPrivateKey.prototype._normalize = function _normalize(data) { @@ -978,7 +978,7 @@ HDPublicKey.isExtended = function isExtended(data) { if (typeof data !== 'string') return false; - return data.indexOf('xpub') === 0 || data.indexOf('tpub') === 0; + return network.xpubkeys[data.slice(0, 4)]; }; HDPublicKey.prototype._normalize = HDPrivateKey.prototype._normalize; diff --git a/lib/bcoin/input.js b/lib/bcoin/input.js index be5a548f..93b3b19f 100644 --- a/lib/bcoin/input.js +++ b/lib/bcoin/input.js @@ -82,7 +82,7 @@ Input.prototype.getRedeem = function getRedeem() { if (type === 'scripthash') { redeem = bcoin.script.getRedeem(this.script); - if (script.isWitnessScripthash(redeem)) + if (bcoin.script.isWitnessScripthash(redeem)) return bcoin.script.getRedeem(this.witness); return redeem; } diff --git a/lib/bcoin/pool.js b/lib/bcoin/pool.js index 80a26773..536632fc 100644 --- a/lib/bcoin/pool.js +++ b/lib/bcoin/pool.js @@ -114,9 +114,15 @@ function Pool(options) { this.tx = { state: {}, - count: 0 + count: 0, + type: 'tx' }; + if (network.type === 'segnet') { + this.block.type = 'witness' + this.block.type; + this.tx.type = 'witness' + this.tx.type; + } + this.request = { map: {}, active: 0, @@ -695,7 +701,8 @@ Pool.prototype._handleBlock = function _handleBlock(block, peer, callback) { if (self.chain.height % 20 === 0) { utils.debug( 'Status: tip=%s ts=%s height=%d blocks=%d orphans=%d active=%d' - + ' queue=%d target=%s peers=%d pending=%d highest=%d jobs=%d', + + ' queue=%d target=%s peers=%d pending=%d highest=%d jobs=%d' + + ' blockdelta=%d', block.rhash, new Date(block.ts * 1000).toISOString().slice(0, -5) + 'Z', self.chain.height, @@ -707,7 +714,8 @@ Pool.prototype._handleBlock = function _handleBlock(block, peer, callback) { self.peers.all.length, self.chain.pending.length, self.chain.bestHeight, - self.chain.jobs.length); + self.chain.jobs.length, + self.chain.blockDelta); } return callback(null, true); @@ -805,7 +813,7 @@ Pool.prototype._createPeer = function _createPeer(options) { txs.forEach(function(hash) { hash = utils.toHex(hash); if (self.markTX(hash, 0)) - self.getData(peer, 'tx', hash); + self.getData(peer, self.tx.type, hash); }); }); @@ -1473,7 +1481,7 @@ Pool.prototype.getData = function getData(peer, type, hash, options, callback) { return; } - if (!options.force && type !== 'tx') { + if (!options.force && type !== self.tx.type) { if (this.chain.has(hash)) return; } @@ -1483,7 +1491,7 @@ Pool.prototype.getData = function getData(peer, type, hash, options, callback) { item = new LoadRequest(this, peer, type, hash, callback); - if (type === 'tx') { + if (type === self.tx.type) { if (peer.queue.tx.length === 0) { utils.nextTick(function() { utils.debug( @@ -1614,7 +1622,7 @@ Pool.prototype.getTX = function getTX(hash, range, callback) { // Add request without queueing it to get notification at the time of load tx = null; found = false; - this.getData(this.peers.load, 'tx', hash, { noQueue: true }, function(t) { + this.getData(this.peers.load, self.tx.type, hash, { noQueue: true }, function(t) { found = true; tx = t; }); @@ -1926,7 +1934,7 @@ LoadRequest.prototype.start = function start() { this.peer.on('close', this._finish); this.pool.request.active++; - if (this.type === 'tx') + if (this.type === this.pool.tx.type) this.pool.request.activeTX++; else this.pool.request.activeBlocks++; @@ -1940,13 +1948,13 @@ LoadRequest.prototype.finish = function finish() { if (this.pool.request.map[this.hash]) { delete this.pool.request.map[this.hash]; this.pool.request.active--; - if (this.type === 'tx') + if (this.type === this.pool.tx.type) this.pool.request.activeTX--; else this.pool.request.activeBlocks--; } - if (this.type === 'tx') { + if (this.type === this.pool.tx.type) { index = this.peer.queue.tx.indexOf(this); if (index !== -1) this.peer.queue.tx.splice(index, 1); diff --git a/lib/bcoin/protocol/network.js b/lib/bcoin/protocol/network.js index 5c4c0f28..a681e4d2 100644 --- a/lib/bcoin/protocol/network.js +++ b/lib/bcoin/protocol/network.js @@ -21,6 +21,8 @@ network.set = function set(type) { utils.merge(network, net); }; +network.types = ['main', 'testnet', 'regtest', 'segnet']; + /** * Main */ @@ -139,7 +141,8 @@ main.powNoRetargeting = false; main.block = { majorityEnforceUpgrade: 750, majorityRejectOutdated: 950, - majorityWindow: 1000 + majorityWindow: 1000, + bip34height: 227931 }; main.segwitHeight = 2000000000; @@ -251,7 +254,8 @@ testnet.powNoRetargeting = false; testnet.block = { majorityEnforceUpgrade: 51, majorityRejectOutdated: 75, - majorityWindow: 100 + majorityWindow: 100, + bip34height: 21111 }; testnet.segwitHeight = 2000000000; @@ -345,7 +349,8 @@ regtest.powNoRetargeting = true; regtest.block = { majorityEnforceUpgrade: 750, majorityRejectOutdated: 950, - majorityWindow: 1000 + majorityWindow: 1000, + bip34height: -1 }; regtest.segwitHeight = 0; @@ -437,7 +442,7 @@ segnet.genesis = { nonce: 0 }; -segnet.magic = 0x99f57166; +segnet.magic = 0xcaea962e; segnet.powLimit = new bn( '00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff', @@ -452,9 +457,28 @@ segnet.powNoRetargeting = false; segnet.block = { majorityEnforceUpgrade: 7, majorityRejectOutdated: 9, - majorityWindow: 10 + majorityWindow: 10, + bip34height: -1 }; segnet.segwitHeight = 0; segnet.genesisBlock = '0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a7d719856ffff001d000000000101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000'; + +network.xprivkeys = { + '76066276': 'main', + '70615956': 'testnet', + '87393172': 'segnet', + xprv: 'main', + tprv: 'testnet', + '2791': 'segnet' +}; + +network.xpubkeys = { + '76067358': 'main', + '70617039': 'testnet', + '87394255': 'segnet', + xpub: 'main', + tpub: 'testnet', + '2793': 'segnet' +}; diff --git a/lib/bcoin/script.js b/lib/bcoin/script.js index 75ab1ff0..a4a9441b 100644 --- a/lib/bcoin/script.js +++ b/lib/bcoin/script.js @@ -393,7 +393,7 @@ script.verify = function verify(input, witness, output, tx, i, flags) { }; script.verifyProgram = function verifyProgram(witness, output, tx, i, flags) { - var program, witnessScript, script, stack, j; + var program, witnessScript, redeem, stack, j; assert((flags & constants.flags.VERIFY_WITNESS) !== 0); assert(script.isWitnessProgram(output)); @@ -433,7 +433,7 @@ script.verifyProgram = function verifyProgram(witness, output, tx, i, flags) { if (stack[0].length > 520 || stack[1].length > 520) return false; - script = ['dup', 'hash160', program.data, 'equalverify', 'checksig']; + redeem = ['dup', 'hash160', program.data, 'equalverify', 'checksig']; } else if (program.type === 'witnessscripthash') { if (stack.length === 0) return false; @@ -449,25 +449,31 @@ script.verifyProgram = function verifyProgram(witness, output, tx, i, flags) { if (!utils.isEqual(utils.sha256(witnessScript), program.data)) return false; - script = script.decode(witnessScript); + redeem = script.decode(witnessScript); } else { assert(false); } for (j = 0; j < stack.length; j++) { - if (stack[j].length > constants.script.maxSize) + if (stack[j].length > constants.script.maxSize) { + throw new Eror('max size'); return false; + } } - res = script.execute(script, stack, tx, i, flags); + res = script.execute(redeem, stack, tx, i, flags); // Verify the script did not fail as well as the stack values - if (!res || stack.length === 0 || !script.bool(stack.pop())) + if (!res || stack.length === 0 || !script.bool(stack.pop())) { + throw new Error('script failed'); return false; + } // Witnesses always require cleanstack - if (stack.length !== 0) + if (stack.length !== 0) { + throw new Error('cleanstack'); return false; + } return true; }; @@ -1114,12 +1120,12 @@ script.execute = function execute(data, stack, tx, index, flags, recurse) { succ++; } + if (stack.length < 1) + return false; + + val = stack.pop(); + if (flags & constants.flags.VERIFY_NULLDUMMY) { - if (stack.length < 1) - return false; - - val = stack.pop(); - if (!script.isDummy(val)) return false; } @@ -1975,10 +1981,10 @@ script.isCommitment = function isCommitment(s) { && s[0] === 'return' && Buffer.isBuffer(s[1]) && s[1].length === 36 - && utils.readU32(s[1], 0) === 0xeda921aa24; + && utils.readU32BE(s[1], 0) === 0xaa21a9ed; }; -script.getWitnessRoot = function getWitnessRoot(s) { +script.getCommitmentHash = function getCommitmentHash(s) { if (!script.isCommitment(s)) return; diff --git a/lib/bcoin/tx.js b/lib/bcoin/tx.js index a3d7649e..f060899d 100644 --- a/lib/bcoin/tx.js +++ b/lib/bcoin/tx.js @@ -114,6 +114,12 @@ TX.prototype.hash = function hash(enc) { }; TX.prototype.witnessHash = function witnessHash(enc) { + if (this.isCoinbase()) { + return enc === 'hex' + ? utils.toHex(constants.zeroHash) + : new Buffer(constants.zeroHash); + } + if (!this._whash) this._whash = utils.dsha256(this.renderWitness()); @@ -805,6 +811,8 @@ TX.prototype.inspect = function inspect() { return { type: this.type, hash: utils.revHex(this.hash('hex')), + witnessHash: utils.revHex(this.witnessHash('hex')), + witness: this._witness, height: this.height, value: utils.btc(this.getValue()), fee: utils.btc(this.getFee()),