Skip to content

Commit 833bf9f

Browse files
committed
transaction: add SIGHASH_* implementations
1 parent 7ce2866 commit 833bf9f

File tree

1 file changed

+60
-15
lines changed

1 file changed

+60
-15
lines changed

src/transaction.js

Lines changed: 60 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,8 @@ Transaction.prototype.clone = function () {
177177
return newTx
178178
}
179179

180+
var one = new Buffer('0000000000000000000000000000000000000000000000000000000000000001', 'hex')
181+
180182
/**
181183
* Hash transaction for signing a specific input.
182184
*
@@ -191,33 +193,71 @@ Transaction.prototype.hashForSignature = function (inIndex, prevOutScript, hashT
191193
typeForce('Number', hashType)
192194

193195
assert(inIndex >= 0, 'Invalid vin index')
194-
assert(inIndex < this.ins.length, 'Invalid vin index')
196+
197+
// https://github.com/bitcoin/bitcoin/blob/master/src/test/sighash_tests.cpp#L29
198+
if (inIndex >= this.ins.length) return one
195199

196200
var txTmp = this.clone()
201+
202+
// in case concatenating two scripts ends up with two codeseparators,
203+
// or an extra one at the end, this prevents all those possible incompatibilities.
197204
var hashScript = prevOutScript.without(opcodes.OP_CODESEPARATOR)
205+
var i
198206

199-
// Blank out other inputs' signatures
200-
txTmp.ins.forEach(function (txIn) {
201-
txIn.script = Script.EMPTY
202-
})
207+
// blank out other inputs' signatures
208+
txTmp.ins.forEach(function (input) { input.script = Script.EMPTY })
203209
txTmp.ins[inIndex].script = hashScript
204210

205-
var hashTypeModifier = hashType & 0x1f
211+
// blank out some of the inputs
212+
if ((hashType & 0x1f) === Transaction.SIGHASH_NONE) {
213+
// wildcard payee
214+
txTmp.outs = []
215+
216+
// let the others update at will
217+
txTmp.ins.forEach(function (input, i) {
218+
if (i !== inIndex) {
219+
input.sequence = 0
220+
}
221+
})
222+
223+
} else if ((hashType & 0x1f) === Transaction.SIGHASH_SINGLE) {
224+
var nOut = inIndex
225+
226+
// only lock-in the txout payee at same index as txIn
227+
// https://github.com/bitcoin/bitcoin/blob/master/src/test/sighash_tests.cpp#L60
228+
if (nOut >= this.outs.length) return one
229+
230+
txTmp.outs = txTmp.outs.slice(0, nOut + 1)
206231

207-
if (hashTypeModifier === Transaction.SIGHASH_NONE) {
208-
assert(false, 'SIGHASH_NONE not yet supported')
209-
} else if (hashTypeModifier === Transaction.SIGHASH_SINGLE) {
210-
assert(false, 'SIGHASH_SINGLE not yet supported')
232+
// blank all other outputs (clear scriptPubKey, value === -1)
233+
var stubOut = {
234+
script: Script.EMPTY,
235+
valueBuffer: new Buffer('ffffffffffffffff', 'hex')
236+
}
237+
238+
for (i = 0; i < nOut; i++) {
239+
txTmp.outs[i] = stubOut
240+
}
241+
242+
// let the others update at will
243+
txTmp.ins.forEach(function (input, i) {
244+
if (i !== inIndex) {
245+
input.sequence = 0
246+
}
247+
})
211248
}
212249

250+
// blank out other inputs completely, not recommended for open transactions
213251
if (hashType & Transaction.SIGHASH_ANYONECANPAY) {
214-
assert(false, 'SIGHASH_ANYONECANPAY not yet supported')
252+
txTmp.ins[0] = txTmp.ins[inIndex]
253+
txTmp.ins = txTmp.ins.slice(0, 1)
215254
}
216255

217-
var hashTypeBuffer = new Buffer(4)
218-
hashTypeBuffer.writeInt32LE(hashType, 0)
256+
// serialize and hash
257+
var buffer = new Buffer(txTmp.byteLength() + 4)
258+
buffer.writeInt32LE(hashType, buffer.length - 4)
259+
txTmp.toBuffer().copy(buffer, 0)
219260

220-
var buffer = Buffer.concat([txTmp.toBuffer(), hashTypeBuffer])
221261
return crypto.hash256(buffer)
222262
}
223263

@@ -267,7 +307,12 @@ Transaction.prototype.toBuffer = function () {
267307

268308
writeVarInt(this.outs.length)
269309
this.outs.forEach(function (txOut) {
270-
writeUInt64(txOut.value)
310+
if (!txOut.valueBuffer) {
311+
writeUInt64(txOut.value)
312+
} else {
313+
writeSlice(txOut.valueBuffer)
314+
}
315+
271316
writeVarInt(txOut.script.buffer.length)
272317
writeSlice(txOut.script.buffer)
273318
})

0 commit comments

Comments
 (0)