'use strict'; var buffer = require('buffer'); var Signature = require('../crypto/signature'); var Script = require('../script'); var Output = require('./output'); var BufferReader = require('../encoding/bufferreader'); var BufferWriter = require('../encoding/bufferwriter'); var BN = require('../crypto/bn'); var Hash = require('../crypto/hash'); var ECDSA = require('../crypto/ecdsa'); var SIGHASH_SINGLE_BUG = '0000000000000000000000000000000000000000000000000000000000000001'; var BITS_64_ON = 'ffffffffffffffff'; /** * Returns a buffer of length 32 bytes with the hash that needs to be signed * for OP_CHECKSIG. * * @param {Transaction} transaction the transaction to sign * @param {number} sighashType the type of the hash * @param {number} inputNumber the input index for the signature * @param {Script} subscript the script that will be signed */ function sighash(transaction, sighashType, inputNumber, subscript) { var Transaction = require('./transaction'); var Input = require('./input'); var i; // Copy transaction var txcopy = Transaction.shallowCopy(transaction); // Copy script subscript = new Script(subscript); subscript.removeCodeseparators(); for (i = 0; i < txcopy.inputs.length; i++) { // Blank signatures for other inputs txcopy.inputs[i] = new Input(txcopy.inputs[i]).setScript(Script.empty()); } txcopy.inputs[inputNumber] = new Input(txcopy.inputs[inputNumber]).setScript(subscript); if ((sighashType & 31) === Signature.SIGHASH_NONE || (sighashType & 31) === Signature.SIGHASH_SINGLE) { // clear all sequenceNumbers for (i = 0; i < txcopy.inputs.length; i++) { if (i !== inputNumber) { txcopy.inputs[i].sequenceNumber = 0; } } } if ((sighashType & 31) === Signature.SIGHASH_NONE) { txcopy.outputs = []; } else if ((sighashType & 31) === Signature.SIGHASH_SINGLE) { // The SIGHASH_SINGLE bug. // https://bitcointalk.org/index.php?topic=260595.0 if (inputNumber > txcopy.outputs.length - 1) { return new Buffer(SIGHASH_SINGLE_BUG, 'hex'); } if (txcopy.outputs.length <= inputNumber) { throw new Error('Missing output to sign'); } txcopy.outputs.length = inputNumber + 1; for (i = 0; i < inputNumber; i++) { txcopy.outputs[i] = new Output({ satoshis: BN().fromBuffer(new buffer.Buffer(BITS_64_ON, 'hex')), script: Script.empty() }); } } if (sighashType & Signature.SIGHASH_ANYONECANPAY) { txcopy.inputs = [txcopy.inputs[inputNumber]]; } var buf = BufferWriter() .write(txcopy.toBuffer()) .writeInt32LE(sighashType) .toBuffer(); //console.log('actual:'); //console.log(buf.toString('hex')); //console.log('expected:'); //console.log('01000000019ce5586f04dd407719ab7e2ed3583583b9022f29652702cfac5ed082013461fe0000000043410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8acffffffff010000000000000000000000000001000000'); return BufferReader(Hash.sha256sha256(buf)).readReverse(); } function sign(transaction, keypair, nhashtype, nin, subscript) { var hashbuf = sighash(transaction, nhashtype, nin, subscript); hashbuf = new BufferReader(hashbuf).readReverse(); var sig = ECDSA.sign(hashbuf, keypair, 'little').set({nhashtype: nhashtype}); return sig; } function verify(transaction, sig, pubkey, nin, subscript) { var hashbuf = sighash(transaction, sig.nhashtype, nin, subscript); console.log('actual:'); console.log(hashbuf.toString('hex')); console.log('expected:'); console.log('f4a222b692e7f86c299f878c4b981242238f49b467b8d990219fbf5cfc0838cd'); return ECDSA.verify(hashbuf, sig, pubkey, 'little'); } module.exports = { sighash: sighash, sign: sign, verify: verify };