UNPKG

11.5 kBJavaScriptView Raw
1/**
2 * Transaction
3 * ===========
4 *
5 * A bitcoin transaction.
6 */
7'use strict'
8
9import { Bn } from './bn'
10import { Br } from './br'
11import { Bw } from './bw'
12import { Ecdsa } from './ecdsa'
13import { Hash } from './hash'
14import { HashCache } from './hash-cache'
15import { Script } from './script'
16import { Sig } from './sig'
17import { Struct } from './struct'
18import { TxIn } from './tx-in'
19import { TxOut } from './tx-out'
20import { VarInt } from './var-int'
21import { Workers } from './workers'
22
23class Tx extends Struct {
24 constructor (
25 versionBytesNum = 1,
26 txInsVi = VarInt.fromNumber(0),
27 txIns = [],
28 txOutsVi = VarInt.fromNumber(0),
29 txOuts = [],
30 nLockTime = 0
31 ) {
32 super({ versionBytesNum, txInsVi, txIns, txOutsVi, txOuts, nLockTime })
33 }
34
35 fromJSON (json) {
36 const txIns = []
37 json.txIns.forEach(function (txIn) {
38 txIns.push(new TxIn().fromJSON(txIn))
39 })
40 const txOuts = []
41 json.txOuts.forEach(function (txOut) {
42 txOuts.push(new TxOut().fromJSON(txOut))
43 })
44 this.fromObject({
45 versionBytesNum: json.versionBytesNum,
46 txInsVi: new VarInt().fromJSON(json.txInsVi),
47 txIns: txIns,
48 txOutsVi: new VarInt().fromJSON(json.txOutsVi),
49 txOuts: txOuts,
50 nLockTime: json.nLockTime
51 })
52 return this
53 }
54
55 toJSON () {
56 const txIns = []
57 this.txIns.forEach(function (txIn) {
58 txIns.push(txIn.toJSON())
59 })
60 const txOuts = []
61 this.txOuts.forEach(function (txOut) {
62 txOuts.push(txOut.toJSON())
63 })
64 return {
65 versionBytesNum: this.versionBytesNum,
66 txInsVi: this.txInsVi.toJSON(),
67 txIns: txIns,
68 txOutsVi: this.txOutsVi.toJSON(),
69 txOuts: txOuts,
70 nLockTime: this.nLockTime
71 }
72 }
73
74 fromBr (br) {
75 this.versionBytesNum = br.readUInt32LE()
76 this.txInsVi = new VarInt(br.readVarIntBuf())
77 const txInsNum = this.txInsVi.toNumber()
78 this.txIns = []
79 for (let i = 0; i < txInsNum; i++) {
80 this.txIns.push(new TxIn().fromBr(br))
81 }
82 this.txOutsVi = new VarInt(br.readVarIntBuf())
83 const txOutsNum = this.txOutsVi.toNumber()
84 this.txOuts = []
85 for (let i = 0; i < txOutsNum; i++) {
86 this.txOuts.push(new TxOut().fromBr(br))
87 }
88 this.nLockTime = br.readUInt32LE()
89 return this
90 }
91
92 toBw (bw) {
93 if (!bw) {
94 bw = new Bw()
95 }
96 bw.writeUInt32LE(this.versionBytesNum)
97 bw.write(this.txInsVi.buf)
98 for (let i = 0; i < this.txIns.length; i++) {
99 this.txIns[i].toBw(bw)
100 }
101 bw.write(this.txOutsVi.buf)
102 for (let i = 0; i < this.txOuts.length; i++) {
103 this.txOuts[i].toBw(bw)
104 }
105 bw.writeUInt32LE(this.nLockTime)
106 return bw
107 }
108
109 // https://github.com/Bitcoin-UAHF/spec/blob/master/replay-protected-sighash.md
110 hashPrevouts () {
111 const bw = new Bw()
112 for (const i in this.txIns) {
113 const txIn = this.txIns[i]
114 bw.write(txIn.txHashBuf) // outpoint (1/2)
115 bw.writeUInt32LE(txIn.txOutNum) // outpoint (2/2)
116 }
117 return Hash.sha256Sha256(bw.toBuffer())
118 }
119
120 hashSequence () {
121 const bw = new Bw()
122 for (const i in this.txIns) {
123 const txIn = this.txIns[i]
124 bw.writeUInt32LE(txIn.nSequence)
125 }
126 return Hash.sha256Sha256(bw.toBuffer())
127 }
128
129 hashOutputs () {
130 const bw = new Bw()
131 for (const i in this.txOuts) {
132 const txOut = this.txOuts[i]
133 bw.write(txOut.toBuffer())
134 }
135 return Hash.sha256Sha256(bw.toBuffer())
136 }
137
138 /**
139 * For a normal transaction, subScript is usually the scriptPubKey. For a
140 * p2sh transaction, subScript is usually the redeemScript. If you're not
141 * normal because you're using OP_CODESEPARATORs, you know what to do.
142 */
143 sighash (nHashType, nIn, subScript, valueBn, flags = 0, hashCache = new HashCache()) {
144 // start with UAHF part (Bitcoin SV)
145 // https://github.com/Bitcoin-UAHF/spec/blob/master/replay-protected-sighash.md
146 if (
147 nHashType & Sig.SIGHASH_FORKID &&
148 flags & Tx.SCRIPT_ENABLE_SIGHASH_FORKID
149 ) {
150 let hashPrevouts = Buffer.alloc(32, 0)
151 let hashSequence = Buffer.alloc(32, 0)
152 let hashOutputs = Buffer.alloc(32, 0)
153
154 if (!(nHashType & Sig.SIGHASH_ANYONECANPAY)) {
155 hashPrevouts = hashCache.prevoutsHashBuf ? hashCache.prevoutsHashBuf : hashCache.prevoutsHashBuf = this.hashPrevouts()
156 }
157
158 if (
159 !(nHashType & Sig.SIGHASH_ANYONECANPAY) &&
160 (nHashType & 0x1f) !== Sig.SIGHASH_SINGLE &&
161 (nHashType & 0x1f) !== Sig.SIGHASH_NONE
162 ) {
163 hashSequence = hashCache.sequenceHashBuf ? hashCache.sequenceHashBuf : hashCache.sequenceHashBuf = this.hashSequence()
164 }
165
166 if (
167 (nHashType & 0x1f) !== Sig.SIGHASH_SINGLE &&
168 (nHashType & 0x1f) !== Sig.SIGHASH_NONE
169 ) {
170 hashOutputs = hashCache.outputsHashBuf ? hashCache.outputsHashBuf : hashCache.outputsHashBuf = this.hashOutputs()
171 } else if (
172 (nHashType & 0x1f) === Sig.SIGHASH_SINGLE &&
173 nIn < this.txOuts.length
174 ) {
175 hashOutputs = Hash.sha256Sha256(this.txOuts[nIn].toBuffer())
176 }
177
178 const bw = new Bw()
179 bw.writeUInt32LE(this.versionBytesNum)
180 bw.write(hashPrevouts)
181 bw.write(hashSequence)
182 bw.write(this.txIns[nIn].txHashBuf) // outpoint (1/2)
183 bw.writeUInt32LE(this.txIns[nIn].txOutNum) // outpoint (2/2)
184 bw.writeVarIntNum(subScript.toBuffer().length)
185 bw.write(subScript.toBuffer())
186 bw.writeUInt64LEBn(valueBn)
187 bw.writeUInt32LE(this.txIns[nIn].nSequence)
188 bw.write(hashOutputs)
189 bw.writeUInt32LE(this.nLockTime)
190 bw.writeUInt32LE(nHashType >>> 0)
191
192 return new Br(Hash.sha256Sha256(bw.toBuffer())).readReverse()
193 }
194
195 // original bitcoin code follows - not related to UAHF (Bitcoin SV)
196 const txcopy = this.cloneByBuffer()
197
198 subScript = new Script().fromBuffer(subScript.toBuffer())
199 subScript.removeCodeseparators()
200
201 for (let i = 0; i < txcopy.txIns.length; i++) {
202 txcopy.txIns[i] = TxIn.fromBuffer(txcopy.txIns[i].toBuffer()).setScript(
203 new Script()
204 )
205 }
206
207 txcopy.txIns[nIn] = TxIn.fromBuffer(
208 txcopy.txIns[nIn].toBuffer()
209 ).setScript(subScript)
210
211 if ((nHashType & 31) === Sig.SIGHASH_NONE) {
212 txcopy.txOuts.length = 0
213 txcopy.txOutsVi = VarInt.fromNumber(0)
214
215 for (let i = 0; i < txcopy.txIns.length; i++) {
216 if (i !== nIn) {
217 txcopy.txIns[i].nSequence = 0
218 }
219 }
220 } else if ((nHashType & 31) === Sig.SIGHASH_SINGLE) {
221 // The SIGHASH_SINGLE bug.
222 // https://bitcointalk.org/index.php?topic=260595.0
223 if (nIn > txcopy.txOuts.length - 1) {
224 return Buffer.from(
225 '0000000000000000000000000000000000000000000000000000000000000001',
226 'hex'
227 )
228 }
229
230 txcopy.txOuts.length = nIn + 1
231 txcopy.txOutsVi = VarInt.fromNumber(nIn + 1)
232
233 for (let i = 0; i < txcopy.txOuts.length; i++) {
234 if (i < nIn) {
235 txcopy.txOuts[i] = TxOut.fromProperties(
236 new Bn().fromBuffer(Buffer.from('ffffffffffffffff', 'hex')),
237 new Script()
238 )
239 }
240 }
241
242 for (let i = 0; i < txcopy.txIns.length; i++) {
243 if (i !== nIn) {
244 txcopy.txIns[i].nSequence = 0
245 }
246 }
247 }
248 // else, SIGHASH_ALL
249
250 if (nHashType & Sig.SIGHASH_ANYONECANPAY) {
251 txcopy.txIns[0] = txcopy.txIns[nIn]
252 txcopy.txIns.length = 1
253 txcopy.txInsVi = VarInt.fromNumber(1)
254 }
255
256 const buf = new Bw()
257 .write(txcopy.toBuffer())
258 .writeInt32LE(nHashType)
259 .toBuffer()
260 return new Br(Hash.sha256Sha256(buf)).readReverse()
261 }
262
263 async asyncSighash (nHashType, nIn, subScript, valueBn, flags = 0, hashCache = {}) {
264 const workersResult = await Workers.asyncObjectMethod(this, 'sighash', [
265 nHashType,
266 nIn,
267 subScript,
268 valueBn,
269 flags,
270 hashCache
271 ])
272 return workersResult.resbuf
273 }
274
275 // This function returns a signature but does not update any inputs
276 sign (keyPair, nHashType = Sig.SIGHASH_ALL | Sig.SIGHASH_FORKID, nIn, subScript, valueBn, flags = Tx.SCRIPT_ENABLE_SIGHASH_FORKID, hashCache = {}) {
277 const hashBuf = this.sighash(nHashType, nIn, subScript, valueBn, flags, hashCache)
278 const sig = Ecdsa.sign(hashBuf, keyPair, 'little').fromObject({
279 nHashType: nHashType
280 })
281 return sig
282 }
283
284 async asyncSign (keyPair, nHashType = Sig.SIGHASH_ALL | Sig.SIGHASH_FORKID, nIn, subScript, valueBn, flags = Tx.SCRIPT_ENABLE_SIGHASH_FORKID, hashCache = {}) {
285 const workersResult = await Workers.asyncObjectMethod(this, 'sign', [
286 keyPair,
287 nHashType,
288 nIn,
289 subScript,
290 valueBn,
291 flags,
292 hashCache
293 ])
294 return new Sig().fromFastBuffer(workersResult.resbuf)
295 }
296
297 // This function takes a signature as input and does not parse any inputs
298 verify (
299 sig,
300 pubKey,
301 nIn,
302 subScript,
303 enforceLowS = false,
304 valueBn,
305 flags = Tx.SCRIPT_ENABLE_SIGHASH_FORKID,
306 hashCache = {}
307 ) {
308 const hashBuf = this.sighash(sig.nHashType, nIn, subScript, valueBn, flags, hashCache)
309 return Ecdsa.verify(hashBuf, sig, pubKey, 'little', enforceLowS)
310 }
311
312 async asyncVerify (
313 sig,
314 pubKey,
315 nIn,
316 subScript,
317 enforceLowS = false,
318 valueBn,
319 flags = Tx.SCRIPT_ENABLE_SIGHASH_FORKID,
320 hashCache = {}
321 ) {
322 const workersResult = await Workers.asyncObjectMethod(this, 'verify', [
323 sig,
324 pubKey,
325 nIn,
326 subScript,
327 enforceLowS,
328 valueBn,
329 flags,
330 hashCache
331 ])
332 return JSON.parse(workersResult.resbuf.toString())
333 }
334
335 hash () {
336 return Hash.sha256Sha256(this.toBuffer())
337 }
338
339 async asyncHash () {
340 const workersResult = await Workers.asyncObjectMethod(this, 'hash', [])
341 return workersResult.resbuf
342 }
343
344 id () {
345 return new Br(this.hash()).readReverse().toString('hex')
346 }
347
348 async asyncId () {
349 const workersResult = await Workers.asyncObjectMethod(this, 'id', [])
350 return JSON.parse(workersResult.resbuf.toString())
351 }
352
353 addTxIn (txHashBuf, txOutNum, script, nSequence) {
354 let txIn
355 if (txHashBuf instanceof TxIn) {
356 txIn = txHashBuf
357 } else {
358 txIn = new TxIn()
359 .fromObject({ txHashBuf, txOutNum, nSequence })
360 .setScript(script)
361 }
362 this.txIns.push(txIn)
363 this.txInsVi = VarInt.fromNumber(this.txInsVi.toNumber() + 1)
364 return this
365 }
366
367 addTxOut (valueBn, script) {
368 let txOut
369 if (valueBn instanceof TxOut) {
370 txOut = valueBn
371 } else {
372 txOut = new TxOut().fromObject({ valueBn }).setScript(script)
373 }
374 this.txOuts.push(txOut)
375 this.txOutsVi = VarInt.fromNumber(this.txOutsVi.toNumber() + 1)
376 return this
377 }
378
379 /**
380 * Analagous to bitcoind's IsCoinBase function in transaction.h
381 */
382 isCoinbase () {
383 return this.txIns.length === 1 && this.txIns[0].hasNullInput()
384 }
385
386 /**
387 * BIP 69 sorting. Be sure to sign after sorting.
388 */
389 sort () {
390 this.txIns.sort((first, second) => {
391 return new Br(first.txHashBuf).readReverse().compare(new Br(second.txHashBuf).readReverse()) ||
392 first.txOutNum - second.txOutNum
393 })
394
395 this.txOuts.sort((first, second) => {
396 return first.valueBn.sub(second.valueBn).toNumber() ||
397 first.script.toBuffer().compare(second.script.toBuffer())
398 })
399
400 return this
401 }
402}
403
404Tx.MAX_MONEY = 21000000 * 1e8
405
406// This is defined on Interp, but Tx cannot depend on Interp - must redefine here
407Tx.SCRIPT_ENABLE_SIGHASH_FORKID = 1 << 16
408
409export { Tx }