diff --git a/test_libs/pyspec/eth2spec/test/block_processing/test_process_attestation.py b/test_libs/pyspec/eth2spec/test/block_processing/test_process_attestation.py index 248b04ef4..98cb62ef7 100644 --- a/test_libs/pyspec/eth2spec/test/block_processing/test_process_attestation.py +++ b/test_libs/pyspec/eth2spec/test/block_processing/test_process_attestation.py @@ -3,17 +3,18 @@ from copy import deepcopy import eth2spec.phase0.spec as spec from eth2spec.phase0.state_transition import ( - state_transition, + state_transition_to, ) from eth2spec.phase0.spec import ( get_current_epoch, process_attestation ) from eth2spec.test.helpers import ( - build_empty_block_for_next_slot, get_valid_attestation, + apply_empty_block, next_epoch, next_slot, + make_attestation_signature, ) from eth2spec.test.context import spec_state_test, expect_assertion_error @@ -65,9 +66,8 @@ def test_success(state): @spec_state_test def test_success_previous_epoch(state): attestation = get_valid_attestation(state) - block = build_empty_block_for_next_slot(state) - block.slot = state.slot + spec.SLOTS_PER_EPOCH - state_transition(state, block) + next_epoch(state) + apply_empty_block(state) yield from run_attestation_processing(state, attestation) @@ -83,10 +83,9 @@ def test_before_inclusion_delay(state): @spec_state_test def test_after_epoch_slots(state): attestation = get_valid_attestation(state) - block = build_empty_block_for_next_slot(state) # increment past latest inclusion slot - block.slot = state.slot + spec.SLOTS_PER_EPOCH + 1 - state_transition(state, block) + state_transition_to(state, state.slot + spec.SLOTS_PER_EPOCH + 1) + apply_empty_block(state) yield from run_attestation_processing(state, attestation, False) @@ -105,6 +104,9 @@ def test_old_source_epoch(state): # Now go beyond that, it will be invalid attestation.data.source_epoch -= 1 + # Re do signature + make_attestation_signature(state, attestation) + yield from run_attestation_processing(state, attestation, False) @@ -115,6 +117,9 @@ def test_wrong_shard(state): attestation.data.shard += 1 + # Re do signature + make_attestation_signature(state, attestation) + yield from run_attestation_processing(state, attestation, False) @@ -125,6 +130,9 @@ def test_new_source_epoch(state): attestation.data.source_epoch += 1 + # Re do signature + make_attestation_signature(state, attestation) + yield from run_attestation_processing(state, attestation, False) @@ -135,6 +143,9 @@ def test_source_root_is_target_root(state): attestation.data.source_root = attestation.data.target_root + # Re do signature + make_attestation_signature(state, attestation) + yield from run_attestation_processing(state, attestation, False) @@ -159,6 +170,9 @@ def test_invalid_current_source_root(state): # Make attestation source root invalid: should be previous justified, not current one attestation.data.source_root = state.current_justified_root + # Re do signature + make_attestation_signature(state, attestation) + yield from run_attestation_processing(state, attestation, False) @@ -169,6 +183,9 @@ def test_bad_source_root(state): attestation.data.source_root = b'\x42' * 32 + # Re do signature + make_attestation_signature(state, attestation) + yield from run_attestation_processing(state, attestation, False) @@ -179,15 +196,21 @@ def test_non_zero_crosslink_data_root(state): attestation.data.crosslink_data_root = b'\x42' * 32 + # Re do signature + make_attestation_signature(state, attestation) + yield from run_attestation_processing(state, attestation, False) @spec_state_test def test_bad_previous_crosslink(state): next_epoch(state) + apply_empty_block(state) + attestation = get_valid_attestation(state) for _ in range(spec.MIN_ATTESTATION_INCLUSION_DELAY): next_slot(state) + apply_empty_block(state) state.current_crosslinks[attestation.data.shard].epoch += 10 diff --git a/test_libs/pyspec/eth2spec/test/block_processing/test_process_attester_slashing.py b/test_libs/pyspec/eth2spec/test/block_processing/test_process_attester_slashing.py index 957b9a9f0..a0334c892 100644 --- a/test_libs/pyspec/eth2spec/test/block_processing/test_process_attester_slashing.py +++ b/test_libs/pyspec/eth2spec/test/block_processing/test_process_attester_slashing.py @@ -3,14 +3,15 @@ from eth2spec.phase0.spec import ( get_beacon_proposer_index, process_attester_slashing, ) +from eth2spec.test.context import spec_state_test, expect_assertion_error from eth2spec.test.helpers import ( get_balance, get_valid_attester_slashing, next_epoch, + apply_empty_block, + make_indexed_attestation_signature ) -from eth2spec.test.context import spec_state_test, expect_assertion_error - def run_attester_slashing_processing(state, attester_slashing, valid=True): """ @@ -47,14 +48,14 @@ def run_attester_slashing_processing(state, attester_slashing, valid=True): # lost whistleblower reward assert ( - get_balance(state, slashed_index) < - pre_slashed_balance + get_balance(state, slashed_index) < + pre_slashed_balance ) # gained whistleblower reward assert ( - get_balance(state, proposer_index) > - pre_proposer_balance + get_balance(state, proposer_index) > + pre_proposer_balance ) yield 'post', state @@ -70,6 +71,8 @@ def test_success_double(state): @spec_state_test def test_success_surround(state): next_epoch(state) + apply_empty_block(state) + state.current_justified_epoch += 1 attester_slashing = get_valid_attester_slashing(state) @@ -77,6 +80,9 @@ def test_success_surround(state): attester_slashing.attestation_1.data.source_epoch = attester_slashing.attestation_2.data.source_epoch - 1 attester_slashing.attestation_1.data.target_epoch = attester_slashing.attestation_2.data.target_epoch + 1 + # correct the signature of attestation 1 + make_indexed_attestation_signature(state, attester_slashing.attestation_1) + yield from run_attester_slashing_processing(state, attester_slashing) diff --git a/test_libs/pyspec/eth2spec/test/block_processing/test_process_block_header.py b/test_libs/pyspec/eth2spec/test/block_processing/test_process_block_header.py index a176f7958..f2695ad09 100644 --- a/test_libs/pyspec/eth2spec/test/block_processing/test_process_block_header.py +++ b/test_libs/pyspec/eth2spec/test/block_processing/test_process_block_header.py @@ -6,13 +6,13 @@ from eth2spec.phase0.spec import ( advance_slot, process_block_header, ) +from eth2spec.test.context import spec_state_test, expect_assertion_error from eth2spec.test.helpers import ( build_empty_block_for_next_slot, next_slot, + make_block_signature ) -from eth2spec.test.context import spec_state_test, expect_assertion_error - def prepare_state_for_header_processing(state): cache_state(state) @@ -43,7 +43,7 @@ def run_block_header_processing(state, block, valid=True): @spec_state_test def test_success(state): - block = build_empty_block_for_next_slot(state) + block = build_empty_block_for_next_slot(state, signed=True) yield from run_block_header_processing(state, block) @@ -59,6 +59,7 @@ def test_invalid_slot(state): def test_invalid_previous_block_root(state): block = build_empty_block_for_next_slot(state) block.previous_block_root = b'\12' * 32 # invalid prev root + make_block_signature(state, block) yield from run_block_header_processing(state, block, valid=False) @@ -73,6 +74,6 @@ def test_proposer_slashed(state): # set proposer to slashed state.validator_registry[proposer_index].slashed = True - block = build_empty_block_for_next_slot(state) + block = build_empty_block_for_next_slot(state, signed=True) yield from run_block_header_processing(state, block, valid=False) diff --git a/test_libs/pyspec/eth2spec/test/block_processing/test_process_transfer.py b/test_libs/pyspec/eth2spec/test/block_processing/test_process_transfer.py index 10d2ccede..64539d56e 100644 --- a/test_libs/pyspec/eth2spec/test/block_processing/test_process_transfer.py +++ b/test_libs/pyspec/eth2spec/test/block_processing/test_process_transfer.py @@ -1,18 +1,17 @@ import eth2spec.phase0.spec as spec - from eth2spec.phase0.spec import ( get_active_validator_indices, get_beacon_proposer_index, get_current_epoch, process_transfer, ) +from eth2spec.test.context import spec_state_test, expect_assertion_error from eth2spec.test.helpers import ( get_valid_transfer, next_epoch, + apply_empty_block ) -from eth2spec.test.context import spec_state_test, expect_assertion_error - def run_transfer_processing(state, transfer, valid=True): """ @@ -58,6 +57,7 @@ def test_success_non_activated(state): @spec_state_test def test_success_withdrawable(state): next_epoch(state) + apply_empty_block(state) transfer = get_valid_transfer(state) @@ -97,7 +97,7 @@ def test_active_but_transfer_past_effective_balance(state): @spec_state_test def test_incorrect_slot(state): - transfer = get_valid_transfer(state, slot=state.slot+1) + transfer = get_valid_transfer(state, slot=state.slot + 1) # un-activate so validator can transfer state.validator_registry[transfer.sender].activation_epoch = spec.FAR_FUTURE_EPOCH diff --git a/test_libs/pyspec/eth2spec/test/context.py b/test_libs/pyspec/eth2spec/test/context.py index afabd4a57..99c58ce3d 100644 --- a/test_libs/pyspec/eth2spec/test/context.py +++ b/test_libs/pyspec/eth2spec/test/context.py @@ -5,7 +5,7 @@ from .helpers import create_genesis_state from .utils import spectest, with_args # Provides a genesis state as first argument to the function decorated with this -with_state = with_args(lambda: [create_genesis_state(spec.SLOTS_PER_EPOCH * 8, list())]) +with_state = with_args(lambda: [create_genesis_state(spec.SLOTS_PER_EPOCH * 8)]) # shorthand for decorating @with_state @spectest() diff --git a/test_libs/pyspec/eth2spec/test/epoch_processing/test_process_crosslinks.py b/test_libs/pyspec/eth2spec/test/epoch_processing/test_process_crosslinks.py index 203978d29..d4e6dc938 100644 --- a/test_libs/pyspec/eth2spec/test/epoch_processing/test_process_crosslinks.py +++ b/test_libs/pyspec/eth2spec/test/epoch_processing/test_process_crosslinks.py @@ -1,15 +1,15 @@ from copy import deepcopy import eth2spec.phase0.spec as spec - -from eth2spec.phase0.state_transition import ( - state_transition, -) from eth2spec.phase0.spec import ( cache_state, get_crosslink_deltas, process_crosslinks, ) +from eth2spec.phase0.state_transition import ( + state_transition, +) +from eth2spec.test.context import spec_state_test from eth2spec.test.helpers import ( add_attestation_to_state, build_empty_block_for_next_slot, @@ -18,8 +18,8 @@ from eth2spec.test.helpers import ( get_valid_attestation, next_epoch, next_slot, + apply_empty_block ) -from eth2spec.test.context import spec_state_test def run_process_crosslinks(state, valid=True): @@ -115,6 +115,8 @@ def test_double_late_crosslink(state): if attestation_2.data.shard == attestation_1.data.shard: break next_slot(state) + apply_empty_block(state) + fill_aggregate_attestation(state, attestation_2) # add attestation_2 in the next epoch after attestation_1 has @@ -126,7 +128,7 @@ def test_double_late_crosslink(state): assert len(state.current_epoch_attestations) == 0 crosslink_deltas = get_crosslink_deltas(state) - + yield from run_process_crosslinks(state) shard = attestation_2.data.shard diff --git a/test_libs/pyspec/eth2spec/test/helpers.py b/test_libs/pyspec/eth2spec/test/helpers.py index 8eb6d8891..f6cedd479 100644 --- a/test_libs/pyspec/eth2spec/test/helpers.py +++ b/test_libs/pyspec/eth2spec/test/helpers.py @@ -2,20 +2,20 @@ from copy import deepcopy from py_ecc import bls -from eth2spec.phase0.state_transition import ( - state_transition, -) +from typing import List + import eth2spec.phase0.spec as spec -from eth2spec.utils.minimal_ssz import signing_root from eth2spec.phase0.spec import ( # constants ZERO_HASH, # SSZ Attestation, + IndexedAttestation, AttestationData, AttestationDataAndCustodyBit, AttesterSlashing, BeaconBlock, + BeaconState, BeaconBlockHeader, Deposit, DepositData, @@ -33,7 +33,6 @@ from eth2spec.phase0.spec import ( get_current_epoch, get_domain, get_epoch_start_slot, - get_genesis_beacon_state, get_previous_epoch, get_shard_delta, hash_tree_root, @@ -41,12 +40,15 @@ from eth2spec.phase0.spec import ( verify_merkle_branch, hash, ) +from eth2spec.phase0.state_transition import ( + state_transition, state_transition_to +) from eth2spec.utils.merkle_minimal import ( calc_merkle_tree_from_leaves, get_merkle_proof, get_merkle_root, ) - +from eth2spec.utils.minimal_ssz import signing_root privkeys = [i + 1 for i in range(1024)] pubkeys = [bls.privtopub(privkey) for privkey in privkeys] @@ -64,72 +66,109 @@ def set_bitfield_bit(bitfield, i): byte_index = i // 8 bit_index = i % 8 return ( - bitfield[:byte_index] + - bytes([bitfield[byte_index] | (1 << bit_index)]) + - bitfield[byte_index+1:] + bitfield[:byte_index] + + bytes([bitfield[byte_index] | (1 << bit_index)]) + + bitfield[byte_index + 1:] ) -def create_mock_genesis_validator_deposits(num_validators, deposit_data_leaves=None): - if not deposit_data_leaves: - deposit_data_leaves = [] - signature = b'\x33' * 96 - - deposit_data_list = [] - for i in range(num_validators): - pubkey = pubkeys[i] - deposit_data = DepositData( - pubkey=pubkey, - # insecurely use pubkey as withdrawal key as well - withdrawal_credentials=spec.BLS_WITHDRAWAL_PREFIX_BYTE + hash(pubkey)[1:], - amount=spec.MAX_EFFECTIVE_BALANCE, - signature=signature, - ) - item = deposit_data.hash_tree_root() - deposit_data_leaves.append(item) - tree = calc_merkle_tree_from_leaves(tuple(deposit_data_leaves)) - root = get_merkle_root((tuple(deposit_data_leaves))) - proof = list(get_merkle_proof(tree, item_index=i)) - assert verify_merkle_branch(item, proof, spec.DEPOSIT_CONTRACT_TREE_DEPTH, i, root) - deposit_data_list.append(deposit_data) - - genesis_validator_deposits = [] - for i in range(num_validators): - genesis_validator_deposits.append(Deposit( - proof=list(get_merkle_proof(tree, item_index=i)), - index=i, - data=deposit_data_list[i] - )) - return genesis_validator_deposits, root - - -def create_genesis_state(num_validators, deposit_data_leaves=None): - initial_deposits, deposit_root = create_mock_genesis_validator_deposits( - num_validators, - deposit_data_leaves, +def build_mock_validator(i: int, balance: int): + pubkey = pubkeys[i] + # insecurely use pubkey as withdrawal key as well + withdrawal_credentials = spec.BLS_WITHDRAWAL_PREFIX_BYTE + hash(pubkey)[1:] + return spec.Validator( + pubkey=pubkeys[i], + withdrawal_credentials=withdrawal_credentials, + activation_eligibility_epoch=spec.FAR_FUTURE_EPOCH, + activation_epoch=spec.FAR_FUTURE_EPOCH, + exit_epoch=spec.FAR_FUTURE_EPOCH, + withdrawable_epoch=spec.FAR_FUTURE_EPOCH, + effective_balance=min(balance - balance % spec.EFFECTIVE_BALANCE_INCREMENT, spec.MAX_EFFECTIVE_BALANCE) ) - return get_genesis_beacon_state( - initial_deposits, + + +def create_genesis_state(num_validators): + deposit_root = b'\x42' * 32 + + state = spec.BeaconState( genesis_time=0, - genesis_eth1_data=Eth1Data( + deposit_index=num_validators, + latest_eth1_data=Eth1Data( deposit_root=deposit_root, - deposit_count=len(initial_deposits), + deposit_count=num_validators, block_hash=spec.ZERO_HASH, - ), + )) + + # We "hack" in the initial validators, + # as it is much faster than creating and processing genesis deposits for every single test case. + state.balances = [spec.MAX_EFFECTIVE_BALANCE] * num_validators + state.validator_registry = [build_mock_validator(i, state.balances[i]) for i in range(num_validators)] + + # Process genesis activations + for validator in state.validator_registry: + if validator.effective_balance >= spec.MAX_EFFECTIVE_BALANCE: + validator.activation_eligibility_epoch = spec.GENESIS_EPOCH + validator.activation_epoch = spec.GENESIS_EPOCH + + genesis_active_index_root = hash_tree_root(get_active_validator_indices(state, spec.GENESIS_EPOCH)) + for index in range(spec.LATEST_ACTIVE_INDEX_ROOTS_LENGTH): + state.latest_active_index_roots[index] = genesis_active_index_root + + return state + + +def make_block_signature(state, block): + assert block.slot == state.slot or block.slot == state.slot + 1 + if block.slot == state.slot: + proposer_index = spec.get_beacon_proposer_index(state) + else: + # use stub state to get proposer index of next slot + stub_state = deepcopy(state) + next_slot(stub_state) + proposer_index = spec.get_beacon_proposer_index(stub_state) + + privkey = privkeys[proposer_index] + + block.body.randao_reveal = bls.sign( + privkey=privkey, + message_hash=hash_tree_root(slot_to_epoch(block.slot)), + domain=get_domain( + state, + message_epoch=slot_to_epoch(block.slot), + domain_type=spec.DOMAIN_RANDAO, + ) ) + block.signature = bls.sign( + message_hash=signing_root(block), + privkey=privkey, + domain=get_domain( + state, + spec.DOMAIN_BEACON_PROPOSER, + slot_to_epoch(block.slot))) + return block -def build_empty_block_for_next_slot(state): +def build_empty_block(state, slot=None, signed=False): + if slot is None: + slot = state.slot empty_block = BeaconBlock() - empty_block.slot = state.slot + 1 + empty_block.slot = slot empty_block.body.eth1_data.deposit_count = state.deposit_index previous_block_header = deepcopy(state.latest_block_header) if previous_block_header.state_root == spec.ZERO_HASH: previous_block_header.state_root = state.hash_tree_root() empty_block.previous_block_root = signing_root(previous_block_header) + + if signed: + make_block_signature(state, empty_block) + return empty_block +def build_empty_block_for_next_slot(state, signed=False): + return build_empty_block(state, state.slot + 1, signed=signed) + + def build_deposit_data(state, pubkey, privkey, amount): deposit_data = DepositData( pubkey=pubkey, @@ -172,7 +211,8 @@ def build_attestation_data(state, slot, shard): justified_epoch = state.current_justified_epoch justified_block_root = state.current_justified_root - crosslinks = state.current_crosslinks if slot_to_epoch(slot) == get_current_epoch(state) else state.previous_crosslinks + crosslinks = state.current_crosslinks if slot_to_epoch(slot) == get_current_epoch( + state) else state.previous_crosslinks return AttestationData( shard=shard, beacon_block_root=block_root, @@ -270,6 +310,8 @@ def get_valid_attester_slashing(state): attestation_2 = deepcopy(attestation_1) attestation_2.data.target_root = b'\x01' * 32 + make_attestation_signature(state, attestation_2) + return AttesterSlashing( attestation_1=convert_to_indexed(state, attestation_1), attestation_2=convert_to_indexed(state, attestation_2), @@ -299,26 +341,38 @@ def get_valid_attestation(state, slot=None): data=attestation_data, custody_bitfield=custody_bitfield, ) - participants = get_attesting_indices( - state, - attestation.data, - attestation.aggregation_bitfield, - ) - assert len(participants) == 2 + make_attestation_signature(state, attestation) + return attestation + +def make_aggregate_attestation_signature(state: BeaconState, data: AttestationData, participants: List[int]): signatures = [] for validator_index in participants: privkey = privkeys[validator_index] signatures.append( get_attestation_signature( state, - attestation.data, + data, privkey ) ) - attestation.aggregation_signature = bls.aggregate_signatures(signatures) - return attestation + return bls.aggregate_signatures(signatures) + + +def make_indexed_attestation_signature(state, indexed_attestation: IndexedAttestation): + participants = indexed_attestation.custody_bit_0_indices + indexed_attestation.custody_bit_1_indices + indexed_attestation.signature = make_aggregate_attestation_signature(state, indexed_attestation.data, participants) + + +def make_attestation_signature(state, attestation: Attestation): + participants = get_attesting_indices( + state, + attestation.data, + attestation.aggregation_bitfield, + ) + + attestation.signature = make_aggregate_attestation_signature(state, attestation.data, participants) def get_valid_transfer(state, slot=None, sender_index=None, amount=None, fee=None): @@ -357,7 +411,7 @@ def get_valid_transfer(state, slot=None, sender_index=None, amount=None, fee=Non # ensure withdrawal_credentials reproducable state.validator_registry[transfer.sender].withdrawal_credentials = ( - spec.BLS_WITHDRAWAL_PREFIX_BYTE + spec.hash(transfer.pubkey)[1:] + spec.BLS_WITHDRAWAL_PREFIX_BYTE + spec.hash(transfer.pubkey)[1:] ) return transfer @@ -390,26 +444,32 @@ def add_attestation_to_state(state, attestation, slot): block = build_empty_block_for_next_slot(state) block.slot = slot block.body.attestations.append(attestation) + state_transition_to(state, block.slot) + make_block_signature(state, block) state_transition(state, block) def next_slot(state): """ - Transition to the next slot via an empty block. - Return the empty block that triggered the transition. + Transition to the next slot. """ - block = build_empty_block_for_next_slot(state) - state_transition(state, block) - return block + state_transition_to(state, state.slot + 1) def next_epoch(state): """ - Transition to the start slot of the next epoch via an empty block. - Return the empty block that triggered the transition. + Transition to the start slot of the next epoch """ - block = build_empty_block_for_next_slot(state) - block.slot += spec.SLOTS_PER_EPOCH - (state.slot % spec.SLOTS_PER_EPOCH) + slot = state.slot + spec.SLOTS_PER_EPOCH - (state.slot % spec.SLOTS_PER_EPOCH) + state_transition_to(state, slot) + + +def apply_empty_block(state): + """ + Transition via an empty block (on current slot, assuming no block has been applied yet). + :return: the empty block that triggered the transition. + """ + block = build_empty_block(state) state_transition(state, block) return block diff --git a/test_libs/pyspec/eth2spec/test/test_finality.py b/test_libs/pyspec/eth2spec/test/test_finality.py index 16bf24a4e..75191e59a 100644 --- a/test_libs/pyspec/eth2spec/test/test_finality.py +++ b/test_libs/pyspec/eth2spec/test/test_finality.py @@ -1,10 +1,10 @@ from copy import deepcopy import eth2spec.phase0.spec as spec - from eth2spec.phase0.state_transition import ( state_transition, ) +from .context import spec_state_test from .helpers import ( build_empty_block_for_next_slot, fill_aggregate_attestation, @@ -12,10 +12,9 @@ from .helpers import ( get_epoch_start_slot, get_valid_attestation, next_epoch, + apply_empty_block ) -from .context import spec_state_test - def check_finality(state, prev_state, @@ -101,7 +100,9 @@ def test_finality_rule_4(state): def test_finality_rule_1(state): # get past first two epochs that finality does not run on next_epoch(state) + apply_empty_block(state) next_epoch(state) + apply_empty_block(state) yield 'pre', state @@ -128,7 +129,9 @@ def test_finality_rule_1(state): def test_finality_rule_2(state): # get past first two epochs that finality does not run on next_epoch(state) + apply_empty_block(state) next_epoch(state) + apply_empty_block(state) yield 'pre', state @@ -161,7 +164,9 @@ def test_finality_rule_3(state): """ # get past first two epochs that finality does not run on next_epoch(state) + apply_empty_block(state) next_epoch(state) + apply_empty_block(state) yield 'pre', state diff --git a/test_libs/pyspec/eth2spec/utils/bls_stub.py b/test_libs/pyspec/eth2spec/utils/bls_stub.py index 108c4ef71..0a52d2f65 100644 --- a/test_libs/pyspec/eth2spec/utils/bls_stub.py +++ b/test_libs/pyspec/eth2spec/utils/bls_stub.py @@ -1,12 +1,13 @@ +from py_ecc import bls def bls_verify(pubkey, message_hash, signature, domain): - return True + return bls.verify(message_hash=message_hash, pubkey=pubkey, signature=signature, domain=domain) def bls_verify_multiple(pubkeys, message_hashes, signature, domain): - return True + return bls.verify_multiple(pubkeys, message_hashes, signature, domain) def bls_aggregate_pubkeys(pubkeys): - return b'\x42' * 96 + return bls.aggregate_pubkeys(pubkeys)