sighashing.
This commit is contained in:
parent
b356790136
commit
41eab6e27c
148
lib/bcoin/tx.js
148
lib/bcoin/tx.js
@ -176,18 +176,33 @@ TX.prototype._inputIndex = function _inputIndex(hash, index) {
|
|||||||
return -1;
|
return -1;
|
||||||
};
|
};
|
||||||
|
|
||||||
TX.prototype.signatureHash = function signatureHash(index, s, type, version) {
|
TX.prototype.signatureHash = function signatureHash(index, prev, type, version) {
|
||||||
assert(version >= 0 && version <= 1);
|
assert(version >= 0 && version <= 1);
|
||||||
|
|
||||||
|
// Traditional sighashing
|
||||||
if (version === 0)
|
if (version === 0)
|
||||||
return this.signatureHashV0(index, s, type);
|
return this.signatureHashV0(index, prev, type);
|
||||||
|
|
||||||
|
// Segwit sighashing
|
||||||
if (version === 1)
|
if (version === 1)
|
||||||
return this.signatureHashV1(index, s, type);
|
return this.signatureHashV1(index, prev, type);
|
||||||
};
|
};
|
||||||
|
|
||||||
TX.prototype.signatureHashV0 = function signatureHashV0(index, s, type) {
|
TX.prototype.signatureHashV0 = function signatureHashV0(index, prev, type) {
|
||||||
var i, msg, hash;
|
var p = new BufferWriter();
|
||||||
|
var i, copy;
|
||||||
|
|
||||||
var copy = {
|
if (typeof index !== 'number')
|
||||||
|
index = this.inputs.indexOf(index);
|
||||||
|
|
||||||
|
if (typeof type === 'string')
|
||||||
|
type = constants.hashType[type];
|
||||||
|
|
||||||
|
assert(index >= 0 && index < copy.inputs.length);
|
||||||
|
assert(prev instanceof bcoin.script);
|
||||||
|
|
||||||
|
// Clone the transaction.
|
||||||
|
copy = {
|
||||||
version: this.version,
|
version: this.version,
|
||||||
inputs: [],
|
inputs: [],
|
||||||
outputs: [],
|
outputs: [],
|
||||||
@ -210,31 +225,16 @@ TX.prototype.signatureHashV0 = function signatureHashV0(index, s, type) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof index !== 'number')
|
|
||||||
index = this.inputs.indexOf(index);
|
|
||||||
|
|
||||||
if (typeof type === 'string')
|
|
||||||
type = constants.hashType[type];
|
|
||||||
|
|
||||||
assert(index >= 0 && index < copy.inputs.length);
|
|
||||||
assert(s instanceof bcoin.script);
|
|
||||||
|
|
||||||
// Disable this for now. We allow null hash types
|
|
||||||
// because bitcoind allows empty signatures. On
|
|
||||||
// another note, we allow all weird sighash types
|
|
||||||
// if strictenc is not enabled.
|
|
||||||
// assert(utils.isFinite(type));
|
|
||||||
|
|
||||||
// Remove all signatures.
|
// Remove all signatures.
|
||||||
for (i = 0; i < copy.inputs.length; i++)
|
for (i = 0; i < copy.inputs.length; i++)
|
||||||
copy.inputs[i].script = new Script([]);
|
copy.inputs[i].script = new Script([]);
|
||||||
|
|
||||||
// Set our input to previous output's script
|
// Set our input to previous output's script
|
||||||
copy.inputs[index].script = s;
|
copy.inputs[index].script = prev;
|
||||||
|
|
||||||
if ((type & 0x1f) === constants.hashType.none) {
|
if ((type & 0x1f) === constants.hashType.none) {
|
||||||
// Drop all outputs. We don't want to sign them.
|
// Drop all outputs. We don't want to sign them.
|
||||||
copy.outputs = [];
|
copy.outputs.length = 0;
|
||||||
|
|
||||||
// Allow input sequence updates for other inputs.
|
// Allow input sequence updates for other inputs.
|
||||||
for (i = 0; i < copy.inputs.length; i++) {
|
for (i = 0; i < copy.inputs.length; i++) {
|
||||||
@ -271,20 +271,16 @@ TX.prototype.signatureHashV0 = function signatureHashV0(index, s, type) {
|
|||||||
copy.inputs.length = 1;
|
copy.inputs.length = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
copy = bcoin.protocol.framer.tx(copy);
|
// Render the copy and append the hashtype.
|
||||||
|
bcoin.protocol.framer.tx(copy, p);
|
||||||
|
p.writeU32(type);
|
||||||
|
|
||||||
msg = new Buffer(copy.length + 4);
|
return utils.dsha256(p.render());
|
||||||
utils.copy(copy, msg, 0);
|
|
||||||
utils.writeU32(msg, type, copy.length);
|
|
||||||
|
|
||||||
hash = utils.dsha256(msg);
|
|
||||||
|
|
||||||
return hash;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
TX.prototype.signatureHashV1 = function signatureHashV1(index, s, type) {
|
TX.prototype.signatureHashV1 = function signatureHashV1(index, prev, type) {
|
||||||
var i, msg, hash, hashPrevouts, hashSequence, hashOutputs;
|
var p = new BufferWriter();
|
||||||
var size, outputs, output, off, prev;
|
var i, prevout, hashPrevouts, hashSequence, hashOutputs;
|
||||||
|
|
||||||
if (typeof index !== 'number')
|
if (typeof index !== 'number')
|
||||||
index = this.inputs.indexOf(index);
|
index = this.inputs.indexOf(index);
|
||||||
@ -293,85 +289,57 @@ TX.prototype.signatureHashV1 = function signatureHashV1(index, s, type) {
|
|||||||
type = constants.hashType[type];
|
type = constants.hashType[type];
|
||||||
|
|
||||||
assert(index >= 0 && index < this.inputs.length);
|
assert(index >= 0 && index < this.inputs.length);
|
||||||
assert(s instanceof bcoin.script);
|
assert(prev instanceof bcoin.script);
|
||||||
|
|
||||||
if (!(type & constants.hashType.anyonecanpay)) {
|
if (!(type & constants.hashType.anyonecanpay)) {
|
||||||
hashPrevouts = new Buffer(36 * this.inputs.length);
|
hashPrevouts = new BufferWriter();
|
||||||
off = 0;
|
|
||||||
for (i = 0; i < this.inputs.length; i++) {
|
for (i = 0; i < this.inputs.length; i++) {
|
||||||
prev = this.inputs[i].prevout;
|
prevout = this.inputs[i].prevout;
|
||||||
off += utils.copy(new Buffer(prev.hash, 'hex'), hashPrevouts, off);
|
hashPrevouts.writeHash(prevout.hash);
|
||||||
off += utils.writeU32(hashPrevouts, prev.index, off);
|
hashPrevouts.writeU32(prevout.index);
|
||||||
}
|
}
|
||||||
hashPrevouts = utils.dsha256(hashPrevouts);
|
hashPrevouts = utils.dsha256(hashPrevouts.render());
|
||||||
} else {
|
} else {
|
||||||
hashPrevouts = new Buffer(32);
|
hashPrevouts = utils.slice(constants.zeroHash);
|
||||||
hashPrevouts.fill(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(type & constants.hashType.anyonecanpay)
|
if (!(type & constants.hashType.anyonecanpay)
|
||||||
&& (type & 0x1f) !== constants.hashType.single
|
&& (type & 0x1f) !== constants.hashType.single
|
||||||
&& (type & 0x1f) !== constants.hashType.none) {
|
&& (type & 0x1f) !== constants.hashType.none) {
|
||||||
hashSequence = new Buffer(4 * this.inputs.length);
|
hashSequence = new BufferWriter();
|
||||||
off = 0;
|
|
||||||
for (i = 0; i < this.inputs.length; i++)
|
for (i = 0; i < this.inputs.length; i++)
|
||||||
off += utils.writeU32(hashSequence, this.inputs[i].sequence, off);
|
p.writeU32(this.inputs[i].sequence);
|
||||||
hashSequence = utils.dsha256(hashSequence);
|
hashSequence = utils.dsha256(hashSequence.render());
|
||||||
} else {
|
} else {
|
||||||
hashSequence = new Buffer(32);
|
hashSequence = utils.slice(constants.zeroHash);
|
||||||
hashSequence.fill(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((type & 0x1f) !== constants.hashType.single
|
if ((type & 0x1f) !== constants.hashType.single
|
||||||
&& (type & 0x1f) !== constants.hashType.none) {
|
&& (type & 0x1f) !== constants.hashType.none) {
|
||||||
size = 0;
|
hashOutputs = new BufferWriter();
|
||||||
outputs = [];
|
for (i = 0; i < this.outputs.length; i++)
|
||||||
for (i = 0; i < this.outputs.length; i++) {
|
bcoin.protocol.framer.output(this.outputs[i], hashOutputs);
|
||||||
output = bcoin.protocol.framer.output(this.outputs[i]);
|
hashOutputs = utils.dsha256(hashOutputs.render());
|
||||||
size += output.length;
|
|
||||||
outputs.push(output);
|
|
||||||
}
|
|
||||||
hashOutputs = new Buffer(size);
|
|
||||||
off = 0;
|
|
||||||
for (i = 0; i < outputs.length; i++)
|
|
||||||
off += utils.copy(outputs[i], hashOutputs, off);
|
|
||||||
hashOutputs = utils.dsha256(hashOutputs);
|
|
||||||
} else if ((type & 0x1f) === constants.hashType.single && index < this.outputs.length) {
|
} else if ((type & 0x1f) === constants.hashType.single && index < this.outputs.length) {
|
||||||
hashOutputs = bcoin.protocol.framer.output(this.outputs[index]);
|
hashOutputs = bcoin.protocol.framer.output(this.outputs[index]);
|
||||||
hashOutputs = utils.dsha256(hashOutputs);
|
hashOutputs = utils.dsha256(hashOutputs);
|
||||||
} else {
|
} else {
|
||||||
hashOutputs = new Buffer(32);
|
hashOutputs = utils.slice(constants.zeroHash);
|
||||||
hashOutputs.fill(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
s = s.encode();
|
p.write32(this.version);
|
||||||
|
p.writeBytes(hashPrevouts);
|
||||||
|
p.writeBytes(hashSequence);
|
||||||
|
p.writeHash(this.inputs[index].prevout.hash);
|
||||||
|
p.writeU32(this.inputs[index].prevout.index);
|
||||||
|
p.writeVarBytes(prev.encode());
|
||||||
|
p.write64(this.inputs[index].output.value);
|
||||||
|
p.writeU32(this.inputs[index].sequence);
|
||||||
|
p.writeBytes(hashOutputs);
|
||||||
|
p.writeU32(this.locktime);
|
||||||
|
p.writeU32(type);
|
||||||
|
|
||||||
msg = new Buffer(
|
return utils.dsha256(p.render());
|
||||||
4 + 32 + 32 + 36
|
|
||||||
+ utils.sizeIntv(s.length)
|
|
||||||
+ s.length
|
|
||||||
+ 8 + 4 + 32 + 4 + 4);
|
|
||||||
|
|
||||||
off = 0;
|
|
||||||
off += utils.write32(msg, this.version, off);
|
|
||||||
off += utils.copy(hashPrevouts, msg, off);
|
|
||||||
off += utils.copy(hashSequence, msg, off);
|
|
||||||
off += utils.copy(new Buffer(this.inputs[index].prevout.hash, 'hex'), msg, off);
|
|
||||||
off += utils.writeU32(msg, this.inputs[index].prevout.index, off);
|
|
||||||
assert(off === 4 + 32 + 32 + 36);
|
|
||||||
off += utils.writeIntv(msg, s.length, off);
|
|
||||||
off += utils.copy(s, msg, off);
|
|
||||||
off += utils.write64(msg, this.inputs[index].output.value, off);
|
|
||||||
off += utils.writeU32(msg, this.inputs[index].sequence, off);
|
|
||||||
off += utils.copy(hashOutputs, msg, off);
|
|
||||||
off += utils.writeU32(msg, this.locktime, off);
|
|
||||||
assert(off === msg.length - 4);
|
|
||||||
off += utils.writeU32(msg, type, off);
|
|
||||||
assert(off === msg.length);
|
|
||||||
|
|
||||||
hash = utils.dsha256(msg);
|
|
||||||
|
|
||||||
return hash;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
TX.prototype.verify = function verify(index, force, flags) {
|
TX.prototype.verify = function verify(index, force, flags) {
|
||||||
|
|||||||
@ -52,6 +52,12 @@ BufferWriter.prototype.render = function render() {
|
|||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
BufferWriter.prototype.destroy = function destroy() {
|
||||||
|
this.data.length = 0;
|
||||||
|
delete this.data;
|
||||||
|
delete this.written;
|
||||||
|
};
|
||||||
|
|
||||||
BufferWriter.prototype.writeU8 = function writeU8(value) {
|
BufferWriter.prototype.writeU8 = function writeU8(value) {
|
||||||
this.written += 1;
|
this.written += 1;
|
||||||
this.data.push(['u8', value]);
|
this.data.push(['u8', value]);
|
||||||
@ -171,6 +177,13 @@ BufferWriter.prototype.writeUIntv = function writeUIntv(value) {
|
|||||||
this.data.push(['varint', value]);
|
this.data.push(['varint', value]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
BufferWriter.prototype.fill = function fill(value, size) {
|
||||||
|
var buf = new Buffer(size);
|
||||||
|
buf.fill(value);
|
||||||
|
this.written += buf.length;
|
||||||
|
this.data.push(['bytes', buf]);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Expose
|
* Expose
|
||||||
*/
|
*/
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user