Skip to content

Commit 70bb9dd

Browse files
committed
update test_dedup_by_fee() and fix big in make_bundle_spends_map_and_fee()
1 parent 12fa33a commit 70bb9dd

File tree

3 files changed

+73
-80
lines changed

3 files changed

+73
-80
lines changed

chia/_tests/core/mempool/test_mempool.py

Lines changed: 67 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -2907,14 +2907,7 @@ async def test_invalid_coin_spend_coin(
29072907
],
29082908
)
29092909
def test_items_by_feerate(items: list[MempoolItem], expected: list[Coin]) -> None:
2910-
fee_estimator = create_bitcoin_fee_estimator(uint64(11000000000))
2911-
2912-
mempool_info = MempoolInfo(
2913-
CLVMCost(uint64(11000000000 * 3)),
2914-
FeeRate(uint64(1000000)),
2915-
CLVMCost(uint64(11000000000)),
2916-
)
2917-
mempool = Mempool(mempool_info, fee_estimator)
2910+
mempool = construct_mempool()
29182911
for i in items:
29192912
mempool.add_to_pool(i)
29202913

@@ -2932,13 +2925,7 @@ def test_items_by_feerate(items: list[MempoolItem], expected: list[Coin]) -> Non
29322925

29332926
@pytest.mark.parametrize("old", [True, False])
29342927
def test_timeout(old: bool) -> None:
2935-
fee_estimator = create_bitcoin_fee_estimator(uint64(11000000000))
2936-
mempool_info = MempoolInfo(
2937-
CLVMCost(uint64(11000000000 * 3)),
2938-
FeeRate(uint64(1000000)),
2939-
CLVMCost(uint64(11000000000)),
2940-
)
2941-
mempool = Mempool(mempool_info, fee_estimator)
2928+
mempool = construct_mempool()
29422929

29432930
for i in range(50):
29442931
item = mk_item(coins[i : i + 1], flags=[0], fee=0, cost=50)
@@ -3108,13 +3095,7 @@ def test_limit_expiring_transactions(height: bool, items: list[int], expected: l
31083095
],
31093096
)
31103097
def test_get_items_by_coin_ids(items: list[MempoolItem], coin_ids: list[bytes32], expected: list[MempoolItem]) -> None:
3111-
fee_estimator = create_bitcoin_fee_estimator(uint64(11000000000))
3112-
mempool_info = MempoolInfo(
3113-
CLVMCost(uint64(11000000000 * 3)),
3114-
FeeRate(uint64(1000000)),
3115-
CLVMCost(uint64(11000000000)),
3116-
)
3117-
mempool = Mempool(mempool_info, fee_estimator)
3098+
mempool = construct_mempool()
31183099
for i in items:
31193100
mempool.add_to_pool(i)
31203101
invariant_check_mempool(mempool)
@@ -3128,63 +3109,86 @@ def make_test_spendbundle(coin: Coin, *, fee: int = 0, with_higher_cost: bool =
31283109
if with_higher_cost:
31293110
conditions.extend([[ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, i] for i in range(3)])
31303111
actual_fee += 3
3131-
conditions.append([ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, coin.amount - actual_fee])
3112+
conditions.append([ConditionOpcode.CREATE_COIN, IDENTITY_PUZZLE_HASH, uint64(coin.amount - actual_fee)])
31323113
sb = spend_bundle_from_conditions(conditions, coin)
31333114
return sb
31343115

31353116

3136-
def test_aggregating_on_a_solution_then_a_more_cost_saving_one_appears() -> None:
3137-
def agg_and_add_sb_returning_cost_info(mempool: Mempool, spend_bundles: list[SpendBundle]) -> None:
3117+
def construct_mempool() -> Mempool:
3118+
fee_estimator = create_bitcoin_fee_estimator(test_constants.MAX_BLOCK_COST_CLVM)
3119+
mempool_info = MempoolInfo(
3120+
CLVMCost(uint64(test_constants.MAX_BLOCK_COST_CLVM * 3)),
3121+
FeeRate(uint64(1000000)),
3122+
CLVMCost(test_constants.MAX_BLOCK_COST_CLVM),
3123+
)
3124+
return Mempool(mempool_info, fee_estimator)
3125+
3126+
3127+
def make_coin(idx: int) -> Coin:
3128+
return Coin(IDENTITY_PUZZLE_HASH, IDENTITY_PUZZLE_HASH, uint64(2_000_000_000 + idx * 2))
3129+
3130+
3131+
def test_dedup_by_fee() -> None:
3132+
"""
3133+
We pick the solution to use for dedup based on the spend with the highest
3134+
fee per cost, not based on which one would give the overall best fee per cost
3135+
"""
3136+
mempool = construct_mempool()
3137+
3138+
def add_spend_bundles(spend_bundles: list[SpendBundle]) -> None:
31383139
sb = SpendBundle.aggregate(spend_bundles)
31393140
mi = mempool_item_from_spendbundle(sb)
31403141
mempool.add_to_pool(mi)
31413142
invariant_check_mempool(mempool)
31423143

3143-
fee_estimator = create_bitcoin_fee_estimator(uint64(11000000000))
3144-
mempool_info = MempoolInfo(
3145-
CLVMCost(uint64(11000000000 * 3)),
3146-
FeeRate(uint64(1000000)),
3147-
CLVMCost(uint64(11000000000)),
3148-
)
3149-
mempool = Mempool(mempool_info, fee_estimator)
3150-
coins = [
3151-
Coin(IDENTITY_PUZZLE_HASH, IDENTITY_PUZZLE_HASH, uint64(amount)) for amount in range(2000000000, 2000000020, 2)
3152-
]
3153-
# Create a ~10 FPC item that spends the eligible coin[0]
3154-
sb_A = make_test_spendbundle(coins[0])
3155-
highest_fee = 58282830
3156-
sb_high_rate = make_test_spendbundle(coins[1], fee=highest_fee)
3157-
agg_and_add_sb_returning_cost_info(mempool, [sb_A, sb_high_rate])
3158-
invariant_check_mempool(mempool)
3159-
# Create a ~2 FPC item that spends the eligible coin using the same solution A
3160-
sb_low_rate = make_test_spendbundle(coins[2], fee=highest_fee // 5)
3161-
agg_and_add_sb_returning_cost_info(mempool, [sb_A, sb_low_rate])
3162-
invariant_check_mempool(mempool)
3144+
# Create a spend bundle with a high fee, spending sb_A, which supports dedup
3145+
sb_A = make_test_spendbundle(make_coin(0))
3146+
sb_high_rate = make_test_spendbundle(make_coin(1), fee=10)
3147+
add_spend_bundles([sb_A, sb_high_rate])
3148+
3149+
# Create a spend bundle, with a low fee, that spends the dedup coin using the same solution A
3150+
sb_low_rate = make_test_spendbundle(make_coin(2), fee=10)
3151+
add_spend_bundles([sb_A, sb_low_rate])
3152+
3153+
# validate that dedup happens at all for sb_A
31633154
result = mempool.create_bundle_from_mempool_items(test_constants, uint32(0))
31643155
assert result is not None
31653156
agg, _ = result
31663157
# Make sure both items would be processed
3167-
assert [c.coin for c in agg.coin_spends] == [coins[0], coins[1], coins[2]]
3168-
# Now let's add 3 x ~3 FPC items that spend the eligible coin differently
3169-
# (solution B). It creates a higher (saved) cost than solution A
3170-
sb_B = make_test_spendbundle(coins[0], with_higher_cost=True)
3171-
for i in range(3, 6):
3172-
# We're picking this fee to get a ~3 FPC, and get picked after sb_A1
3173-
# (which has ~10 FPC) but before sb_A2 (which has ~2 FPC)
3174-
sb_mid_rate = make_test_spendbundle(coins[i], fee=38004852 - i)
3175-
agg_and_add_sb_returning_cost_info(mempool, [sb_B, sb_mid_rate])
3176-
invariant_check_mempool(mempool)
3177-
# If we process everything now, the 3 x ~3 FPC items get skipped because
3178-
# sb_A1 gets picked before them (~10 FPC), so from then on only sb_A2 (~2 FPC)
3179-
# would get picked
3158+
assert [c.coin for c in agg.coin_spends] == [make_coin(0), make_coin(1), make_coin(2)]
3159+
3160+
# Now we add a bunch of alternative spends for coin 0, with lower fees
3161+
# Even though the total fee would be higher if we deduped on this solution,
3162+
# we won't.
3163+
sb_B = make_test_spendbundle(make_coin(0), with_higher_cost=True)
3164+
for i in range(3, 600):
3165+
sb_high_rate = make_test_spendbundle(make_coin(i), fee=10)
3166+
add_spend_bundles([sb_B, sb_high_rate])
3167+
31803168
result = mempool.create_bundle_from_mempool_items(test_constants, uint32(0))
31813169
assert result is not None
31823170
agg, _ = result
3183-
# The 3 items got skipped here
31843171
# We ran with solution A and missed bigger savings on solution B
3185-
assert mempool.size() == 5
3186-
assert [c.coin for c in agg.coin_spends] == [coins[0], coins[1], coins[2]]
3187-
invariant_check_mempool(mempool)
3172+
assert mempool.size() == 599
3173+
assert [c.coin for c in agg.coin_spends] == [make_coin(0), make_coin(1), make_coin(2)]
3174+
3175+
# Now, if we add a high fee per-cost-for sb_B, it should be picked
3176+
sb_high_rate = make_test_spendbundle(make_coin(600), fee=1_000_000_000)
3177+
add_spend_bundles([sb_B, sb_high_rate])
3178+
3179+
result = mempool.create_bundle_from_mempool_items(test_constants, uint32(0))
3180+
assert result is not None
3181+
agg, _ = result
3182+
# The 3 items got skipped here
3183+
# We ran with solution B
3184+
assert mempool.size() == 600
3185+
spends_in_block = {c.coin for c in agg.coin_spends}
3186+
assert make_coin(0) in spends_in_block
3187+
assert make_coin(1) not in spends_in_block
3188+
assert make_coin(2) not in spends_in_block
3189+
3190+
for i in range(3, 601):
3191+
assert make_coin(i) in spends_in_block
31883192

31893193

31903194
def test_get_puzzle_and_solution_for_coin_failure() -> None:
@@ -3196,14 +3200,7 @@ def test_get_puzzle_and_solution_for_coin_failure() -> None:
31963200

31973201
@pytest.mark.parametrize("old", [True, False])
31983202
def test_create_block_generator(old: bool) -> None:
3199-
mempool_info = MempoolInfo(
3200-
CLVMCost(uint64(11000000000 * 3)),
3201-
FeeRate(uint64(1000000)),
3202-
CLVMCost(uint64(11000000000)),
3203-
)
3204-
3205-
fee_estimator = create_bitcoin_fee_estimator(test_constants.MAX_BLOCK_COST_CLVM)
3206-
mempool = Mempool(mempool_info, fee_estimator)
3203+
mempool = construct_mempool()
32073204
coins = [
32083205
Coin(IDENTITY_PUZZLE_HASH, IDENTITY_PUZZLE_HASH, uint64(amount)) for amount in range(2000000000, 2000000020, 2)
32093206
]

chia/_tests/core/mempool/test_mempool_manager.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -567,11 +567,10 @@ def make_bundle_spends_map_and_fee(
567567
removals_amount += coin_spend.coin.amount
568568
spend_conds = spend_conditions.pop(coin_id)
569569

570-
additions_amount += coin_spend.coin.amount
571-
572570
additions = []
573571
for puzzle_hash, amount, _ in spend_conds.create_coin:
574572
additions.append(Coin(coin_id, puzzle_hash, uint64(amount)))
573+
additions_amount += amount
575574

576575
bundle_coin_spends[coin_id] = BundleCoinSpend(
577576
coin_spend=coin_spend,
@@ -583,6 +582,8 @@ def make_bundle_spends_map_and_fee(
583582
if bool(spend_conds.flags & ELIGIBLE_FOR_FF)
584583
else None,
585584
)
585+
assert additions_amount == conds.addition_amount
586+
assert removals_amount == conds.removal_amount
586587
fee = uint64(removals_amount - additions_amount)
587588
return bundle_coin_spends, fee
588589

chia/full_node/eligible_coin_spends.py

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ def get_deduplication_info(
133133
134134
Raises:
135135
ValueError to skip the mempool item we're currently in, if it's
136-
attempting to spend an eligible coin with a different solution than the
136+
attempting to spend an dedup coin with a different solution than the
137137
one we're already deduplicating on.
138138
"""
139139
cost_saving = 0
@@ -158,13 +158,8 @@ def get_deduplication_info(
158158
continue
159159
# See if the solution was identical
160160
if dedup_coin_spend.solution != spend_data.coin_spend.solution:
161-
# It wasn't, so let's skip this whole item because it's relying on
162-
# spending this coin with a different solution and that would
163-
# conflict with the coin spends that we're deduplicating already
164-
# NOTE: We can miss an opportunity to deduplicate on other solutions
165-
# even if they end up saving more cost, as we're going for the first
166-
# solution we see from the relatively highest FPC item, to avoid
167-
# severe performance and/or time-complexity impact
161+
# This should not happen. DEDUP spends of the same coin with
162+
# different solutions are rejected in check_removals().
168163
raise SkipDedup("Solution is different from what we're deduplicating on")
169164
cost_saving += dedup_coin_spend.cost
170165
# Update the eligible coin spends data

0 commit comments

Comments
 (0)