def build_attestation_data(state, slot, shard): assert state.slot >= slot if slot == state.slot: block_root = build_empty_block_for_next_slot(state).previous_block_root else: block_root = get_block_root_at_slot(state, slot) current_epoch_start_slot = get_epoch_start_slot(get_current_epoch(state)) if slot < current_epoch_start_slot: epoch_boundary_root = get_block_root(state, get_previous_epoch(state)) elif slot == current_epoch_start_slot: epoch_boundary_root = block_root else: epoch_boundary_root = get_block_root(state, get_current_epoch(state)) if slot < current_epoch_start_slot: justified_epoch = state.previous_justified_epoch justified_block_root = state.previous_justified_root else: 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 return AttestationData( slot=slot, shard=shard, beacon_block_root=block_root, source_epoch=justified_epoch, source_root=justified_block_root, target_root=epoch_boundary_root, crosslink_data_root=spec.ZERO_HASH, previous_crosslink_root=hash_tree_root(crosslinks[shard]), )
def test_activation(state): index = 0 assert is_active_validator(state.validator_registry[index], get_current_epoch(state)) # Mock a new deposit state.validator_registry[index].activation_eligibility_epoch = spec.FAR_FUTURE_EPOCH state.validator_registry[index].activation_epoch = spec.FAR_FUTURE_EPOCH state.validator_registry[index].effective_balance = spec.MAX_EFFECTIVE_BALANCE assert not is_active_validator(state.validator_registry[index], get_current_epoch(state)) pre_state = deepcopy(state) blocks = [] for _ in range(spec.ACTIVATION_EXIT_DELAY + 1): block = next_epoch(state) blocks.append(block) assert state.validator_registry[index].activation_eligibility_epoch != spec.FAR_FUTURE_EPOCH assert state.validator_registry[index].activation_epoch != spec.FAR_FUTURE_EPOCH assert is_active_validator( state.validator_registry[index], get_current_epoch(state), ) return pre_state, blocks, state
def test_transfer(state): # overwrite default 0 to test spec.MAX_TRANSFERS = 1 sender_index = get_active_validator_indices(state, get_current_epoch(state))[-1] amount = get_balance(state, sender_index) transfer = get_valid_transfer(state, state.slot + 1, sender_index, amount, signed=True) recipient_index = transfer.recipient pre_transfer_recipient_balance = get_balance(state, recipient_index) # un-activate so validator can transfer state.validator_registry[sender_index].activation_eligibility_epoch = spec.FAR_FUTURE_EPOCH yield 'pre', state # Add to state via block transition block = build_empty_block_for_next_slot(state) block.body.transfers.append(transfer) sign_block(state, block) yield 'blocks', [block], [spec.BeaconBlock] state_transition(state, block) yield 'post', state sender_balance = get_balance(state, sender_index) recipient_balance = get_balance(state, recipient_index) assert sender_balance == 0 assert recipient_balance == pre_transfer_recipient_balance + amount
def run_attestation_processing(state, attestation, valid=True): """ Run ``process_attestation``, yielding: - pre-state ('pre') - attestation ('attestation') - post-state ('post'). If ``valid == False``, run expecting ``AssertionError`` """ # yield pre-state yield 'pre', state yield 'attestation', attestation # If the attestation is invalid, processing is aborted, and there is no post-state. if not valid: expect_assertion_error(lambda: process_attestation(state, attestation)) yield 'post', None return current_epoch_count = len(state.current_epoch_attestations) previous_epoch_count = len(state.previous_epoch_attestations) # process attestation process_attestation(state, attestation) # Make sure the attestation has been processed if attestation.data.target_epoch == get_current_epoch(state): assert len(state.current_epoch_attestations) == current_epoch_count + 1 else: assert len(state.previous_epoch_attestations) == previous_epoch_count + 1 # yield post-state yield 'post', state
def get_valid_proposer_slashing(state, signed_1=False, signed_2=False): current_epoch = get_current_epoch(state) validator_index = get_active_validator_indices(state, current_epoch)[-1] privkey = pubkey_to_privkey[ state.validator_registry[validator_index].pubkey] slot = state.slot header_1 = BeaconBlockHeader( slot=slot, previous_block_root=b'\x33' * 32, state_root=b'\x44' * 32, block_body_root=b'\x55' * 32, ) header_2 = deepcopy(header_1) header_2.previous_block_root = b'\x99' * 32 header_2.slot = slot + 1 if signed_1: sign_block_header(state, header_1, privkey) if signed_2: sign_block_header(state, header_2, privkey) return ProposerSlashing( proposer_index=validator_index, header_1=header_1, header_2=header_2, )
def sign_transfer(state, transfer, privkey): transfer.signature = bls_sign(message_hash=signing_root(transfer), privkey=privkey, domain=get_domain( state=state, domain_type=spec.DOMAIN_TRANSFER, message_epoch=get_current_epoch(state), )) return transfer
def test_ejection(state): index = 0 assert is_active_validator(state.validator_registry[index], get_current_epoch(state)) assert state.validator_registry[index].exit_epoch == spec.FAR_FUTURE_EPOCH # Mock an ejection state.validator_registry[index].effective_balance = spec.EJECTION_BALANCE for _ in range(spec.ACTIVATION_EXIT_DELAY + 1): next_epoch(state) yield from run_process_registry_updates(state) assert state.validator_registry[index].exit_epoch != spec.FAR_FUTURE_EPOCH assert not is_active_validator( state.validator_registry[index], get_current_epoch(state), )
def test_voluntary_exit(state): pre_state = deepcopy(state) validator_index = get_active_validator_indices( pre_state, get_current_epoch(pre_state))[-1] # move state forward PERSISTENT_COMMITTEE_PERIOD epochs to allow for exit pre_state.slot += spec.PERSISTENT_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH post_state = deepcopy(pre_state) voluntary_exit = VoluntaryExit( epoch=get_current_epoch(pre_state), validator_index=validator_index, ) voluntary_exit.signature = bls.sign( message_hash=signing_root(voluntary_exit), privkey=privkeys[validator_index], domain=get_domain( state=pre_state, domain_type=spec.DOMAIN_VOLUNTARY_EXIT, )) # # Add to state via block transition # initiate_exit_block = build_empty_block_for_next_slot(post_state) initiate_exit_block.body.voluntary_exits.append(voluntary_exit) state_transition(post_state, initiate_exit_block) assert post_state.validator_registry[ validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH # # Process within epoch transition # exit_block = build_empty_block_for_next_slot(post_state) exit_block.slot += spec.SLOTS_PER_EPOCH state_transition(post_state, exit_block) assert post_state.validator_registry[ validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH return pre_state, [initiate_exit_block, exit_block], post_state
def test_voluntary_exit(state): validator_index = get_active_validator_indices( state, get_current_epoch(state) )[-1] # move state forward PERSISTENT_COMMITTEE_PERIOD epochs to allow for exit state.slot += spec.PERSISTENT_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH yield 'pre', state voluntary_exit = VoluntaryExit( epoch=get_current_epoch(state), validator_index=validator_index, ) voluntary_exit.signature = bls_sign( message_hash=signing_root(voluntary_exit), privkey=privkeys[validator_index], domain=get_domain( state=state, domain_type=spec.DOMAIN_VOLUNTARY_EXIT, ) ) # Add to state via block transition initiate_exit_block = build_empty_block_for_next_slot(state) initiate_exit_block.body.voluntary_exits.append(voluntary_exit) sign_block(state, initiate_exit_block) state_transition(state, initiate_exit_block) assert state.validator_registry[validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH # Process within epoch transition exit_block = build_empty_block_for_next_slot(state) exit_block.slot += spec.SLOTS_PER_EPOCH sign_block(state, exit_block) state_transition(state, exit_block) yield 'blocks', [initiate_exit_block, exit_block], [spec.BeaconBlock] yield 'post', state assert state.validator_registry[validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH
def test_invalid_signature(state): # move state forward PERSISTENT_COMMITTEE_PERIOD epochs to allow for exit state.slot += spec.PERSISTENT_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH current_epoch = get_current_epoch(state) validator_index = get_active_validator_indices(state, current_epoch)[0] privkey = pubkey_to_privkey[state.validator_registry[validator_index].pubkey] voluntary_exit = build_voluntary_exit(state, current_epoch, validator_index, privkey) yield from run_voluntary_exit_processing(state, voluntary_exit, False)
def test_success_withdrawable(state): next_epoch(state) apply_empty_block(state) transfer = get_valid_transfer(state, signed=True) # withdrawable_epoch in past so can transfer state.validator_registry[ transfer.sender].withdrawable_epoch = get_current_epoch(state) - 1 yield from run_transfer_processing(state, transfer)
def test_success_active_above_max_effective_fee(state): sender_index = get_active_validator_indices(state, get_current_epoch(state))[-1] state.balances[sender_index] = spec.MAX_EFFECTIVE_BALANCE + 1 transfer = get_valid_transfer(state, sender_index=sender_index, amount=0, fee=1, signed=True) yield from run_transfer_processing(state, transfer)
def get_valid_transfer(state, slot=None, sender_index=None, amount=None, fee=None): if slot is None: slot = state.slot current_epoch = get_current_epoch(state) if sender_index is None: sender_index = get_active_validator_indices(state, current_epoch)[-1] recipient_index = get_active_validator_indices(state, current_epoch)[0] transfer_pubkey = pubkeys[-1] transfer_privkey = privkeys[-1] if fee is None: fee = get_balance(state, sender_index) // 32 if amount is None: amount = get_balance(state, sender_index) - fee transfer = Transfer( sender=sender_index, recipient=recipient_index, amount=amount, fee=fee, slot=slot, pubkey=transfer_pubkey, signature=ZERO_HASH, ) transfer.signature = bls.sign(message_hash=signing_root(transfer), privkey=transfer_privkey, domain=get_domain( state=state, domain_type=spec.DOMAIN_TRANSFER, message_epoch=get_current_epoch(state), )) # ensure withdrawal_credentials reproducable state.validator_registry[transfer.sender].withdrawal_credentials = ( spec.BLS_WITHDRAWAL_PREFIX_BYTE + spec.hash(transfer.pubkey)[1:]) return transfer
def test_proposer_is_not_activated(state): proposer_slashing = get_valid_proposer_slashing(state, signed_1=True, signed_2=True) # set proposer to be not active yet state.validator_registry[ proposer_slashing. proposer_index].activation_epoch = get_current_epoch(state) + 1 yield from run_proposer_slashing_processing(state, proposer_slashing, False)
def test_activation(state): index = 0 assert is_active_validator(state.validator_registry[index], get_current_epoch(state)) # Mock a new deposit state.validator_registry[index].activation_eligibility_epoch = spec.FAR_FUTURE_EPOCH state.validator_registry[index].activation_epoch = spec.FAR_FUTURE_EPOCH state.validator_registry[index].effective_balance = spec.MAX_EFFECTIVE_BALANCE assert not is_active_validator(state.validator_registry[index], get_current_epoch(state)) for _ in range(spec.ACTIVATION_EXIT_DELAY + 1): next_epoch(state) yield from run_process_registry_updates(state) assert state.validator_registry[index].activation_eligibility_epoch != spec.FAR_FUTURE_EPOCH assert state.validator_registry[index].activation_epoch != spec.FAR_FUTURE_EPOCH assert is_active_validator( state.validator_registry[index], get_current_epoch(state), )
def test_success_withdrawable(state): next_epoch(state) transfer = get_valid_transfer(state) # withdrawable_epoch in past so can transfer state.validator_registry[ transfer.sender].withdrawable_epoch = get_current_epoch(state) - 1 pre_state, post_state = run_transfer_processing(state, transfer) return pre_state, transfer, post_state
def test_active_but_transfer_past_effective_balance(state): sender_index = get_active_validator_indices(state, get_current_epoch(state))[-1] amount = spec.MAX_EFFECTIVE_BALANCE // 32 state.balances[sender_index] = spec.MAX_EFFECTIVE_BALANCE transfer = get_valid_transfer(state, sender_index=sender_index, amount=amount, fee=0, signed=True) yield from run_transfer_processing(state, transfer, False)
def test_empty_epoch_transition_not_finalizing(state): test_state = deepcopy(state) block = build_empty_block_for_next_slot(test_state) block.slot += spec.SLOTS_PER_EPOCH * 5 state_transition(test_state, block) assert test_state.slot == block.slot assert test_state.finalized_epoch < get_current_epoch(test_state) - 4 for index in range(len(test_state.validator_registry)): assert get_balance(test_state, index) < get_balance(state, index) return state, [block], test_state
def test_ejection(state): index = 0 assert is_active_validator(state.validator_registry[index], get_current_epoch(state)) assert state.validator_registry[index].exit_epoch == spec.FAR_FUTURE_EPOCH # Mock an ejection state.validator_registry[index].effective_balance = spec.EJECTION_BALANCE pre_state = deepcopy(state) blocks = [] for _ in range(spec.ACTIVATION_EXIT_DELAY + 1): block = next_epoch(state) blocks.append(block) assert state.validator_registry[index].exit_epoch != spec.FAR_FUTURE_EPOCH assert not is_active_validator( state.validator_registry[index], get_current_epoch(state), ) return pre_state, blocks, state
def test_success_active_above_max_effective(state): sender_index = get_active_validator_indices(state, get_current_epoch(state))[-1] amount = spec.MAX_EFFECTIVE_BALANCE // 32 state.balances[sender_index] = spec.MAX_EFFECTIVE_BALANCE + amount transfer = get_valid_transfer(state, sender_index=sender_index, amount=amount, fee=0) pre_state, post_state = run_transfer_processing(state, transfer) return pre_state, transfer, post_state
def test_proposer_is_withdrawn(state): proposer_slashing = get_valid_proposer_slashing(state) # set proposer withdrawable_epoch in past current_epoch = get_current_epoch(state) proposer_index = proposer_slashing.proposer_index state.validator_registry[ proposer_index].withdrawable_epoch = current_epoch - 1 pre_state, post_state = run_proposer_slashing_processing( state, proposer_slashing, False) return pre_state, proposer_slashing, post_state
def test_double_late_crosslink(state): if spec.get_epoch_committee_count( state, spec.get_current_epoch(state)) < spec.SHARD_COUNT: print( "warning: ignoring test, test-assumptions are incompatible with configuration" ) return next_epoch(state) state.slot += 4 attestation_1 = get_valid_attestation(state, signed=True) fill_aggregate_attestation(state, attestation_1) # add attestation_1 to next epoch next_epoch(state) add_attestation_to_state(state, attestation_1, state.slot + 1) for slot in range(spec.SLOTS_PER_EPOCH): attestation_2 = get_valid_attestation(state) if attestation_2.data.shard == attestation_1.data.shard: sign_attestation(state, attestation_2) 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 # already updated the relevant crosslink next_epoch(state) add_attestation_to_state(state, attestation_2, state.slot + 1) assert len(state.previous_epoch_attestations) == 1 assert len(state.current_epoch_attestations) == 0 crosslink_deltas = get_crosslink_deltas(state) yield from run_process_crosslinks(state) shard = attestation_2.data.shard # ensure that the current crosslinks were not updated by the second attestation assert state.previous_crosslinks[shard] == state.current_crosslinks[shard] # ensure no reward, only penalties for the failed crosslink for index in get_crosslink_committee(state, attestation_2.data.target_epoch, attestation_2.data.shard): assert crosslink_deltas[0][index] == 0 assert crosslink_deltas[1][index] > 0
def test_proposer_is_withdrawn(state): proposer_slashing = get_valid_proposer_slashing(state, signed_1=True, signed_2=True) # move 1 epoch into future, to allow for past withdrawable epoch state.slot += spec.SLOTS_PER_EPOCH # set proposer withdrawable_epoch in past current_epoch = get_current_epoch(state) proposer_index = proposer_slashing.proposer_index state.validator_registry[ proposer_index].withdrawable_epoch = current_epoch - 1 yield from run_proposer_slashing_processing(state, proposer_slashing, False)
def test_historical_batch(state): state.slot += spec.SLOTS_PER_HISTORICAL_ROOT - (state.slot % spec.SLOTS_PER_HISTORICAL_ROOT) - 1 pre_historical_roots_len = len(state.historical_roots) yield 'pre', state block = build_empty_block_for_next_slot(state, signed=True) state_transition(state, block) yield 'blocks', [block], [spec.BeaconBlock] yield 'post', state assert state.slot == block.slot assert get_current_epoch(state) % (spec.SLOTS_PER_HISTORICAL_ROOT // spec.SLOTS_PER_EPOCH) == 0 assert len(state.historical_roots) == pre_historical_roots_len + 1
def test_insufficient_balance(state): sender_index = get_active_validator_indices(state, get_current_epoch(state))[-1] state.balances[sender_index] = spec.MAX_EFFECTIVE_BALANCE transfer = get_valid_transfer(state, sender_index=sender_index, amount=1, fee=0, signed=True) # un-activate so validator can transfer state.validator_registry[ transfer.sender].activation_epoch = spec.FAR_FUTURE_EPOCH yield from run_transfer_processing(state, transfer, False)
def test_no_dust_sender(state): sender_index = get_active_validator_indices(state, get_current_epoch(state))[-1] balance = state.balances[sender_index] transfer = get_valid_transfer(state, sender_index=sender_index, amount=balance - spec.MIN_DEPOSIT_AMOUNT + 1, fee=0, signed=True) # un-activate so validator can transfer state.validator_registry[ transfer.sender].activation_epoch = spec.FAR_FUTURE_EPOCH yield from run_transfer_processing(state, transfer, False)
def test_transfer(state): # overwrite default 0 to test spec.MAX_TRANSFERS = 1 pre_state = deepcopy(state) current_epoch = get_current_epoch(pre_state) sender_index = get_active_validator_indices(pre_state, current_epoch)[-1] recipient_index = get_active_validator_indices(pre_state, current_epoch)[0] transfer_pubkey = pubkeys[-1] transfer_privkey = privkeys[-1] amount = get_balance(pre_state, sender_index) pre_transfer_recipient_balance = get_balance(pre_state, recipient_index) transfer = Transfer( sender=sender_index, recipient=recipient_index, amount=amount, fee=0, slot=pre_state.slot + 1, pubkey=transfer_pubkey, ) transfer.signature = bls.sign(message_hash=signing_root(transfer), privkey=transfer_privkey, domain=get_domain( state=pre_state, domain_type=spec.DOMAIN_TRANSFER, )) # ensure withdrawal_credentials reproducable pre_state.validator_registry[sender_index].withdrawal_credentials = ( spec.BLS_WITHDRAWAL_PREFIX_BYTE + hash(transfer_pubkey)[1:]) # un-activate so validator can transfer pre_state.validator_registry[ sender_index].activation_eligibility_epoch = spec.FAR_FUTURE_EPOCH post_state = deepcopy(pre_state) # # Add to state via block transition # block = build_empty_block_for_next_slot(post_state) block.body.transfers.append(transfer) state_transition(post_state, block) sender_balance = get_balance(post_state, sender_index) recipient_balance = get_balance(post_state, recipient_index) assert sender_balance == 0 assert recipient_balance == pre_transfer_recipient_balance + amount return pre_state, [block], post_state
def test_historical_batch(state): state.slot += spec.SLOTS_PER_HISTORICAL_ROOT - ( state.slot % spec.SLOTS_PER_HISTORICAL_ROOT) - 1 post_state = deepcopy(state) block = build_empty_block_for_next_slot(post_state) state_transition(post_state, block) assert post_state.slot == block.slot assert get_current_epoch(post_state) % (spec.SLOTS_PER_HISTORICAL_ROOT // spec.SLOTS_PER_EPOCH) == 0 assert len(post_state.historical_roots) == len(state.historical_roots) + 1 return state, [block], post_state
def test_success_exit_queue(state): # move state forward PERSISTENT_COMMITTEE_PERIOD epochs to allow for exit state.slot += spec.PERSISTENT_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH current_epoch = get_current_epoch(state) # exit `MAX_EXITS_PER_EPOCH` initial_indices = get_active_validator_indices(state, current_epoch)[:get_churn_limit(state)] # Prepare a bunch of exits, based on the current state exit_queue = [] for index in initial_indices: privkey = pubkey_to_privkey[state.validator_registry[index].pubkey] exit_queue.append(build_voluntary_exit( state, current_epoch, index, privkey, signed=True, )) # Now run all the exits for voluntary_exit in exit_queue: # the function yields data, but we are just interested in running it here, ignore yields. for _ in run_voluntary_exit_processing(state, voluntary_exit): continue # exit an additional validator validator_index = get_active_validator_indices(state, current_epoch)[-1] privkey = pubkey_to_privkey[state.validator_registry[validator_index].pubkey] voluntary_exit = build_voluntary_exit( state, current_epoch, validator_index, privkey, signed=True, ) # This is the interesting part of the test: on a pre-state with a full exit queue, # when processing an additional exit, it results in an exit in a later epoch yield from run_voluntary_exit_processing(state, voluntary_exit) assert ( state.validator_registry[validator_index].exit_epoch == state.validator_registry[initial_indices[0]].exit_epoch + 1 )
def test_insufficient_balance(state): sender_index = get_active_validator_indices(state, get_current_epoch(state))[-1] amount = spec.MAX_EFFECTIVE_BALANCE state.balances[sender_index] = spec.MAX_EFFECTIVE_BALANCE transfer = get_valid_transfer(state, sender_index=sender_index, amount=amount + 1, fee=0) # un-activate so validator can transfer state.validator_registry[ transfer.sender].activation_epoch = spec.FAR_FUTURE_EPOCH pre_state, post_state = run_transfer_processing(state, transfer, False) return pre_state, transfer, post_state