tx: refactor sighashing and hasStandardWitness.
This commit is contained in:
parent
ff856fd6c2
commit
47adb5a5a7
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user