比特币全节点Go语言实现BTCD之交易的独立校验源码

释放双眼,带上耳机,听听看~!

钱包软件通过收集UTXO、提供正确的解锁脚本、构造一个新的支出(支付)给接收者这一系列的方式来创建交易。产生的交易随后将被发送到比特币网络临近的节点,从而使得该交易能够在整个比特币网络中传播。

​然而,在交易传递到临近的节点前,每一个收到交易的比特币节点将会首先验证该交易,这将确保只有有效的交易才会 在网络中传播,而无效的交易将会在第一个节点处被废弃。

每一个节点在校验每一笔交易时,都需要对照一个长长的标准列表:

▷交易的语法和数据结构必须正确。

▷输入与输出列表都不能为空。

▷交易的字节大小是小于 MAX_BLOCK_SIZE 的。

▷每一个输出值,以及总量,必须在规定值的范围内 (小于2,100万个币,大于0)。

▷没有哈希等于0,N等于-1的输入(coinbase交易不应当被传递)。

▷nLockTime是小于或等于 INT_MAX 的。或者nLocktime and nSequence的值满足MedianTimePast(译者注:MedianTime是这个块的前面11个块按照block time排序后的中间时间)

▷交易的字节大小是大于或等于100的。

▷交易中的签名数量(SIGOPS)应小于签名操作数量上限。

▷解锁脚本( scriptSig )只能够将数字压入栈中,并且锁定脚本( scriptPubkey )必须要符合isStandard的格式 (该格式将会拒绝非标准交易)。

▷池中或位于主分支区块中的一个匹配交易必须是存在的。

▷对于每一个输入,引用的输出是必须存在的,并且没有被花费。

▷对于每一个输入,如果引用的输出存在于池中任何别的交易中,该交易将被拒绝。

▷对于每一个输入,在主分支和交易池中寻找引用的输出交易。如果输出交易缺少任何一个输入,该交易将成为一个孤 立的交易。如果与其匹配的交易还没有出现在池中,那么将被加入到孤立交易池中。

▷对于每一个输入,如果引用的输出交易是一个coinbase输出,该输入必须至少获得 COINBASE_MATURITY(100)个确认。

▷使用引用的输出交易获得输入值,并检查每一个输入值和总值是否在规定值的范围内 (小于2100万个币,大于0)。

▷如果输入值的总和小于输出值的总和,交易将被中止。

▷如果交易费用太低以至于无法进入一个空的区块,交易将被拒绝。

▷每一个输入的解锁脚本必须依据相应输出的锁定脚本来验证。

主要代码如下:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
1func (mp *TxPool) maybeAcceptTransaction(tx *btcutil.Tx, isNew, rateLimit, rejectDupOrphans bool) ([]*chainhash.Hash, *TxDesc, error) {
2   txHash := tx.Hash()
3
4   if tx.MsgTx().HasWitness() {
5      segwitActive, err := mp.cfg.IsDeploymentActive(chaincfg.DeploymentSegwit)
6      if err != nil {
7         return nil, nil, err
8      }
9
10      if !segwitActive {
11         str := fmt.Sprintf("transaction %v has witness data, "+
12            "but segwit isn't active yet", txHash)
13         return nil, nil, txRuleError(wire.RejectNonstandard, str)
14      }
15   }
16
17   if mp.isTransactionInPool(txHash) || (rejectDupOrphans &&
18      mp.isOrphanInPool(txHash)) {
19
20      str := fmt.Sprintf("already have transaction %v", txHash)
21      return nil, nil, txRuleError(wire.RejectDuplicate, str)
22   }
23
24   err := blockchain.CheckTransactionSanity(tx)
25   if err != nil {
26      if cerr, ok := err.(blockchain.RuleError); ok {
27         return nil, nil, chainRuleError(cerr)
28      }
29      return nil, nil, err
30   }
31
32   // A standalone transaction must not be a coinbase transaction.
33   if blockchain.IsCoinBase(tx) {
34      str := fmt.Sprintf("transaction %v is an individual coinbase",
35         txHash)
36      return nil, nil, txRuleError(wire.RejectInvalid, str)
37   }
38
39   bestHeight := mp.cfg.BestHeight()
40   nextBlockHeight := bestHeight + 1
41
42   medianTimePast := mp.cfg.MedianTimePast()
43
44   if !mp.cfg.Policy.AcceptNonStd {
45      err = checkTransactionStandard(tx, nextBlockHeight,
46         medianTimePast, mp.cfg.Policy.MinRelayTxFee,
47         mp.cfg.Policy.MaxTxVersion)
48      if err != nil {
49         // Attempt to extract a reject code from the error so
50         // it can be retained.  When not possible, fall back to
51         // a non standard error.
52         rejectCode, found := extractRejectCode(err)
53         if !found {
54            rejectCode = wire.RejectNonstandard
55         }
56         str := fmt.Sprintf("transaction %v is not standard: %v",
57            txHash, err)
58         return nil, nil, txRuleError(rejectCode, str)
59      }
60   }
61
62   err = mp.checkPoolDoubleSpend(tx)
63   if err != nil {
64      return nil, nil, err
65   }
66
67   utxoView, err := mp.fetchInputUtxos(tx)
68   if err != nil {
69      if cerr, ok := err.(blockchain.RuleError); ok {
70         return nil, nil, chainRuleError(cerr)
71      }
72      return nil, nil, err
73   }
74
75   // Don't allow the transaction if it exists in the main chain and is not
76   // not already fully spent.
77   prevOut := wire.OutPoint{Hash: *txHash}
78   for txOutIdx := range tx.MsgTx().TxOut {
79      prevOut.Index = uint32(txOutIdx)
80      entry := utxoView.LookupEntry(prevOut)
81      if entry != nil && !entry.IsSpent() {
82         return nil, nil, txRuleError(wire.RejectDuplicate,
83            "transaction already exists")
84      }
85      utxoView.RemoveEntry(prevOut)
86   }
87
88   var missingParents []*chainhash.Hash
89   for outpoint, entry := range utxoView.Entries() {
90      if entry == nil || entry.IsSpent() {
91
92         hashCopy := outpoint.Hash
93         missingParents = append(missingParents, &hashCopy)
94      }
95   }
96   if len(missingParents) > 0 {
97      return missingParents, nil, nil
98   }
99
100   sequenceLock, err := mp.cfg.CalcSequenceLock(tx, utxoView)
101   if err != nil {
102      if cerr, ok := err.(blockchain.RuleError); ok {
103         return nil, nil, chainRuleError(cerr)
104      }
105      return nil, nil, err
106   }
107   if !blockchain.SequenceLockActive(sequenceLock, nextBlockHeight,
108      medianTimePast) {
109      return nil, nil, txRuleError(wire.RejectNonstandard,
110         "transaction's sequence locks on inputs not met")
111   }
112
113   txFee, err := blockchain.CheckTransactionInputs(tx, nextBlockHeight,
114      utxoView, mp.cfg.ChainParams)
115   if err != nil {
116      if cerr, ok := err.(blockchain.RuleError); ok {
117         return nil, nil, chainRuleError(cerr)
118      }
119      return nil, nil, err
120   }
121
122   if !mp.cfg.Policy.AcceptNonStd {
123      err := checkInputsStandard(tx, utxoView)
124      if err != nil {
125         // Attempt to extract a reject code from the error so
126         // it can be retained.  When not possible, fall back to
127         // a non standard error.
128         rejectCode, found := extractRejectCode(err)
129         if !found {
130            rejectCode = wire.RejectNonstandard
131         }
132         str := fmt.Sprintf("transaction %v has a non-standard "+
133            "input: %v", txHash, err)
134         return nil, nil, txRuleError(rejectCode, str)
135      }
136   }
137
138   // TODO(roasbeef): last bool should be conditional on segwit activation
139sigOpCost, err := blockchain.GetSigOpCost(tx, false, utxoView, true, true)
140   if err != nil {
141      if cerr, ok := err.(blockchain.RuleError); ok {
142         return nil, nil, chainRuleError(cerr)
143      }
144      return nil, nil, err
145   }
146   if sigOpCost > mp.cfg.Policy.MaxSigOpCostPerTx {
147      str := fmt.Sprintf("transaction %v sigop cost is too high: %d > %d",
148         txHash, sigOpCost, mp.cfg.Policy.MaxSigOpCostPerTx)
149      return nil, nil, txRuleError(wire.RejectNonstandard, str)
150   }
151
152   serializedSize := GetTxVirtualSize(tx)
153   minFee := calcMinRequiredTxRelayFee(serializedSize,
154      mp.cfg.Policy.MinRelayTxFee)
155   if serializedSize >= (DefaultBlockPrioritySize-1000) && txFee < minFee {
156      str := fmt.Sprintf("transaction %v has %d fees which is under "+
157         "the required amount of %d", txHash, txFee,
158         minFee)
159      return nil, nil, txRuleError(wire.RejectInsufficientFee, str)
160   }
161
162   if isNew && !mp.cfg.Policy.DisableRelayPriority && txFee < minFee {
163      currentPriority := mining.CalcPriority(tx.MsgTx(), utxoView,
164         nextBlockHeight)
165      if currentPriority <= mining.MinHighPriority {
166         str := fmt.Sprintf("transaction %v has insufficient "+
167            "priority (%g <= %g)", txHash,
168            currentPriority, mining.MinHighPriority)
169         return nil, nil, txRuleError(wire.RejectInsufficientFee, str)
170      }
171   }
172   if rateLimit && txFee < minFee {
173      nowUnix := time.Now().Unix()
174      // Decay passed data with an exponentially decaying ~10 minute
175      // window - matches bitcoind handling.
176      mp.pennyTotal *= math.Pow(1.0-1.0/600.0,
177         float64(nowUnix-mp.lastPennyUnix))
178      mp.lastPennyUnix = nowUnix
179
180      // Are we still over the limit?
181      if mp.pennyTotal >= mp.cfg.Policy.FreeTxRelayLimit*10*1000 {
182         str := fmt.Sprintf("transaction %v has been rejected "+
183            "by the rate limiter due to low fees", txHash)
184         return nil, nil, txRuleError(wire.RejectInsufficientFee, str)
185      }
186      oldTotal := mp.pennyTotal
187
188      mp.pennyTotal += float64(serializedSize)
189      log.Tracef("rate limit: curTotal %v, nextTotal: %v, "+
190         "limit %v", oldTotal, mp.pennyTotal,
191         mp.cfg.Policy.FreeTxRelayLimit*10*1000)
192   }
193  
194   err = blockchain.ValidateTransactionScripts(tx, utxoView,
195      txscript.StandardVerifyFlags, mp.cfg.SigCache,
196      mp.cfg.HashCache)
197   if err != nil {
198      if cerr, ok := err.(blockchain.RuleError); ok {
199         return nil, nil, chainRuleError(cerr)
200      }
201      return nil, nil, err
202   }
203
204   // Add to transaction pool.
205   txD := mp.addTransaction(utxoView, tx, bestHeight, txFee)
206
207   log.Debugf("Accepted transaction %v (pool size: %v)", txHash,
208      len(mp.pool))
209
210   return nil, txD, nil
211}
212
213func CheckTransactionSanity(tx *btcutil.Tx) error {
214   // A transaction must have at least one input.
215   msgTx := tx.MsgTx()
216   if len(msgTx.TxIn) == 0 {
217      return ruleError(ErrNoTxInputs, "transaction has no inputs")
218   }
219
220   // A transaction must have at least one output.
221   if len(msgTx.TxOut) == 0 {
222      return ruleError(ErrNoTxOutputs, "transaction has no outputs")
223   }
224
225   // A transaction must not exceed the maximum allowed block payload when
226   // serialized.
227   serializedTxSize := tx.MsgTx().SerializeSizeStripped()
228   if serializedTxSize > MaxBlockBaseSize {
229      str := fmt.Sprintf("serialized transaction is too big - got "+
230         "%d, max %d", serializedTxSize, MaxBlockBaseSize)
231      return ruleError(ErrTxTooBig, str)
232   }
233
234   var totalSatoshi int64
235   for _, txOut := range msgTx.TxOut {
236      satoshi := txOut.Value
237      if satoshi < 0 {
238         str := fmt.Sprintf("transaction output has negative "+
239            "value of %v", satoshi)
240         return ruleError(ErrBadTxOutValue, str)
241      }
242      if satoshi > btcutil.MaxSatoshi {
243         str := fmt.Sprintf("transaction output value of %v is "+
244            "higher than max allowed value of %v", satoshi,
245            btcutil.MaxSatoshi)
246         return ruleError(ErrBadTxOutValue, str)
247      }
248
249      // Two's complement int64 overflow guarantees that any overflow
250      // is detected and reported.  This is impossible for Bitcoin, but
251      // perhaps possible if an alt increases the total money supply.
252      totalSatoshi += satoshi
253      if totalSatoshi < 0 {
254         str := fmt.Sprintf("total value of all transaction "+
255            "outputs exceeds max allowed value of %v",
256            btcutil.MaxSatoshi)
257         return ruleError(ErrBadTxOutValue, str)
258      }
259      if totalSatoshi > btcutil.MaxSatoshi {
260         str := fmt.Sprintf("total value of all transaction "+
261            "outputs is %v which is higher than max "+
262            "allowed value of %v", totalSatoshi,
263            btcutil.MaxSatoshi)
264         return ruleError(ErrBadTxOutValue, str)
265      }
266   }
267
268   // Check for duplicate transaction inputs.
269   existingTxOut := make(map[wire.OutPoint]struct{})
270   for _, txIn := range msgTx.TxIn {
271      if _, exists := existingTxOut[txIn.PreviousOutPoint]; exists {
272         return ruleError(ErrDuplicateTxInputs, "transaction "+
273            "contains duplicate inputs")
274      }
275      existingTxOut[txIn.PreviousOutPoint] = struct{}{}
276   }
277
278   // Coinbase script length must be between min and max length.
279   if IsCoinBase(tx) {
280      slen := len(msgTx.TxIn[0].SignatureScript)
281      if slen < MinCoinbaseScriptLen || slen > MaxCoinbaseScriptLen {
282         str := fmt.Sprintf("coinbase transaction script length "+
283            "of %d is out of range (min: %d, max: %d)",
284            slen, MinCoinbaseScriptLen, MaxCoinbaseScriptLen)
285         return ruleError(ErrBadCoinbaseScriptLen, str)
286      }
287   } else {
288      // Previous transaction outputs referenced by the inputs to this
289      // transaction must not be null.
290      for _, txIn := range msgTx.TxIn {
291         if isNullOutpoint(&txIn.PreviousOutPoint) {
292            return ruleError(ErrBadTxInput, "transaction "+
293               "input refers to previous output that "+
294               "is null")
295         }
296      }
297   }
298
299   return nil
300}
301

给TA打赏
共{{data.count}}人
人已打赏
安全技术

C/C++内存泄漏及检测

2022-1-11 12:36:11

安全经验

深入理解JDBC的超时设置

2021-11-28 16:36:11

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索