diff --git a/tests/core/pyspec/eth2spec/test/helpers/deposits.py b/tests/core/pyspec/eth2spec/test/helpers/deposits.py index 6a2e30497..9f7d96981 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/deposits.py +++ b/tests/core/pyspec/eth2spec/test/helpers/deposits.py @@ -1,3 +1,5 @@ +from random import Random + from eth2spec.test.helpers.keys import pubkeys, privkeys from eth2spec.utils import bls from eth2spec.utils.merkle_minimal import calc_merkle_tree_from_leaves, get_merkle_proof @@ -62,29 +64,69 @@ def deposit_from_context(spec, deposit_data_list, index): return deposit, root, deposit_data_list -def prepare_genesis_deposits(spec, genesis_validator_count, amount, signed=False, deposit_data_list=None): +def prepare_full_genesis_deposits(spec, + amount, + deposit_count, + min_pubkey_index=0, + signed=False, + deposit_data_list=None): if deposit_data_list is None: deposit_data_list = [] genesis_deposits = [] - for validator_index in range(genesis_validator_count): - pubkey = pubkeys[validator_index] - privkey = privkeys[validator_index] + for pubkey_index in range(min_pubkey_index, min_pubkey_index + deposit_count): + pubkey = pubkeys[pubkey_index] + privkey = privkeys[pubkey_index] # insecurely use pubkey as withdrawal key if no credentials provided withdrawal_credentials = spec.BLS_WITHDRAWAL_PREFIX + spec.hash(pubkey)[1:] deposit, root, deposit_data_list = build_deposit( spec, - deposit_data_list, - pubkey, - privkey, - amount, - withdrawal_credentials, - signed, + deposit_data_list=deposit_data_list, + pubkey=pubkey, + privkey=privkey, + amount=amount, + withdrawal_credentials=withdrawal_credentials, + signed=signed, ) genesis_deposits.append(deposit) return genesis_deposits, root, deposit_data_list +def prepare_random_genesis_deposits(spec, + deposit_count, + max_pubkey_index, + min_pubkey_index=0, + max_amount=None, + min_amount=None, + deposit_data_list=None, + rng=Random(3131)): + if max_amount is None: + max_amount = spec.MAX_EFFECTIVE_BALANCE + if min_amount is None: + min_amount = spec.MIN_DEPOSIT_AMOUNT + if deposit_data_list is None: + deposit_data_list = [] + deposits = [] + for _ in range(deposit_count): + pubkey_index = rng.randint(min_pubkey_index, max_pubkey_index) + pubkey = pubkeys[pubkey_index] + privkey = privkeys[pubkey_index] + amount = rng.randint(min_amount, max_amount) + random_byte = bytes([rng.randint(0, 255)]) + withdrawal_credentials = spec.BLS_WITHDRAWAL_PREFIX + spec.hash(random_byte)[1:] + deposit, root, deposit_data_list = build_deposit( + spec, + deposit_data_list=deposit_data_list, + pubkey=pubkey, + privkey=privkey, + amount=amount, + withdrawal_credentials=withdrawal_credentials, + signed=True, + ) + deposits.append(deposit) + return deposits, root, deposit_data_list + + def prepare_state_and_deposit(spec, state, validator_index, amount, withdrawal_credentials=None, signed=False): """ Prepare the state for the deposit, and create a deposit for the given validator, depositing the given amount. diff --git a/tests/core/pyspec/eth2spec/test/phase0/genesis/test_initialization.py b/tests/core/pyspec/eth2spec/test/phase0/genesis/test_initialization.py index faade2d17..b39f5ecfd 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/genesis/test_initialization.py +++ b/tests/core/pyspec/eth2spec/test/phase0/genesis/test_initialization.py @@ -1,6 +1,7 @@ from eth2spec.test.context import PHASE0, spec_test, with_phases, single_phase from eth2spec.test.helpers.deposits import ( - prepare_genesis_deposits, + prepare_full_genesis_deposits, + prepare_random_genesis_deposits, ) @@ -9,7 +10,12 @@ from eth2spec.test.helpers.deposits import ( @single_phase def test_initialize_beacon_state_from_eth1(spec): deposit_count = spec.MIN_GENESIS_ACTIVE_VALIDATOR_COUNT - deposits, deposit_root, _ = prepare_genesis_deposits(spec, deposit_count, spec.MAX_EFFECTIVE_BALANCE, signed=True) + deposits, deposit_root, _ = prepare_full_genesis_deposits( + spec, + spec.MAX_EFFECTIVE_BALANCE, + deposit_count, + signed=True, + ) eth1_block_hash = b'\x12' * 32 eth1_timestamp = spec.MIN_GENESIS_TIME @@ -37,14 +43,18 @@ def test_initialize_beacon_state_from_eth1(spec): @single_phase def test_initialize_beacon_state_some_small_balances(spec): main_deposit_count = spec.MIN_GENESIS_ACTIVE_VALIDATOR_COUNT - main_deposits, _, deposit_data_list = prepare_genesis_deposits(spec, main_deposit_count, - spec.MAX_EFFECTIVE_BALANCE, signed=True) + main_deposits, _, deposit_data_list = prepare_full_genesis_deposits( + spec, spec.MAX_EFFECTIVE_BALANCE, + deposit_count=main_deposit_count, signed=True, + ) # For deposits above, and for another deposit_count, add a balance of EFFECTIVE_BALANCE_INCREMENT small_deposit_count = main_deposit_count * 2 - small_deposits, deposit_root, _ = prepare_genesis_deposits(spec, small_deposit_count, - spec.MIN_DEPOSIT_AMOUNT, - signed=True, - deposit_data_list=deposit_data_list) + small_deposits, deposit_root, _ = prepare_full_genesis_deposits( + spec, spec.MIN_DEPOSIT_AMOUNT, + deposit_count=small_deposit_count, + signed=True, + deposit_data_list=deposit_data_list, + ) deposits = main_deposits + small_deposits eth1_block_hash = b'\x12' * 32 @@ -67,3 +77,109 @@ def test_initialize_beacon_state_some_small_balances(spec): # yield state yield 'state', state + + +@with_phases([PHASE0]) +@spec_test +@single_phase +def test_initialize_beacon_state_one_topup_activation(spec): + # Submit all but one deposit as MAX_EFFECTIVE_BALANCE + main_deposit_count = spec.MIN_GENESIS_ACTIVE_VALIDATOR_COUNT - 1 + main_deposits, _, deposit_data_list = prepare_full_genesis_deposits( + spec, spec.MAX_EFFECTIVE_BALANCE, + deposit_count=main_deposit_count, signed=True, + ) + + # Submit last pubkey deposit as MAX_EFFECTIVE_BALANCE - MIN_DEPOSIT_AMOUNT + partial_deposits, _, deposit_data_list = prepare_full_genesis_deposits( + spec, spec.MAX_EFFECTIVE_BALANCE - spec.MIN_DEPOSIT_AMOUNT, + deposit_count=1, + min_pubkey_index=main_deposit_count, + signed=True, + deposit_data_list=deposit_data_list, + ) + + # Top up thelast pubkey deposit as MIN_DEPOSIT_AMOUNT to complete the deposit + top_up_deposits, _, _ = prepare_full_genesis_deposits( + spec, spec.MIN_DEPOSIT_AMOUNT, + deposit_count=1, + min_pubkey_index=main_deposit_count, + signed=True, + deposit_data_list=deposit_data_list, + ) + + deposits = main_deposits + partial_deposits + top_up_deposits + + eth1_block_hash = b'\x13' * 32 + eth1_timestamp = spec.MIN_GENESIS_TIME + + yield 'eth1_block_hash', eth1_block_hash + yield 'eth1_timestamp', eth1_timestamp + yield 'deposits', deposits + + # initialize beacon_state + state = spec.initialize_beacon_state_from_eth1(eth1_block_hash, eth1_timestamp, deposits) + assert spec.is_valid_genesis_state(state) + + # yield state + yield 'state', state + + +@with_phases([PHASE0]) +@spec_test +@single_phase +def test_initialize_beacon_state_random_invalid_genesis(spec): + # Make a bunch of random deposits + deposits, _, deposit_data_list = prepare_random_genesis_deposits( + spec, + deposit_count=20, + max_pubkey_index=10, + ) + eth1_block_hash = b'\x14' * 32 + eth1_timestamp = spec.MIN_GENESIS_TIME + 1 + + yield 'eth1_block_hash', eth1_block_hash + yield 'eth1_timestamp', eth1_timestamp + yield 'deposits', deposits + + # initialize beacon_state + state = spec.initialize_beacon_state_from_eth1(eth1_block_hash, eth1_timestamp, deposits) + assert not spec.is_valid_genesis_state(state) + + yield 'state', state + + +@with_phases([PHASE0]) +@spec_test +@single_phase +def test_initialize_beacon_state_random_valid_genesis(spec): + # Make a bunch of random deposits + random_deposits, _, deposit_data_list = prepare_random_genesis_deposits( + spec, + deposit_count=20, + min_pubkey_index=spec.MIN_GENESIS_ACTIVE_VALIDATOR_COUNT - 5, + max_pubkey_index=spec.MIN_GENESIS_ACTIVE_VALIDATOR_COUNT + 5, + ) + + # Then make spec.MIN_GENESIS_ACTIVE_VALIDATOR_COUNT full deposits + full_deposits, _, _ = prepare_full_genesis_deposits( + spec, + spec.MAX_EFFECTIVE_BALANCE, + deposit_count=spec.MIN_GENESIS_ACTIVE_VALIDATOR_COUNT, + signed=True, + deposit_data_list=deposit_data_list + ) + + deposits = random_deposits + full_deposits + eth1_block_hash = b'\x15' * 32 + eth1_timestamp = spec.MIN_GENESIS_TIME + 2 + + yield 'eth1_block_hash', eth1_block_hash + yield 'eth1_timestamp', eth1_timestamp + yield 'deposits', deposits + + # initialize beacon_state + state = spec.initialize_beacon_state_from_eth1(eth1_block_hash, eth1_timestamp, deposits) + assert spec.is_valid_genesis_state(state) + + yield 'state', state diff --git a/tests/core/pyspec/eth2spec/test/phase0/genesis/test_validity.py b/tests/core/pyspec/eth2spec/test/phase0/genesis/test_validity.py index dbaf3f951..7bdfe1a42 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/genesis/test_validity.py +++ b/tests/core/pyspec/eth2spec/test/phase0/genesis/test_validity.py @@ -1,12 +1,17 @@ from eth2spec.test.context import PHASE0, spec_test, with_phases, single_phase from eth2spec.test.helpers.deposits import ( - prepare_genesis_deposits, + prepare_full_genesis_deposits, ) def create_valid_beacon_state(spec): deposit_count = spec.MIN_GENESIS_ACTIVE_VALIDATOR_COUNT - deposits, _, _ = prepare_genesis_deposits(spec, deposit_count, spec.MAX_EFFECTIVE_BALANCE, signed=True) + deposits, _, _ = prepare_full_genesis_deposits( + spec, + amount=spec.MAX_EFFECTIVE_BALANCE, + deposit_count=deposit_count, + signed=True, + ) eth1_block_hash = b'\x12' * 32 eth1_timestamp = spec.MIN_GENESIS_TIME @@ -69,7 +74,12 @@ def test_is_valid_genesis_state_true_more_balance(spec): @single_phase def test_is_valid_genesis_state_true_one_more_validator(spec): deposit_count = spec.MIN_GENESIS_ACTIVE_VALIDATOR_COUNT + 1 - deposits, _, _ = prepare_genesis_deposits(spec, deposit_count, spec.MAX_EFFECTIVE_BALANCE, signed=True) + deposits, _, _ = prepare_full_genesis_deposits( + spec, + amount=spec.MAX_EFFECTIVE_BALANCE, + deposit_count=deposit_count, + signed=True, + ) eth1_block_hash = b'\x12' * 32 eth1_timestamp = spec.MIN_GENESIS_TIME @@ -83,7 +93,12 @@ def test_is_valid_genesis_state_true_one_more_validator(spec): @single_phase def test_is_valid_genesis_state_false_not_enough_validator(spec): deposit_count = spec.MIN_GENESIS_ACTIVE_VALIDATOR_COUNT - 1 - deposits, _, _ = prepare_genesis_deposits(spec, deposit_count, spec.MAX_EFFECTIVE_BALANCE, signed=True) + deposits, _, _ = prepare_full_genesis_deposits( + spec, + amount=spec.MAX_EFFECTIVE_BALANCE, + deposit_count=deposit_count, + signed=True, + ) eth1_block_hash = b'\x12' * 32 eth1_timestamp = spec.MIN_GENESIS_TIME