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_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 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 create_genesis_state(num_validators): deposit_root = b'\x42' * 32 state = spec.BeaconState(genesis_time=0, deposit_index=num_validators, latest_eth1_data=Eth1Data( deposit_root=deposit_root, deposit_count=num_validators, block_hash=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 calc_beacon_proposer_index(state: BeaconState, slot: spec.Slot) -> spec.ValidatorIndex: epoch = spec.compute_epoch_at_slot(slot) seed = spec.hash( spec.get_seed(state, epoch, spec.DOMAIN_BEACON_PROPOSER) + spec.int_to_bytes(state.slot, length=8)) indices = spec.get_active_validator_indices(state, epoch) return spec.compute_proposer_index(state, indices, seed)
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_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_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_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_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_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 get_valid_transfer(state, slot=None, sender_index=None, amount=None, fee=None, signed=False): 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, ) if signed: sign_transfer(state, transfer, transfer_privkey) # ensure withdrawal_credentials reproducible state.validator_registry[transfer.sender].withdrawal_credentials = ( spec.BLS_WITHDRAWAL_PREFIX_BYTE + spec.hash(transfer.pubkey)[1:]) return transfer
def test_validator_not_active(state): 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] state.validator_registry[validator_index].activation_epoch = spec.FAR_FUTURE_EPOCH # build and test voluntary exit voluntary_exit = build_voluntary_exit( state, current_epoch, validator_index, privkey, signed=True, ) yield from run_voluntary_exit_processing(state, voluntary_exit, False)
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
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_validator_not_active_long_enough(state): 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, signed=True, ) assert ( current_epoch - state.validator_registry[validator_index].activation_epoch < spec.PERSISTENT_COMMITTEE_PERIOD ) yield from run_voluntary_exit_processing(state, voluntary_exit, False)
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_validator_already_exited(state): # move state forward PERSISTENT_COMMITTEE_PERIOD epochs to allow validator able to 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] # but validator already has exited state.validator_registry[validator_index].exit_epoch = current_epoch + 2 voluntary_exit = build_voluntary_exit( state, current_epoch, validator_index, privkey, signed=True, ) yield from run_voluntary_exit_processing(state, voluntary_exit, False)
def test_balance_driven_status_transitions(state): current_epoch = get_current_epoch(state) validator_index = get_active_validator_indices(state, current_epoch)[-1] assert state.validator_registry[validator_index].exit_epoch == spec.FAR_FUTURE_EPOCH # set validator balance to below ejection threshold state.validator_registry[validator_index].effective_balance = spec.EJECTION_BALANCE yield 'pre', state # trigger epoch transition block = build_empty_block_for_next_slot(state) block.slot += spec.SLOTS_PER_EPOCH sign_block(state, block) state_transition(state, block) yield 'blocks', [block], [spec.BeaconBlock] yield 'post', state assert state.validator_registry[validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH
def test_balance_driven_status_transitions(state): current_epoch = get_current_epoch(state) validator_index = get_active_validator_indices(state, current_epoch)[-1] assert state.validator_registry[ validator_index].exit_epoch == spec.FAR_FUTURE_EPOCH # set validator balance to below ejection threshold state.validator_registry[ validator_index].effective_balance = spec.EJECTION_BALANCE post_state = deepcopy(state) # # trigger epoch transition # block = build_empty_block_for_next_slot(post_state) block.slot += spec.SLOTS_PER_EPOCH state_transition(post_state, block) assert post_state.validator_registry[ validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH return state, [block], post_state
def get_valid_proposer_slashing(state): 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=ZERO_HASH, state_root=ZERO_HASH, block_body_root=ZERO_HASH, ) header_2 = deepcopy(header_1) header_2.previous_block_root = b'\x02' * 32 header_2.slot = slot + 1 domain = get_domain( state=state, domain_type=spec.DOMAIN_BEACON_PROPOSER, ) header_1.signature = bls.sign( message_hash=signing_root(header_1), privkey=privkey, domain=domain, ) header_2.signature = bls.sign( message_hash=signing_root(header_2), privkey=privkey, domain=domain, ) return ProposerSlashing( proposer_index=validator_index, header_1=header_1, header_2=header_2, )