/ test / functional / rpc_packages.py
rpc_packages.py
  1  #!/usr/bin/env python3
  2  # Copyright (c) 2021-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  """RPCs that handle raw transaction packages."""
  6  
  7  from decimal import Decimal
  8  import random
  9  
 10  from test_framework.blocktools import COINBASE_MATURITY
 11  from test_framework.mempool_util import (
 12      fill_mempool,
 13  )
 14  from test_framework.messages import (
 15      MAX_BIP125_RBF_SEQUENCE,
 16      tx_from_hex,
 17  )
 18  from test_framework.p2p import P2PTxInvStore
 19  from test_framework.test_framework import BitcoinTestFramework
 20  from test_framework.util import (
 21      assert_equal,
 22      assert_fee_amount,
 23      assert_raises_rpc_error,
 24  )
 25  from test_framework.wallet import (
 26      COIN,
 27      DEFAULT_FEE,
 28      MiniWallet,
 29  )
 30  
 31  
 32  MAX_PACKAGE_COUNT = 25
 33  
 34  
 35  class RPCPackagesTest(BitcoinTestFramework):
 36      def set_test_params(self):
 37          self.num_nodes = 1
 38          self.setup_clean_chain = True
 39          # whitelist peers to speed up tx relay / mempool sync
 40          self.noban_tx_relay = True
 41  
 42      def assert_testres_equal(self, package_hex, testres_expected):
 43          """Shuffle package_hex and assert that the testmempoolaccept result matches testres_expected. This should only
 44          be used to test packages where the order does not matter. The ordering of transactions in package_hex and
 45          testres_expected must match.
 46          """
 47          shuffled_indices = list(range(len(package_hex)))
 48          random.shuffle(shuffled_indices)
 49          shuffled_package = [package_hex[i] for i in shuffled_indices]
 50          shuffled_testres = [testres_expected[i] for i in shuffled_indices]
 51          assert_equal(shuffled_testres, self.nodes[0].testmempoolaccept(shuffled_package))
 52  
 53      def run_test(self):
 54          node = self.nodes[0]
 55  
 56          # get an UTXO that requires signature to be spent
 57          deterministic_address = node.get_deterministic_priv_key().address
 58          blockhash = self.generatetoaddress(node, 1, deterministic_address)[0]
 59          coinbase = node.getblock(blockhash=blockhash, verbosity=2)["tx"][0]
 60          coin = {
 61                  "txid": coinbase["txid"],
 62                  "amount": coinbase["vout"][0]["value"],
 63                  "scriptPubKey": coinbase["vout"][0]["scriptPubKey"],
 64                  "vout": 0,
 65                  "height": 0
 66              }
 67  
 68          self.wallet = MiniWallet(self.nodes[0])
 69          self.generate(self.wallet, COINBASE_MATURITY + 100)  # blocks generated for inputs
 70  
 71          self.log.info("Create some transactions")
 72          # Create some transactions that can be reused throughout the test. Never submit these to mempool.
 73          self.independent_txns_hex = []
 74          self.independent_txns_testres = []
 75          for _ in range(3):
 76              tx_hex = self.wallet.create_self_transfer(fee_rate=Decimal("0.0001"))["hex"]
 77              testres = self.nodes[0].testmempoolaccept([tx_hex])
 78              assert testres[0]["allowed"]
 79              self.independent_txns_hex.append(tx_hex)
 80              # testmempoolaccept returns a list of length one, avoid creating a 2D list
 81              self.independent_txns_testres.append(testres[0])
 82          self.independent_txns_testres_blank = [{
 83              "txid": res["txid"], "wtxid": res["wtxid"]} for res in self.independent_txns_testres]
 84  
 85          self.test_submitpackage_with_ancestors()
 86          self.test_independent(coin)
 87          self.test_chain()
 88          self.test_multiple_children()
 89          self.test_multiple_parents()
 90          self.test_conflicting()
 91          self.test_rbf()
 92          self.test_submitpackage()
 93          self.test_maxfeerate_submitpackage()
 94          self.test_maxburn_submitpackage()
 95  
 96      def test_independent(self, coin):
 97          self.log.info("Test multiple independent transactions in a package")
 98          node = self.nodes[0]
 99          # For independent transactions, order doesn't matter.
100          self.assert_testres_equal(self.independent_txns_hex, self.independent_txns_testres)
101  
102          self.log.info("Test an otherwise valid package with an extra garbage tx appended")
103          address = node.get_deterministic_priv_key().address
104          garbage_tx = node.createrawtransaction([{"txid": "00" * 32, "vout": 5}], {address: 1})
105          tx = tx_from_hex(garbage_tx)
106          # Only the txid and wtxids are returned because validation is incomplete for the independent txns.
107          # Package validation is atomic: if the node cannot find a UTXO for any single tx in the package,
108          # it terminates immediately to avoid unnecessary, expensive signature verification.
109          package_bad = self.independent_txns_hex + [garbage_tx]
110          testres_bad = self.independent_txns_testres_blank + [{"txid": tx.txid_hex, "wtxid": tx.wtxid_hex, "allowed": False, "reject-reason": "missing-inputs"}]
111          self.assert_testres_equal(package_bad, testres_bad)
112  
113          self.log.info("Check testmempoolaccept tells us when some transactions completed validation successfully")
114          tx_bad_sig_hex = node.createrawtransaction([{"txid": coin["txid"], "vout": coin["vout"]}],
115                                             {address : coin["amount"] - Decimal("0.0001")})
116          tx_bad_sig = tx_from_hex(tx_bad_sig_hex)
117          testres_bad_sig = node.testmempoolaccept(self.independent_txns_hex + [tx_bad_sig_hex])
118          # By the time the signature for the last transaction is checked, all the other transactions
119          # have been fully validated, which is why the node returns full validation results for all
120          # transactions here but empty results in other cases.
121          tx_bad_sig_txid = tx_bad_sig.txid_hex
122          tx_bad_sig_wtxid = tx_bad_sig.wtxid_hex
123          assert_equal(testres_bad_sig, self.independent_txns_testres + [{
124              "txid": tx_bad_sig_txid,
125              "wtxid": tx_bad_sig_wtxid, "allowed": False,
126              "reject-reason": "mempool-script-verify-flag-failed (Operation not valid with the current stack size)",
127              "reject-details": "mempool-script-verify-flag-failed (Operation not valid with the current stack size), " +
128                                f"input 0 of {tx_bad_sig_txid} (wtxid {tx_bad_sig_wtxid}), spending {coin['txid']}:{coin['vout']}"
129          }])
130  
131          self.log.info("Check testmempoolaccept reports txns in packages that exceed max feerate")
132          tx_high_fee = self.wallet.create_self_transfer(fee=Decimal("0.999"))
133          testres_high_fee = node.testmempoolaccept([tx_high_fee["hex"]])
134          assert_equal(testres_high_fee, [
135              {"txid": tx_high_fee["txid"], "wtxid": tx_high_fee["wtxid"], "allowed": False, "reject-reason": "max-fee-exceeded"}
136          ])
137          package_high_fee = [tx_high_fee["hex"]] + self.independent_txns_hex
138          testres_package_high_fee = node.testmempoolaccept(package_high_fee)
139          assert_equal(testres_package_high_fee, testres_high_fee + self.independent_txns_testres_blank)
140  
141      def test_chain(self):
142          node = self.nodes[0]
143  
144          chain = self.wallet.create_self_transfer_chain(chain_length=25)
145          chain_hex = [t["hex"] for t in chain]
146          chain_txns = [t["tx"] for t in chain]
147  
148          self.log.info("Check that testmempoolaccept requires packages to be sorted by dependency")
149          assert_equal(node.testmempoolaccept(rawtxs=chain_hex[::-1]),
150                  [{"txid": tx.txid_hex, "wtxid": tx.wtxid_hex, "package-error": "package-not-sorted"} for tx in chain_txns[::-1]])
151  
152          self.log.info("Testmempoolaccept a chain of 25 transactions")
153          testres_multiple = node.testmempoolaccept(rawtxs=chain_hex)
154  
155          testres_single = []
156          # Test accept and then submit each one individually, which should be identical to package test accept
157          for rawtx in chain_hex:
158              testres = node.testmempoolaccept([rawtx])
159              testres_single.append(testres[0])
160              # Submit the transaction now so its child should have no problem validating
161              node.sendrawtransaction(rawtx)
162          assert_equal(testres_single, testres_multiple)
163  
164          # Clean up by clearing the mempool
165          self.generate(node, 1)
166  
167      def test_multiple_children(self):
168          node = self.nodes[0]
169          self.log.info("Testmempoolaccept a package in which a transaction has two children within the package")
170  
171          parent_tx = self.wallet.create_self_transfer_multi(num_outputs=2)
172          assert node.testmempoolaccept([parent_tx["hex"]])[0]["allowed"]
173  
174          # Child A
175          child_a_tx = self.wallet.create_self_transfer(utxo_to_spend=parent_tx["new_utxos"][0])
176          assert not node.testmempoolaccept([child_a_tx["hex"]])[0]["allowed"]
177  
178          # Child B
179          child_b_tx = self.wallet.create_self_transfer(utxo_to_spend=parent_tx["new_utxos"][1])
180          assert not node.testmempoolaccept([child_b_tx["hex"]])[0]["allowed"]
181  
182          self.log.info("Testmempoolaccept with entire package, should work with children in either order")
183          testres_multiple_ab = node.testmempoolaccept(rawtxs=[parent_tx["hex"], child_a_tx["hex"], child_b_tx["hex"]])
184          testres_multiple_ba = node.testmempoolaccept(rawtxs=[parent_tx["hex"], child_b_tx["hex"], child_a_tx["hex"]])
185          assert all([testres["allowed"] for testres in testres_multiple_ab + testres_multiple_ba])
186  
187          testres_single = []
188          # Test accept and then submit each one individually, which should be identical to package testaccept
189          for rawtx in [parent_tx["hex"], child_a_tx["hex"], child_b_tx["hex"]]:
190              testres = node.testmempoolaccept([rawtx])
191              testres_single.append(testres[0])
192              # Submit the transaction now so its child should have no problem validating
193              node.sendrawtransaction(rawtx)
194          assert_equal(testres_single, testres_multiple_ab)
195  
196      def test_multiple_parents(self):
197          node = self.nodes[0]
198          self.log.info("Testmempoolaccept a package in which a transaction has multiple parents within the package")
199  
200          for num_parents in [2, 10, 24]:
201              # Test a package with num_parents parents and 1 child transaction.
202              parent_coins = []
203              package_hex = []
204  
205              for _ in range(num_parents):
206                  # Package accept should work with the parents in any order (as long as parents come before child)
207                  parent_tx = self.wallet.create_self_transfer()
208                  parent_coins.append(parent_tx["new_utxo"])
209                  package_hex.append(parent_tx["hex"])
210  
211              child_tx = self.wallet.create_self_transfer_multi(utxos_to_spend=parent_coins, fee_per_output=2000)
212              for _ in range(10):
213                  random.shuffle(package_hex)
214                  testres_multiple = node.testmempoolaccept(rawtxs=package_hex + [child_tx['hex']])
215                  assert all([testres["allowed"] for testres in testres_multiple])
216  
217              testres_single = []
218              # Test accept and then submit each one individually, which should be identical to package testaccept
219              for rawtx in package_hex + [child_tx["hex"]]:
220                  testres_single.append(node.testmempoolaccept([rawtx])[0])
221                  # Submit the transaction now so its child should have no problem validating
222                  node.sendrawtransaction(rawtx)
223              assert_equal(testres_single, testres_multiple)
224  
225      def test_conflicting(self):
226          node = self.nodes[0]
227          coin = self.wallet.get_utxo()
228  
229          # tx1 and tx2 share the same inputs
230          tx1 = self.wallet.create_self_transfer(utxo_to_spend=coin, fee_rate=DEFAULT_FEE)
231          tx2 = self.wallet.create_self_transfer(utxo_to_spend=coin, fee_rate=2*DEFAULT_FEE)
232  
233          # Ensure tx1 and tx2 are valid by themselves
234          assert node.testmempoolaccept([tx1["hex"]])[0]["allowed"]
235          assert node.testmempoolaccept([tx2["hex"]])[0]["allowed"]
236  
237          self.log.info("Test duplicate transactions in the same package")
238          testres = node.testmempoolaccept([tx1["hex"], tx1["hex"]])
239          assert_equal(testres, [
240              {"txid": tx1["txid"], "wtxid": tx1["wtxid"], "package-error": "package-contains-duplicates"},
241              {"txid": tx1["txid"], "wtxid": tx1["wtxid"], "package-error": "package-contains-duplicates"}
242          ])
243  
244          self.log.info("Test conflicting transactions in the same package")
245          testres = node.testmempoolaccept([tx1["hex"], tx2["hex"]])
246          assert_equal(testres, [
247              {"txid": tx1["txid"], "wtxid": tx1["wtxid"], "package-error": "conflict-in-package"},
248              {"txid": tx2["txid"], "wtxid": tx2["wtxid"], "package-error": "conflict-in-package"}
249          ])
250  
251          # Add a child that spends both at high feerate to submit via submitpackage
252          tx_child = self.wallet.create_self_transfer_multi(
253              fee_per_output=int(DEFAULT_FEE * 5 * COIN),
254              utxos_to_spend=[tx1["new_utxo"], tx2["new_utxo"]],
255          )
256  
257          testres = node.testmempoolaccept([tx1["hex"], tx2["hex"], tx_child["hex"]])
258  
259          assert_equal(testres, [
260              {"txid": tx1["txid"], "wtxid": tx1["wtxid"], "package-error": "conflict-in-package"},
261              {"txid": tx2["txid"], "wtxid": tx2["wtxid"], "package-error": "conflict-in-package"},
262              {"txid": tx_child["txid"], "wtxid": tx_child["wtxid"], "package-error": "conflict-in-package"}
263          ])
264  
265          submitres = node.submitpackage([tx1["hex"], tx2["hex"], tx_child["hex"]])
266          expected = {
267              tx1["wtxid"]: {"txid": tx1["txid"], "error": "package-not-validated"},
268              tx2["wtxid"]: {"txid": tx2["txid"], "error": "package-not-validated"},
269              tx_child["wtxid"]: {"txid": tx_child["txid"], "error": "package-not-validated"},
270          }
271          assert_equal(submitres, {"package_msg": "conflict-in-package", "tx-results": expected,"replaced-transactions": []})
272  
273          # Submit tx1 to mempool, then try the same package again
274          node.sendrawtransaction(tx1["hex"])
275  
276          submitres = node.submitpackage([tx1["hex"], tx2["hex"], tx_child["hex"]])
277          expected = {
278              tx1["wtxid"]: {"txid": tx1["txid"], "error": "package-not-validated"},
279              tx2["wtxid"]: {"txid": tx2["txid"], "error": "package-not-validated"},
280              tx_child["wtxid"]: {"txid": tx_child["txid"], "error": "package-not-validated"},
281          }
282          assert_equal(submitres, {"package_msg": "conflict-in-package", "tx-results": expected,"replaced-transactions": []})
283          assert tx_child["txid"] not in node.getrawmempool()
284  
285          # without the in-mempool ancestor tx1 included in the call, tx2 can be submitted, but
286          # tx_child is missing an input.
287          submitres = node.submitpackage([tx2["hex"], tx_child["hex"]])
288          assert_equal(submitres["tx-results"][tx_child["wtxid"]], {"txid": tx_child["txid"], "error": "bad-txns-inputs-missingorspent"})
289          assert tx2["txid"] in node.getrawmempool()
290  
291          # Regardless of error type, the child can never enter the mempool
292          assert tx_child["txid"] not in node.getrawmempool()
293  
294      def test_rbf(self):
295          node = self.nodes[0]
296  
297          coin = self.wallet.get_utxo()
298          fee = Decimal("0.00125000")
299          replaceable_tx = self.wallet.create_self_transfer(utxo_to_spend=coin, sequence=MAX_BIP125_RBF_SEQUENCE, fee = fee)
300          testres_replaceable = node.testmempoolaccept([replaceable_tx["hex"]])[0]
301          assert_equal(testres_replaceable["txid"], replaceable_tx["txid"])
302          assert_equal(testres_replaceable["wtxid"], replaceable_tx["wtxid"])
303          assert testres_replaceable["allowed"]
304          assert_equal(testres_replaceable["vsize"], replaceable_tx["tx"].get_vsize())
305          assert_equal(testres_replaceable["fees"]["base"], fee)
306          assert_fee_amount(fee, replaceable_tx["tx"].get_vsize(), testres_replaceable["fees"]["effective-feerate"])
307          assert_equal(testres_replaceable["fees"]["effective-includes"], [replaceable_tx["wtxid"]])
308  
309          # Replacement transaction is identical except has double the fee
310          replacement_tx = self.wallet.create_self_transfer(utxo_to_spend=coin, sequence=MAX_BIP125_RBF_SEQUENCE, fee = 2 * fee)
311          testres_rbf_conflicting = node.testmempoolaccept([replaceable_tx["hex"], replacement_tx["hex"]])
312          assert_equal(testres_rbf_conflicting, [
313              {"txid": replaceable_tx["txid"], "wtxid": replaceable_tx["wtxid"], "package-error": "conflict-in-package"},
314              {"txid": replacement_tx["txid"], "wtxid": replacement_tx["wtxid"], "package-error": "conflict-in-package"}
315          ])
316  
317          self.log.info("Test that packages cannot conflict with mempool transactions, even if a valid BIP125 RBF")
318          # This transaction is a valid BIP125 replace-by-fee
319          self.wallet.sendrawtransaction(from_node=node, tx_hex=replaceable_tx["hex"])
320          testres_rbf_single = node.testmempoolaccept([replacement_tx["hex"]])
321          assert testres_rbf_single[0]["allowed"]
322          testres_rbf_package = self.independent_txns_testres_blank + [{
323              "txid": replacement_tx["txid"], "wtxid": replacement_tx["wtxid"], "allowed": False,
324              "reject-reason": "bip125-replacement-disallowed",
325              "reject-details": "bip125-replacement-disallowed"
326          }]
327          self.assert_testres_equal(self.independent_txns_hex + [replacement_tx["hex"]], testres_rbf_package)
328  
329      def assert_equal_package_results(self, node, testmempoolaccept_result, submitpackage_result):
330          """Assert that a successful submitpackage result is consistent with testmempoolaccept
331          results and getmempoolentry info. Note that the result structs are different and, due to
332          policy differences between testmempoolaccept and submitpackage (i.e. package feerate),
333          some information may be different.
334          """
335          for testres_tx in testmempoolaccept_result:
336              # Grab this result from the submitpackage_result
337              submitres_tx = submitpackage_result["tx-results"][testres_tx["wtxid"]]
338              assert_equal(submitres_tx["txid"], testres_tx["txid"])
339              # No "allowed" if the tx was already in the mempool
340              if "allowed" in testres_tx and testres_tx["allowed"]:
341                  assert_equal(submitres_tx["vsize"], testres_tx["vsize"])
342                  assert_equal(submitres_tx["fees"]["base"], testres_tx["fees"]["base"])
343              entry_info = node.getmempoolentry(submitres_tx["txid"])
344              assert_equal(submitres_tx["vsize"], entry_info["vsize"])
345              assert_equal(submitres_tx["fees"]["base"], entry_info["fees"]["base"])
346  
347      def test_submit_child_with_parents(self, num_parents, partial_submit):
348          node = self.nodes[0]
349          peer = node.add_p2p_connection(P2PTxInvStore())
350  
351          package_txns = []
352          presubmitted_wtxids = set()
353          for _ in range(num_parents):
354              parent_tx = self.wallet.create_self_transfer(fee=DEFAULT_FEE)
355              package_txns.append(parent_tx)
356              if partial_submit and random.choice([True, False]):
357                  node.sendrawtransaction(parent_tx["hex"])
358                  presubmitted_wtxids.add(parent_tx["wtxid"])
359          child_tx = self.wallet.create_self_transfer_multi(utxos_to_spend=[tx["new_utxo"] for tx in package_txns], fee_per_output=10000) #DEFAULT_FEE
360          package_txns.append(child_tx)
361  
362          testmempoolaccept_result = node.testmempoolaccept(rawtxs=[tx["hex"] for tx in package_txns])
363          submitpackage_result = node.submitpackage(package=[tx["hex"] for tx in package_txns])
364  
365          # Check that each result is present, with the correct size and fees
366          assert_equal(submitpackage_result["package_msg"], "success")
367          for package_txn in package_txns:
368              tx = package_txn["tx"]
369              assert tx.wtxid_hex in submitpackage_result["tx-results"]
370              wtxid = tx.wtxid_hex
371              assert wtxid in submitpackage_result["tx-results"]
372              tx_result = submitpackage_result["tx-results"][wtxid]
373              assert_equal(tx_result["txid"], tx.txid_hex)
374              assert_equal(tx_result["vsize"], tx.get_vsize())
375              assert_equal(tx_result["fees"]["base"], DEFAULT_FEE)
376              if wtxid not in presubmitted_wtxids:
377                  assert_fee_amount(DEFAULT_FEE, tx.get_vsize(), tx_result["fees"]["effective-feerate"])
378                  assert_equal(tx_result["fees"]["effective-includes"], [wtxid])
379  
380          # submitpackage result should be consistent with testmempoolaccept and getmempoolentry
381          self.assert_equal_package_results(node, testmempoolaccept_result, submitpackage_result)
382  
383          # The node should announce each transaction. No guarantees for propagation.
384          peer.wait_for_broadcast([tx["tx"].wtxid_hex for tx in package_txns])
385          self.generate(node, 1)
386  
387      def test_submitpackage(self):
388          node = self.nodes[0]
389  
390          self.log.info("Submitpackage only allows valid hex inputs")
391          valid_tx_list = self.wallet.create_self_transfer_chain(chain_length=2)
392          hex_list = [valid_tx_list[0]["hex"][:-1] + 'X', valid_tx_list[1]["hex"]]
393          txid_list = [valid_tx_list[0]["txid"], valid_tx_list[1]["txid"]]
394          assert_raises_rpc_error(-22, "TX decode failed:", node.submitpackage, hex_list)
395          assert txid_list[0] not in node.getrawmempool()
396          assert txid_list[1] not in node.getrawmempool()
397  
398          self.log.info("Submitpackage valid packages with 1 child and some number of parents (or none)")
399          for num_parents in [0, 1, 2, 24]:
400              self.test_submit_child_with_parents(num_parents, False)
401              self.test_submit_child_with_parents(num_parents, True)
402  
403          self.log.info("Submitpackage only allows packages of 1 child with its parents")
404          # Chain of 3 transactions has too many generations
405          legacy_pool = node.getrawmempool()
406          chain_hex = [t["hex"] for t in self.wallet.create_self_transfer_chain(chain_length=3)]
407          assert_raises_rpc_error(-25, "package topology disallowed", node.submitpackage, chain_hex)
408          assert_equal(legacy_pool, node.getrawmempool())
409  
410          assert_raises_rpc_error(-8, f"Array must contain between 1 and {MAX_PACKAGE_COUNT} transactions.", node.submitpackage, [])
411          assert_raises_rpc_error(
412              -8, f"Array must contain between 1 and {MAX_PACKAGE_COUNT} transactions.",
413              node.submitpackage, [chain_hex[0]] * (MAX_PACKAGE_COUNT + 1)
414          )
415  
416          # Create a transaction chain such as only the parent gets accepted (by making the child's
417          # version non-standard). Make sure the parent does get broadcast.
418          self.log.info("If a package is partially submitted, transactions included in mempool get broadcast")
419          peer = node.add_p2p_connection(P2PTxInvStore())
420          txs = self.wallet.create_self_transfer_chain(chain_length=2)
421          bad_child = tx_from_hex(txs[1]["hex"])
422          bad_child.version = 0xffffffff
423          hex_partial_acceptance = [txs[0]["hex"], bad_child.serialize().hex()]
424          res = node.submitpackage(hex_partial_acceptance)
425          assert_equal(res["package_msg"], "transaction failed")
426          first_wtxid = txs[0]["tx"].wtxid_hex
427          assert "error" not in res["tx-results"][first_wtxid]
428          sec_wtxid = bad_child.wtxid_hex
429          assert_equal(res["tx-results"][sec_wtxid]["error"], "version")
430          peer.wait_for_broadcast([first_wtxid])
431  
432      def test_maxfeerate_submitpackage(self):
433          node = self.nodes[0]
434          # clear mempool
435          deterministic_address = node.get_deterministic_priv_key().address
436          self.generatetoaddress(node, 1, deterministic_address)
437  
438          self.log.info("Submitpackage maxfeerate arg testing")
439          chained_txns = self.wallet.create_self_transfer_chain(chain_length=2)
440          minrate_btc_kvb = min([chained_txn["fee"] / chained_txn["tx"].get_vsize() * 1000 for chained_txn in chained_txns])
441          chain_hex = [t["hex"] for t in chained_txns]
442          pkg_result = node.submitpackage(chain_hex, maxfeerate=minrate_btc_kvb - Decimal("0.00000001"))
443  
444          # First tx failed in single transaction evaluation, so package message is generic
445          assert_equal(pkg_result["package_msg"], "transaction failed")
446          assert_equal(pkg_result["tx-results"][chained_txns[0]["wtxid"]]["error"], "max feerate exceeded")
447          assert_equal(pkg_result["tx-results"][chained_txns[1]["wtxid"]]["error"], "bad-txns-inputs-missingorspent")
448          assert_equal(node.getrawmempool(), [])
449  
450          # Make chain of two transactions where parent doesn't make minfee threshold
451          # but child is too high fee
452          # Lower mempool limit to make it easier to fill_mempool
453          self.restart_node(0, extra_args=[
454              "-maxmempool=5",
455              "-persistmempool=0",
456          ])
457          self.wallet.rescan_utxos()
458  
459          fill_mempool(self, node)
460  
461          minrelay = node.getmempoolinfo()["minrelaytxfee"]
462          parent = self.wallet.create_self_transfer(
463              fee_rate=minrelay,
464              confirmed_only=True,
465          )
466  
467          child = self.wallet.create_self_transfer(
468              fee_rate=DEFAULT_FEE,
469              utxo_to_spend=parent["new_utxo"],
470          )
471  
472          pkg_result = node.submitpackage([parent["hex"], child["hex"]], maxfeerate=DEFAULT_FEE - Decimal("0.00000001"))
473  
474          # Child is connected even though parent is invalid and still reports fee exceeded
475          # this implies sub-package evaluation of both entries together.
476          assert_equal(pkg_result["package_msg"], "transaction failed")
477          assert "mempool min fee not met" in pkg_result["tx-results"][parent["wtxid"]]["error"]
478          assert_equal(pkg_result["tx-results"][child["wtxid"]]["error"], "max feerate exceeded")
479          assert parent["txid"] not in node.getrawmempool()
480          assert child["txid"] not in node.getrawmempool()
481  
482          # Reset maxmempool, reset dynamic mempool minimum feerate, and empty mempool.
483          self.restart_node(0)
484          self.wallet.rescan_utxos()
485  
486          assert_equal(node.getrawmempool(), [])
487  
488      def test_maxburn_submitpackage(self):
489          node = self.nodes[0]
490  
491          assert_equal(node.getrawmempool(), [])
492  
493          self.log.info("Submitpackage maxburnamount arg testing")
494          chained_txns_burn = self.wallet.create_self_transfer_chain(
495              chain_length=2,
496              utxo_to_spend=self.wallet.get_utxo(confirmed_only=True),
497          )
498          chained_burn_hex = [t["hex"] for t in chained_txns_burn]
499  
500          tx = tx_from_hex(chained_burn_hex[1])
501          tx.vout[-1].scriptPubKey = b'a' * 10001 # scriptPubKey bigger than 10k IsUnspendable
502          chained_burn_hex = [chained_burn_hex[0], tx.serialize().hex()]
503          # burn test is run before any package evaluation; nothing makes it in and we get broader exception
504          assert_raises_rpc_error(-25, "Unspendable output exceeds maximum configured by user", node.submitpackage, chained_burn_hex, 0, chained_txns_burn[1]["new_utxo"]["value"] - Decimal("0.00000001"))
505          assert_equal(node.getrawmempool(), [])
506  
507          minrate_btc_kvb_burn = min([chained_txn_burn["fee"] / chained_txn_burn["tx"].get_vsize() * 1000 for chained_txn_burn in chained_txns_burn])
508  
509          # Relax the restrictions for both and send it; parent gets through as own subpackage
510          pkg_result = node.submitpackage(chained_burn_hex, maxfeerate=minrate_btc_kvb_burn, maxburnamount=chained_txns_burn[1]["new_utxo"]["value"])
511          assert "error" not in pkg_result["tx-results"][chained_txns_burn[0]["wtxid"]]
512          assert_equal(pkg_result["tx-results"][tx.wtxid_hex]["error"], "scriptpubkey")
513          assert_equal(node.getrawmempool(), [chained_txns_burn[0]["txid"]])
514  
515      def test_submitpackage_with_ancestors(self):
516          self.log.info("Test that submitpackage can send a package that has in-mempool ancestors")
517          node = self.nodes[0]
518          peer = node.add_p2p_connection(P2PTxInvStore())
519  
520          parent_tx = self.wallet.create_self_transfer()
521          child_tx = self.wallet.create_self_transfer(utxo_to_spend=parent_tx["new_utxo"])
522          grandchild_tx = self.wallet.create_self_transfer(utxo_to_spend=child_tx["new_utxo"])
523          ggrandchild_tx = self.wallet.create_self_transfer(utxo_to_spend=grandchild_tx["new_utxo"])
524  
525          # Submitting them all together doesn't work, as the topology is not child-with-parents
526          assert_raises_rpc_error(-25, "package topology disallowed", node.submitpackage, [parent_tx["hex"], child_tx["hex"], grandchild_tx["hex"], ggrandchild_tx["hex"]])
527  
528          # Submit older package and check acceptance
529          result_submit_older = node.submitpackage(package=[parent_tx["hex"], child_tx["hex"]])
530          assert_equal(result_submit_older["package_msg"], "success")
531          mempool = node.getrawmempool()
532          assert parent_tx["txid"] in mempool
533          assert child_tx["txid"] in mempool
534  
535          # Submit younger package and check acceptance
536          result_submit_younger = node.submitpackage(package=[grandchild_tx["hex"], ggrandchild_tx["hex"]])
537          assert_equal(result_submit_younger["package_msg"], "success")
538          mempool = node.getrawmempool()
539  
540          assert parent_tx["txid"] in mempool
541          assert child_tx["txid"] in mempool
542          assert grandchild_tx["txid"] in mempool
543          assert ggrandchild_tx["txid"] in mempool
544  
545          # The node should announce each transaction.
546          peer.wait_for_broadcast([tx["tx"].wtxid_hex for tx in [parent_tx, child_tx, grandchild_tx, ggrandchild_tx]])
547          self.generate(node, 1)
548  
549  
550  if __name__ == "__main__":
551      RPCPackagesTest(__file__).main()