@exodus/bitcoinjs-lib-forks 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +3 -0
- package/LICENSE +21 -0
- package/README.md +207 -0
- package/package.json +67 -0
- package/src/address.js +97 -0
- package/src/block.js +177 -0
- package/src/bufferutils.js +56 -0
- package/src/crypto.js +29 -0
- package/src/ecdsa.js +161 -0
- package/src/ecpair.js +131 -0
- package/src/ecsignature.js +97 -0
- package/src/hdnode.js +316 -0
- package/src/index.js +23 -0
- package/src/networks.js +47 -0
- package/src/script.js +214 -0
- package/src/script_number.js +68 -0
- package/src/templates/index.js +74 -0
- package/src/templates/multisig/index.js +4 -0
- package/src/templates/multisig/input.js +71 -0
- package/src/templates/multisig/output.js +64 -0
- package/src/templates/nulldata.js +34 -0
- package/src/templates/pubkey/index.js +4 -0
- package/src/templates/pubkey/input.js +39 -0
- package/src/templates/pubkey/output.js +33 -0
- package/src/templates/pubkeyhash/index.js +4 -0
- package/src/templates/pubkeyhash/input.js +51 -0
- package/src/templates/pubkeyhash/output.js +42 -0
- package/src/templates/scripthash/index.js +4 -0
- package/src/templates/scripthash/input.js +84 -0
- package/src/templates/scripthash/output.js +34 -0
- package/src/templates/witnesscommitment/index.js +3 -0
- package/src/templates/witnesscommitment/output.js +42 -0
- package/src/templates/witnesspubkeyhash/index.js +4 -0
- package/src/templates/witnesspubkeyhash/input.js +44 -0
- package/src/templates/witnesspubkeyhash/output.js +33 -0
- package/src/templates/witnessscripthash/index.js +4 -0
- package/src/templates/witnessscripthash/input.js +63 -0
- package/src/templates/witnessscripthash/output.js +33 -0
- package/src/transaction.js +547 -0
- package/src/transaction_builder.js +825 -0
- package/src/types.js +53 -0
|
@@ -0,0 +1,825 @@
|
|
|
1
|
+
var Buffer = require('safe-buffer').Buffer
|
|
2
|
+
var baddress = require('./address')
|
|
3
|
+
var bcrypto = require('./crypto')
|
|
4
|
+
var bscript = require('./script')
|
|
5
|
+
var btemplates = require('./templates')
|
|
6
|
+
var networks = require('./networks')
|
|
7
|
+
var ops = require('bitcoin-ops')
|
|
8
|
+
var typeforce = require('typeforce')
|
|
9
|
+
var types = require('./types')
|
|
10
|
+
var scriptTypes = btemplates.types
|
|
11
|
+
var SIGNABLE = [btemplates.types.P2PKH, btemplates.types.P2PK, btemplates.types.MULTISIG]
|
|
12
|
+
var P2SH = SIGNABLE.concat([btemplates.types.P2WPKH, btemplates.types.P2WSH])
|
|
13
|
+
|
|
14
|
+
var ECPair = require('./ecpair')
|
|
15
|
+
var ECSignature = require('./ecsignature')
|
|
16
|
+
var Transaction = require('./transaction')
|
|
17
|
+
|
|
18
|
+
function supportedType (type) {
|
|
19
|
+
return SIGNABLE.indexOf(type) !== -1
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function supportedP2SHType (type) {
|
|
23
|
+
return P2SH.indexOf(type) !== -1
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function extractChunks (type, chunks, script) {
|
|
27
|
+
var pubKeys = []
|
|
28
|
+
var signatures = []
|
|
29
|
+
switch (type) {
|
|
30
|
+
case scriptTypes.P2PKH:
|
|
31
|
+
// if (redeemScript) throw new Error('Nonstandard... P2SH(P2PKH)')
|
|
32
|
+
pubKeys = chunks.slice(1)
|
|
33
|
+
signatures = chunks.slice(0, 1)
|
|
34
|
+
break
|
|
35
|
+
|
|
36
|
+
case scriptTypes.P2PK:
|
|
37
|
+
pubKeys[0] = script ? btemplates.pubKey.output.decode(script) : undefined
|
|
38
|
+
signatures = chunks.slice(0, 1)
|
|
39
|
+
break
|
|
40
|
+
|
|
41
|
+
case scriptTypes.MULTISIG:
|
|
42
|
+
if (script) {
|
|
43
|
+
var multisig = btemplates.multisig.output.decode(script)
|
|
44
|
+
pubKeys = multisig.pubKeys
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
signatures = chunks.slice(1).map(function (chunk) {
|
|
48
|
+
return chunk.length === 0 ? undefined : chunk
|
|
49
|
+
})
|
|
50
|
+
break
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
pubKeys: pubKeys,
|
|
55
|
+
signatures: signatures
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function expandInput (scriptSig, witnessStack) {
|
|
59
|
+
if (scriptSig.length === 0 && witnessStack.length === 0) return {}
|
|
60
|
+
|
|
61
|
+
var prevOutScript
|
|
62
|
+
var prevOutType
|
|
63
|
+
var scriptType
|
|
64
|
+
var script
|
|
65
|
+
var redeemScript
|
|
66
|
+
var witnessScript
|
|
67
|
+
var witnessScriptType
|
|
68
|
+
var redeemScriptType
|
|
69
|
+
var witness = false
|
|
70
|
+
var p2wsh = false
|
|
71
|
+
var p2sh = false
|
|
72
|
+
var witnessProgram
|
|
73
|
+
var chunks
|
|
74
|
+
|
|
75
|
+
var scriptSigChunks = bscript.decompile(scriptSig)
|
|
76
|
+
var sigType = btemplates.classifyInput(scriptSigChunks, true)
|
|
77
|
+
if (sigType === scriptTypes.P2SH) {
|
|
78
|
+
p2sh = true
|
|
79
|
+
redeemScript = scriptSigChunks[scriptSigChunks.length - 1]
|
|
80
|
+
redeemScriptType = btemplates.classifyOutput(redeemScript)
|
|
81
|
+
prevOutScript = btemplates.scriptHash.output.encode(bcrypto.hash160(redeemScript))
|
|
82
|
+
prevOutType = scriptTypes.P2SH
|
|
83
|
+
script = redeemScript
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
var classifyWitness = btemplates.classifyWitness(witnessStack, true)
|
|
87
|
+
if (classifyWitness === scriptTypes.P2WSH) {
|
|
88
|
+
witnessScript = witnessStack[witnessStack.length - 1]
|
|
89
|
+
witnessScriptType = btemplates.classifyOutput(witnessScript)
|
|
90
|
+
p2wsh = true
|
|
91
|
+
witness = true
|
|
92
|
+
if (scriptSig.length === 0) {
|
|
93
|
+
prevOutScript = btemplates.witnessScriptHash.output.encode(bcrypto.sha256(witnessScript))
|
|
94
|
+
prevOutType = scriptTypes.P2WSH
|
|
95
|
+
if (redeemScript !== undefined) {
|
|
96
|
+
throw new Error('Redeem script given when unnecessary')
|
|
97
|
+
}
|
|
98
|
+
// bare witness
|
|
99
|
+
} else {
|
|
100
|
+
if (!redeemScript) {
|
|
101
|
+
throw new Error('No redeemScript provided for P2WSH, but scriptSig non-empty')
|
|
102
|
+
}
|
|
103
|
+
witnessProgram = btemplates.witnessScriptHash.output.encode(bcrypto.sha256(witnessScript))
|
|
104
|
+
if (!redeemScript.equals(witnessProgram)) {
|
|
105
|
+
throw new Error('Redeem script didn\'t match witnessScript')
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (!supportedType(btemplates.classifyOutput(witnessScript))) {
|
|
110
|
+
throw new Error('unsupported witness script')
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
script = witnessScript
|
|
114
|
+
scriptType = witnessScriptType
|
|
115
|
+
chunks = witnessStack.slice(0, -1)
|
|
116
|
+
} else if (classifyWitness === scriptTypes.P2WPKH) {
|
|
117
|
+
witness = true
|
|
118
|
+
var key = witnessStack[witnessStack.length - 1]
|
|
119
|
+
var keyHash = bcrypto.hash160(key)
|
|
120
|
+
if (scriptSig.length === 0) {
|
|
121
|
+
prevOutScript = btemplates.witnessPubKeyHash.output.encode(keyHash)
|
|
122
|
+
prevOutType = scriptTypes.P2WPKH
|
|
123
|
+
if (typeof redeemScript !== 'undefined') {
|
|
124
|
+
throw new Error('Redeem script given when unnecessary')
|
|
125
|
+
}
|
|
126
|
+
} else {
|
|
127
|
+
if (!redeemScript) {
|
|
128
|
+
throw new Error('No redeemScript provided for P2WPKH, but scriptSig wasn\'t empty')
|
|
129
|
+
}
|
|
130
|
+
witnessProgram = btemplates.witnessPubKeyHash.output.encode(keyHash)
|
|
131
|
+
if (!redeemScript.equals(witnessProgram)) {
|
|
132
|
+
throw new Error('Redeem script did not have the right witness program')
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
scriptType = scriptTypes.P2PKH
|
|
137
|
+
chunks = witnessStack
|
|
138
|
+
} else if (redeemScript) {
|
|
139
|
+
if (!supportedP2SHType(redeemScriptType)) {
|
|
140
|
+
throw new Error('Bad redeemscript!')
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
script = redeemScript
|
|
144
|
+
scriptType = redeemScriptType
|
|
145
|
+
chunks = scriptSigChunks.slice(0, -1)
|
|
146
|
+
} else {
|
|
147
|
+
prevOutType = scriptType = btemplates.classifyInput(scriptSig)
|
|
148
|
+
chunks = scriptSigChunks
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
var expanded = extractChunks(scriptType, chunks, script)
|
|
152
|
+
|
|
153
|
+
var result = {
|
|
154
|
+
pubKeys: expanded.pubKeys,
|
|
155
|
+
signatures: expanded.signatures,
|
|
156
|
+
prevOutScript: prevOutScript,
|
|
157
|
+
prevOutType: prevOutType,
|
|
158
|
+
signType: scriptType,
|
|
159
|
+
signScript: script,
|
|
160
|
+
witness: Boolean(witness)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (p2sh) {
|
|
164
|
+
result.redeemScript = redeemScript
|
|
165
|
+
result.redeemScriptType = redeemScriptType
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (p2wsh) {
|
|
169
|
+
result.witnessScript = witnessScript
|
|
170
|
+
result.witnessScriptType = witnessScriptType
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return result
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// could be done in expandInput, but requires the original Transaction for hashForSignature
|
|
177
|
+
function fixMultisigOrder (input, transaction, vin, value, forkId) {
|
|
178
|
+
if (input.redeemScriptType !== scriptTypes.MULTISIG || !input.redeemScript) return
|
|
179
|
+
if (input.pubKeys.length === input.signatures.length) return
|
|
180
|
+
|
|
181
|
+
var unmatched = input.signatures.concat()
|
|
182
|
+
|
|
183
|
+
input.signatures = input.pubKeys.map(function (pubKey) {
|
|
184
|
+
var keyPair = ECPair.fromPublicKeyBuffer(pubKey)
|
|
185
|
+
var match
|
|
186
|
+
|
|
187
|
+
// check for a signature
|
|
188
|
+
unmatched.some(function (signature, i) {
|
|
189
|
+
// skip if undefined || OP_0
|
|
190
|
+
if (!signature) return false
|
|
191
|
+
|
|
192
|
+
// TODO: avoid O(n) hashForSignature
|
|
193
|
+
var parsed = ECSignature.parseScriptSignature(signature)
|
|
194
|
+
var hash
|
|
195
|
+
switch (forkId) {
|
|
196
|
+
case Transaction.FORKID_BCH:
|
|
197
|
+
hash = transaction.hashForCashSignature(vin, input.signScript, value, parsed.hashType)
|
|
198
|
+
break
|
|
199
|
+
case Transaction.FORKID_BTG:
|
|
200
|
+
hash = transaction.hashForGoldSignature(vin, input.signScript, value, parsed.hashType)
|
|
201
|
+
break
|
|
202
|
+
default:
|
|
203
|
+
if (input.witness) {
|
|
204
|
+
hash = transaction.hashForWitnessV0(vin, input.signScript, value, parsed.hashType)
|
|
205
|
+
} else {
|
|
206
|
+
hash = transaction.hashForSignature(vin, input.signScript, parsed.hashType)
|
|
207
|
+
}
|
|
208
|
+
break
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// skip if signature does not match pubKey
|
|
212
|
+
if (!keyPair.verify(hash, parsed.signature)) return false
|
|
213
|
+
|
|
214
|
+
// remove matched signature from unmatched
|
|
215
|
+
unmatched[i] = undefined
|
|
216
|
+
match = signature
|
|
217
|
+
|
|
218
|
+
return true
|
|
219
|
+
})
|
|
220
|
+
|
|
221
|
+
return match
|
|
222
|
+
})
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function expandOutput (script, scriptType, ourPubKey) {
|
|
226
|
+
typeforce(types.Buffer, script)
|
|
227
|
+
|
|
228
|
+
var scriptChunks = bscript.decompile(script)
|
|
229
|
+
if (!scriptType) {
|
|
230
|
+
scriptType = btemplates.classifyOutput(script)
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
var pubKeys = []
|
|
234
|
+
|
|
235
|
+
switch (scriptType) {
|
|
236
|
+
// does our hash160(pubKey) match the output scripts?
|
|
237
|
+
case scriptTypes.P2PKH:
|
|
238
|
+
if (!ourPubKey) break
|
|
239
|
+
|
|
240
|
+
var pkh1 = scriptChunks[2]
|
|
241
|
+
var pkh2 = bcrypto.hash160(ourPubKey)
|
|
242
|
+
if (pkh1.equals(pkh2)) pubKeys = [ourPubKey]
|
|
243
|
+
break
|
|
244
|
+
|
|
245
|
+
// does our hash160(pubKey) match the output scripts?
|
|
246
|
+
case scriptTypes.P2WPKH:
|
|
247
|
+
if (!ourPubKey) break
|
|
248
|
+
|
|
249
|
+
var wpkh1 = scriptChunks[1]
|
|
250
|
+
var wpkh2 = bcrypto.hash160(ourPubKey)
|
|
251
|
+
if (wpkh1.equals(wpkh2)) pubKeys = [ourPubKey]
|
|
252
|
+
break
|
|
253
|
+
|
|
254
|
+
case scriptTypes.P2PK:
|
|
255
|
+
pubKeys = scriptChunks.slice(0, 1)
|
|
256
|
+
break
|
|
257
|
+
|
|
258
|
+
case scriptTypes.MULTISIG:
|
|
259
|
+
pubKeys = scriptChunks.slice(1, -2)
|
|
260
|
+
break
|
|
261
|
+
|
|
262
|
+
default: return { scriptType: scriptType }
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
return {
|
|
266
|
+
pubKeys: pubKeys,
|
|
267
|
+
scriptType: scriptType,
|
|
268
|
+
signatures: pubKeys.map(function () { return undefined })
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function checkP2SHInput (input, redeemScriptHash) {
|
|
273
|
+
if (input.prevOutType) {
|
|
274
|
+
if (input.prevOutType !== scriptTypes.P2SH) throw new Error('PrevOutScript must be P2SH')
|
|
275
|
+
|
|
276
|
+
var prevOutScriptScriptHash = bscript.decompile(input.prevOutScript)[1]
|
|
277
|
+
if (!prevOutScriptScriptHash.equals(redeemScriptHash)) throw new Error('Inconsistent hash160(RedeemScript)')
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function checkP2WSHInput (input, witnessScriptHash) {
|
|
282
|
+
if (input.prevOutType) {
|
|
283
|
+
if (input.prevOutType !== scriptTypes.P2WSH) throw new Error('PrevOutScript must be P2WSH')
|
|
284
|
+
|
|
285
|
+
var scriptHash = bscript.decompile(input.prevOutScript)[1]
|
|
286
|
+
if (!scriptHash.equals(witnessScriptHash)) throw new Error('Inconsistent sha25(WitnessScript)')
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function prepareInput (input, kpPubKey, redeemScript, witnessValue, witnessScript) {
|
|
291
|
+
var expanded
|
|
292
|
+
var prevOutType
|
|
293
|
+
var prevOutScript
|
|
294
|
+
|
|
295
|
+
var p2sh = false
|
|
296
|
+
var p2shType
|
|
297
|
+
var redeemScriptHash
|
|
298
|
+
|
|
299
|
+
var witness = false
|
|
300
|
+
var p2wsh = false
|
|
301
|
+
var witnessType
|
|
302
|
+
var witnessScriptHash
|
|
303
|
+
|
|
304
|
+
var signType
|
|
305
|
+
var signScript
|
|
306
|
+
|
|
307
|
+
if (redeemScript && witnessScript) {
|
|
308
|
+
redeemScriptHash = bcrypto.hash160(redeemScript)
|
|
309
|
+
witnessScriptHash = bcrypto.sha256(witnessScript)
|
|
310
|
+
checkP2SHInput(input, redeemScriptHash)
|
|
311
|
+
|
|
312
|
+
if (!redeemScript.equals(btemplates.witnessScriptHash.output.encode(witnessScriptHash))) throw new Error('Witness script inconsistent with redeem script')
|
|
313
|
+
|
|
314
|
+
expanded = expandOutput(witnessScript, undefined, kpPubKey)
|
|
315
|
+
if (!expanded.pubKeys) throw new Error('WitnessScript not supported "' + bscript.toASM(redeemScript) + '"')
|
|
316
|
+
|
|
317
|
+
prevOutType = btemplates.types.P2SH
|
|
318
|
+
prevOutScript = btemplates.scriptHash.output.encode(redeemScriptHash)
|
|
319
|
+
p2sh = witness = p2wsh = true
|
|
320
|
+
p2shType = btemplates.types.P2WSH
|
|
321
|
+
signType = witnessType = expanded.scriptType
|
|
322
|
+
signScript = witnessScript
|
|
323
|
+
} else if (redeemScript) {
|
|
324
|
+
redeemScriptHash = bcrypto.hash160(redeemScript)
|
|
325
|
+
checkP2SHInput(input, redeemScriptHash)
|
|
326
|
+
|
|
327
|
+
expanded = expandOutput(redeemScript, undefined, kpPubKey)
|
|
328
|
+
if (!expanded.pubKeys) throw new Error('RedeemScript not supported "' + bscript.toASM(redeemScript) + '"')
|
|
329
|
+
|
|
330
|
+
prevOutType = btemplates.types.P2SH
|
|
331
|
+
prevOutScript = btemplates.scriptHash.output.encode(redeemScriptHash)
|
|
332
|
+
p2sh = true
|
|
333
|
+
signType = p2shType = expanded.scriptType
|
|
334
|
+
signScript = redeemScript
|
|
335
|
+
witness = signType === btemplates.types.P2WPKH
|
|
336
|
+
} else if (witnessScript) {
|
|
337
|
+
witnessScriptHash = bcrypto.sha256(witnessScript)
|
|
338
|
+
checkP2WSHInput(input, witnessScriptHash)
|
|
339
|
+
|
|
340
|
+
expanded = expandOutput(witnessScript, undefined, kpPubKey)
|
|
341
|
+
if (!expanded.pubKeys) throw new Error('WitnessScript not supported "' + bscript.toASM(redeemScript) + '"')
|
|
342
|
+
|
|
343
|
+
prevOutType = btemplates.types.P2WSH
|
|
344
|
+
prevOutScript = btemplates.witnessScriptHash.output.encode(witnessScriptHash)
|
|
345
|
+
witness = p2wsh = true
|
|
346
|
+
signType = witnessType = expanded.scriptType
|
|
347
|
+
signScript = witnessScript
|
|
348
|
+
} else if (input.prevOutType) {
|
|
349
|
+
// embedded scripts are not possible without a redeemScript
|
|
350
|
+
if (input.prevOutType === scriptTypes.P2SH ||
|
|
351
|
+
input.prevOutType === scriptTypes.P2WSH) {
|
|
352
|
+
throw new Error('PrevOutScript is ' + input.prevOutType + ', requires redeemScript')
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
prevOutType = input.prevOutType
|
|
356
|
+
prevOutScript = input.prevOutScript
|
|
357
|
+
expanded = expandOutput(input.prevOutScript, input.prevOutType, kpPubKey)
|
|
358
|
+
if (!expanded.pubKeys) return
|
|
359
|
+
|
|
360
|
+
witness = (input.prevOutType === scriptTypes.P2WPKH)
|
|
361
|
+
signType = prevOutType
|
|
362
|
+
signScript = prevOutScript
|
|
363
|
+
} else {
|
|
364
|
+
prevOutScript = btemplates.pubKeyHash.output.encode(bcrypto.hash160(kpPubKey))
|
|
365
|
+
expanded = expandOutput(prevOutScript, scriptTypes.P2PKH, kpPubKey)
|
|
366
|
+
|
|
367
|
+
prevOutType = scriptTypes.P2PKH
|
|
368
|
+
witness = false
|
|
369
|
+
signType = prevOutType
|
|
370
|
+
signScript = prevOutScript
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
if (signType === scriptTypes.P2WPKH) {
|
|
374
|
+
signScript = btemplates.pubKeyHash.output.encode(btemplates.witnessPubKeyHash.output.decode(signScript))
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
if (p2sh) {
|
|
378
|
+
input.redeemScript = redeemScript
|
|
379
|
+
input.redeemScriptType = p2shType
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
if (p2wsh) {
|
|
383
|
+
input.witnessScript = witnessScript
|
|
384
|
+
input.witnessScriptType = witnessType
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
input.pubKeys = expanded.pubKeys
|
|
388
|
+
input.signatures = expanded.signatures
|
|
389
|
+
input.signScript = signScript
|
|
390
|
+
input.signType = signType
|
|
391
|
+
input.prevOutScript = prevOutScript
|
|
392
|
+
input.prevOutType = prevOutType
|
|
393
|
+
input.witness = witness
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
function buildStack (type, signatures, pubKeys, allowIncomplete) {
|
|
397
|
+
if (type === scriptTypes.P2PKH) {
|
|
398
|
+
if (signatures.length === 1 && Buffer.isBuffer(signatures[0]) && pubKeys.length === 1) return btemplates.pubKeyHash.input.encodeStack(signatures[0], pubKeys[0])
|
|
399
|
+
} else if (type === scriptTypes.P2PK) {
|
|
400
|
+
if (signatures.length === 1 && Buffer.isBuffer(signatures[0])) return btemplates.pubKey.input.encodeStack(signatures[0])
|
|
401
|
+
} else if (type === scriptTypes.MULTISIG) {
|
|
402
|
+
if (signatures.length > 0) {
|
|
403
|
+
signatures = signatures.map(function (signature) {
|
|
404
|
+
return signature || ops.OP_0
|
|
405
|
+
})
|
|
406
|
+
if (!allowIncomplete) {
|
|
407
|
+
// remove blank signatures
|
|
408
|
+
signatures = signatures.filter(function (x) { return x !== ops.OP_0 })
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
return btemplates.multisig.input.encodeStack(signatures)
|
|
412
|
+
}
|
|
413
|
+
} else {
|
|
414
|
+
throw new Error('Not yet supported')
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
if (!allowIncomplete) throw new Error('Not enough signatures provided')
|
|
418
|
+
return []
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
function buildInput (input, allowIncomplete) {
|
|
422
|
+
var scriptType = input.prevOutType
|
|
423
|
+
var sig = []
|
|
424
|
+
var witness = []
|
|
425
|
+
|
|
426
|
+
if (supportedType(scriptType)) {
|
|
427
|
+
sig = buildStack(scriptType, input.signatures, input.pubKeys, allowIncomplete)
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
var p2sh = false
|
|
431
|
+
if (scriptType === btemplates.types.P2SH) {
|
|
432
|
+
// We can remove this error later when we have a guarantee prepareInput
|
|
433
|
+
// rejects unsignable scripts - it MUST be signable at this point.
|
|
434
|
+
if (!allowIncomplete && !supportedP2SHType(input.redeemScriptType)) {
|
|
435
|
+
throw new Error('Impossible to sign this type')
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
if (supportedType(input.redeemScriptType)) {
|
|
439
|
+
sig = buildStack(input.redeemScriptType, input.signatures, input.pubKeys, allowIncomplete)
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
// If it wasn't SIGNABLE, it's witness, defer to that
|
|
443
|
+
if (input.redeemScriptType) {
|
|
444
|
+
p2sh = true
|
|
445
|
+
scriptType = input.redeemScriptType
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
switch (scriptType) {
|
|
450
|
+
// P2WPKH is a special case of P2PKH
|
|
451
|
+
case btemplates.types.P2WPKH:
|
|
452
|
+
witness = buildStack(btemplates.types.P2PKH, input.signatures, input.pubKeys, allowIncomplete)
|
|
453
|
+
break
|
|
454
|
+
|
|
455
|
+
case btemplates.types.P2WSH:
|
|
456
|
+
// We can remove this check later
|
|
457
|
+
if (!allowIncomplete && !supportedType(input.witnessScriptType)) {
|
|
458
|
+
throw new Error('Impossible to sign this type')
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
if (supportedType(input.witnessScriptType)) {
|
|
462
|
+
witness = buildStack(input.witnessScriptType, input.signatures, input.pubKeys, allowIncomplete)
|
|
463
|
+
witness.push(input.witnessScript)
|
|
464
|
+
scriptType = input.witnessScriptType
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
break
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// append redeemScript if necessary
|
|
471
|
+
if (p2sh) {
|
|
472
|
+
sig.push(input.redeemScript)
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
return {
|
|
476
|
+
type: scriptType,
|
|
477
|
+
script: bscript.compile(sig),
|
|
478
|
+
witness: witness
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
function TransactionBuilder (network, maximumFeeRate) {
|
|
483
|
+
this.prevTxMap = {}
|
|
484
|
+
this.network = network || networks.bitcoin
|
|
485
|
+
|
|
486
|
+
// WARNING: This is __NOT__ to be relied on, its just another potential safety mechanism (safety in-depth)
|
|
487
|
+
this.maximumFeeRate = maximumFeeRate || 2500
|
|
488
|
+
|
|
489
|
+
this.inputs = []
|
|
490
|
+
this.bitcoinCash = false
|
|
491
|
+
this.bitcoinGold = false
|
|
492
|
+
this.tx = new Transaction()
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
TransactionBuilder.prototype.enableBitcoinCash = function (enable) {
|
|
496
|
+
if (typeof enable === 'undefined') {
|
|
497
|
+
enable = true
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
this.bitcoinCash = enable
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
TransactionBuilder.prototype.enableBitcoinGold = function (enable) {
|
|
504
|
+
if (typeof enable === 'undefined') {
|
|
505
|
+
enable = true
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
this.bitcoinGold = enable
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
TransactionBuilder.prototype.setLockTime = function (locktime) {
|
|
512
|
+
typeforce(types.UInt32, locktime)
|
|
513
|
+
|
|
514
|
+
// if any signatures exist, throw
|
|
515
|
+
if (this.inputs.some(function (input) {
|
|
516
|
+
if (!input.signatures) return false
|
|
517
|
+
|
|
518
|
+
return input.signatures.some(function (s) { return s })
|
|
519
|
+
})) {
|
|
520
|
+
throw new Error('No, this would invalidate signatures')
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
this.tx.locktime = locktime
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
TransactionBuilder.prototype.setVersion = function (version) {
|
|
527
|
+
typeforce(types.UInt32, version)
|
|
528
|
+
|
|
529
|
+
// XXX: this might eventually become more complex depending on what the versions represent
|
|
530
|
+
this.tx.version = version
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
TransactionBuilder.fromTransaction = function (transaction, network, forkId) {
|
|
534
|
+
var txb = new TransactionBuilder(network)
|
|
535
|
+
|
|
536
|
+
if (typeof forkId === 'number') {
|
|
537
|
+
if (forkId === Transaction.FORKID_BTG) {
|
|
538
|
+
txb.enableBitcoinGold(true)
|
|
539
|
+
} else if (forkId === Transaction.FORKID_BCH) {
|
|
540
|
+
txb.enableBitcoinCash(true)
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
// Copy transaction fields
|
|
545
|
+
txb.setVersion(transaction.version)
|
|
546
|
+
txb.setLockTime(transaction.locktime)
|
|
547
|
+
|
|
548
|
+
// Copy outputs (done first to avoid signature invalidation)
|
|
549
|
+
transaction.outs.forEach(function (txOut) {
|
|
550
|
+
txb.addOutput(txOut.script, txOut.value)
|
|
551
|
+
})
|
|
552
|
+
|
|
553
|
+
// Copy inputs
|
|
554
|
+
transaction.ins.forEach(function (txIn) {
|
|
555
|
+
txb.__addInputUnsafe(txIn.hash, txIn.index, {
|
|
556
|
+
sequence: txIn.sequence,
|
|
557
|
+
script: txIn.script,
|
|
558
|
+
witness: txIn.witness,
|
|
559
|
+
value: txIn.value
|
|
560
|
+
})
|
|
561
|
+
})
|
|
562
|
+
|
|
563
|
+
// fix some things not possible through the public API
|
|
564
|
+
txb.inputs.forEach(function (input, i) {
|
|
565
|
+
fixMultisigOrder(input, transaction, i, input.value, forkId)
|
|
566
|
+
})
|
|
567
|
+
|
|
568
|
+
return txb
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
TransactionBuilder.prototype.addInput = function (txHash, vout, sequence, prevOutScript) {
|
|
572
|
+
if (!this.__canModifyInputs()) {
|
|
573
|
+
throw new Error('No, this would invalidate signatures')
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
var value
|
|
577
|
+
|
|
578
|
+
// is it a hex string?
|
|
579
|
+
if (typeof txHash === 'string') {
|
|
580
|
+
// transaction hashs's are displayed in reverse order, un-reverse it
|
|
581
|
+
txHash = Buffer.from(txHash, 'hex').reverse()
|
|
582
|
+
|
|
583
|
+
// is it a Transaction object?
|
|
584
|
+
} else if (txHash instanceof Transaction) {
|
|
585
|
+
var txOut = txHash.outs[vout]
|
|
586
|
+
prevOutScript = txOut.script
|
|
587
|
+
value = txOut.value
|
|
588
|
+
|
|
589
|
+
txHash = txHash.getHash()
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
return this.__addInputUnsafe(txHash, vout, {
|
|
593
|
+
sequence: sequence,
|
|
594
|
+
prevOutScript: prevOutScript,
|
|
595
|
+
value: value
|
|
596
|
+
})
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
TransactionBuilder.prototype.__addInputUnsafe = function (txHash, vout, options) {
|
|
600
|
+
if (Transaction.isCoinbaseHash(txHash)) {
|
|
601
|
+
throw new Error('coinbase inputs not supported')
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
var prevTxOut = txHash.toString('hex') + ':' + vout
|
|
605
|
+
if (this.prevTxMap[prevTxOut] !== undefined) throw new Error('Duplicate TxOut: ' + prevTxOut)
|
|
606
|
+
|
|
607
|
+
var input = {}
|
|
608
|
+
|
|
609
|
+
// derive what we can from the scriptSig
|
|
610
|
+
if (options.script !== undefined) {
|
|
611
|
+
input = expandInput(options.script, options.witness || [])
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
// if an input value was given, retain it
|
|
615
|
+
if (options.value !== undefined) {
|
|
616
|
+
input.value = options.value
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
// derive what we can from the previous transactions output script
|
|
620
|
+
if (!input.prevOutScript && options.prevOutScript) {
|
|
621
|
+
var prevOutType
|
|
622
|
+
|
|
623
|
+
if (!input.pubKeys && !input.signatures) {
|
|
624
|
+
var expanded = expandOutput(options.prevOutScript)
|
|
625
|
+
|
|
626
|
+
if (expanded.pubKeys) {
|
|
627
|
+
input.pubKeys = expanded.pubKeys
|
|
628
|
+
input.signatures = expanded.signatures
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
prevOutType = expanded.scriptType
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
input.prevOutScript = options.prevOutScript
|
|
635
|
+
input.prevOutType = prevOutType || btemplates.classifyOutput(options.prevOutScript)
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
var vin = this.tx.addInput(txHash, vout, options.sequence, options.scriptSig)
|
|
639
|
+
this.inputs[vin] = input
|
|
640
|
+
this.prevTxMap[prevTxOut] = vin
|
|
641
|
+
return vin
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
TransactionBuilder.prototype.addOutput = function (scriptPubKey, value) {
|
|
645
|
+
if (!this.__canModifyOutputs()) {
|
|
646
|
+
throw new Error('No, this would invalidate signatures')
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
// Attempt to get a script if it's a base58 address string
|
|
650
|
+
if (typeof scriptPubKey === 'string') {
|
|
651
|
+
scriptPubKey = baddress.toOutputScript(scriptPubKey, this.network)
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
return this.tx.addOutput(scriptPubKey, value)
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
TransactionBuilder.prototype.build = function () {
|
|
658
|
+
return this.__build(false)
|
|
659
|
+
}
|
|
660
|
+
TransactionBuilder.prototype.buildIncomplete = function () {
|
|
661
|
+
return this.__build(true)
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
TransactionBuilder.prototype.__build = function (allowIncomplete) {
|
|
665
|
+
if (!allowIncomplete) {
|
|
666
|
+
if (!this.tx.ins.length) throw new Error('Transaction has no inputs')
|
|
667
|
+
if (!this.tx.outs.length) throw new Error('Transaction has no outputs')
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
var tx = this.tx.clone()
|
|
671
|
+
// Create script signatures from inputs
|
|
672
|
+
this.inputs.forEach(function (input, i) {
|
|
673
|
+
var scriptType = input.witnessScriptType || input.redeemScriptType || input.prevOutType
|
|
674
|
+
if (!scriptType && !allowIncomplete) throw new Error('Transaction is not complete')
|
|
675
|
+
var result = buildInput(input, allowIncomplete)
|
|
676
|
+
|
|
677
|
+
// skip if no result
|
|
678
|
+
if (!allowIncomplete) {
|
|
679
|
+
if (!supportedType(result.type) && result.type !== btemplates.types.P2WPKH) {
|
|
680
|
+
throw new Error(result.type + ' not supported')
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
tx.setInputScript(i, result.script)
|
|
685
|
+
tx.setWitness(i, result.witness)
|
|
686
|
+
})
|
|
687
|
+
|
|
688
|
+
if (!allowIncomplete) {
|
|
689
|
+
// do not rely on this, its merely a last resort
|
|
690
|
+
if (this.__overMaximumFees(tx.virtualSize())) {
|
|
691
|
+
throw new Error('Transaction has absurd fees')
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
return tx
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
function canSign (input) {
|
|
699
|
+
return input.prevOutScript !== undefined &&
|
|
700
|
+
input.signScript !== undefined &&
|
|
701
|
+
input.pubKeys !== undefined &&
|
|
702
|
+
input.signatures !== undefined &&
|
|
703
|
+
input.signatures.length === input.pubKeys.length &&
|
|
704
|
+
input.pubKeys.length > 0 &&
|
|
705
|
+
(
|
|
706
|
+
input.witness === false ||
|
|
707
|
+
(input.witness === true && input.value !== undefined)
|
|
708
|
+
)
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
TransactionBuilder.prototype.sign = function (vin, keyPair, redeemScript, hashType, witnessValue, witnessScript) {
|
|
712
|
+
// TODO: remove keyPair.network matching in 4.0.0
|
|
713
|
+
if (keyPair.network && keyPair.network !== this.network) throw new TypeError('Inconsistent network')
|
|
714
|
+
if (!this.inputs[vin]) throw new Error('No input at index: ' + vin)
|
|
715
|
+
hashType = hashType || Transaction.SIGHASH_ALL
|
|
716
|
+
|
|
717
|
+
var input = this.inputs[vin]
|
|
718
|
+
|
|
719
|
+
// if redeemScript was previously provided, enforce consistency
|
|
720
|
+
if (input.redeemScript !== undefined &&
|
|
721
|
+
redeemScript &&
|
|
722
|
+
!input.redeemScript.equals(redeemScript)) {
|
|
723
|
+
throw new Error('Inconsistent redeemScript')
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
var kpPubKey = keyPair.publicKey || keyPair.getPublicKeyBuffer()
|
|
727
|
+
if (!canSign(input)) {
|
|
728
|
+
if (witnessValue !== undefined) {
|
|
729
|
+
if (input.value !== undefined && input.value !== witnessValue) throw new Error('Input didn\'t match witnessValue')
|
|
730
|
+
typeforce(types.Satoshi, witnessValue)
|
|
731
|
+
input.value = witnessValue
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
if (!canSign(input)) prepareInput(input, kpPubKey, redeemScript, witnessValue, witnessScript)
|
|
735
|
+
if (!canSign(input)) throw Error(input.prevOutType + ' not supported')
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
// ready to sign
|
|
739
|
+
var signatureHash
|
|
740
|
+
if (this.bitcoinGold) {
|
|
741
|
+
signatureHash = this.tx.hashForGoldSignature(vin, input.signScript, witnessValue, hashType, input.witness)
|
|
742
|
+
} else if (this.bitcoinCash) {
|
|
743
|
+
signatureHash = this.tx.hashForCashSignature(vin, input.signScript, witnessValue, hashType)
|
|
744
|
+
} else {
|
|
745
|
+
if (input.witness) {
|
|
746
|
+
signatureHash = this.tx.hashForWitnessV0(vin, input.signScript, witnessValue, hashType)
|
|
747
|
+
} else {
|
|
748
|
+
signatureHash = this.tx.hashForSignature(vin, input.signScript, hashType)
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
// enforce in order signing of public keys
|
|
753
|
+
var signed = input.pubKeys.some(function (pubKey, i) {
|
|
754
|
+
if (!kpPubKey.equals(pubKey)) return false
|
|
755
|
+
if (input.signatures[i]) throw new Error('Signature already exists')
|
|
756
|
+
if (kpPubKey.length !== 33 &&
|
|
757
|
+
input.signType === scriptTypes.P2WPKH) throw new Error('BIP143 rejects uncompressed public keys in P2WPKH or P2WSH')
|
|
758
|
+
|
|
759
|
+
var signature = keyPair.sign(signatureHash)
|
|
760
|
+
if (Buffer.isBuffer(signature)) signature = ECSignature.fromRSBuffer(signature)
|
|
761
|
+
|
|
762
|
+
input.signatures[i] = signature.toScriptSignature(hashType)
|
|
763
|
+
return true
|
|
764
|
+
})
|
|
765
|
+
|
|
766
|
+
if (!signed) throw new Error('Key pair cannot sign for this input')
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
function signatureHashType (buffer) {
|
|
770
|
+
return buffer.readUInt8(buffer.length - 1)
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
TransactionBuilder.prototype.__canModifyInputs = function () {
|
|
774
|
+
return this.inputs.every(function (input) {
|
|
775
|
+
// any signatures?
|
|
776
|
+
if (input.signatures === undefined) return true
|
|
777
|
+
|
|
778
|
+
return input.signatures.every(function (signature) {
|
|
779
|
+
if (!signature) return true
|
|
780
|
+
var hashType = signatureHashType(signature)
|
|
781
|
+
|
|
782
|
+
// if SIGHASH_ANYONECANPAY is set, signatures would not
|
|
783
|
+
// be invalidated by more inputs
|
|
784
|
+
return hashType & Transaction.SIGHASH_ANYONECANPAY
|
|
785
|
+
})
|
|
786
|
+
})
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
TransactionBuilder.prototype.__canModifyOutputs = function () {
|
|
790
|
+
var nInputs = this.tx.ins.length
|
|
791
|
+
var nOutputs = this.tx.outs.length
|
|
792
|
+
|
|
793
|
+
return this.inputs.every(function (input) {
|
|
794
|
+
if (input.signatures === undefined) return true
|
|
795
|
+
|
|
796
|
+
return input.signatures.every(function (signature) {
|
|
797
|
+
if (!signature) return true
|
|
798
|
+
var hashType = signatureHashType(signature)
|
|
799
|
+
|
|
800
|
+
var hashTypeMod = hashType & 0x1f
|
|
801
|
+
if (hashTypeMod === Transaction.SIGHASH_NONE) return true
|
|
802
|
+
if (hashTypeMod === Transaction.SIGHASH_SINGLE) {
|
|
803
|
+
// if SIGHASH_SINGLE is set, and nInputs > nOutputs
|
|
804
|
+
// some signatures would be invalidated by the addition
|
|
805
|
+
// of more outputs
|
|
806
|
+
return nInputs <= nOutputs
|
|
807
|
+
}
|
|
808
|
+
})
|
|
809
|
+
})
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
TransactionBuilder.prototype.__overMaximumFees = function (bytes) {
|
|
813
|
+
// not all inputs will have .value defined
|
|
814
|
+
var incoming = this.inputs.reduce(function (a, x) { return a + (x.value >>> 0) }, 0)
|
|
815
|
+
|
|
816
|
+
// but all outputs do, and if we have any input value
|
|
817
|
+
// we can immediately determine if the outputs are too small
|
|
818
|
+
var outgoing = this.tx.outs.reduce(function (a, x) { return a + x.value }, 0)
|
|
819
|
+
var fee = incoming - outgoing
|
|
820
|
+
var feeRate = fee / bytes
|
|
821
|
+
|
|
822
|
+
return feeRate > this.maximumFeeRate
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
module.exports = TransactionBuilder
|