UNPKG

5.46 kBJavaScriptView Raw
1'use strict';
2
3/* jshint maxparams:5 */
4
5var Signature = require('../crypto/signature');
6var Script = require('../script');
7var Output = require('./output');
8var BufferReader = require('../encoding/bufferreader');
9var BufferWriter = require('../encoding/bufferwriter');
10var BN = require('../crypto/bn');
11var Hash = require('../crypto/hash');
12var ECDSA = require('../crypto/ecdsa');
13var $ = require('../util/preconditions');
14var _ = require('lodash');
15
16/**
17 * Returns a buffer of length 32 bytes with the hash that needs to be signed
18 * for witness programs as defined by:
19 * https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki
20 *
21 * @name Signing.sighash
22 * @param {Transaction} transaction the transaction to sign
23 * @param {number} sighashType the type of the hash
24 * @param {number} inputNumber the input index for the signature
25 * @param {Buffer} scriptCode
26 * @param {Buffer} satoshisBuffer
27 */
28var sighash = function sighash(transaction, sighashType, inputNumber, scriptCode, satoshisBuffer) {
29 /* jshint maxstatements: 50 */
30
31 var hashPrevouts;
32 var hashSequence;
33 var hashOutputs;
34
35 if (!(sighashType & Signature.SIGHASH_ANYONECANPAY)) {
36 var buffers = [];
37 for (var n = 0; n < transaction.inputs.length; n++) {
38 var input = transaction.inputs[n];
39 var prevTxIdBuffer = new BufferReader(input.prevTxId).readReverse();
40 buffers.push(prevTxIdBuffer);
41 var outputIndexBuffer = Buffer.alloc(4);
42 outputIndexBuffer.writeUInt32LE(input.outputIndex, 0);
43 buffers.push(outputIndexBuffer);
44 }
45 hashPrevouts = Hash.sha256sha256(Buffer.concat(buffers));
46 }
47
48 if (!(sighashType & Signature.SIGHASH_ANYONECANPAY) &&
49 (sighashType & 0x1f) !== Signature.SIGHASH_SINGLE && (sighashType & 0x1f) !== Signature.SIGHASH_NONE) {
50
51 var sequenceBuffers = [];
52 for (var m = 0; m < transaction.inputs.length; m++) {
53 var sequenceBuffer = Buffer.alloc(4);
54 sequenceBuffer.writeUInt32LE(transaction.inputs[m].sequenceNumber, 0);
55 sequenceBuffers.push(sequenceBuffer);
56 }
57 hashSequence = Hash.sha256sha256(Buffer.concat(sequenceBuffers));
58 }
59
60 var outputWriter = new BufferWriter();
61 if ((sighashType & 0x1f) !== Signature.SIGHASH_SINGLE && (sighashType & 0x1f) !== Signature.SIGHASH_NONE) {
62 for (var p = 0; p < transaction.outputs.length; p++) {
63 transaction.outputs[p].toBufferWriter(outputWriter);
64 }
65 hashOutputs = Hash.sha256sha256(outputWriter.toBuffer());
66 } else if ((sighashType & 0x1f) === Signature.SIGHASH_SINGLE && inputNumber < transaction.outputs.length) {
67 transaction.outputs[inputNumber].toBufferWriter(outputWriter);
68 hashOutputs = Hash.sha256sha256(outputWriter.toBuffer());
69 }
70
71 // Version
72 var writer = new BufferWriter();
73 writer.writeUInt32LE(transaction.version);
74
75 // Input prevouts/nSequence (none/all, depending on flags)
76 writer.write(hashPrevouts);
77 writer.write(hashSequence);
78
79 // The input being signed (replacing the scriptSig with scriptCode + amount)
80 // The prevout may already be contained in hashPrevout, and the nSequence
81 // may already be contain in hashSequence.
82 var outpointId = new BufferReader(transaction.inputs[inputNumber].prevTxId).readReverse();
83 writer.write(outpointId);
84 writer.writeUInt32LE(transaction.inputs[inputNumber].outputIndex);
85
86 writer.write(scriptCode);
87
88 writer.write(satoshisBuffer);
89
90 writer.writeUInt32LE(transaction.inputs[inputNumber].sequenceNumber);
91
92 // Outputs (none/one/all, depending on flags)
93 writer.write(hashOutputs);
94
95 // Locktime
96 writer.writeUInt32LE(transaction.nLockTime);
97
98 // Sighash type
99 writer.writeInt32LE(sighashType);
100
101 return Hash.sha256sha256(writer.toBuffer());
102
103};
104
105/**
106 * Create a signature
107 *
108 * @name Signing.sign
109 * @param {Transaction} transaction
110 * @param {PrivateKey} privateKey
111 * @param {number} sighash
112 * @param {number} inputIndex
113 * @param {Script} subscript
114 * @param {String} signingMethod - method used to sign - 'ecdsa' or 'schnorr'
115 * @return {Signature}
116 */
117function sign(transaction, privateKey, sighashType, inputIndex, scriptCode, satoshisBuffer, signingMethod) {
118 signingMethod = signingMethod || 'ecdsa';
119 var sig;
120
121 if (signingMethod === 'ecdsa') {
122 let hashbuf = sighash(transaction, sighashType, inputIndex, scriptCode, satoshisBuffer);
123 sig = ECDSA.sign(hashbuf, privateKey).set({
124 nhashtype: sighashType
125 });
126 return sig;
127 }
128 throw new Error("signingMethod not supported ", signingMethod);
129}
130
131/**
132 * Verify a signature
133 *
134 * @name Signing.verify
135 * @param {Transaction} transaction
136 * @param {Signature} signature
137 * @param {PublicKey} publicKey
138 * @param {number} inputIndex
139 * @param {Script} subscript
140 * @param {String} signingMethod - method used to sign - 'ecdsa' or 'schnorr' (future signing method)
141 * @return {boolean}
142 */
143function verify(transaction, signature, publicKey, inputIndex, scriptCode, satoshisBuffer, signingMethod) {
144 $.checkArgument(!_.isUndefined(transaction));
145 $.checkArgument(!_.isUndefined(signature) && !_.isUndefined(signature.nhashtype));
146 signingMethod = signingMethod || 'ecdsa';
147
148 if (signingMethod === 'ecdsa') {
149 let hashbuf = sighash(transaction, signature.nhashtype, inputIndex, scriptCode, satoshisBuffer);
150 return ECDSA.verify(hashbuf, signature, publicKey);
151 }
152 throw new Error("signingMethod not supported ", signingMethod);
153}
154
155/**
156 * @namespace Signing
157 */
158module.exports = {
159 sighash: sighash,
160 sign: sign,
161 verify: verify
162};