1 |
|
2 |
|
3 |
|
4 |
|
5 | 'use strict'
|
6 |
|
7 | import { Bn } from './bn'
|
8 | import { Block } from './block'
|
9 | import { Interp } from './interp'
|
10 | import { Struct } from './struct'
|
11 | import { Tx } from './tx'
|
12 | import { Workers } from './workers'
|
13 |
|
14 | class TxVerifier extends Struct {
|
15 | constructor (tx, txOutMap, errStr, interp) {
|
16 | super({ tx, txOutMap, errStr, interp })
|
17 | }
|
18 |
|
19 | |
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 |
|
27 |
|
28 |
|
29 |
|
30 | verify (flags = Interp.SCRIPT_ENABLE_SIGHASH_FORKID) {
|
31 | return !this.checkStr() && !this.verifyStr(flags)
|
32 | }
|
33 |
|
34 | |
35 |
|
36 |
|
37 |
|
38 |
|
39 | async asyncVerify (flags) {
|
40 | const verifyStr = await this.asyncVerifyStr(flags)
|
41 | return !this.checkStr() && !verifyStr
|
42 | }
|
43 |
|
44 | |
45 |
|
46 |
|
47 | static verify (tx, txOutMap, flags) {
|
48 | return new TxVerifier(tx, txOutMap).verify(flags)
|
49 | }
|
50 |
|
51 | static asyncVerify (tx, txOutMap, flags) {
|
52 | return new TxVerifier(tx, txOutMap).asyncVerify(flags)
|
53 | }
|
54 |
|
55 | |
56 |
|
57 |
|
58 |
|
59 |
|
60 | checkStr () {
|
61 |
|
62 | if (this.tx.txIns.length === 0 || this.tx.txInsVi.toNumber() === 0) {
|
63 | this.errStr = 'transaction txIns empty'
|
64 | return this.errStr
|
65 | }
|
66 | if (this.tx.txOuts.length === 0 || this.tx.txOutsVi.toNumber() === 0) {
|
67 | this.errStr = 'transaction txOuts empty'
|
68 | return this.errStr
|
69 | }
|
70 |
|
71 |
|
72 | if (this.tx.toBuffer().length > Block.MAX_BLOCK_SIZE) {
|
73 | this.errStr = 'transaction over the maximum block size'
|
74 | return this.errStr
|
75 | }
|
76 |
|
77 |
|
78 | let valueoutbn = new Bn(0)
|
79 | for (let i = 0; i < this.tx.txOuts.length; i++) {
|
80 | const txOut = this.tx.txOuts[i]
|
81 | if (txOut.valueBn.lt(0)) {
|
82 | this.errStr = 'transaction txOut ' + i + ' negative'
|
83 | return this.errStr
|
84 | }
|
85 | if (txOut.valueBn.gt(Tx.MAX_MONEY)) {
|
86 | this.errStr = 'transaction txOut ' + i + ' greater than MAX_MONEY'
|
87 | return this.errStr
|
88 | }
|
89 | valueoutbn = valueoutbn.add(txOut.valueBn)
|
90 | if (valueoutbn.gt(Tx.MAX_MONEY)) {
|
91 | this.errStr =
|
92 | 'transaction txOut ' + i + ' total output greater than MAX_MONEY'
|
93 | return this.errStr
|
94 | }
|
95 | }
|
96 |
|
97 |
|
98 | const txInmap = {}
|
99 | for (let i = 0; i < this.tx.txIns.length; i++) {
|
100 | const txIn = this.tx.txIns[i]
|
101 | const inputid = txIn.txHashBuf.toString('hex') + ':' + txIn.txOutNum
|
102 | if (txInmap[inputid] !== undefined) {
|
103 | this.errStr = 'transaction input ' + i + ' duplicate input'
|
104 | return this.errStr
|
105 | }
|
106 | txInmap[inputid] = true
|
107 | }
|
108 |
|
109 | if (this.tx.isCoinbase()) {
|
110 | const buf = this.tx.txIns[0].script.toBuffer()
|
111 | if (buf.length < 2 || buf.length > 100) {
|
112 | this.errStr = 'coinbase trasaction script size invalid'
|
113 | return this.errStr
|
114 | }
|
115 | } else {
|
116 | for (let i = 0; i < this.tx.txIns.length; i++) {
|
117 | if (this.tx.txIns[i].hasNullInput()) {
|
118 | this.errStr = 'transaction input ' + i + ' has null input'
|
119 | return this.errStr
|
120 | }
|
121 | }
|
122 | }
|
123 | return false
|
124 | }
|
125 |
|
126 | |
127 |
|
128 |
|
129 |
|
130 | verifyStr (flags) {
|
131 | for (let i = 0; i < this.tx.txIns.length; i++) {
|
132 | if (!this.verifyNIn(i, flags)) {
|
133 | this.errStr = 'input ' + i + ' failed script verify'
|
134 | return this.errStr
|
135 | }
|
136 | }
|
137 | return false
|
138 | }
|
139 |
|
140 | async asyncVerifyStr (flags) {
|
141 | for (let i = 0; i < this.tx.txIns.length; i++) {
|
142 | const verifyNIn = await this.asyncVerifyNIn(i, flags)
|
143 | if (!verifyNIn) {
|
144 | this.errStr = 'input ' + i + ' failed script verify'
|
145 | return this.errStr
|
146 | }
|
147 | }
|
148 | return false
|
149 | }
|
150 |
|
151 | |
152 |
|
153 |
|
154 |
|
155 | verifyNIn (nIn, flags) {
|
156 | const txIn = this.tx.txIns[nIn]
|
157 | const scriptSig = txIn.script
|
158 | const txOut = this.txOutMap.get(txIn.txHashBuf, txIn.txOutNum)
|
159 | if (!txOut) {
|
160 | console.log('output ' + txIn.txOutNum + ' not found')
|
161 | return false
|
162 | }
|
163 | const scriptPubKey = txOut.script
|
164 | const valueBn = txOut.valueBn
|
165 | this.interp = new Interp()
|
166 | const verified = this.interp.verify(
|
167 | scriptSig,
|
168 | scriptPubKey,
|
169 | this.tx,
|
170 | nIn,
|
171 | flags,
|
172 | valueBn
|
173 | )
|
174 | return verified
|
175 | }
|
176 |
|
177 | async asyncVerifyNIn (nIn, flags) {
|
178 | const txIn = this.tx.txIns[nIn]
|
179 | const scriptSig = txIn.script
|
180 | const txOut = this.txOutMap.get(txIn.txHashBuf, txIn.txOutNum)
|
181 | if (!txOut) {
|
182 | console.log('output ' + txIn.txOutNum + ' not found')
|
183 | return false
|
184 | }
|
185 | const scriptPubKey = txOut.script
|
186 | const valueBn = txOut.valueBn
|
187 | this.interp = new Interp()
|
188 | const workersResult = await Workers.asyncObjectMethod(
|
189 | this.interp,
|
190 | 'verify',
|
191 | [scriptSig, scriptPubKey, this.tx, nIn, flags, valueBn]
|
192 | )
|
193 | const verified = JSON.parse(workersResult.resbuf.toString())
|
194 | return verified
|
195 | }
|
196 |
|
197 | getDebugObject () {
|
198 | return {
|
199 | errStr: this.errStr,
|
200 | interpFailure: this.interp ? this.interp.getDebugObject() : undefined
|
201 | }
|
202 | }
|
203 |
|
204 | getDebugString () {
|
205 | return JSON.stringify(this.getDebugObject(), null, 2)
|
206 | }
|
207 | }
|
208 |
|
209 | export { TxVerifier }
|