|
|
@ -95,111 +95,37 @@ var randomTx = function(single) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var oneBuffer = function() { |
|
|
|
// bug present in bitcoind which must be also present in bitcore
|
|
|
|
// see https://bitcointalk.org/index.php?topic=260595
|
|
|
|
var ret = new Buffer(1); |
|
|
|
ret.writeUInt8(1, 0); |
|
|
|
return ret; // return 1 bug
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var signatureHashOld = function(tx, script, inIndex, hashType) { |
|
|
|
if (+inIndex !== inIndex || |
|
|
|
inIndex < 0 || inIndex >= tx.ins.length) { |
|
|
|
throw new Error('Input index "' + inIndex + '" invalid or out of bounds ' + |
|
|
|
'(' + tx.ins.length + ' inputs)'); |
|
|
|
return oneBuffer(); |
|
|
|
} |
|
|
|
|
|
|
|
// Clone transaction
|
|
|
|
var txTmp = new Transaction(); |
|
|
|
tx.ins.forEach(function(txin) { |
|
|
|
txTmp.ins.push(new Transaction.In(txin)); |
|
|
|
}); |
|
|
|
tx.outs.forEach(function(txout) { |
|
|
|
txTmp.outs.push(new Transaction.Out(txout)); |
|
|
|
}); |
|
|
|
txTmp.version = tx.version; |
|
|
|
txTmp.lock_time = tx.lock_time; |
|
|
|
|
|
|
|
// In case concatenating two scripts ends up with two codeseparators,
|
|
|
|
// or an extra one at the end, this prevents all those possible
|
|
|
|
// incompatibilities.
|
|
|
|
script.findAndDelete(Opcode.map.OP_CODESEPARATOR); |
|
|
|
|
|
|
|
// Get mode portion of hashtype
|
|
|
|
// Check for invalid use of SIGHASH_SINGLE
|
|
|
|
var hashTypeMode = hashType & 0x1f; |
|
|
|
|
|
|
|
// Generate modified transaction data for hash
|
|
|
|
var bytes = (new Put()); |
|
|
|
bytes.word32le(tx.version); |
|
|
|
|
|
|
|
// Serialize inputs
|
|
|
|
if (hashType & Transaction.SIGHASH_ANYONECANPAY) { |
|
|
|
// Blank out all inputs except current one, not recommended for open
|
|
|
|
// transactions.
|
|
|
|
bytes.varint(1); |
|
|
|
bytes.put(tx.ins[inIndex].o); |
|
|
|
bytes.varint(script.buffer.length); |
|
|
|
bytes.put(script.buffer); |
|
|
|
bytes.word32le(tx.ins[inIndex].q); |
|
|
|
} else { |
|
|
|
bytes.varint(tx.ins.length); |
|
|
|
for (var i = 0, l = tx.ins.length; i < l; i++) { |
|
|
|
var txin = tx.ins[i]; |
|
|
|
bytes.put(txin.o); |
|
|
|
|
|
|
|
// Current input's script gets set to the script to be signed, all others
|
|
|
|
// get blanked.
|
|
|
|
if (inIndex === i) { |
|
|
|
bytes.varint(script.buffer.length); |
|
|
|
bytes.put(script.buffer); |
|
|
|
} else { |
|
|
|
bytes.varint(0); |
|
|
|
} |
|
|
|
|
|
|
|
if (hashTypeMode === Transaction.SIGHASH_NONE && inIndex !== i) { |
|
|
|
bytes.word32le(0); |
|
|
|
} else { |
|
|
|
bytes.word32le(tx.ins[i].q); |
|
|
|
} |
|
|
|
if (hashTypeMode === Transaction.SIGHASH_SINGLE) { |
|
|
|
if (inIndex >= tx.outs.length) { |
|
|
|
return oneBuffer(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// Serialize outputs
|
|
|
|
if (hashTypeMode === Transaction.SIGHASH_NONE) { |
|
|
|
bytes.varint(0); |
|
|
|
} else { |
|
|
|
var outsLen; |
|
|
|
if (hashTypeMode === Transaction.SIGHASH_SINGLE) { |
|
|
|
if (inIndex >= txTmp.outs.length) { |
|
|
|
// bug present in bitcoind which must be also present in bitcore
|
|
|
|
// Transaction.hashForSignature(): SIGHASH_SINGLE
|
|
|
|
// no corresponding txout found - out of bounds
|
|
|
|
var ret = new Buffer(1); |
|
|
|
ret.writeUInt8(1, 0); |
|
|
|
return ret; // return 1 bug
|
|
|
|
} |
|
|
|
outsLen = inIndex + 1; |
|
|
|
} else { |
|
|
|
outsLen = tx.outs.length; |
|
|
|
} |
|
|
|
|
|
|
|
bytes.varint(outsLen); |
|
|
|
for (var i = 0; i < outsLen; i++) { |
|
|
|
if (hashTypeMode === Transaction.SIGHASH_SINGLE && i !== inIndex) { |
|
|
|
// Zero all outs except the one we want to keep
|
|
|
|
bytes.put(util.INT64_MAX); |
|
|
|
bytes.varint(0); |
|
|
|
} else { |
|
|
|
bytes.put(tx.outs[i].v); |
|
|
|
bytes.varint(tx.outs[i].s.length); |
|
|
|
bytes.put(tx.outs[i].s); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
bytes.word32le(tx.lock_time); |
|
|
|
|
|
|
|
var buffer = bytes.buffer(); |
|
|
|
|
|
|
|
// Wrapper to serialize only the necessary parts of the transaction being signed
|
|
|
|
var serializer = new Transaction.Serializer(tx, script, inIndex, hashType); |
|
|
|
// Serialize
|
|
|
|
var buffer = serializer.buffer(); |
|
|
|
// Append hashType
|
|
|
|
buffer = Buffer.concat([buffer, new Buffer([parseInt(hashType), 0, 0, 0])]); |
|
|
|
|
|
|
|
return util.twoSha256(buffer); |
|
|
|
var hashBuf = new Put().word32le(hashType).buffer(); |
|
|
|
buffer = Buffer.concat([buffer, hashBuf]); |
|
|
|
return buffertools.reverse(util.twoSha256(buffer)); |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
@ -211,7 +137,7 @@ var signatureHashOld = function(tx, script, inIndex, hashType) { |
|
|
|
|
|
|
|
describe('Transaction sighash (#hashForSignature)', function() { |
|
|
|
for (var i = 0; i < 250; i++) { |
|
|
|
it.skip('should hash correctly random tx #' + (i + 1), function() { |
|
|
|
it('should hash correctly random tx #' + (i + 1), function() { |
|
|
|
var tx = randomTx(); |
|
|
|
var l = tx.ins.length; |
|
|
|
for (var i = 0; i < l; i++) { |
|
|
@ -237,7 +163,7 @@ describe('Transaction sighash (#hashForSignature)', function() { |
|
|
|
var ser_tx = buffertools.toHex(tx.serialize()); |
|
|
|
ser_tx.should.equal(buffertools.toHex(raw_tx)); |
|
|
|
var h = buffertools.toHex(tx.hashForSignature(scriptPubKey, input_index, hashType)); |
|
|
|
h.should.equal(sighash); // compare our output with bitcoind's
|
|
|
|
h.should.equal(sighash); // compare our output with bitcoind's output
|
|
|
|
}); |
|
|
|
|
|
|
|
}); |
|
|
|