Interestingly, the effect of inserting by the child’s txid is that you get “missing inputs” instead of the slightly more correct “did not spend parent’s ephemeral dust” from the submitpackage
results. There is a result for both wtxid and txid in the map: we put “missing inputs” when we tried the child initially, and then failed to overwrite it (because we’re using the wrong key here) the second time. The RPC code copies the result from a query by wtxid.
Here is the diff for mempool_ephemeral_dust.py
to see this bug. You don’t get a KeyError, but a string mismatch:
0diff --git a/test/functional/mempool_ephemeral_dust.py b/test/functional/mempool_ephemeral_dust.py
1index 1e55a6079fa..0ea9c585ed5 100755
2--- a/test/functional/mempool_ephemeral_dust.py
3+++ b/test/functional/mempool_ephemeral_dust.py
4@@ -226,14 +226,17 @@ class EphemeralDustTest(BitcoinTestFramework):
5 dusty_tx, sweep_tx = self.create_ephemeral_dust_package(tx_version=3, dust_value=329)
6
7 # Valid sweep we will RBF incorrectly by not spending dust as well
8- self.nodes[0].submitpackage([dusty_tx["hex"], sweep_tx["hex"]])
9- assert_mempool_contents(self, self.nodes[0], expected=[dusty_tx["tx"], sweep_tx["tx"]])
10+ # self.nodes[0].submitpackage([dusty_tx["hex"], sweep_tx["hex"]])
11+ # assert_mempool_contents(self, self.nodes[0], expected=[dusty_tx["tx"], sweep_tx["tx"]])
12
13 # Doesn't spend in-mempool dust output from parent
14 unspent_sweep_tx = self.wallet.create_self_transfer_multi(fee_per_output=2000, utxos_to_spend=[dusty_tx["new_utxos"][0]], version=3)
15+ unspent_sweep_tx["tx"].wit.vtxinwit[0].scriptWitness.stack = [b'a']
16+ assert unspent_sweep_tx["txid"] != unspent_sweep_tx["wtxid"]
17 assert_greater_than(unspent_sweep_tx["fee"], sweep_tx["fee"])
18 res = self.nodes[0].submitpackage([dusty_tx["hex"], unspent_sweep_tx["hex"]])
19- assert_equal(res["tx-results"][unspent_sweep_tx["wtxid"]]["error"], f"missing-ephemeral-spends, tx {unspent_sweep_tx['wtxid']} did not spend parent's ephemeral dust")
20+ print(res)
21+ assert_equal(res["tx-results"][unspent_sweep_tx["wtxid"]]["error"], f"missing-ephemeral-spends, tx {unspent_sweep_tx['txid']} did not spend parent's ephemeral dust")
22 assert_raises_rpc_error(-26, f"missing-ephemeral-spends, tx {unspent_sweep_tx['wtxid']} did not spend parent's ephemeral dust", self.nodes[0].sendrawtransaction, unspent_sweep_tx["hex"])
23 assert_mempool_contents(self, self.nodes[0], expected=[dusty_tx["tx"], sweep_tx["tx"]])
02025-03-11T15:27:59.777000Z TestFramework (INFO): Test that spending from a tx with ephemeral outputs is only allowed if dust is spent as well
1{'package_msg': 'unspent-dust', 'tx-results': {'4e04718b0923cda667236ab6aab84731cb75f23c08124b0f589b4afa20fd798c': {'txid': '521317a6d852bad16c4b30e9b50be61247e7f47616163e6deb6cc446f77ec818', 'error': 'min relay fee not met, 0 < 147'}, '566d682ea9dbe55317436363bb76b5e4930c420dd126a4f29c03ffde82f7bb9f': {'txid': '9564fdd635de730de72bcdc807b494e7065dc8a01d32f14124d70b6497572f9f', 'error': 'bad-txns-inputs-missingorspent'}}, 'replaced-transactions': []}
22025-03-11T15:27:59.781000Z TestFramework (ERROR): Assertion failed
3Traceback (most recent call last):
4 File "/Users/gloria/bitcoin/test/functional/test_framework/test_framework.py", line 135, in main
5 self.run_test()
6 ~~~~~~~~~~~~~^^
7 File "/Users/gloria/bitcoin/build_debug/test/functional/mempool_ephemeral_dust.py", line 78, in run_test
8 self.test_unspent_ephemeral()
9 ~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
10 File "/Users/gloria/bitcoin/build_debug/test/functional/mempool_ephemeral_dust.py", line 239, in test_unspent_ephemeral
11 assert_equal(res["tx-results"][unspent_sweep_tx["wtxid"]]["error"], f"missing-ephemeral-spends, tx {unspent_sweep_tx['txid']} did not spend parent's ephemeral dust")
12 ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
13 File "/Users/gloria/bitcoin/test/functional/test_framework/util.py", line 77, in assert_equal
14 raise AssertionError("not(%s)" % " == ".join(str(arg) for arg in (thing1, thing2) + args))
15AssertionError: not(bad-txns-inputs-missingorspent == missing-ephemeral-spends, tx 9564fdd635de730de72bcdc807b494e7065dc8a01d32f14124d70b6497572f9f did not spend parent's ephemeral dust)