@@ -278,11 +278,13 @@ async def setup_mempool_with_coins(
278278 max_block_clvm_cost : Optional [int ] = None ,
279279 max_tx_clvm_cost : Optional [uint64 ] = None ,
280280 mempool_block_buffer : Optional [int ] = None ,
281+ puzzle_hash : bytes32 = IDENTITY_PUZZLE_HASH ,
282+ height : uint32 = TEST_HEIGHT ,
281283) -> tuple [MempoolManager , list [Coin ]]:
282284 coins = []
283285 test_coin_records = {}
284286 for amount in coin_amounts :
285- coin = Coin (IDENTITY_PUZZLE_HASH , IDENTITY_PUZZLE_HASH , uint64 (amount ))
287+ coin = Coin (bytes32 . random (), puzzle_hash , uint64 (amount ))
286288 coins .append (coin )
287289 test_coin_records [coin .name ()] = CoinRecord (coin , uint32 (0 ), uint32 (0 ), False , uint64 (0 ))
288290
@@ -300,7 +302,7 @@ async def get_coin_records(coin_ids: Collection[bytes32]) -> list[CoinRecord]:
300302 if mempool_block_buffer is not None :
301303 constants = constants .replace (MEMPOOL_BLOCK_BUFFER = uint8 (mempool_block_buffer ))
302304 mempool_manager = await instantiate_mempool_manager (
303- get_coin_records , constants = constants , max_tx_clvm_cost = max_tx_clvm_cost
305+ get_coin_records , block_height = height , constants = constants , max_tx_clvm_cost = max_tx_clvm_cost
304306 )
305307 return (mempool_manager , coins )
306308
@@ -3161,6 +3163,120 @@ def test_get_items_by_coin_ids(coin_ids: list[bytes32]) -> list[MempoolItem]:
31613163 assert set (conflicts ) == set (expected_conflicts )
31623164
31633165
3166+ # this puzzle just creates coins, however many are requested by the solution
3167+ # (mod (A)
3168+ # (defun loop (n)
3169+ # (if (= n 1)
3170+ # (list)
3171+ # (c (list 51 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff n) (loop (- n 1))))
3172+ # )
3173+ # (loop A)
3174+ # )
3175+ create_coins_loop : str = (
3176+ "ff02ffff01ff02ff02ffff04ff02ffff04ff05ff80808080ffff04ffff01ff02"
3177+ "ffff03ffff09ff05ffff010180ff80ffff01ff04ffff04ffff0133ffff04ffff"
3178+ "01a0ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
3179+ "ffffffff04ff05ff80808080ffff02ff02ffff04ff02ffff04ffff11ff05ffff"
3180+ "010180ff808080808080ff0180ff018080"
3181+ )
3182+
3183+ # (mod (A)
3184+ # (defun loop (n)
3185+ # (if (= n 0) (list) (c n (loop (- n 1))))
3186+ # )
3187+ # (c (c 1 (loop A)) ())
3188+ # )
3189+ deep_recursion : str = (
3190+ "ff02ffff01ff04ffff04ffff0101ffff02ff02ffff04ff02ffff04ff05ff8080"
3191+ "808080ff8080ffff04ffff01ff02ffff03ffff09ff05ff8080ff80ffff01ff04"
3192+ "ff05ffff02ff02ffff04ff02ffff04ffff11ff05ffff010180ff808080808080"
3193+ "ff0180ff018080"
3194+ )
3195+
3196+
3197+ # this test uses artificial puzzles just to exercise the block creation. These
3198+ # spends are expected not to verify any signatures
3199+ # This is to keep the test simple.
3200+ @pytest .mark .parametrize (
3201+ "puzzle, solution" ,
3202+ [
3203+ pytest .param (create_coins_loop , "ff8207d180" , id = "2000-coins" ),
3204+ pytest .param (create_coins_loop , "ff8203e980" , id = "1000-coins" ),
3205+ pytest .param (create_coins_loop , "ff8201f580" , id = "500 coins" ),
3206+ pytest .param (deep_recursion , "ff830f424080" , id = "recurse-1000000" ),
3207+ pytest .param (deep_recursion , "ff82271080" , id = "recurse-10000" ),
3208+ pytest .param (deep_recursion , "ff6480" , id = "recurse-100" ),
3209+ ],
3210+ )
3211+ @pytest .mark .parametrize ("old" , [True , False ])
3212+ @pytest .mark .anyio
3213+ async def test_create_block_generator_custom_spend (
3214+ puzzle : str , solution : str , old : bool , softfork_height : uint32
3215+ ) -> None :
3216+ solution_str = SerializedProgram .fromhex (solution )
3217+ puzzle_reveal = SerializedProgram .fromhex (puzzle )
3218+ puzzle_hash = puzzle_reveal .get_tree_hash ()
3219+
3220+ mempool_manager , coins = await setup_mempool_with_coins (
3221+ coin_amounts = list (range (100000000 , 100000022 )), puzzle_hash = puzzle_hash , height = softfork_height
3222+ )
3223+
3224+ spend_bundles = [
3225+ SpendBundle (
3226+ coin_spends = [CoinSpend (coin , puzzle_reveal = puzzle_reveal , solution = solution_str )],
3227+ aggregated_signature = G2Element (),
3228+ )
3229+ for coin in coins
3230+ ]
3231+
3232+ for sb in spend_bundles :
3233+ try :
3234+ conds2 = await mempool_manager .pre_validate_spendbundle (sb )
3235+ await mempool_manager .add_spend_bundle (sb , conds2 , sb .name (), softfork_height )
3236+ invariant_check_mempool (mempool_manager .mempool )
3237+ except Exception as e :
3238+ print (f"not adding bundle: { e } " )
3239+ # we don't expect this coin to be spent by the resulting generator
3240+ # so remove it from the list
3241+ for cs in sb .coin_spends :
3242+ coins .remove (cs .coin )
3243+
3244+ create_block = mempool_manager .create_block_generator if old else mempool_manager .create_block_generator2
3245+ assert mempool_manager .peak is not None
3246+ generator = create_block (mempool_manager .peak .header_hash , 10.0 )
3247+
3248+ if len (coins ) == 0 :
3249+ assert generator is None
3250+ else :
3251+ assert generator is not None
3252+
3253+ assert generator .signature == G2Element ()
3254+
3255+ removals = set (generator .removals )
3256+
3257+ err , conds = run_block_generator2 (
3258+ bytes (generator .program ),
3259+ generator .generator_refs ,
3260+ DEFAULT_CONSTANTS .MAX_BLOCK_COST_CLVM ,
3261+ 0 ,
3262+ generator .signature ,
3263+ None ,
3264+ DEFAULT_CONSTANTS ,
3265+ )
3266+
3267+ assert err is None
3268+ assert conds is not None
3269+
3270+ assert len (conds .spends ) == len (removals )
3271+
3272+ for spend in conds .spends :
3273+ removal = Coin (spend .parent_id , spend .puzzle_hash , uint64 (spend .coin_amount ))
3274+ assert removal in coins
3275+ assert removal in removals
3276+
3277+ invariant_check_mempool (mempool_manager .mempool )
3278+
3279+
31643280@pytest .mark .anyio
31653281async def test_new_peak_deferred_ff_items () -> None :
31663282 """
0 commit comments