sighashing.

This commit is contained in:
Christopher Jeffrey 2016-03-15 14:14:53 -07:00
parent b356790136
commit 41eab6e27c
2 changed files with 71 additions and 90 deletions

View File

@ -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) {

View File

@ -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
*/ */