mempool_accept.py raw
1 #!/usr/bin/env python3
2 # Copyright (c) 2017-present The Bitcoin Core developers
3 # Distributed under the MIT software license, see the accompanying
4 # file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 """Test mempool acceptance of raw transactions."""
6
7 from copy import deepcopy
8 from decimal import Decimal
9 import math
10
11 from test_framework.test_framework import BitcoinTestFramework
12 from test_framework.blocktools import MAX_STANDARD_TX_WEIGHT
13 from test_framework.mempool_util import (
14 DEFAULT_MIN_RELAY_TX_FEE,
15 DEFAULT_INCREMENTAL_RELAY_FEE,
16 )
17 from test_framework.messages import (
18 MAX_BIP125_RBF_SEQUENCE,
19 COIN,
20 COutPoint,
21 CTransaction,
22 CTxIn,
23 CTxInWitness,
24 CTxOut,
25 MAX_BLOCK_WEIGHT,
26 WITNESS_SCALE_FACTOR,
27 MAX_MONEY,
28 SEQUENCE_FINAL,
29 tx_from_hex,
30 )
31 from test_framework.script import (
32 CScript,
33 OP_0,
34 OP_HASH160,
35 OP_RETURN,
36 OP_TRUE,
37 SIGHASH_ALL,
38 sign_input_legacy,
39 )
40 from test_framework.script_util import (
41 DUMMY_MIN_OP_RETURN_SCRIPT,
42 keys_to_multisig_script,
43 MIN_PADDING,
44 MIN_STANDARD_TX_NONWITNESS_SIZE,
45 PAY_TO_ANCHOR,
46 script_to_p2sh_script,
47 script_to_p2wsh_script,
48 )
49 from test_framework.util import (
50 assert_equal,
51 assert_greater_than,
52 assert_raises_rpc_error,
53 sync_txindex,
54 )
55 from test_framework.wallet import MiniWallet
56 from test_framework.wallet_util import generate_keypair
57
58
59 class MempoolAcceptanceTest(BitcoinTestFramework):
60 def set_test_params(self):
61 self.num_nodes = 1
62 self.extra_args = [[
63 '-txindex','-permitbaremultisig=0',
64 ]] * self.num_nodes
65 self.supports_cli = False
66
67 def check_mempool_result(self, result_expected, *args, **kwargs):
68 """Wrapper to check result of testmempoolaccept on node_0's mempool"""
69 result_test = self.nodes[0].testmempoolaccept(*args, **kwargs)
70 for r in result_test:
71 # Skip these checks for now
72 r.pop('wtxid')
73 if "fees" in r:
74 r["fees"].pop("effective-feerate")
75 r["fees"].pop("effective-includes")
76 if "reject-details" in r:
77 r.pop("reject-details")
78 assert_equal(result_expected, result_test)
79 assert_equal(self.nodes[0].getmempoolinfo()['size'], self.mempool_size) # Must not change mempool state
80
81 def run_test(self):
82 node = self.nodes[0]
83 self.wallet = MiniWallet(node)
84
85 assert_equal(node.getmempoolinfo()['permitbaremultisig'], False)
86
87 self.log.info('Start with empty mempool, and 200 blocks')
88 self.mempool_size = 0
89 assert_equal(node.getblockcount(), 200)
90 assert_equal(node.getmempoolinfo()['size'], self.mempool_size)
91
92 self.log.info("Check default settings")
93 # Settings are listed in BTC/kvB
94 assert_equal(node.getmempoolinfo()['minrelaytxfee'], Decimal(DEFAULT_MIN_RELAY_TX_FEE) / COIN)
95 assert_equal(node.getmempoolinfo()['incrementalrelayfee'], Decimal(DEFAULT_INCREMENTAL_RELAY_FEE) / COIN)
96
97 self.log.info('Should not accept garbage to testmempoolaccept')
98 assert_raises_rpc_error(-3, 'JSON value of type string is not of expected type array', lambda: node.testmempoolaccept(rawtxs='ff00baar'))
99 assert_raises_rpc_error(-8, 'Array must contain between 1 and 25 transactions.', lambda: node.testmempoolaccept(rawtxs=['ff22']*26))
100 assert_raises_rpc_error(-8, 'Array must contain between 1 and 25 transactions.', lambda: node.testmempoolaccept(rawtxs=[]))
101 assert_raises_rpc_error(-22, 'TX decode failed', lambda: node.testmempoolaccept(rawtxs=['ff00baar']))
102
103 self.log.info('A transaction already in the blockchain')
104 tx = self.wallet.create_self_transfer()['tx'] # Pick a random coin(base) to spend
105 tx.vout.append(deepcopy(tx.vout[0]))
106 tx.vout[0].nValue = int(0.3 * COIN)
107 tx.vout[1].nValue = int(49 * COIN)
108 raw_tx_in_block = tx.serialize().hex()
109 txid_in_block = self.wallet.sendrawtransaction(from_node=node, tx_hex=raw_tx_in_block)
110 self.generate(node, 1)
111 self.mempool_size = 0
112 # Also check feerate. 1BTC/kvB fails
113 assert_raises_rpc_error(-8, "Fee rates larger than or equal to 1BTC/kvB are not accepted", lambda: self.check_mempool_result(
114 result_expected=None,
115 rawtxs=[raw_tx_in_block],
116 maxfeerate=1,
117 ))
118 # Check negative feerate
119 assert_raises_rpc_error(-3, "Amount out of range", lambda: self.check_mempool_result(
120 result_expected=None,
121 rawtxs=[raw_tx_in_block],
122 maxfeerate=-0.01,
123 ))
124 # ... 0.99 passes
125 self.check_mempool_result(
126 result_expected=[{'txid': txid_in_block, 'allowed': False, 'reject-reason': 'txn-already-known'}],
127 rawtxs=[raw_tx_in_block],
128 maxfeerate=0.99,
129 )
130
131 self.log.info('A transaction not in the mempool')
132 fee = Decimal('0.000007')
133 utxo_to_spend = self.wallet.get_utxo(txid=txid_in_block) # use 0.3 BTC UTXO
134 tx = self.wallet.create_self_transfer(utxo_to_spend=utxo_to_spend, sequence=MAX_BIP125_RBF_SEQUENCE)['tx']
135 tx.vout[0].nValue = int((Decimal('0.3') - fee) * COIN)
136 raw_tx_0 = tx.serialize().hex()
137 txid_0 = tx.txid_hex
138 self.check_mempool_result(
139 result_expected=[{'txid': txid_0, 'allowed': True, 'vsize': tx.get_vsize(), 'fees': {'base': fee}}],
140 rawtxs=[raw_tx_0],
141 )
142
143 self.log.info('A final transaction not in the mempool')
144 output_amount = Decimal('0.025')
145 tx = self.wallet.create_self_transfer(
146 sequence=SEQUENCE_FINAL,
147 locktime=node.getblockcount() + 2000, # Can be anything
148 )['tx']
149 tx.vout[0].nValue = int(output_amount * COIN)
150 raw_tx_final = tx.serialize().hex()
151 tx = tx_from_hex(raw_tx_final)
152 fee_expected = Decimal('50.0') - output_amount
153 self.check_mempool_result(
154 result_expected=[{'txid': tx.txid_hex, 'allowed': True, 'vsize': tx.get_vsize(), 'fees': {'base': fee_expected}}],
155 rawtxs=[tx.serialize().hex()],
156 maxfeerate=0,
157 )
158 node.sendrawtransaction(hexstring=raw_tx_final, maxfeerate=0)
159 self.mempool_size += 1
160
161 self.log.info('A transaction in the mempool')
162 node.sendrawtransaction(hexstring=raw_tx_0)
163 self.mempool_size += 1
164 self.check_mempool_result(
165 result_expected=[{'txid': txid_0, 'allowed': False, 'reject-reason': 'txn-already-in-mempool'}],
166 rawtxs=[raw_tx_0],
167 )
168
169 self.log.info('A transaction that replaces a mempool transaction')
170 tx = tx_from_hex(raw_tx_0)
171 tx.vout[0].nValue -= int(fee * COIN) # Double the fee
172 raw_tx_0 = tx.serialize().hex()
173 txid_0 = tx.txid_hex
174 self.check_mempool_result(
175 result_expected=[{'txid': txid_0, 'allowed': True, 'vsize': tx.get_vsize(), 'fees': {'base': (2 * fee)}}],
176 rawtxs=[raw_tx_0],
177 )
178 node.sendrawtransaction(hexstring=tx.serialize().hex(), maxfeerate=0)
179
180 self.log.info('A transaction with missing inputs, that never existed')
181 tx = tx_from_hex(raw_tx_0)
182 tx.vin[0].prevout = COutPoint(hash=int('ff' * 32, 16), n=14)
183 self.check_mempool_result(
184 result_expected=[{'txid': tx.txid_hex, 'allowed': False, 'reject-reason': 'missing-inputs'}],
185 rawtxs=[tx.serialize().hex()],
186 )
187
188 self.log.info('A transaction with missing inputs, that existed once in the past')
189 tx = tx_from_hex(raw_tx_0)
190 tx.vin[0].prevout.n = 1 # Set vout to 1, to spend the other outpoint (49 coins) of the in-chain-tx we want to double spend
191 raw_tx_1 = tx.serialize().hex()
192 txid_1 = node.sendrawtransaction(hexstring=raw_tx_1, maxfeerate=0)
193 # Now spend both to "clearly hide" the outputs, ie. remove the coins from the utxo set by spending them
194 tx = self.wallet.create_self_transfer()['tx']
195 tx.vin.append(deepcopy(tx.vin[0]))
196 tx.wit.vtxinwit.append(deepcopy(tx.wit.vtxinwit[0]))
197 tx.vin[0].prevout = COutPoint(hash=int(txid_0, 16), n=0)
198 tx.vin[1].prevout = COutPoint(hash=int(txid_1, 16), n=0)
199 tx.vout[0].nValue = int(0.1 * COIN)
200 raw_tx_spend_both = tx.serialize().hex()
201 txid_spend_both = self.wallet.sendrawtransaction(from_node=node, tx_hex=raw_tx_spend_both)
202 self.generate(node, 1)
203 self.mempool_size = 0
204 # Now see if we can add the coins back to the utxo set by sending the exact txs again
205 self.check_mempool_result(
206 result_expected=[{'txid': txid_0, 'allowed': False, 'reject-reason': 'missing-inputs'}],
207 rawtxs=[raw_tx_0],
208 )
209 self.check_mempool_result(
210 result_expected=[{'txid': txid_1, 'allowed': False, 'reject-reason': 'missing-inputs'}],
211 rawtxs=[raw_tx_1],
212 )
213
214 self.log.info('Create a "reference" tx for later use')
215 utxo_to_spend = self.wallet.get_utxo(txid=txid_spend_both)
216 tx = self.wallet.create_self_transfer(utxo_to_spend=utxo_to_spend, sequence=SEQUENCE_FINAL)['tx']
217 tx.vout[0].nValue = int(0.05 * COIN)
218 raw_tx_reference = tx.serialize().hex()
219 # Reference tx should be valid on itself
220 self.check_mempool_result(
221 result_expected=[{'txid': tx.txid_hex, 'allowed': True, 'vsize': tx.get_vsize(), 'fees': { 'base': Decimal('0.1') - Decimal('0.05')}}],
222 rawtxs=[tx.serialize().hex()],
223 maxfeerate=0,
224 )
225
226 self.log.info('A transaction with no outputs')
227 tx = tx_from_hex(raw_tx_reference)
228 tx.vout = []
229 self.check_mempool_result(
230 result_expected=[{'txid': tx.txid_hex, 'allowed': False, 'reject-reason': 'bad-txns-vout-empty'}],
231 rawtxs=[tx.serialize().hex()],
232 )
233
234 self.log.info('A really large transaction')
235 tx = tx_from_hex(raw_tx_reference)
236 tx.vin = [tx.vin[0]] * math.ceil((MAX_BLOCK_WEIGHT // WITNESS_SCALE_FACTOR) / len(tx.vin[0].serialize()))
237 self.check_mempool_result(
238 result_expected=[{'txid': tx.txid_hex, 'allowed': False, 'reject-reason': 'bad-txns-oversize'}],
239 rawtxs=[tx.serialize().hex()],
240 )
241
242 self.log.info('A transaction with negative output value')
243 tx = tx_from_hex(raw_tx_reference)
244 tx.vout[0].nValue *= -1
245 self.check_mempool_result(
246 result_expected=[{'txid': tx.txid_hex, 'allowed': False, 'reject-reason': 'bad-txns-vout-negative'}],
247 rawtxs=[tx.serialize().hex()],
248 )
249
250 # The following two validations prevent overflow of the output amounts (see CVE-2010-5139).
251 self.log.info('A transaction with too large output value')
252 tx = tx_from_hex(raw_tx_reference)
253 tx.vout[0].nValue = MAX_MONEY + 1
254 self.check_mempool_result(
255 result_expected=[{'txid': tx.txid_hex, 'allowed': False, 'reject-reason': 'bad-txns-vout-toolarge'}],
256 rawtxs=[tx.serialize().hex()],
257 )
258
259 self.log.info('A transaction with too large sum of output values')
260 tx = tx_from_hex(raw_tx_reference)
261 tx.vout = [tx.vout[0]] * 2
262 tx.vout[0].nValue = MAX_MONEY
263 self.check_mempool_result(
264 result_expected=[{'txid': tx.txid_hex, 'allowed': False, 'reject-reason': 'bad-txns-txouttotal-toolarge'}],
265 rawtxs=[tx.serialize().hex()],
266 )
267
268 self.log.info('A transaction with duplicate inputs')
269 tx = tx_from_hex(raw_tx_reference)
270 tx.vin = [tx.vin[0]] * 2
271 self.check_mempool_result(
272 result_expected=[{'txid': tx.txid_hex, 'allowed': False, 'reject-reason': 'bad-txns-inputs-duplicate'}],
273 rawtxs=[tx.serialize().hex()],
274 )
275
276 self.log.info('A non-coinbase transaction with coinbase-like outpoint')
277 tx = tx_from_hex(raw_tx_reference)
278 tx.vin.append(CTxIn(COutPoint(hash=0, n=0xffffffff)))
279 self.check_mempool_result(
280 result_expected=[{'txid': tx.txid_hex, 'allowed': False, 'reject-reason': 'bad-txns-prevout-null'}],
281 rawtxs=[tx.serialize().hex()],
282 )
283
284 self.log.info('A coinbase transaction')
285 # Pick the input of the first tx we created, so it has to be a coinbase tx
286 sync_txindex(self, node)
287 raw_tx_coinbase_spent = node.getrawtransaction(txid=node.decoderawtransaction(hexstring=raw_tx_in_block)['vin'][0]['txid'])
288 tx = tx_from_hex(raw_tx_coinbase_spent)
289 self.check_mempool_result(
290 result_expected=[{'txid': tx.txid_hex, 'allowed': False, 'reject-reason': 'coinbase'}],
291 rawtxs=[tx.serialize().hex()],
292 )
293
294 self.log.info('Some nonstandard transactions')
295 tx = tx_from_hex(raw_tx_reference)
296 tx.version = 4 # A version currently non-standard
297 self.check_mempool_result(
298 result_expected=[{'txid': tx.txid_hex, 'allowed': False, 'reject-reason': 'version'}],
299 rawtxs=[tx.serialize().hex()],
300 )
301 tx = tx_from_hex(raw_tx_reference)
302 tx.vout[0].scriptPubKey = CScript([OP_0]) # Some non-standard script
303 self.check_mempool_result(
304 result_expected=[{'txid': tx.txid_hex, 'allowed': False, 'reject-reason': 'scriptpubkey'}],
305 rawtxs=[tx.serialize().hex()],
306 )
307 tx = tx_from_hex(raw_tx_reference)
308 _, pubkey = generate_keypair()
309 tx.vout[0].scriptPubKey = keys_to_multisig_script([pubkey] * 3, k=2) # Some bare multisig script (2-of-3)
310 self.check_mempool_result(
311 result_expected=[{'txid': tx.txid_hex, 'allowed': False, 'reject-reason': 'bare-multisig'}],
312 rawtxs=[tx.serialize().hex()],
313 )
314 tx = tx_from_hex(raw_tx_reference)
315 tx.vin[0].scriptSig = CScript([OP_HASH160]) # Some not-pushonly scriptSig
316 self.check_mempool_result(
317 result_expected=[{'txid': tx.txid_hex, 'allowed': False, 'reject-reason': 'scriptsig-not-pushonly'}],
318 rawtxs=[tx.serialize().hex()],
319 )
320 tx = tx_from_hex(raw_tx_reference)
321 tx.vin[0].scriptSig = CScript([b'a' * 1648]) # Some too large scriptSig (>1650 bytes)
322 self.check_mempool_result(
323 result_expected=[{'txid': tx.txid_hex, 'allowed': False, 'reject-reason': 'scriptsig-size'}],
324 rawtxs=[tx.serialize().hex()],
325 )
326 tx = tx_from_hex(raw_tx_reference)
327 output_p2sh_burn = CTxOut(nValue=540, scriptPubKey=script_to_p2sh_script(b'burn'))
328 num_scripts = 100000 // len(output_p2sh_burn.serialize()) # Use enough outputs to make the tx too large for our policy
329 tx.vout = [output_p2sh_burn] * num_scripts
330 self.check_mempool_result(
331 result_expected=[{'txid': tx.txid_hex, 'allowed': False, 'reject-reason': 'tx-size'}],
332 rawtxs=[tx.serialize().hex()],
333 )
334 tx = tx_from_hex(raw_tx_reference)
335 tx.vout[0] = output_p2sh_burn
336 tx.vout[0].nValue -= 1 # Make output smaller, such that it is dust for our policy
337 self.check_mempool_result(
338 result_expected=[{'txid': tx.txid_hex, 'allowed': False, 'reject-reason': 'dust'}],
339 rawtxs=[tx.serialize().hex()],
340 )
341
342 # OP_RETURN followed by non-push
343 tx = tx_from_hex(raw_tx_reference)
344 tx.vout[0].scriptPubKey = CScript([OP_RETURN, OP_HASH160])
345 self.check_mempool_result(
346 result_expected=[{'txid': tx.txid_hex, 'allowed': False, 'reject-reason': 'scriptpubkey'}],
347 rawtxs=[tx.serialize().hex()],
348 )
349
350 # Multiple OP_RETURN and more than 83 bytes, even if over MAX_SCRIPT_ELEMENT_SIZE
351 # are standard since v30
352 tx = tx_from_hex(raw_tx_reference)
353 tx.vout.append(CTxOut(0, CScript([OP_RETURN, b'\xff'])))
354 tx.vout.append(CTxOut(0, CScript([OP_RETURN, b'\xff' * 50000])))
355
356 self.check_mempool_result(
357 result_expected=[{'txid': tx.txid_hex, 'allowed': True, 'vsize': tx.get_vsize(), 'fees': {'base': Decimal('0.05')}}],
358 rawtxs=[tx.serialize().hex()],
359 maxfeerate=0
360 )
361
362 self.log.info("A transaction with several OP_RETURN outputs.")
363 tx = tx_from_hex(raw_tx_reference)
364 op_return_count = 42
365 tx.vout[0].nValue = int(tx.vout[0].nValue / op_return_count)
366 tx.vout[0].scriptPubKey = CScript([OP_RETURN, b'\xff'])
367 tx.vout = [tx.vout[0]] * op_return_count
368 self.check_mempool_result(
369 result_expected=[{"txid": tx.txid_hex, "allowed": True, "vsize": tx.get_vsize(), "fees": {"base": Decimal("0.05000026")}}],
370 rawtxs=[tx.serialize().hex()],
371 )
372
373 self.log.info("A transaction with an OP_RETURN output that bumps into the max standardness tx size.")
374 tx = tx_from_hex(raw_tx_reference)
375 tx.vout[0].scriptPubKey = CScript([OP_RETURN])
376 data_len = int(MAX_STANDARD_TX_WEIGHT / 4) - tx.get_vsize() - 5 - 4 # -5 for PUSHDATA4 and -4 for script size
377 tx.vout[0].scriptPubKey = CScript([OP_RETURN, b"\xff" * (data_len)])
378 assert_equal(tx.get_vsize(), int(MAX_STANDARD_TX_WEIGHT / 4))
379 self.check_mempool_result(
380 result_expected=[{"txid": tx.txid_hex, "allowed": True, "vsize": tx.get_vsize(), "fees": {"base": Decimal("0.1") - Decimal("0.05")}}],
381 rawtxs=[tx.serialize().hex()],
382 )
383 tx.vout[0].scriptPubKey = CScript([OP_RETURN, b"\xff" * (data_len + 1)])
384 assert_greater_than(tx.get_vsize(), int(MAX_STANDARD_TX_WEIGHT / 4))
385 self.check_mempool_result(
386 result_expected=[{"txid": tx.txid_hex, "allowed": False, "reject-reason": "tx-size"}],
387 rawtxs=[tx.serialize().hex()],
388 )
389
390 self.log.info('A timelocked transaction')
391 tx = tx_from_hex(raw_tx_reference)
392 tx.vin[0].nSequence -= 1 # Should be non-max, so locktime is not ignored
393 tx.nLockTime = node.getblockcount() + 1
394 self.check_mempool_result(
395 result_expected=[{'txid': tx.txid_hex, 'allowed': False, 'reject-reason': 'non-final'}],
396 rawtxs=[tx.serialize().hex()],
397 )
398
399 self.log.info('A transaction that is locked by BIP68 sequence logic')
400 tx = tx_from_hex(raw_tx_reference)
401 tx.vin[0].nSequence = 2 # We could include it in the second block mined from now, but not the very next one
402 self.check_mempool_result(
403 result_expected=[{'txid': tx.txid_hex, 'allowed': False, 'reject-reason': 'non-BIP68-final'}],
404 rawtxs=[tx.serialize().hex()],
405 maxfeerate=0,
406 )
407
408 # Prep for tiny-tx tests with wsh(OP_TRUE) output
409 seed_tx = self.wallet.send_to(from_node=node, scriptPubKey=script_to_p2wsh_script(CScript([OP_TRUE])), amount=COIN)
410 self.generate(node, 1)
411
412 self.log.info('A tiny transaction(in non-witness bytes) that is disallowed')
413 tx = CTransaction()
414 tx.vin.append(CTxIn(COutPoint(int(seed_tx["txid"], 16), seed_tx["sent_vout"]), b"", SEQUENCE_FINAL))
415 tx.wit.vtxinwit = [CTxInWitness()]
416 tx.wit.vtxinwit[0].scriptWitness.stack = [CScript([OP_TRUE])]
417 tx.vout.append(CTxOut(0, CScript([OP_RETURN] + ([OP_0] * (MIN_PADDING - 2)))))
418 # Note it's only non-witness size that matters!
419 assert_equal(len(tx.serialize_without_witness()), 64)
420 assert_equal(MIN_STANDARD_TX_NONWITNESS_SIZE - 1, 64)
421 assert_greater_than(len(tx.serialize()), 64)
422
423 self.check_mempool_result(
424 result_expected=[{'txid': tx.txid_hex, 'allowed': False, 'reject-reason': 'tx-size-small'}],
425 rawtxs=[tx.serialize().hex()],
426 maxfeerate=0,
427 )
428
429 self.log.info('Minimally-small transaction(in non-witness bytes) that is allowed')
430 tx.vout[0] = CTxOut(COIN - 1000, DUMMY_MIN_OP_RETURN_SCRIPT)
431 assert_equal(len(tx.serialize_without_witness()), MIN_STANDARD_TX_NONWITNESS_SIZE)
432 self.check_mempool_result(
433 result_expected=[{'txid': tx.txid_hex, 'allowed': True, 'vsize': tx.get_vsize(), 'fees': { 'base': Decimal('0.00001000')}}],
434 rawtxs=[tx.serialize().hex()],
435 maxfeerate=0,
436 )
437
438 self.log.info('OP_1 <0x4e73> is able to be created and spent')
439 anchor_value = 10000
440 create_anchor_tx = self.wallet.send_to(from_node=node, scriptPubKey=PAY_TO_ANCHOR, amount=anchor_value)
441 self.generate(node, 1)
442
443 # First spend has non-empty witness, will be rejected to prevent third party wtxid malleability
444 anchor_nonempty_wit_spend = CTransaction()
445 anchor_nonempty_wit_spend.vin.append(CTxIn(COutPoint(int(create_anchor_tx["txid"], 16), create_anchor_tx["sent_vout"]), b""))
446 anchor_nonempty_wit_spend.vout.append(CTxOut(anchor_value - int(fee*COIN), script_to_p2wsh_script(CScript([OP_TRUE]))))
447 anchor_nonempty_wit_spend.wit.vtxinwit.append(CTxInWitness())
448 anchor_nonempty_wit_spend.wit.vtxinwit[0].scriptWitness.stack.append(b"f")
449
450 self.check_mempool_result(
451 result_expected=[{'txid': anchor_nonempty_wit_spend.txid_hex, 'allowed': False, 'reject-reason': 'bad-witness-nonstandard'}],
452 rawtxs=[anchor_nonempty_wit_spend.serialize().hex()],
453 maxfeerate=0,
454 )
455
456 # but is consensus-legal
457 self.generateblock(node, self.wallet.get_address(), [anchor_nonempty_wit_spend.serialize().hex()])
458
459 # Without witness elements it is standard
460 create_anchor_tx = self.wallet.send_to(from_node=node, scriptPubKey=PAY_TO_ANCHOR, amount=anchor_value)
461 self.generate(node, 1)
462
463 anchor_spend = CTransaction()
464 anchor_spend.vin.append(CTxIn(COutPoint(int(create_anchor_tx["txid"], 16), create_anchor_tx["sent_vout"]), b""))
465 anchor_spend.vout.append(CTxOut(anchor_value - int(fee*COIN), script_to_p2wsh_script(CScript([OP_TRUE]))))
466 anchor_spend.wit.vtxinwit.append(CTxInWitness())
467 # It's "segwit" but txid == wtxid since there is no witness data
468 assert_equal(anchor_spend.txid_hex, anchor_spend.wtxid_hex)
469
470 self.check_mempool_result(
471 result_expected=[{'txid': anchor_spend.txid_hex, 'allowed': True, 'vsize': anchor_spend.get_vsize(), 'fees': { 'base': Decimal('0.00000700')}}],
472 rawtxs=[anchor_spend.serialize().hex()],
473 maxfeerate=0,
474 )
475
476 self.log.info('But cannot be spent if nested sh()')
477 nested_anchor_tx = self.wallet.create_self_transfer(sequence=SEQUENCE_FINAL)['tx']
478 nested_anchor_tx.vout[0].scriptPubKey = script_to_p2sh_script(PAY_TO_ANCHOR)
479 self.generateblock(node, self.wallet.get_address(), [nested_anchor_tx.serialize().hex()])
480
481 nested_anchor_spend = CTransaction()
482 nested_anchor_spend.vin.append(CTxIn(COutPoint(nested_anchor_tx.txid_int, 0), b""))
483 nested_anchor_spend.vin[0].scriptSig = CScript([bytes(PAY_TO_ANCHOR)])
484 nested_anchor_spend.vout.append(CTxOut(nested_anchor_tx.vout[0].nValue - int(fee*COIN), script_to_p2wsh_script(CScript([OP_TRUE]))))
485
486 self.check_mempool_result(
487 result_expected=[{'txid': nested_anchor_spend.txid_hex, 'allowed': False, 'reject-reason': 'mempool-script-verify-flag-failed (Witness version reserved for soft-fork upgrades)'}],
488 rawtxs=[nested_anchor_spend.serialize().hex()],
489 maxfeerate=0,
490 )
491 # but is consensus-legal
492 self.generateblock(node, self.wallet.get_address(), [nested_anchor_spend.serialize().hex()])
493
494 self.log.info('Spending a confirmed bare multisig is okay')
495 address = self.wallet.get_address()
496 tx = tx_from_hex(raw_tx_reference)
497 privkey, pubkey = generate_keypair()
498 tx.vout[0].scriptPubKey = keys_to_multisig_script([pubkey] * 3, k=1) # Some bare multisig script (1-of-3)
499 self.generateblock(node, address, [tx.serialize().hex()])
500 tx_spend = CTransaction()
501 tx_spend.vin.append(CTxIn(COutPoint(tx.txid_int, 0), b""))
502 tx_spend.vout.append(CTxOut(tx.vout[0].nValue - int(fee*COIN), script_to_p2wsh_script(CScript([OP_TRUE]))))
503 sign_input_legacy(tx_spend, 0, tx.vout[0].scriptPubKey, privkey, sighash_type=SIGHASH_ALL)
504 tx_spend.vin[0].scriptSig = bytes(CScript([OP_0])) + tx_spend.vin[0].scriptSig
505 self.check_mempool_result(
506 result_expected=[{'txid': tx_spend.txid_hex, 'allowed': True, 'vsize': tx_spend.get_vsize(), 'fees': { 'base': Decimal('0.00000700')}}],
507 rawtxs=[tx_spend.serialize().hex()],
508 maxfeerate=0,
509 )
510
511 if __name__ == '__main__':
512 MempoolAcceptanceTest(__file__).main()
513