From fb3bbfccaa4c18e3fdafb5b7c5f95b950443166b Mon Sep 17 00:00:00 2001 From: Dustin Brody Date: Fri, 22 Feb 2019 09:53:37 -0800 Subject: [PATCH] small 0.3.0 adjustments; comment updates/clarifications; add Transfer processing --- beacon_chain/spec/beaconstate.nim | 11 ++- beacon_chain/state_transition.nim | 111 +++++++++++++++++++++++------- 2 files changed, 91 insertions(+), 31 deletions(-) diff --git a/beacon_chain/spec/beaconstate.nim b/beacon_chain/spec/beaconstate.nim index c9aabfce2..989aecbd6 100644 --- a/beacon_chain/spec/beaconstate.nim +++ b/beacon_chain/spec/beaconstate.nim @@ -106,8 +106,8 @@ func activate_validator(state: var BeaconState, get_entry_exit_effect_epoch(get_current_epoch(state)) # https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#initiate_validator_exit -func initiate_validator_exit(state: var BeaconState, - index: ValidatorIndex) = +func initiate_validator_exit*(state: var BeaconState, + index: ValidatorIndex) = ## Initiate exit for the validator with the given ``index``. ## Note that this function mutates ``state``. var validator = addr state.validator_registry[index] @@ -352,7 +352,7 @@ func update_validator_registry*(state: var BeaconState) = # TODO "If a validator registry update does not happen do the following: ..." -## https://github.com/ethereum/eth2.0-specs/blob/v0.2.0/specs/core/0_beacon-chain.md#attestations-1 +# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#attestations-1 proc checkAttestation*( state: BeaconState, attestation: Attestation, flags: UpdateFlags): bool = ## Check that an attestation follows the rules of being included in the state @@ -365,6 +365,7 @@ proc checkAttestation*( attestation_slot = attestation.data.slot, state_slot = state.slot) return + # Can't underflow, because GENESIS_SLOT > MIN_ATTESTATION_INCLUSION_DELAY if not (state.slot - MIN_ATTESTATION_INCLUSION_DELAY < attestation.data.slot + SLOTS_PER_EPOCH): warn("Attestation too old", @@ -372,8 +373,7 @@ proc checkAttestation*( return let expected_justified_epoch = - # https://github.com/ethereum/eth2.0-specs/issues/618 - if attestation.data.slot + 1 >= get_epoch_start_slot(get_current_epoch(state)): + if slot_to_epoch(attestation.data.slot + 1) >= get_current_epoch(state): state.justified_epoch else: state.previous_justified_epoch @@ -443,7 +443,6 @@ proc checkAttestation*( group_public_key = bls_aggregate_pubkeys( participants.mapIt(state.validator_registry[it].pubkey)) - ## the rest; turns into expensive NOP until then. if skipValidation notin flags: # Verify that aggregate_signature verifies using the group pubkey. assert bls_verify_multiple( diff --git a/beacon_chain/state_transition.nim b/beacon_chain/state_transition.nim index 2fdd53ee3..33a047225 100644 --- a/beacon_chain/state_transition.nim +++ b/beacon_chain/state_transition.nim @@ -277,6 +277,7 @@ proc processAttesterSlashings(state: var BeaconState, blck: BeaconBlock): bool = true +# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#attestations-1 proc processAttestations( state: var BeaconState, blck: BeaconBlock, flags: UpdateFlags): bool = ## Each block includes a number of attestations that the proposer chose. Each @@ -285,8 +286,6 @@ proc processAttestations( ## Here we make sanity checks for each attestation and it to the state - most ## updates will happen at the epoch boundary where state updates happen in ## bulk. - ## - ## https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#attestations-1 if blck.body.attestations.len > MAX_ATTESTATIONS: notice "Attestation: too many!", attestations = blck.body.attestations.len return false @@ -304,20 +303,16 @@ proc processAttestations( ) ) - return true - -proc processDeposits(state: var BeaconState, blck: BeaconBlock): bool = - ## https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#deposits-1 - # TODO! Spec writing in progress true -func initiate_validator_exit(state: var BeaconState, index: int) = - var validator = state.validator_registry[index] - validator.status_flags = validator.status_flags or INITIATED_EXIT +# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#deposits-1 +func processDeposits(state: var BeaconState, blck: BeaconBlock): bool = + # TODO! Spec writing in progress as of v0.3.0 + true +# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#voluntary-exits-1 proc processExits( state: var BeaconState, blck: BeaconBlock, flags: UpdateFlags): bool = - ## https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#exits-1 if len(blck.body.voluntary_exits) > MAX_VOLUNTARY_EXITS: notice "Exit: too many!" return false @@ -325,13 +320,6 @@ proc processExits( for exit in blck.body.voluntary_exits: let validator = state.validator_registry[exit.validator_index.int] - if skipValidation notin flags: - if not bls_verify( - validator.pubkey, ZERO_HASH.data, exit.signature, - get_domain(state.fork, exit.epoch, DOMAIN_EXIT)): - notice "Exit: invalid signature" - return false - if not (validator.exit_epoch > get_entry_exit_effect_epoch(get_current_epoch(state))): notice "Exit: exit/entry too close" return false @@ -340,9 +328,82 @@ proc processExits( notice "Exit: bad epoch" return false - initiate_validator_exit(state, exit.validator_index.int) + if skipValidation notin flags: + let exit_message = hash_tree_root( + # In 0.3.0 spec, this is "Exit", but that's a renaming mismatch + VoluntaryExit( + epoch: exit.epoch, validator_index: exit.validator_index, + signature: EMPTY_SIGNATURE)) + if not bls_verify( + validator.pubkey, exit_message, exit.signature, + get_domain(state.fork, exit.epoch, DOMAIN_EXIT)): + notice "Exit: invalid signature" + return false - return true + initiate_validator_exit(state, exit.validator_index.ValidatorIndex) + + true + +# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#transfers-1 +proc processTransfers(state: var BeaconState, blck: BeaconBlock, + flags: UpdateFlags): bool = + ## Note: Transfers are a temporary functionality for phases 0 and 1, to be + ## removed in phase 2. + if not (len(blck.body.transfers) <= MAX_TRANSFERS): + notice "Transfer: too many transfers" + return false + + for transfer in blck.body.transfers: + let from_balance = state.validator_balances[transfer.from_field.int] + + if not (from_balance >= transfer.amount): + notice "Transfer: source balance too low for amount" + return false + + if not (from_balance >= transfer.fee): + notice "Transfer: source balance too low for fee" + return false + + if not (from_balance == transfer.amount + transfer.fee or from_balance >= + transfer.amount + transfer.fee + MIN_DEPOSIT_AMOUNT): + notice "Transfer: source balance too low for amount + fee" + return false + + if not (state.slot == transfer.slot): + notice "Transfer: slot mismatch" + return false + + if not (get_current_epoch(state) >= + state.validator_registry[transfer.from_field.int].withdrawable_epoch): + notice "Transfer: epoch mismatch" + return false + + let wc = state.validator_registry[transfer.from_field.int]. + withdrawal_credentials + if not (wc.data[0] == BLS_WITHDRAWAL_PREFIX_BYTE and + wc.data[1..^1] == eth2hash(transfer.pubkey.getBytes).data[1..^1]): + notice "Transfer: incorrect withdrawal credentials" + return false + + if skipValidation notin flags: + let transfer_message = hash_tree_root( + Transfer( + from_field: transfer.from_field, to: transfer.to, + amount: transfer.amount, fee: transfer.fee, slot: transfer.slot, + signature: EMPTY_SIGNATURE)) + if bls_verify( + pubkey=transfer.pubkey, transfer_message, transfer.signature, + get_domain(state.fork, slot_to_epoch(transfer.slot), DOMAIN_TRANSFER)): + notice "Transfer: incorrect signature" + return false + + state.validator_balances[ + transfer.from_field.int] -= transfer.amount + transfer.fee + state.validator_balances[transfer.to.int] += transfer.amount + state.validator_balances[ + get_beacon_proposer_index(state, state.slot)] += transfer.fee + + true proc process_ejections(state: var BeaconState) = ## Iterate through the validator registry and eject active validators with @@ -427,9 +488,10 @@ proc processBlock( if not processExits(state, blck, flags): return false - process_ejections(state) + if not processTransfers(state, blck, flags): + return false - return true + true func get_attester_indices( state: BeaconState, @@ -460,12 +522,11 @@ func lowerThan(candidate, current: Eth2Digest): bool = return false func processEpoch(state: var BeaconState) = - ## https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#per-epoch-processing - + # https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#per-epoch-processing if (state.slot + 1) mod SLOTS_PER_EPOCH != 0: return - # Precomputation + # https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#helper-variables let active_validator_indices = get_active_validator_indices(state.validator_registry, state.slot)