Skip to content

Commit 54bdf37

Browse files
authored
introduce test_create_block_generator_custom_spend() (#20240)
introduce test_create_block_generator_custom_spend() test for the mempool_manager
1 parent 87bf96b commit 54bdf37

File tree

3 files changed

+125
-4
lines changed

3 files changed

+125
-4
lines changed

chia/_tests/core/mempool/test_mempool_manager.py

Lines changed: 118 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -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
31653281
async def test_new_peak_deferred_ff_items() -> None:
31663282
"""

chia/full_node/mempool.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111

1212
from chia_rs import (
1313
DONT_VALIDATE_SIGNATURE,
14-
MEMPOOL_MODE,
1514
AugSchemeMPL,
1615
BlockBuilder,
1716
Coin,
@@ -535,7 +534,7 @@ def create_block_generator(
535534
f"spends: {len(removals)} additions: {len(additions)}",
536535
)
537536

538-
flags = get_flags_for_height_and_constants(height, constants) | MEMPOOL_MODE | DONT_VALIDATE_SIGNATURE
537+
flags = get_flags_for_height_and_constants(height, constants) | DONT_VALIDATE_SIGNATURE
539538

540539
err, conds = run_block_generator2(
541540
block_program,

chia/full_node/mempool_manager.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,12 @@ async def pre_validate_spendbundle(
469469
finally:
470470
self._worker_queue_size -= 1
471471

472+
if sbc.num_atoms > sbc.cost * 60_000_000 / self.constants.MAX_BLOCK_COST_CLVM:
473+
raise ValidationError(Err.INVALID_SPEND_BUNDLE, "too many atoms")
474+
475+
if sbc.num_pairs > sbc.cost * 60_000_000 / self.constants.MAX_BLOCK_COST_CLVM:
476+
raise ValidationError(Err.INVALID_SPEND_BUNDLE, "too many pairs")
477+
472478
if bls_cache is not None:
473479
bls_cache.update(new_cache_entries)
474480

0 commit comments

Comments
 (0)