transaction: add SIGHASH_* implementations

This commit is contained in:
Daniel Cousens 2015-04-28 10:35:32 +10:00
parent 7ce286654c
commit 833bf9fa86

View file

@ -177,6 +177,8 @@ Transaction.prototype.clone = function () {
return newTx
}
var one = new Buffer('0000000000000000000000000000000000000000000000000000000000000001', 'hex')
/**
* Hash transaction for signing a specific input.
*
@ -191,33 +193,71 @@ Transaction.prototype.hashForSignature = function (inIndex, prevOutScript, hashT
typeForce('Number', hashType)
assert(inIndex >= 0, 'Invalid vin index')
assert(inIndex < this.ins.length, 'Invalid vin index')
// https://github.com/bitcoin/bitcoin/blob/master/src/test/sighash_tests.cpp#L29
if (inIndex >= this.ins.length) return one
var txTmp = this.clone()
var hashScript = prevOutScript.without(opcodes.OP_CODESEPARATOR)
// Blank out other inputs' signatures
txTmp.ins.forEach(function (txIn) {
txIn.script = Script.EMPTY
})
// in case concatenating two scripts ends up with two codeseparators,
// or an extra one at the end, this prevents all those possible incompatibilities.
var hashScript = prevOutScript.without(opcodes.OP_CODESEPARATOR)
var i
// blank out other inputs' signatures
txTmp.ins.forEach(function (input) { input.script = Script.EMPTY })
txTmp.ins[inIndex].script = hashScript
var hashTypeModifier = hashType & 0x1f
// blank out some of the inputs
if ((hashType & 0x1f) === Transaction.SIGHASH_NONE) {
// wildcard payee
txTmp.outs = []
if (hashTypeModifier === Transaction.SIGHASH_NONE) {
assert(false, 'SIGHASH_NONE not yet supported')
} else if (hashTypeModifier === Transaction.SIGHASH_SINGLE) {
assert(false, 'SIGHASH_SINGLE not yet supported')
// let the others update at will
txTmp.ins.forEach(function (input, i) {
if (i !== inIndex) {
input.sequence = 0
}
})
} else if ((hashType & 0x1f) === Transaction.SIGHASH_SINGLE) {
var nOut = inIndex
// only lock-in the txout payee at same index as txIn
// https://github.com/bitcoin/bitcoin/blob/master/src/test/sighash_tests.cpp#L60
if (nOut >= this.outs.length) return one
txTmp.outs = txTmp.outs.slice(0, nOut + 1)
// blank all other outputs (clear scriptPubKey, value === -1)
var stubOut = {
script: Script.EMPTY,
valueBuffer: new Buffer('ffffffffffffffff', 'hex')
}
for (i = 0; i < nOut; i++) {
txTmp.outs[i] = stubOut
}
// let the others update at will
txTmp.ins.forEach(function (input, i) {
if (i !== inIndex) {
input.sequence = 0
}
})
}
// blank out other inputs completely, not recommended for open transactions
if (hashType & Transaction.SIGHASH_ANYONECANPAY) {
assert(false, 'SIGHASH_ANYONECANPAY not yet supported')
txTmp.ins[0] = txTmp.ins[inIndex]
txTmp.ins = txTmp.ins.slice(0, 1)
}
var hashTypeBuffer = new Buffer(4)
hashTypeBuffer.writeInt32LE(hashType, 0)
// serialize and hash
var buffer = new Buffer(txTmp.byteLength() + 4)
buffer.writeInt32LE(hashType, buffer.length - 4)
txTmp.toBuffer().copy(buffer, 0)
var buffer = Buffer.concat([txTmp.toBuffer(), hashTypeBuffer])
return crypto.hash256(buffer)
}
@ -267,7 +307,12 @@ Transaction.prototype.toBuffer = function () {
writeVarInt(this.outs.length)
this.outs.forEach(function (txOut) {
if (!txOut.valueBuffer) {
writeUInt64(txOut.value)
} else {
writeSlice(txOut.valueBuffer)
}
writeVarInt(txOut.script.buffer.length)
writeSlice(txOut.script.buffer)
})