@@ -2907,14 +2907,7 @@ async def test_invalid_coin_spend_coin(
29072907 ],
29082908)
29092909def 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 ])
29342927def 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)
31103097def 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
31903194def 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 ])
31983202def 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 ]
0 commit comments