diff --git a/chia/_tests/core/mempool/test_mempool.py b/chia/_tests/core/mempool/test_mempool.py index 134b12a655fe..4c5559cffdbc 100644 --- a/chia/_tests/core/mempool/test_mempool.py +++ b/chia/_tests/core/mempool/test_mempool.py @@ -117,7 +117,7 @@ def make_item( ) -> MempoolItem: spend_bundle_name = bytes32([idx] * 32) return MempoolItem( - SpendBundle([], G2Element()), + G2Element(), fee, SpendBundleConditions([], 0, 0, 0, None, None, [], cost, 0, 0, False, 0, 0, 0, 0, 0), spend_bundle_name, @@ -2922,8 +2922,8 @@ def test_items_by_feerate(items: list[MempoolItem], expected: list[Coin]) -> Non last_fpc: Optional[float] = None for mi, expected_coin in zip(ordered_items, expected): - assert len(mi.spend_bundle.coin_spends) == 1 - assert mi.spend_bundle.coin_spends[0].coin == expected_coin + assert len(mi.bundle_coin_spends) == 1 + assert next(iter(mi.bundle_coin_spends.values())).coin_spend.coin == expected_coin assert last_fpc is None or last_fpc >= mi.fee_per_cost last_fpc = mi.fee_per_cost diff --git a/chia/_tests/core/mempool/test_mempool_item_queries.py b/chia/_tests/core/mempool/test_mempool_item_queries.py index 300bd52efa13..adec7c3eaaa8 100644 --- a/chia/_tests/core/mempool/test_mempool_item_queries.py +++ b/chia/_tests/core/mempool/test_mempool_item_queries.py @@ -48,7 +48,7 @@ def make_item(coin_spends: list[CoinSpend]) -> MempoolItem: assert npc_result.conds is not None bundle_coin_spends, fee = make_bundle_spends_map_and_fee(spend_bundle, npc_result.conds) return MempoolItem( - spend_bundle=spend_bundle, + aggregated_signature=spend_bundle.aggregated_signature, fee=fee, conds=npc_result.conds, spend_bundle_name=spend_bundle.name(), diff --git a/chia/_tests/core/mempool/test_mempool_manager.py b/chia/_tests/core/mempool/test_mempool_manager.py index 61d224333ce5..e4e8433cc1ec 100644 --- a/chia/_tests/core/mempool/test_mempool_manager.py +++ b/chia/_tests/core/mempool/test_mempool_manager.py @@ -597,7 +597,7 @@ def mempool_item_from_spendbundle(spend_bundle: SpendBundle) -> MempoolItem: ) bundle_coin_spends, fee = make_bundle_spends_map_and_fee(spend_bundle, conds) return MempoolItem( - spend_bundle=spend_bundle, + aggregated_signature=spend_bundle.aggregated_signature, fee=fee, conds=conds, spend_bundle_name=spend_bundle.name(), @@ -927,7 +927,7 @@ def mk_item( spend_bundle = SpendBundle(coin_spends, G2Element()) conds = make_test_conds(cost=cost, spend_ids=spend_ids) return MempoolItem( - spend_bundle=spend_bundle, + aggregated_signature=spend_bundle.aggregated_signature, fee=uint64(fee), conds=conds, spend_bundle_name=spend_bundle.name(), @@ -3310,3 +3310,21 @@ async def test_new_peak_txs_added(condition_and_error: tuple[ConditionOpcode, Er # The item gets retried successfully now assert new_peak_info.spend_bundle_ids == [sb_name] assert mempool_manager.get_mempool_item(sb_name, include_pending=False) is not None + + +@pytest.mark.anyio +async def test_mempool_item_to_spend_bundle() -> None: + """ + Tests that we can properly go back to a `SpendBundle` from a `MempoolItem`. + """ + coins = [Coin(bytes32.random(), IDENTITY_PUZZLE_HASH, uint64(i + 1)) for i in range(random.randint(42, 1337))] + mempool_manager = await setup_mempool(TestCoins(coins, {})) + random_sample = random.sample(coins, 42) + sb = SpendBundle([CoinSpend(c, IDENTITY_PUZZLE, SerializedProgram.to(None)) for c in random_sample], G2Element()) + sb_name = sb.name() + await add_spendbundle(mempool_manager, sb, sb_name) + mi = mempool_manager.get_mempool_item(sb_name) + assert mi is not None + result = mi.to_spend_bundle() + assert result == sb + assert result.name() == sb_name diff --git a/chia/_tests/core/mempool/test_singleton_fast_forward.py b/chia/_tests/core/mempool/test_singleton_fast_forward.py index 183f1b214422..abc7b9714b23 100644 --- a/chia/_tests/core/mempool/test_singleton_fast_forward.py +++ b/chia/_tests/core/mempool/test_singleton_fast_forward.py @@ -53,7 +53,9 @@ def test_process_fast_forward_spends_nothing_to_do() -> None: item = mempool_item_from_spendbundle(sb) # This coin is not eligible for fast forward assert not item.bundle_coin_spends[TEST_COIN_ID].supports_fast_forward - internal_mempool_item = InternalMempoolItem(sb, item.conds, item.height_added_to_mempool, item.bundle_coin_spends) + internal_mempool_item = InternalMempoolItem( + sb.aggregated_signature, item.conds, item.height_added_to_mempool, item.bundle_coin_spends + ) original_version = dataclasses.replace(internal_mempool_item) singleton_ff = SingletonFastForward() bundle_coin_spends = singleton_ff.process_fast_forward_spends( @@ -83,7 +85,9 @@ def test_process_fast_forward_spends_latest_unspent() -> None: item = mempool_item_from_spendbundle(sb) assert item.bundle_coin_spends[test_coin.name()].supports_fast_forward item.bundle_coin_spends[test_coin.name()].latest_singleton_lineage = test_unspent_lineage_info - internal_mempool_item = InternalMempoolItem(sb, item.conds, item.height_added_to_mempool, item.bundle_coin_spends) + internal_mempool_item = InternalMempoolItem( + sb.aggregated_signature, item.conds, item.height_added_to_mempool, item.bundle_coin_spends + ) original_version = dataclasses.replace(internal_mempool_item) singleton_ff = SingletonFastForward() bundle_coin_spends = singleton_ff.process_fast_forward_spends( diff --git a/chia/_tests/fee_estimation/test_fee_estimation_integration.py b/chia/_tests/fee_estimation/test_fee_estimation_integration.py index 4d10f323fc46..66ecd75be1d7 100644 --- a/chia/_tests/fee_estimation/test_fee_estimation_integration.py +++ b/chia/_tests/fee_estimation/test_fee_estimation_integration.py @@ -42,7 +42,7 @@ def make_mempoolitem() -> MempoolItem: spends: list[SpendConditions] = [] conds = SpendBundleConditions(spends, 0, 0, 0, None, None, [], cost, 0, 0, False, 0, 0, 0, 0, 0) mempool_item = MempoolItem( - spend_bundle, + spend_bundle.aggregated_signature, fee, conds, spend_bundle.name(), diff --git a/chia/full_node/eligible_coin_spends.py b/chia/full_node/eligible_coin_spends.py index e67705dcc5f6..2a4515849efc 100644 --- a/chia/full_node/eligible_coin_spends.py +++ b/chia/full_node/eligible_coin_spends.py @@ -268,9 +268,7 @@ def process_fast_forward_spends( # This item doesn't have any fast forward coins, nothing to do here return new_bundle_coin_spends # Update the mempool item after validating the new spend bundle - new_sb = SpendBundle( - coin_spends=new_coin_spends, aggregated_signature=mempool_item.spend_bundle.aggregated_signature - ) + new_sb = SpendBundle(coin_spends=new_coin_spends, aggregated_signature=mempool_item.aggregated_signature) assert mempool_item.conds is not None try: # Run the new spend bundle to make sure it remains valid. What we diff --git a/chia/full_node/mempool.py b/chia/full_node/mempool.py index 55cedf15e92a..791ff0ba60c9 100644 --- a/chia/full_node/mempool.py +++ b/chia/full_node/mempool.py @@ -158,7 +158,7 @@ def _row_to_item(self, row: sqlite3.Row) -> MempoolItem: item = self._items[name] return MempoolItem( - item.spend_bundle, + item.aggregated_signature, uint64(fee), item.conds, name, @@ -481,7 +481,7 @@ def add_to_pool(self, item: MempoolItem) -> MempoolAddInfo: conn.executemany("INSERT OR IGNORE INTO spends VALUES(?, ?)", all_coin_spends) self._items[item_name] = InternalMempoolItem( - item.spend_bundle, item.conds, item.height_added_to_mempool, item.bundle_coin_spends + item.aggregated_signature, item.conds, item.height_added_to_mempool, item.bundle_coin_spends ) self._total_cost += item.cost self._total_fee += item.fee @@ -653,7 +653,7 @@ def create_bundle_from_mempool_items( break coin_spends.extend(unique_coin_spends) additions.extend(unique_additions) - sigs.append(item.spend_bundle.aggregated_signature) + sigs.append(item.aggregated_signature) cost_sum = new_cost_sum fee_sum = new_fee_sum processed_spend_bundles += 1 @@ -761,7 +761,7 @@ def create_block_generator2( break batch_cost += cost - cost_saving - batch_transactions.append(SpendBundle(unique_coin_spends, item.spend_bundle.aggregated_signature)) + batch_transactions.append(SpendBundle(unique_coin_spends, item.aggregated_signature)) batch_spends += len(unique_coin_spends) batch_additions.extend(unique_additions) fee_sum = new_fee_sum diff --git a/chia/full_node/mempool_manager.py b/chia/full_node/mempool_manager.py index 8543be2e8679..855cda2b5ef6 100644 --- a/chia/full_node/mempool_manager.py +++ b/chia/full_node/mempool_manager.py @@ -741,7 +741,7 @@ async def validate_spend_bundle( return Err.IMPOSSIBLE_SECONDS_ABSOLUTE_CONSTRAINTS, None, [] # MempoolInclusionStatus.FAILED potential = MempoolItem( - new_spend, + new_spend.aggregated_signature, uint64(fees), conds, spend_name, @@ -781,7 +781,7 @@ def get_spendbundle(self, bundle_hash: bytes32) -> Optional[SpendBundle]: """Returns a full SpendBundle if it's inside one the mempools""" item: Optional[MempoolItem] = self.mempool.get_item_by_id(bundle_hash) if item is not None: - return item.spend_bundle + return item.to_spend_bundle() return None def get_mempool_item(self, bundle_hash: bytes32, include_pending: bool = False) -> Optional[MempoolItem]: @@ -950,7 +950,7 @@ async def local_get_coin_records(names: Collection[bytes32]) -> list[CoinRecord] for item in old_pool.all_items(): info = await self.add_spend_bundle( - item.spend_bundle, + item.to_spend_bundle(), item.conds, item.spend_bundle_name, item.height_added_to_mempool, @@ -972,7 +972,7 @@ async def local_get_coin_records(names: Collection[bytes32]) -> list[CoinRecord] txs_added = [] for item in potential_txs.values(): info = await self.add_spend_bundle( - item.spend_bundle, + item.to_spend_bundle(), item.conds, item.spend_bundle_name, item.height_added_to_mempool, @@ -1003,7 +1003,7 @@ def get_items_not_in_filter(self, mempool_filter: PyBIP158, limit: int = 100) -> return items if mempool_filter.Match(bytearray(item.spend_bundle_name)): continue - items.append(item.spend_bundle) + items.append(item.to_spend_bundle()) return items diff --git a/chia/types/internal_mempool_item.py b/chia/types/internal_mempool_item.py index 80889e56681b..92c21066dd1b 100644 --- a/chia/types/internal_mempool_item.py +++ b/chia/types/internal_mempool_item.py @@ -2,7 +2,7 @@ from dataclasses import dataclass -from chia_rs import SpendBundle, SpendBundleConditions +from chia_rs import G2Element, SpendBundleConditions from chia_rs.sized_bytes import bytes32 from chia_rs.sized_ints import uint32 @@ -11,7 +11,7 @@ @dataclass(frozen=True) class InternalMempoolItem: - spend_bundle: SpendBundle + aggregated_signature: G2Element conds: SpendBundleConditions height_added_to_mempool: uint32 # Map of coin ID to coin spend data between the bundle and its SpendBundleConditions diff --git a/chia/types/mempool_item.py b/chia/types/mempool_item.py index 1a796d9aaaea..860247a918b7 100644 --- a/chia/types/mempool_item.py +++ b/chia/types/mempool_item.py @@ -3,7 +3,7 @@ from dataclasses import dataclass, field from typing import Any, Optional -from chia_rs import CoinSpend, SpendBundle, SpendBundleConditions +from chia_rs import CoinSpend, G2Element, SpendBundle, SpendBundleConditions from chia_rs.sized_bytes import bytes32 from chia_rs.sized_ints import uint32, uint64 @@ -40,7 +40,7 @@ def supports_fast_forward(self) -> bool: @dataclass(frozen=True) class MempoolItem: - spend_bundle: SpendBundle + aggregated_signature: G2Element fee: uint64 conds: SpendBundleConditions spend_bundle_name: bytes32 @@ -87,11 +87,11 @@ def additions(self) -> list[Coin]: @property def removals(self) -> list[Coin]: - return self.spend_bundle.removals() + return [bcs.coin_spend.coin for bcs in self.bundle_coin_spends.values()] def to_json_dict(self) -> dict[str, Any]: return { - "spend_bundle": recurse_jsonify(self.spend_bundle), + "spend_bundle": recurse_jsonify(self.to_spend_bundle()), "fee": recurse_jsonify(self.fee), "npc_result": {"Error": None, "conds": recurse_jsonify(self.conds)}, "cost": recurse_jsonify(self.cost), @@ -99,3 +99,6 @@ def to_json_dict(self) -> dict[str, Any]: "additions": recurse_jsonify(self.additions), "removals": recurse_jsonify(self.removals), } + + def to_spend_bundle(self) -> SpendBundle: + return SpendBundle([bcs.coin_spend for bcs in self.bundle_coin_spends.values()], self.aggregated_signature)