11
11
#include < consensus/amount.h>
12
12
#include < consensus/consensus.h>
13
13
#include < consensus/validation.h>
14
+ #include < kernel/mempool_options.h>
14
15
#include < policy/feerate.h>
15
16
#include < primitives/transaction.h>
16
17
#include < script/interpreter.h>
@@ -67,6 +68,10 @@ bool IsDust(const CTxOut& txout, const CFeeRate& dustRelayFeeIn)
67
68
return (txout.nValue < GetDustThreshold (txout, dustRelayFeeIn));
68
69
}
69
70
71
+ /* *
72
+ * Note this must assign whichType even if returning false, in case
73
+ * IsStandardTx ignores the "scriptpubkey" rejection.
74
+ */
70
75
bool IsStandard (const CScript& scriptPubKey, const std::optional<unsigned >& max_datacarrier_bytes, TxoutType& whichType)
71
76
{
72
77
std::vector<std::vector<unsigned char > > vSolutions;
@@ -91,11 +96,27 @@ bool IsStandard(const CScript& scriptPubKey, const std::optional<unsigned>& max_
91
96
return true ;
92
97
}
93
98
94
- bool IsStandardTx (const CTransaction& tx, const std::optional<unsigned >& max_datacarrier_bytes, bool permit_bare_multisig, const CFeeRate& dust_relay_fee, std::string& reason)
99
+ static inline bool MaybeReject_ (std::string& out_reason, const std::string& reason, const std::string& reason_prefix, const ignore_rejects_type& ignore_rejects) {
100
+ if (ignore_rejects.count (reason_prefix + reason)) {
101
+ return false ;
102
+ }
103
+
104
+ out_reason = reason_prefix + reason;
105
+ return true ;
106
+ }
107
+
108
+ #define MaybeReject (reason ) do { \
109
+ if (MaybeReject_ (out_reason, reason, reason_prefix, ignore_rejects)) { \
110
+ return false ; \
111
+ } \
112
+ } while (0 )
113
+
114
+ bool IsStandardTx (const CTransaction& tx, const kernel::MemPoolOptions& opts, std::string& out_reason, const ignore_rejects_type& ignore_rejects)
95
115
{
116
+ const std::string reason_prefix;
117
+
96
118
if (tx.version > TX_MAX_STANDARD_VERSION || tx.version < 1 ) {
97
- reason = " version" ;
98
- return false ;
119
+ MaybeReject (" version" );
99
120
}
100
121
101
122
// Extremely large transactions with lots of inputs can cost the network
@@ -104,8 +125,7 @@ bool IsStandardTx(const CTransaction& tx, const std::optional<unsigned>& max_dat
104
125
// to MAX_STANDARD_TX_WEIGHT mitigates CPU exhaustion attacks.
105
126
unsigned int sz = GetTransactionWeight (tx);
106
127
if (sz > MAX_STANDARD_TX_WEIGHT) {
107
- reason = " tx-size" ;
108
- return false ;
128
+ MaybeReject (" tx-size" );
109
129
}
110
130
111
131
for (const CTxIn& txin : tx.vin )
@@ -119,38 +139,39 @@ bool IsStandardTx(const CTransaction& tx, const std::optional<unsigned>& max_dat
119
139
// 20-of-20 CHECKMULTISIG scriptPubKey, though such a scriptPubKey
120
140
// is not considered standard.
121
141
if (txin.scriptSig .size () > MAX_STANDARD_SCRIPTSIG_SIZE) {
122
- reason = " scriptsig-size" ;
123
- return false ;
142
+ MaybeReject (" scriptsig-size" );
124
143
}
125
144
if (!txin.scriptSig .IsPushOnly ()) {
126
- reason = " scriptsig-not-pushonly" ;
127
- return false ;
145
+ MaybeReject (" scriptsig-not-pushonly" );
128
146
}
129
147
}
130
148
131
149
unsigned int nDataOut = 0 ;
132
150
TxoutType whichType;
133
151
for (const CTxOut& txout : tx.vout ) {
134
- if (!::IsStandard (txout.scriptPubKey , max_datacarrier_bytes, whichType)) {
135
- reason = " scriptpubkey" ;
136
- return false ;
152
+ if (!::IsStandard (txout.scriptPubKey , opts.max_datacarrier_bytes , whichType)) {
153
+ if (whichType == TxoutType::WITNESS_UNKNOWN) {
154
+ MaybeReject (" scriptpubkey-unknown-witnessversion" );
155
+ } else {
156
+ MaybeReject (" scriptpubkey" );
157
+ }
137
158
}
138
159
139
- if (whichType == TxoutType::NULL_DATA)
160
+ if (whichType == TxoutType::NULL_DATA) {
140
161
nDataOut++;
141
- else if ((whichType == TxoutType::MULTISIG) && (!permit_bare_multisig)) {
142
- reason = " bare-multisig" ;
143
- return false ;
144
- } else if (IsDust (txout, dust_relay_fee)) {
145
- reason = " dust" ;
146
- return false ;
162
+ continue ;
163
+ }
164
+ else if ((whichType == TxoutType::MULTISIG) && (!opts.permit_bare_multisig )) {
165
+ MaybeReject (" bare-multisig" );
166
+ }
167
+ if (IsDust (txout, opts.dust_relay_feerate )) {
168
+ MaybeReject (" dust" );
147
169
}
148
170
}
149
171
150
172
// only one OP_RETURN txout is permitted
151
173
if (nDataOut > 1 ) {
152
- reason = " multi-op-return" ;
153
- return false ;
174
+ MaybeReject (" multi-op-return" );
154
175
}
155
176
156
177
return true ;
@@ -174,7 +195,7 @@ bool IsStandardTx(const CTransaction& tx, const std::optional<unsigned>& max_dat
174
195
*
175
196
* Note that only the non-witness portion of the transaction is checked here.
176
197
*/
177
- bool AreInputsStandard (const CTransaction& tx, const CCoinsViewCache& mapInputs)
198
+ bool AreInputsStandard (const CTransaction& tx, const CCoinsViewCache& mapInputs, const std::string& reason_prefix, std::string& out_reason, const ignore_rejects_type& ignore_rejects )
178
199
{
179
200
if (tx.IsCoinBase ()) {
180
201
return true ; // Coinbases don't use vin normally
@@ -185,30 +206,46 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
185
206
186
207
std::vector<std::vector<unsigned char > > vSolutions;
187
208
TxoutType whichType = Solver (prev.scriptPubKey , vSolutions);
188
- if (whichType == TxoutType::NONSTANDARD || whichType == TxoutType::WITNESS_UNKNOWN) {
209
+ if (whichType == TxoutType::NONSTANDARD) {
210
+ MaybeReject (" script-unknown" );
211
+ } else if (whichType == TxoutType::WITNESS_UNKNOWN) {
189
212
// WITNESS_UNKNOWN failures are typically also caught with a policy
190
213
// flag in the script interpreter, but it can be helpful to catch
191
214
// this type of NONSTANDARD transaction earlier in transaction
192
215
// validation.
193
- return false ;
216
+ MaybeReject ( " witness-unknown " ) ;
194
217
} else if (whichType == TxoutType::SCRIPTHASH) {
218
+ if (!tx.vin [i].scriptSig .IsPushOnly ()) {
219
+ // The only way we got this far, is if the user ignored scriptsig-not-pushonly.
220
+ // However, this case is invalid, and will be caught later on.
221
+ // But for now, we don't want to run the [possibly expensive] script here.
222
+ continue ;
223
+ }
195
224
std::vector<std::vector<unsigned char > > stack;
196
225
// convert the scriptSig into a stack, so we can inspect the redeemScript
197
226
if (!EvalScript (stack, tx.vin [i].scriptSig , SCRIPT_VERIFY_NONE, BaseSignatureChecker (), SigVersion::BASE))
227
+ {
228
+ // This case is also invalid or a bug
229
+ out_reason = reason_prefix + " scriptsig-failure" ;
198
230
return false ;
231
+ }
199
232
if (stack.empty ())
233
+ {
234
+ // Also invalid
235
+ out_reason = reason_prefix + " scriptcheck-missing" ;
200
236
return false ;
237
+ }
201
238
CScript subscript (stack.back ().begin (), stack.back ().end ());
202
239
if (subscript.GetSigOpCount (true ) > MAX_P2SH_SIGOPS) {
203
- return false ;
240
+ MaybeReject ( " scriptcheck-sigops " ) ;
204
241
}
205
242
}
206
243
}
207
244
208
245
return true ;
209
246
}
210
247
211
- bool IsWitnessStandard (const CTransaction& tx, const CCoinsViewCache& mapInputs)
248
+ bool IsWitnessStandard (const CTransaction& tx, const CCoinsViewCache& mapInputs, const std::string& reason_prefix, std::string& out_reason, const ignore_rejects_type& ignore_rejects )
212
249
{
213
250
if (tx.IsCoinBase ())
214
251
return true ; // Coinbases are skipped
@@ -227,7 +264,7 @@ bool IsWitnessStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
227
264
228
265
// witness stuffing detected
229
266
if (prevScript.IsPayToAnchor ()) {
230
- return false ;
267
+ MaybeReject ( " anchor-not-empty " ) ;
231
268
}
232
269
233
270
bool p2sh = false ;
@@ -237,9 +274,15 @@ bool IsWitnessStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
237
274
// into a stack. We do not check IsPushOnly nor compare the hash as these will be done later anyway.
238
275
// If the check fails at this stage, we know that this txid must be a bad one.
239
276
if (!EvalScript (stack, tx.vin [i].scriptSig , SCRIPT_VERIFY_NONE, BaseSignatureChecker (), SigVersion::BASE))
277
+ {
278
+ out_reason = reason_prefix + " scriptsig-failure" ;
240
279
return false ;
280
+ }
241
281
if (stack.empty ())
282
+ {
283
+ out_reason = reason_prefix + " scriptcheck-missing" ;
242
284
return false ;
285
+ }
243
286
prevScript = CScript (stack.back ().begin (), stack.back ().end ());
244
287
p2sh = true ;
245
288
}
@@ -249,18 +292,21 @@ bool IsWitnessStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
249
292
250
293
// Non-witness program must not be associated with any witness
251
294
if (!prevScript.IsWitnessProgram (witnessversion, witnessprogram))
295
+ {
296
+ out_reason = reason_prefix + " nonwitness-input" ;
252
297
return false ;
298
+ }
253
299
254
300
// Check P2WSH standard limits
255
301
if (witnessversion == 0 && witnessprogram.size () == WITNESS_V0_SCRIPTHASH_SIZE) {
256
302
if (tx.vin [i].scriptWitness .stack .back ().size () > MAX_STANDARD_P2WSH_SCRIPT_SIZE)
257
- return false ;
303
+ MaybeReject ( " script-size " ) ;
258
304
size_t sizeWitnessStack = tx.vin [i].scriptWitness .stack .size () - 1 ;
259
305
if (sizeWitnessStack > MAX_STANDARD_P2WSH_STACK_ITEMS)
260
- return false ;
306
+ MaybeReject ( " stackitem-count " ) ;
261
307
for (unsigned int j = 0 ; j < sizeWitnessStack; j++) {
262
308
if (tx.vin [i].scriptWitness .stack [j].size () > MAX_STANDARD_P2WSH_STACK_ITEM_SIZE)
263
- return false ;
309
+ MaybeReject ( " stackitem-size " ) ;
264
310
}
265
311
}
266
312
@@ -272,24 +318,36 @@ bool IsWitnessStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
272
318
Span stack{tx.vin [i].scriptWitness .stack };
273
319
if (stack.size () >= 2 && !stack.back ().empty () && stack.back ()[0 ] == ANNEX_TAG) {
274
320
// Annexes are nonstandard as long as no semantics are defined for them.
275
- return false ;
321
+ MaybeReject (" taproot-annex" );
322
+ // If reject reason is ignored, continue as if the annex wasn't there.
323
+ SpanPopBack (stack);
276
324
}
277
325
if (stack.size () >= 2 ) {
278
326
// Script path spend (2 or more stack elements after removing optional annex)
279
327
const auto & control_block = SpanPopBack (stack);
280
328
SpanPopBack (stack); // Ignore script
281
- if (control_block.empty ()) return false ; // Empty control block is invalid
329
+ if (control_block.empty ()) {
330
+ // Empty control block is invalid
331
+ out_reason = reason_prefix + " taproot-control-missing" ;
332
+ return false ;
333
+ }
282
334
if ((control_block[0 ] & TAPROOT_LEAF_MASK) == TAPROOT_LEAF_TAPSCRIPT) {
283
335
// Leaf version 0xc0 (aka Tapscript, see BIP 342)
336
+ if (!ignore_rejects.count (reason_prefix + " taproot-stackitem-size" )) {
284
337
for (const auto & item : stack) {
285
- if (item.size () > MAX_STANDARD_TAPSCRIPT_STACK_ITEM_SIZE) return false ;
338
+ if (item.size () > MAX_STANDARD_TAPSCRIPT_STACK_ITEM_SIZE) {
339
+ out_reason = reason_prefix + " taproot-stackitem-size" ;
340
+ return false ;
341
+ }
342
+ }
286
343
}
287
344
}
288
345
} else if (stack.size () == 1 ) {
289
346
// Key path spend (1 stack element after removing optional annex)
290
347
// (no policy rules apply)
291
348
} else {
292
349
// 0 stack elements; this is already invalid by consensus rules
350
+ out_reason = reason_prefix + " taproot-witness-missing" ;
293
351
return false ;
294
352
}
295
353
}
0 commit comments