def test_proposer_slashing(state): test_state = deepcopy(state) current_epoch = get_current_epoch(test_state) validator_index = get_active_validator_indices( test_state.validator_registry, current_epoch)[-1] pubkey = pubkeys[validator_index] privkey = pubkey_to_privkey[pubkey] slot = spec.GENESIS_SLOT header_1 = BeaconBlockHeader(slot=slot, previous_block_root=b'\x00' * 32, state_root=b'\x00' * 32, block_body_root=b'\x00' * 32, signature=b'\x00' * 96) header_2 = deepcopy(header_1) header_2.previous_block_root = b'\x02' * 32 header_2.slot = slot + 1 domain = get_domain( fork=test_state.fork, epoch=get_current_epoch(test_state), domain_type=spec.DOMAIN_BEACON_BLOCK, ) header_1.signature = bls.sign( message_hash=signed_root(header_1), privkey=privkey, domain=domain, ) header_2.signature = bls.sign( message_hash=signed_root(header_2), privkey=privkey, domain=domain, ) proposer_slashing = ProposerSlashing( proposer_index=validator_index, header_1=header_1, header_2=header_2, ) # # Add to state via block transition # block = construct_empty_block_for_next_slot(test_state) block.body.proposer_slashings.append(proposer_slashing) state_transition(test_state, block) assert not state.validator_registry[validator_index].initiated_exit assert not state.validator_registry[validator_index].slashed slashed_validator = test_state.validator_registry[validator_index] assert not slashed_validator.initiated_exit assert slashed_validator.slashed assert slashed_validator.exit_epoch < spec.FAR_FUTURE_EPOCH assert slashed_validator.withdrawable_epoch < spec.FAR_FUTURE_EPOCH # lost whistleblower reward assert test_state.validator_balances[ validator_index] < state.validator_balances[validator_index] return state, [block], test_state
def test_transfer(state): pre_state = deepcopy(state) current_epoch = get_current_epoch(pre_state) sender_index = get_active_validator_indices(pre_state.validator_registry, current_epoch)[-1] recipient_index = get_active_validator_indices( pre_state.validator_registry, current_epoch)[0] transfer_pubkey = pubkeys[-1] transfer_privkey = pubkey_to_privkey[transfer_pubkey] amount = pre_state.validator_balances[sender_index] pre_transfer_recipient_balance = pre_state.validator_balances[ recipient_index] transfer = Transfer( sender=sender_index, recipient=recipient_index, amount=amount, fee=0, slot=pre_state.slot + 1, pubkey=transfer_pubkey, signature=b'\x00' * 96, ) transfer.signature = bls.sign(message_hash=signed_root(transfer), privkey=transfer_privkey, domain=get_domain( fork=pre_state.fork, epoch=get_current_epoch(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_epoch = spec.FAR_FUTURE_EPOCH post_state = deepcopy(pre_state) # # Add to state via block transition # block = construct_empty_block_for_next_slot(post_state) block.body.transfers.append(transfer) state_transition(post_state, block) sender_balance = post_state.validator_balances[sender_index] recipient_balance = post_state.validator_balances[recipient_index] assert sender_balance == 0 assert recipient_balance == pre_transfer_recipient_balance + amount return pre_state, [block], post_state
def test_voluntary_exit(state): pre_state = deepcopy(state) validator_index = get_active_validator_indices( pre_state.validator_registry, get_current_epoch(pre_state))[-1] pubkey = pubkeys[validator_index] # move state forward PERSISTENT_COMMITTEE_PERIOD epochs to allow for exit pre_state.slot += spec.PERSISTENT_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH # artificially trigger registry update at next epoch transition pre_state.validator_registry_update_epoch -= 1 post_state = deepcopy(pre_state) voluntary_exit = VoluntaryExit( epoch=get_current_epoch(pre_state), validator_index=validator_index, signature=b'\x00' * 96, ) voluntary_exit.signature = bls.sign( message_hash=signed_root(voluntary_exit), privkey=pubkey_to_privkey[pubkey], domain=get_domain( fork=pre_state.fork, epoch=get_current_epoch(pre_state), domain_type=spec.DOMAIN_VOLUNTARY_EXIT, )) # # Add to state via block transition # initiate_exit_block = construct_empty_block_for_next_slot(post_state) initiate_exit_block.body.voluntary_exits.append(voluntary_exit) state_transition(post_state, initiate_exit_block) assert not pre_state.validator_registry[validator_index].initiated_exit assert post_state.validator_registry[validator_index].initiated_exit assert post_state.validator_registry[ validator_index].exit_epoch == spec.FAR_FUTURE_EPOCH # # Process within epoch transition # exit_block = construct_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_ejection(state): pre_state = deepcopy(state) current_epoch = get_current_epoch(pre_state) validator_index = get_active_validator_indices( pre_state.validator_registry, current_epoch)[-1] assert pre_state.validator_registry[ validator_index].exit_epoch == spec.FAR_FUTURE_EPOCH # set validator balance to below ejection threshold pre_state.validator_balances[validator_index] = spec.EJECTION_BALANCE - 1 post_state = deepcopy(pre_state) # # trigger epoch transition # block = construct_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 pre_state, [block], post_state
def test_voluntary_exit(state): test_state = deepcopy(state) current_epoch = get_current_epoch(test_state) validator_index = get_active_validator_indices(test_state.validator_registry, current_epoch)[-1] voluntary_exit = VoluntaryExit( epoch=current_epoch, validator_index=validator_index, signature=b'\x00'*96, ) # # Add to state via block transition # block = construct_empty_block_for_next_slot(test_state) block.body.voluntary_exits.append(voluntary_exit) state_transition(test_state, block) assert not state.validator_registry[validator_index].initiated_exit assert test_state.validator_registry[validator_index].initiated_exit assert test_state.validator_registry[validator_index].exit_epoch == FAR_FUTURE_EPOCH # # Process within epoch transition # # artificially trigger registry update test_state.validator_registry_update_epoch -= 1 block = construct_empty_block_for_next_slot(test_state) block.slot += SLOTS_PER_EPOCH state_transition(test_state, block) assert test_state.validator_registry[validator_index].exit_epoch < FAR_FUTURE_EPOCH
def build_attestation_data(state, slot, shard): assert state.slot >= slot block_root = construct_empty_block_for_next_slot(state).previous_block_root epoch_start_slot = get_epoch_start_slot(get_current_epoch(state)) if epoch_start_slot == slot: epoch_boundary_root = block_root else: get_block_root(state, epoch_start_slot) if slot < epoch_start_slot: justified_block_root = state.previous_justified_root else: justified_block_root = state.current_justified_root return AttestationData( slot=slot, shard=shard, beacon_block_root=block_root, source_epoch=state.current_justified_epoch, source_root=justified_block_root, target_root=epoch_boundary_root, crosslink_data_root=spec.ZERO_HASH, previous_crosslink=deepcopy(state.latest_crosslinks[shard]), )
def build_attestation_data(state, slot, shard): assert state.slot >= slot if state.slot == slot: block_root = construct_empty_block_for_next_slot(state).previous_block_root else: block_root = get_block_root(state, slot) epoch_start_slot = get_epoch_start_slot(get_current_epoch(state)) if epoch_start_slot == slot: epoch_boundary_root = block_root else: get_block_root(state, epoch_start_slot) justified_epoch_slot = get_epoch_start_slot(state.justified_epoch) if justified_epoch_slot == slot: justified_block_root = block_root else: justified_block_root = get_block_root(state, justified_epoch_slot) return AttestationData( slot=slot, shard=shard, beacon_block_root=block_root, epoch_boundary_root=epoch_boundary_root, crosslink_data_root=ZERO_HASH, latest_crosslink=deepcopy(state.latest_crosslinks[shard]), justified_epoch=state.justified_epoch, justified_block_root=justified_block_root, )
def test_empty_epoch_transition_not_finalizing(state): test_state = deepcopy(state) block = construct_empty_block_for_next_slot(test_state) block.slot += 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
def test_historical_batch(state): test_state = deepcopy(state) test_state.slot += SLOTS_PER_HISTORICAL_ROOT - (test_state.slot % SLOTS_PER_HISTORICAL_ROOT) - 1 block = construct_empty_block_for_next_slot(test_state) state_transition(test_state, block) assert test_state.slot == block.slot assert get_current_epoch(test_state) % (SLOTS_PER_HISTORICAL_ROOT // SLOTS_PER_EPOCH) == 0 assert len(test_state.historical_roots) == len(state.historical_roots) + 1
def test_attestation(state): test_state = deepcopy(state) current_epoch = get_current_epoch(test_state) slot = state.slot shard = state.current_shuffling_start_shard attestation_data = build_attestation_data(state, slot, shard) crosslink_committees = get_crosslink_committees_at_slot(state, slot) crosslink_committee = [ committee for committee, _shard in crosslink_committees if _shard == attestation_data.shard ][0] committee_size = len(crosslink_committee) bitfield_length = (committee_size + 7) // 8 aggregation_bitfield = b'\x01' + b'\x00' * (bitfield_length - 1) custody_bitfield = b'\x00' * bitfield_length attestation = Attestation( aggregation_bitfield=aggregation_bitfield, data=attestation_data, custody_bitfield=custody_bitfield, aggregate_signature=b'\x42' * 96, ) # # Add to state via block transition # block = construct_empty_block_for_next_slot(test_state) block.slot += MIN_ATTESTATION_INCLUSION_DELAY block.body.attestations.append(attestation) state_transition(test_state, block) assert len(test_state.current_epoch_attestations) == len( state.current_epoch_attestations) + 1 # # Epoch transition should move to previous_epoch_attestations # pre_current_epoch_attestations = deepcopy( test_state.current_epoch_attestations) block = construct_empty_block_for_next_slot(test_state) block.slot += SLOTS_PER_EPOCH state_transition(test_state, block) assert len(test_state.current_epoch_attestations) == 0 assert test_state.previous_epoch_attestations == pre_current_epoch_attestations
def test_historical_batch(state): pre_state = deepcopy(state) pre_state.slot += spec.SLOTS_PER_HISTORICAL_ROOT - ( pre_state.slot % spec.SLOTS_PER_HISTORICAL_ROOT) - 1 post_state = deepcopy(pre_state) block = construct_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(pre_state.historical_roots) + 1 return pre_state, [block], post_state
def test_ejection(state): test_state = deepcopy(state) current_epoch = get_current_epoch(test_state) validator_index = get_active_validator_indices(test_state.validator_registry, current_epoch)[-1] assert test_state.validator_registry[validator_index].exit_epoch == FAR_FUTURE_EPOCH # set validator balance to below ejection threshold test_state.validator_balances[validator_index] = EJECTION_BALANCE - 1 # # trigger epoch transition # block = construct_empty_block_for_next_slot(test_state) block.slot += SLOTS_PER_EPOCH state_transition(test_state, block) assert test_state.validator_registry[validator_index].exit_epoch < FAR_FUTURE_EPOCH
def create_deposit_data(state, pubkey, amount): privkey = pubkey_to_privkey[pubkey] deposit_input = DepositInput( pubkey=pubkey, withdrawal_credentials=privkey.to_bytes(32, byteorder='big'), proof_of_possession=b'00' * 96, ) proof_of_possession = bls.sign(message_hash=signed_root(deposit_input), privkey=privkey, domain=get_domain( state.fork, get_current_epoch(state), spec.DOMAIN_DEPOSIT, )) deposit_input.proof_of_possession = proof_of_possession deposit_data = DepositData( amount=amount, timestamp=0, deposit_input=deposit_input, ) return deposit_data
def test_transfer(state): test_state = deepcopy(state) current_epoch = get_current_epoch(test_state) sender_index = get_active_validator_indices(test_state.validator_registry, current_epoch)[-1] recipient_index = get_active_validator_indices( test_state.validator_registry, current_epoch)[0] pubkey = b'\x00' * 48 amount = test_state.validator_balances[sender_index] pre_transfer_recipient_balance = test_state.validator_balances[ recipient_index] transfer = Transfer( sender=sender_index, recipient=recipient_index, amount=amount, fee=0, slot=test_state.slot + 1, pubkey=pubkey, signature=b'\x42' * 96, ) # ensure withdrawal_credentials reproducable test_state.validator_registry[sender_index].withdrawal_credentials = ( BLS_WITHDRAWAL_PREFIX_BYTE + hash(pubkey)[1:]) # un-activate so validator can transfer test_state.validator_registry[ sender_index].activation_epoch = FAR_FUTURE_EPOCH # # Add to state via block transition # block = construct_empty_block_for_next_slot(test_state) block.body.transfers.append(transfer) state_transition(test_state, block) sender_balance = test_state.validator_balances[sender_index] recipient_balance = test_state.validator_balances[recipient_index] assert sender_balance == 0 assert recipient_balance == pre_transfer_recipient_balance + amount
def test_proposer_slashing(state): test_state = deepcopy(state) current_epoch = get_current_epoch(test_state) validator_index = get_active_validator_indices(test_state.validator_registry, current_epoch)[-1] slot = GENESIS_SLOT header_1 = BeaconBlockHeader( slot=slot, previous_block_root=b'\x00'*32, state_root=b'\x00'*32, block_body_root=b'\x00'*32, signature=b'\x00'*96 ) header_2 = deepcopy(header_1) header_2.previous_block_root = b'\x02'*32 proposer_slashing = ProposerSlashing( proposer_index=validator_index, header_1=header_1, header_2=header_2, ) # # Add to state via block transition # block = construct_empty_block_for_next_slot(test_state) block.body.proposer_slashings.append(proposer_slashing) state_transition(test_state, block) assert not state.validator_registry[validator_index].initiated_exit assert not state.validator_registry[validator_index].slashed slashed_validator = test_state.validator_registry[validator_index] assert not slashed_validator.initiated_exit assert slashed_validator.slashed assert slashed_validator.exit_epoch < FAR_FUTURE_EPOCH assert slashed_validator.withdrawable_epoch < FAR_FUTURE_EPOCH # lost whistleblower reward assert test_state.validator_balances[validator_index] < state.validator_balances[validator_index]
def test_attestation(state): test_state = deepcopy(state) slot = state.slot shard = state.current_shuffling_start_shard attestation_data = build_attestation_data(state, slot, shard) crosslink_committees = get_crosslink_committees_at_slot(state, slot) crosslink_committee = [ committee for committee, _shard in crosslink_committees if _shard == attestation_data.shard ][0] committee_size = len(crosslink_committee) bitfield_length = (committee_size + 7) // 8 aggregation_bitfield = b'\x01' + b'\x00' * (bitfield_length - 1) custody_bitfield = b'\x00' * bitfield_length attestation = Attestation( aggregation_bitfield=aggregation_bitfield, data=attestation_data, custody_bitfield=custody_bitfield, aggregate_signature=b'\x00' * 96, ) participants = get_attestation_participants( test_state, attestation.data, attestation.aggregation_bitfield, ) assert len(participants) == 1 validator_index = participants[0] pubkey = pubkeys[validator_index] privkey = pubkey_to_privkey[pubkey] message_hash = AttestationDataAndCustodyBit( data=attestation.data, custody_bit=0b0, ).hash_tree_root() attestation.aggregation_signature = bls.sign( message_hash=message_hash, privkey=privkey, domain=get_domain( fork=test_state.fork, epoch=get_current_epoch(test_state), domain_type=spec.DOMAIN_ATTESTATION, )) # # Add to state via block transition # attestation_block = construct_empty_block_for_next_slot(test_state) attestation_block.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY attestation_block.body.attestations.append(attestation) state_transition(test_state, attestation_block) assert len(test_state.current_epoch_attestations) == len( state.current_epoch_attestations) + 1 # # Epoch transition should move to previous_epoch_attestations # pre_current_epoch_attestations = deepcopy( test_state.current_epoch_attestations) epoch_block = construct_empty_block_for_next_slot(test_state) epoch_block.slot += spec.SLOTS_PER_EPOCH state_transition(test_state, epoch_block) assert len(test_state.current_epoch_attestations) == 0 assert test_state.previous_epoch_attestations == pre_current_epoch_attestations return state, [attestation_block, epoch_block], test_state