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 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;
}
}