UNPKG

4.83 kBJavaScriptView Raw
1'use strict';
2
3var Signature = require('../crypto/signature');
4var Script = require('../script');
5var Output = require('./output');
6var BufferReader = require('../encoding/bufferreader');
7var BufferWriter = require('../encoding/bufferwriter');
8var BN = require('../crypto/bn');
9var Hash = require('../crypto/hash');
10var ECDSA = require('../crypto/ecdsa');
11var $ = require('../util/preconditions');
12var _ = require('lodash');
13const schnorr = require('bip-schnorr');
14
15var SIGHASH_SINGLE_BUG = '0000000000000000000000000000000000000000000000000000000000000001';
16var BITS_64_ON = 'ffffffffffffffff';
17
18/**
19 * Returns a buffer of length 32 bytes with the hash that needs to be signed
20 * for OP_CHECKSIG.
21 *
22 * @name Signing.sighash
23 * @param {Transaction} transaction the transaction to sign
24 * @param {number} sighashType the type of the hash
25 * @param {number} inputNumber the input index for the signature
26 * @param {Script} subscript the script that will be signed
27 */
28var sighash = function sighash(transaction, sighashType, inputNumber, subscript) {
29 var Transaction = require('./transaction');
30 var Input = require('./input');
31
32 var i;
33 // Copy transaction
34 var txcopy = Transaction.shallowCopy(transaction);
35
36 // Copy script
37 subscript = new Script(subscript);
38 subscript.removeCodeseparators();
39
40 for (i = 0; i < txcopy.inputs.length; i++) {
41 // Blank signatures for other inputs
42 txcopy.inputs[i] = new Input(txcopy.inputs[i]).setScript(Script.empty());
43 }
44
45 txcopy.inputs[inputNumber] = new Input(txcopy.inputs[inputNumber]).setScript(subscript);
46
47 if ((sighashType & 31) === Signature.SIGHASH_NONE ||
48 (sighashType & 31) === Signature.SIGHASH_SINGLE) {
49
50 // clear all sequenceNumbers
51 for (i = 0; i < txcopy.inputs.length; i++) {
52 if (i !== inputNumber) {
53 txcopy.inputs[i].sequenceNumber = 0;
54 }
55 }
56 }
57
58 if ((sighashType & 31) === Signature.SIGHASH_NONE) {
59 txcopy.outputs = [];
60
61 } else if ((sighashType & 31) === Signature.SIGHASH_SINGLE) {
62 // The SIGHASH_SINGLE bug.
63 // https://bitcointalk.org/index.php?topic=260595.0
64 if (inputNumber >= txcopy.outputs.length) {
65 return Buffer.from(SIGHASH_SINGLE_BUG, 'hex');
66 }
67
68 txcopy.outputs.length = inputNumber + 1;
69
70 for (i = 0; i < inputNumber; i++) {
71 txcopy.outputs[i] = new Output({
72 satoshis: BN.fromBuffer(Buffer.from(BITS_64_ON, 'hex')),
73 script: Script.empty()
74 });
75 }
76 }
77
78 if (sighashType & Signature.SIGHASH_ANYONECANPAY) {
79 txcopy.inputs = [txcopy.inputs[inputNumber]];
80 }
81
82 var buf = new BufferWriter()
83 .write(txcopy.toBuffer())
84 .writeInt32LE(sighashType)
85 .toBuffer();
86 var ret = Hash.sha256sha256(buf);
87 ret = new BufferReader(ret).readReverse();
88 return ret;
89};
90
91/**
92 * Create a signature
93 *
94 * @name Signing.sign
95 * @param {Transaction} transaction
96 * @param {PrivateKey} privateKey
97 * @param {number} sighash
98 * @param {number} inputIndex
99 * @param {Script} subscript
100 * @param {String} signingMethod - method used to sign - 'ecdsa' or 'schnorr' (future signing method)
101 * @return {Signature}
102 */
103function sign(transaction, privateKey, sighashType, inputIndex, subscript, signingMethod) {
104 signingMethod = signingMethod || 'ecdsa';
105
106 let hashbuf = sighash(transaction, sighashType, inputIndex, subscript);
107 let sig;
108 switch (signingMethod) {
109 case 'ecdsa':
110 sig = ECDSA.sign(hashbuf, privateKey, 'little').set({ nhashtype: sighashType });
111 break;
112 case 'schnorr':
113 sig = schnorr.sign(privateKey.toString(), hashbuf);
114 break;
115 default:
116 throw new Error("signingMethod not supported ", signingMethod);
117 }
118 return sig;
119}
120
121/**
122 * Verify a signature
123 *
124 * @name Signing.verify
125 * @param {Transaction} transaction
126 * @param {Signature} signature
127 * @param {PublicKey} publicKey
128 * @param {number} inputIndex
129 * @param {Script} subscript
130 * @param {String} signingMethod - method used to sign - 'ecdsa' or 'schnorr'
131 * @return {boolean}
132 */
133function verify(transaction, signature, publicKey, inputIndex, subscript, signingMethod) {
134 $.checkArgument(!_.isUndefined(transaction), "Transaction Undefined");
135 $.checkArgument(!_.isUndefined(signature) && !_.isUndefined(signature.nhashtype), "Signature Undefined");
136
137 signingMethod = signingMethod || 'ecdsa';
138 let hashbuf = sighash(transaction, signature.nhashtype, inputIndex, subscript);
139 let verified = false;
140
141 switch (signingMethod) {
142 case 'ecdsa':
143 verified = ECDSA.verify(hashbuf, signature, publicKey, 'little');
144 break;
145 case 'schnorr':
146 verified = schnorr.verify(publicKey, hashbuf, signature);
147 break;
148 default:
149 throw new Error("signingMethod not supported ", signingMethod);
150 }
151 return verified;
152}
153
154/**
155 * @namespace Signing
156 */
157module.exports = {
158 sighash: sighash,
159 sign: sign,
160 verify: verify
161};