1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 | 'use strict'
|
8 |
|
9 | import { Bn } from './bn'
|
10 | import { Br } from './br'
|
11 | import { Bw } from './bw'
|
12 | import { Ecdsa } from './ecdsa'
|
13 | import { Hash } from './hash'
|
14 | import { HashCache } from './hash-cache'
|
15 | import { Script } from './script'
|
16 | import { Sig } from './sig'
|
17 | import { Struct } from './struct'
|
18 | import { TxIn } from './tx-in'
|
19 | import { TxOut } from './tx-out'
|
20 | import { VarInt } from './var-int'
|
21 | import { Workers } from './workers'
|
22 |
|
23 | class 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 |
|
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)
|
115 | bw.writeUInt32LE(txIn.txOutNum)
|
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 |
|
140 |
|
141 |
|
142 |
|
143 | sighash (nHashType, nIn, subScript, valueBn, flags = 0, hashCache = new HashCache()) {
|
144 |
|
145 |
|
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)
|
183 | bw.writeUInt32LE(this.txIns[nIn].txOutNum)
|
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 |
|
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 |
|
222 |
|
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 |
|
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 |
|
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 |
|
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 |
|
381 |
|
382 | isCoinbase () {
|
383 | return this.txIns.length === 1 && this.txIns[0].hasNullInput()
|
384 | }
|
385 |
|
386 | |
387 |
|
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 |
|
404 | Tx.MAX_MONEY = 21000000 * 1e8
|
405 |
|
406 |
|
407 | Tx.SCRIPT_ENABLE_SIGHASH_FORKID = 1 << 16
|
408 |
|
409 | export { Tx }
|