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