From f2964e06fb38e5d588f74ff62d89a9a9d3925596 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Wed, 22 Feb 2017 20:27:19 -0800 Subject: [PATCH] minerblock: ensure constant coinbase size. --- lib/mining/miner.js | 1 + lib/mining/minerblock.js | 99 +++++++++++++++++++++++++++++++--------- 2 files changed, 79 insertions(+), 21 deletions(-) diff --git a/lib/mining/miner.js b/lib/mining/miner.js index 16f74ebf..3ca66453 100644 --- a/lib/mining/miner.js +++ b/lib/mining/miner.js @@ -550,6 +550,7 @@ MinerOptions.prototype.fromOptions = function fromOptions(options) { if (typeof flags === 'string') flags = new Buffer(flags, 'utf8'); assert(Buffer.isBuffer(flags)); + assert(flags.length <= 20, 'Coinbase flags > 20 bytes.'); this.coinbaseFlags = flags; } diff --git a/lib/mining/minerblock.js b/lib/mining/minerblock.js index 762099b8..c65431ff 100644 --- a/lib/mining/minerblock.js +++ b/lib/mining/minerblock.js @@ -116,9 +116,12 @@ MinerBlock.prototype.getRate = function() { MinerBlock.prototype._init = function _init() { var scale = consensus.WITNESS_SCALE_FACTOR; + var hash = encoding.ZERO_HASH; var block = this.block; var cb = this.coinbase; - var input, output, nonce; + var input, output, commit, padding; + + assert(this.coinbaseFlags.length <= 20); // Setup our block. block.version = this.version; @@ -136,7 +139,7 @@ MinerBlock.prototype._init = function _init() { // Let the world know this little // miner succeeded. - input.script.set(1, this.coinbaseFlags); + input.script.set(1, encoding.ZERO_HASH160); // Smaller nonce for good measure. input.script.set(2, util.nonce().slice(0, 4)); @@ -147,42 +150,97 @@ MinerBlock.prototype._init = function _init() { input.script.compile(); + // Set up the witness nonce. + if (this.witness) { + input.witness.set(0, block.createWitnessNonce()); + input.witness.compile(); + } + cb.inputs.push(input); // Reward output. output = new Output(); - output.script.fromAddress(this.address); + output.script.fromPubkeyhash(encoding.ZERO_HASH160); cb.outputs.push(output); - // If we're using segwit, we need to - // set up the nonce and commitment. + // If we're using segwit, we + // need to set up the commitment. if (this.witness) { - // Our witness nonce is the hash256 - // of the previous block hash. - nonce = block.createWitnessNonce(); - - // Set up the witness nonce. - input.witness.set(0, nonce); - input.witness.compile(); - // Commitment output. - cb.outputs.push(new Output()); + commit = new Output(); + commit.script.fromCommitment(hash); + cb.outputs.push(commit); } block.txs.push(cb); - // Update commitments. - this.refresh(); - // Initialize weight. - this.weight = this.block.getWeight(); + this.weight = block.getWeight(); // 4 extra bytes for varint tx count. this.weight += 4 * scale; + // Padding for the CB height (constant size). + padding = 5 - input.script.code[0].getSize(); + assert(padding >= 0); + + this.weight += padding * scale; + + // Reserved size. + // Without segwit: + // Block weight = 840 + // Block stripped size = 210 + // Block size = 210 + // CB weight = 500 + // CB stripped size = 125 + // CB size = 125 + // Sigops cost = 4 + // With segwit: + // Block weight = 1064 + // Block stripped size = 257 + // Block size = 293 + // CB weight = 724 + // CB stripped size = 172 + // CB size = 208 + // Sigops cost = 4 + if (!this.witness) { + assert.equal(this.weight, 840); + assert.equal(block.getBaseSize() + 4 + padding, 210); + assert.equal(block.getSize() + 4 + padding, 210); + assert.equal(cb.getWeight() + padding * scale, 500); + assert.equal(cb.getBaseSize() + padding, 125); + assert.equal(cb.getSize() + padding, 125); + } else { + assert.equal(this.weight, 1064); + assert.equal(block.getBaseSize() + 4 + padding, 257); + assert.equal(block.getSize() + 4 + padding, 293); + assert.equal(cb.getWeight() + padding * scale, 724); + assert.equal(cb.getBaseSize() + padding, 172); + assert.equal(cb.getSize() + padding, 208); + } + // Initialize sigops weight. - this.sigops = cb.getSigopsCost(null, this.flags); + this.sigops = 4; + + // Setup coinbase flags (variable size). + input.script.set(1, this.coinbaseFlags); + input.script.compile(); + + // Setup output script (variable size). + output.script.clear(); + output.script.fromAddress(this.address); + + // Update commitments. + this.refresh(); + + // Ensure the variable size + // stuff didn't break anything. + assert(block.getWeight() <= this.weight, + 'Coinbase exceeds reserved size!'); + + assert(cb.getSigopsCost(null, this.flags) <= this.sigops, + 'Coinbase exceeds reserved sigops'); }; /** @@ -208,7 +266,6 @@ MinerBlock.prototype.refresh = function refresh() { MinerBlock.prototype.updateCommitment = function updateCommitment() { var output = this.coinbase.outputs[1]; - var flags = this.coinbaseFlags; var hash; // Recalculate witness merkle root. @@ -216,7 +273,7 @@ MinerBlock.prototype.updateCommitment = function updateCommitment() { // Update commitment. output.script.clear(); - output.script.fromCommitment(hash, flags); + output.script.fromCommitment(hash); }; /**