钱包软件通过收集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