tx: refactor sighashing and hasStandardWitness.

This commit is contained in:
Christopher Jeffrey 2017-07-12 16:01:08 -07:00
parent ff856fd6c2
commit 47adb5a5a7
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD

View File

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