From 47adb5a5a7498a3b979eb062644097e3e58d4549 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Wed, 12 Jul 2017 16:01:08 -0700 Subject: [PATCH] tx: refactor sighashing and hasStandardWitness. --- lib/primitives/tx.js | 107 +++++++++++++++++++++++-------------------- 1 file changed, 57 insertions(+), 50 deletions(-) diff --git a/lib/primitives/tx.js b/lib/primitives/tx.js index 78f9b8bb..c6d23842 100644 --- a/lib/primitives/tx.js +++ b/lib/primitives/tx.js @@ -25,6 +25,7 @@ const Bloom = require('../utils/bloom'); const consensus = require('../protocol/consensus'); const policy = require('../protocol/policy'); const {ScriptError} = require('../script/common'); +const hashType = Script.hashType; /** * A static transaction object. @@ -444,10 +445,9 @@ TX.prototype.signatureHash = function signatureHash(index, prev, value, type, ve */ TX.prototype.signatureHashV0 = function signatureHashV0(index, prev, type) { - let i, size, bw, input, output; - let hashType = type & 0x1f; + let size, bw; - if (hashType === Script.hashType.SINGLE) { + if ((type & 0x1f) === hashType.SINGLE) { // Bitcoind used to return 1 as an error code: // it ended up being treated like a hash. if (index >= this.outputs.length) @@ -465,12 +465,13 @@ TX.prototype.signatureHashV0 = function signatureHashV0(index, prev, type) { bw.writeU32(this.version); // Serialize inputs. - if (type & Script.hashType.ANYONECANPAY) { - bw.writeVarint(1); - + if (type & hashType.ANYONECANPAY) { // Serialize only the current // input if ANYONECANPAY. - input = this.inputs[index]; + let input = this.inputs[index]; + + // Count. + bw.writeVarint(1); // Outpoint. input.prevout.toWriter(bw); @@ -481,8 +482,8 @@ TX.prototype.signatureHashV0 = function signatureHashV0(index, prev, type) { bw.writeU32(input.sequence); } else { bw.writeVarint(this.inputs.length); - for (i = 0; i < this.inputs.length; i++) { - input = this.inputs[i]; + for (let i = 0; i < this.inputs.length; i++) { + let input = this.inputs[i]; // Outpoint. input.prevout.toWriter(bw); @@ -499,9 +500,9 @@ TX.prototype.signatureHashV0 = function signatureHashV0(index, prev, type) { bw.writeVarint(0); // Sequences are 0 if NONE or SINGLE. - switch (hashType) { - case Script.hashType.NONE: - case Script.hashType.SINGLE: + switch (type & 0x1f) { + case hashType.NONE: + case hashType.SINGLE: bw.writeU32(0); break; default: @@ -512,17 +513,20 @@ TX.prototype.signatureHashV0 = function signatureHashV0(index, prev, type) { } // Serialize outputs. - switch (hashType) { - case Script.hashType.NONE: + switch (type & 0x1f) { + case hashType.NONE: { // No outputs if NONE. bw.writeVarint(0); break; - case Script.hashType.SINGLE: + } + case hashType.SINGLE: { + let output = this.outputs[index]; + // Drop all outputs after the // current input index if SINGLE. bw.writeVarint(index + 1); - for (i = 0; i < index; i++) { + for (let i = 0; i < index; i++) { // Null all outputs not at // current input index. bw.write64(-1); @@ -531,16 +535,17 @@ TX.prototype.signatureHashV0 = function signatureHashV0(index, prev, type) { // Regular serialization // at current input index. - output = this.outputs[index]; output.toWriter(bw); break; - default: + } + default: { // Regular output serialization if ALL. bw.writeVarint(this.outputs.length); - for (output of this.outputs) + for (let output of this.outputs) output.toWriter(bw); break; + } } bw.writeU32(this.locktime); @@ -565,7 +570,7 @@ TX.prototype.hashSize = function hashSize(index, prev, type) { size += 4; - if (type & Script.hashType.ANYONECANPAY) { + if (type & hashType.ANYONECANPAY) { size += 1; size += 36; size += prev.getVarSize(); @@ -579,10 +584,10 @@ TX.prototype.hashSize = function hashSize(index, prev, type) { } switch (type & 0x1f) { - case Script.hashType.NONE: + case hashType.NONE: size += 1; break; - case Script.hashType.SINGLE: + case hashType.SINGLE: size += encoding.sizeVarint(index + 1); size += 9 * index; size += this.outputs[index].getSize(); @@ -613,15 +618,16 @@ TX.prototype.signatureHashV1 = function signatureHashV1(index, prev, value, type let prevouts = encoding.ZERO_HASH; let sequences = encoding.ZERO_HASH; let outputs = encoding.ZERO_HASH; - let bw, size, input, output; + let input = this.inputs[index]; + let bw, size; - if (!(type & Script.hashType.ANYONECANPAY)) { + if (!(type & hashType.ANYONECANPAY)) { if (this._hashPrevouts) { prevouts = this._hashPrevouts; } else { - bw = new StaticWriter(this.inputs.length * 36); + let bw = new StaticWriter(this.inputs.length * 36); - for (input of this.inputs) + for (let input of this.inputs) input.prevout.toWriter(bw); prevouts = digest.hash256(bw.render()); @@ -631,15 +637,15 @@ TX.prototype.signatureHashV1 = function signatureHashV1(index, prev, value, type } } - if (!(type & Script.hashType.ANYONECANPAY) - && (type & 0x1f) !== Script.hashType.SINGLE - && (type & 0x1f) !== Script.hashType.NONE) { + if (!(type & hashType.ANYONECANPAY) + && (type & 0x1f) !== hashType.SINGLE + && (type & 0x1f) !== hashType.NONE) { if (this._hashSequence) { sequences = this._hashSequence; } else { - bw = new StaticWriter(this.inputs.length * 4); + let bw = new StaticWriter(this.inputs.length * 4); - for (input of this.inputs) + for (let input of this.inputs) bw.writeU32(input.sequence); sequences = digest.hash256(bw.render()); @@ -649,19 +655,20 @@ TX.prototype.signatureHashV1 = function signatureHashV1(index, prev, value, type } } - if ((type & 0x1f) !== Script.hashType.SINGLE - && (type & 0x1f) !== Script.hashType.NONE) { + if ((type & 0x1f) !== hashType.SINGLE + && (type & 0x1f) !== hashType.NONE) { if (this._hashOutputs) { outputs = this._hashOutputs; } else { - size = 0; + let size = 0; + let bw; - for (output of this.outputs) + for (let output of this.outputs) size += output.getSize(); bw = new StaticWriter(size); - for (output of this.outputs) + for (let output of this.outputs) output.toWriter(bw); outputs = digest.hash256(bw.render()); @@ -669,13 +676,11 @@ TX.prototype.signatureHashV1 = function signatureHashV1(index, prev, value, type if (!this.mutable) this._hashOutputs = outputs; } - } else if ((type & 0x1f) === Script.hashType.SINGLE && index < this.outputs.length) { - output = this.outputs[index]; + } else if ((type & 0x1f) === hashType.SINGLE && index < this.outputs.length) { + let output = this.outputs[index]; outputs = digest.hash256(output.toRaw()); } - input = this.inputs[index]; - size = 156 + prev.getVarSize(); bw = new StaticWriter(size); @@ -734,7 +739,7 @@ TX.prototype.signature = function signature(index, prev, value, key, type, versi let hash, sig, bw; if (type == null) - type = Script.hashType.ALL; + type = hashType.ALL; if (version == null) version = 0; @@ -1453,9 +1458,8 @@ TX.prototype.checkStandard = function checkStandard() { if (this.version < 1 || this.version > policy.MAX_TX_VERSION) return [false, 'version', 0]; - if (this.getWeight() >= policy.MAX_TX_WEIGHT) { + if (this.getWeight() >= policy.MAX_TX_WEIGHT) return [false, 'tx-size', 0]; - } for (let input of this.inputs) { if (input.script.getSize() > 1650) @@ -1536,14 +1540,13 @@ TX.prototype.hasStandardInputs = function hasStandardInputs(view) { */ TX.prototype.hasStandardWitness = function hasStandardWitness(view) { - if (this.isCoinbase()) return true; for (let input of this.inputs) { let witness = input.witness; let coin = view.getOutput(input); - let prev, redeem, m; + let prev; if (!coin) continue; @@ -1576,11 +1579,14 @@ TX.prototype.hasStandardWitness = function hasStandardWitness(view) { } if (prev.isWitnessScripthash()) { + let redeem; + if (witness.items.length - 1 > policy.MAX_P2WSH_STACK) return false; for (let i = 0; i < witness.items.length - 1; i++) { - if (witness.items[i].length > policy.MAX_P2WSH_PUSH) + let item = witness.items[i]; + if (item.length > policy.MAX_P2WSH_PUSH) return false; } @@ -1615,7 +1621,7 @@ TX.prototype.hasStandardWitness = function hasStandardWitness(view) { } if (prev.isMultisig()) { - m = prev.getSmall(0); + let m = prev.getSmall(0); if (witness.items.length - 1 !== m + 1) return false; @@ -1624,7 +1630,8 @@ TX.prototype.hasStandardWitness = function hasStandardWitness(view) { return false; for (let i = 1; i < witness.items.length - 1; i++) { - if (witness.items[i].length > 73) + let item = witness.items[i]; + if (item.length > 73) return false; } } @@ -1635,8 +1642,8 @@ TX.prototype.hasStandardWitness = function hasStandardWitness(view) { if (witness.items.length > policy.MAX_P2WSH_STACK) return false; - for (let i = 0; i < witness.items.length; i++) { - if (witness.items[i].length > policy.MAX_P2WSH_PUSH) + for (let item of witness.items) { + if (item.length > policy.MAX_P2WSH_PUSH) return false; } }