Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions chia/_tests/core/mempool/test_mempool_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -3156,3 +3156,53 @@ def test_get_items_by_coin_ids(coin_ids: list[bytes32]) -> list[MempoolItem]:
assert err == expected_err
assert len(conflicts) == len(expected_conflicts)
assert set(conflicts) == set(expected_conflicts)


@pytest.mark.anyio
async def test_new_peak_deferred_ff_items() -> None:
"""
Covers the case where we update lineage info for multiple fast forward
singletons at new peak.
"""
singleton_spend1 = make_singleton_spend(bytes32([1] * 32))
singleton1_id = singleton_spend1.coin.name()
singleton_spend2 = make_singleton_spend(bytes32([2] * 32))
singleton2_id = singleton_spend2.coin.name()
coins = TestCoins(
[singleton_spend1.coin, singleton_spend2.coin, TEST_COIN, TEST_COIN2],
{
singleton_spend1.coin.puzzle_hash: singleton_spend1.coin,
singleton_spend2.coin.puzzle_hash: singleton_spend2.coin,
},
)
mempool_manager = await setup_mempool(coins)
# Let's submit the two singletons transactions to the mempool
sb_names = []
for singleton_spend, regular_coin in [(singleton_spend1, TEST_COIN), (singleton_spend2, TEST_COIN2)]:
sb = SpendBundle([singleton_spend, mk_coin_spend(regular_coin)], G2Element())
sb_name = sb.name()
await mempool_manager.add_spend_bundle(
sb,
make_test_conds(spend_ids=[(singleton_spend.coin, ELIGIBLE_FOR_FF), (regular_coin, 0)], cost=1337),
sb_name,
uint32(1),
)
assert mempool_manager.get_mempool_item(sb_name) is not None
sb_names.append(sb_name)
# Let's advance the mempool by spending these singletons into new lineages
singleton1_new_latest = Coin(singleton1_id, singleton_spend1.coin.puzzle_hash, singleton_spend1.coin.amount)
coins.update_lineage(singleton_spend1.coin.puzzle_hash, singleton1_new_latest)
singleton2_new_latest = Coin(singleton2_id, singleton_spend2.coin.puzzle_hash, singleton_spend2.coin.amount)
coins.update_lineage(singleton_spend2.coin.puzzle_hash, singleton2_new_latest)
await advance_mempool(mempool_manager, [singleton1_id, singleton2_id], use_optimization=True)
# Both items should get updated with their related latest lineages
mi1 = mempool_manager.get_mempool_item(sb_names[0])
assert mi1 is not None
latest_singleton_lineage1 = mi1.bundle_coin_spends[singleton1_id].latest_singleton_lineage
assert latest_singleton_lineage1 is not None
assert latest_singleton_lineage1.coin_id == singleton1_new_latest.name()
mi2 = mempool_manager.get_mempool_item(sb_names[1])
assert mi2 is not None
latest_singleton_lineage2 = mi2.bundle_coin_spends[singleton2_id].latest_singleton_lineage
assert latest_singleton_lineage2 is not None
assert latest_singleton_lineage2.coin_id == singleton2_new_latest.name()
7 changes: 4 additions & 3 deletions chia/full_node/mempool_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -833,7 +833,7 @@ async def new_peak(
# rebasing a fast forward spend is more expensive than to just
# evict the item. So, any FF spend we may need to rebase, defer
# them until after we've gone through all spends
deferred_ff_items: set[tuple[bytes32, bytes32]] = set()
deferred_ff_items: set[tuple[bytes32, MempoolItem]] = set()

for spend in spent_coins:
items = self.mempool.get_items_by_coin_id(spend)
Expand All @@ -856,15 +856,16 @@ async def new_peak(
spendbundle_ids_to_remove.add(item_name)
continue

deferred_ff_items.add((spend, item_name))
deferred_ff_items.add((spend, item))

# fast forward spends are indexed under the latest singleton coin ID
# if it's spent, we need to update the index in the mempool. This
# list lets us perform a bulk update
# new_coin_id, current_coin_id, mempool item name
spends_to_update: list[tuple[bytes32, bytes32, bytes32]] = []

for spend, item_name in deferred_ff_items:
for spend, item in deferred_ff_items:
item_name = item.spend_bundle_name
if item_name in spendbundle_ids_to_remove:
continue
# there may be multiple matching spends in the mempool
Expand Down
Loading