diff --git a/scripts/build_spec.py b/scripts/build_spec.py index a16fa79ac..d7b6d0f59 100644 --- a/scripts/build_spec.py +++ b/scripts/build_spec.py @@ -16,6 +16,7 @@ PHASE0_IMPORTS = '''from typing import ( Callable, Dict, List, + Optional, Set, Tuple, ) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 1dcdbdef9..28d0d72cd 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1176,8 +1176,12 @@ def get_genesis_beacon_state(deposits: List[Deposit], genesis_time: int, genesis ) # Process genesis deposits - for deposit in deposits: - process_deposit(state, deposit) + for deposit_index, deposit in enumerate(deposits): + process_deposit( + state, + deposit, + deposit_index=deposit_index, + ) # Process genesis activations for validator in state.validators: @@ -1726,16 +1730,18 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None: ##### Deposits ```python -def process_deposit(state: BeaconState, deposit: Deposit) -> None: +def process_deposit(state: BeaconState, deposit: Deposit, deposit_index: Optional[uint64]) -> None: """ Process an Eth1 deposit, registering a validator or increasing its balance. """ + if deposit_index is None: + deposit_index = state.eth1_deposit_index # Verify the Merkle branch assert verify_merkle_branch( leaf=hash_tree_root(deposit.data), proof=deposit.proof, depth=DEPOSIT_CONTRACT_TREE_DEPTH, - index=state.eth1_deposit_index, + index=deposit_index, root=state.eth1_data.deposit_root, ) diff --git a/test_libs/pyspec/eth2spec/test/helpers/deposits.py b/test_libs/pyspec/eth2spec/test/helpers/deposits.py index 20ff7440f..ca8b23361 100644 --- a/test_libs/pyspec/eth2spec/test/helpers/deposits.py +++ b/test_libs/pyspec/eth2spec/test/helpers/deposits.py @@ -4,25 +4,31 @@ from eth2spec.utils.merkle_minimal import calc_merkle_tree_from_leaves, get_merk from eth2spec.utils.ssz.ssz_impl import signing_root -def build_deposit_data(spec, state, pubkey, privkey, amount, withdrawal_credentials, signed=False): +def build_deposit_data(spec, pubkey, privkey, amount, withdrawal_credentials, state=None, signed=False): deposit_data = spec.DepositData( pubkey=pubkey, withdrawal_credentials=withdrawal_credentials, amount=amount, ) if signed: - sign_deposit_data(spec, state, deposit_data, privkey) + sign_deposit_data(spec, deposit_data, privkey, state) return deposit_data -def sign_deposit_data(spec, state, deposit_data, privkey): - signature = bls_sign( - message_hash=signing_root(deposit_data), - privkey=privkey, - domain=spec.get_domain( +def sign_deposit_data(spec, deposit_data, privkey, state=None): + if state is None: + # Genesis + domain = spec.bls_domain(spec.DOMAIN_DEPOSIT) + else: + domain = spec.get_domain( state, spec.DOMAIN_DEPOSIT, ) + + signature = bls_sign( + message_hash=signing_root(deposit_data), + privkey=privkey, + domain=domain, ) deposit_data.signature = signature @@ -35,7 +41,7 @@ def build_deposit(spec, amount, withdrawal_credentials, signed): - deposit_data = build_deposit_data(spec, state, pubkey, privkey, amount, withdrawal_credentials, signed) + deposit_data = build_deposit_data(spec, pubkey, privkey, amount, withdrawal_credentials, state=state, signed=signed) item = deposit_data.hash_tree_root() index = len(deposit_data_leaves) @@ -54,6 +60,36 @@ def build_deposit(spec, return deposit, root, deposit_data_leaves +def prepare_genesis_deposits(spec, genesis_validator_count, signed=False): + genesis_deposit_data_list = [] + deposit_data_leaves = [] + for validator_index in range(genesis_validator_count): + pubkey = pubkeys[validator_index] + privkey = privkeys[validator_index] + # insecurely use pubkey as withdrawal key if no credentials provided + withdrawal_credentials = spec.int_to_bytes(spec.BLS_WITHDRAWAL_PREFIX, length=1) + spec.hash(pubkey)[1:] + deposit_data = spec.DepositData( + pubkey=pubkey, + withdrawal_credentials=withdrawal_credentials, + amount=spec.MAX_EFFECTIVE_BALANCE, + ) + if signed: + sign_deposit_data(spec, deposit_data, privkey) # state=None + item = deposit_data.hash_tree_root() + deposit_data_leaves.append(item) + genesis_deposit_data_list.append(deposit_data) + + tree = calc_merkle_tree_from_leaves(tuple(deposit_data_leaves)) + root = get_merkle_root((tuple(deposit_data_leaves))) + + genesis_deposits = ( + spec.Deposit(proof=list(get_merkle_proof(tree, item_index=index)), data=deposit_data) + for index, deposit_data in enumerate(genesis_deposit_data_list) + ) + + return genesis_deposits, root + + 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/test_libs/pyspec/eth2spec/test/sanity/test_genesis.py b/test_libs/pyspec/eth2spec/test/sanity/test_genesis.py new file mode 100644 index 000000000..088df84e8 --- /dev/null +++ b/test_libs/pyspec/eth2spec/test/sanity/test_genesis.py @@ -0,0 +1,29 @@ +from eth2spec.test.context import spec_state_test, with_phases +from eth2spec.test.helpers.deposits import ( + prepare_genesis_deposits, +) + + +@with_phases(['phase0']) +@spec_state_test +def test_genesis(spec, state): + deposit_count = 2 + genesis_deposits, deposit_root = prepare_genesis_deposits(spec, deposit_count) + genesis_time = 1234 + + yield genesis_deposits + yield genesis_time + + genesis_eth1_data = spec.Eth1Data( + deposit_root=deposit_root, + deposit_count=deposit_count, + block_hash=b'\x12' * 32, + ) + + yield genesis_eth1_data + genesis_state = spec.get_genesis_beacon_state( + genesis_deposits, + genesis_time, + genesis_eth1_data, + ) + yield genesis_state