spec version 0.4.0 update: shard_block_root -> crosslink_data_root; switch from validator.slashed_epoch -> validator.slashed approach; a couple assert -> doAssert; note fix for underflow-prone spec changes in checkAttestation; GENESIS_SLOT change from 2^63 to 2^32; refactor Proposer and ProposalSlashing structure; add signed_root; remove duplicate slashValidator (#159)

* spec version 0.4.0 update: shard_block_root -> crosslink_data_root; switch from validator.slashed_epoch -> validator.slashed approach; a couple assert -> doAssert; note fix for underflow-prone spec changes in checkAttestation; GENESIS_SLOT change from 2^63 to 2^32; refactor Proposer and ProposalSlashing structure; add signed_root; remove duplicate slashValidator

* re-apply shard_block_root -> crosslink_data_root

* remove incorrect humaneSlotNum

* add (run-time only, alas) sanity check on signed_root
This commit is contained in:
Dustin Brody 2019-03-08 17:44:31 +00:00 committed by GitHub
parent 6bcefc0e42
commit 1afdcda62d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 189 additions and 222 deletions

View File

@ -274,7 +274,7 @@ proc makeAttestation(node: BeaconNode,
shard: shard,
beacon_block_root: node.state.blck.root,
epoch_boundary_root: Eth2Digest(), # TODO
shard_block_root: Eth2Digest(), # TODO
crosslink_data_root: Eth2Digest(), # TODO
latest_crosslink: state.latest_crosslinks[shard],
justified_epoch: state.justified_epoch,
justified_block_root: justifiedBlockRoot)

View File

@ -16,43 +16,25 @@ func get_effective_balance*(state: BeaconState, index: ValidatorIndex): uint64 =
## validator with the given ``index``.
min(state.validator_balances[index], MAX_DEPOSIT_AMOUNT)
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#validate_proof_of_possession
func validate_proof_of_possession(state: BeaconState,
pubkey: ValidatorPubKey,
proof_of_possession: ValidatorSig,
withdrawal_credentials: Eth2Digest): bool =
let proof_of_possession_data = DepositInput(
pubkey: pubkey,
withdrawal_credentials: withdrawal_credentials,
proof_of_possession: ValidatorSig(),
)
bls_verify(
pubkey,
hash_tree_root_final(proof_of_possession_data).data,
proof_of_possession,
get_domain(
state.fork,
get_current_epoch(state),
DOMAIN_DEPOSIT,
)
)
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#process_deposit
func process_deposit(state: var BeaconState,
pubkey: ValidatorPubKey,
amount: Gwei,
proof_of_possession: ValidatorSig,
withdrawal_credentials: Eth2Digest) =
# https://github.com/ethereum/eth2.0-specs/blob/0.4.0/specs/core/0_beacon-chain.md#process_deposit
func process_deposit(state: var BeaconState, deposit: Deposit) =
## Process a deposit from Ethereum 1.0.
## Note that this function mutates ``state``.
if false:
# TODO return error; currently, just fails if ever called
# but hadn't been set up to run at all
doAssert validate_proof_of_possession(
state, pubkey, proof_of_possession, withdrawal_credentials)
let deposit_input = deposit.deposit_data.deposit_input
let validator_pubkeys = state.validator_registry.mapIt(it.pubkey)
## if not validate_proof_of_possession(
## state, pubkey, proof_of_possession, withdrawal_credentials):
## return
## TODO re-enable (but it wasn't running to begin with, and
## PoP isn't really a phase 0 concern, so this isn't meaningful
## regardless.
let
validator_pubkeys = state.validator_registry.mapIt(it.pubkey)
pubkey = deposit_input.pubkey
amount = deposit.deposit_data.amount
withdrawal_credentials = deposit_input.withdrawal_credentials
if pubkey notin validator_pubkeys:
# Add new validator
@ -62,8 +44,8 @@ func process_deposit(state: var BeaconState,
activation_epoch: FAR_FUTURE_EPOCH,
exit_epoch: FAR_FUTURE_EPOCH,
withdrawable_epoch: FAR_FUTURE_EPOCH,
slashed_epoch: FAR_FUTURE_EPOCH,
status_flags: 0,
initiated_exit: false,
slashed: false,
)
## Note: In phase 2 registry indices that have been withdrawn for a long
@ -74,7 +56,7 @@ func process_deposit(state: var BeaconState,
# Increase balance by deposit amount
let index = validator_pubkeys.find(pubkey)
let validator = addr state.validator_registry[index]
assert state.validator_registry[index].withdrawal_credentials ==
doAssert state.validator_registry[index].withdrawal_credentials ==
withdrawal_credentials
state.validator_balances[index] += amount
@ -99,13 +81,13 @@ func activate_validator(state: var BeaconState,
else:
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
# https://github.com/ethereum/eth2.0-specs/blob/0.4.0/specs/core/0_beacon-chain.md#initiate_validator_exit
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]
validator.status_flags = validator.status_flags or INITIATED_EXIT
validator.initiated_exit = true
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#exit_validator
func exit_validator*(state: var BeaconState,
@ -125,6 +107,7 @@ func reduce_balance*(balance: var uint64, amount: uint64) =
# Not in spec, but useful to avoid underflow.
balance -= min(amount, balance)
# https://github.com/ethereum/eth2.0-specs/blob/0.4.0/specs/core/0_beacon-chain.md#slash_validator
func slash_validator*(state: var BeaconState, index: ValidatorIndex) =
## Slash the validator with index ``index``.
## Note that this function mutates ``state``.
@ -143,13 +126,14 @@ func slash_validator*(state: var BeaconState, index: ValidatorIndex) =
whistleblower_reward = get_effective_balance(state, index) div
WHISTLEBLOWER_REWARD_QUOTIENT
## TODO here and elsewhere, if reduce_balance can't reduce balance by full
## whistleblower_reward (to prevent underflow) should increase be full? It
## seems wrong for the amounts to differ.
state.validator_balances[whistleblower_index] += whistleblower_reward
reduce_balance(state.validator_balances[index], whistleblower_reward)
validator.slashed_epoch = get_current_epoch(state)
# Spec bug in v0.3.0, fixed since: it has LATEST_PENALIZED_EXIT_LENGTH
validator.withdrawable_epoch = get_current_epoch(state) +
LATEST_SLASHED_EXIT_LENGTH
validator.slashed = true
validator.withdrawable_epoch =
get_current_epoch(state) + LATEST_SLASHED_EXIT_LENGTH
func update_shuffling_cache*(state: var BeaconState) =
let
@ -171,9 +155,9 @@ func update_shuffling_cache*(state: var BeaconState) =
state.shuffling_cache.shuffling_1 = shuffling_seq
state.shuffling_cache.index = 1 - state.shuffling_cache.index
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#on-genesis
# https://github.com/ethereum/eth2.0-specs/blob/0.4.0/specs/core/0_beacon-chain.md#on-genesis
func get_genesis_beacon_state*(
initial_validator_deposits: openArray[Deposit],
genesis_validator_deposits: openArray[Deposit],
genesis_time: uint64,
latest_eth1_data: Eth1Data,
flags: UpdateFlags = {}): BeaconState =
@ -191,7 +175,7 @@ func get_genesis_beacon_state*(
# validators - there needs to be at least one member in each committee -
# good to know for testing, though arguably the system is not that useful at
# at that point :)
assert initial_validator_deposits.len >= SLOTS_PER_EPOCH
doAssert genesis_validator_deposits.len >= SLOTS_PER_EPOCH
var state = BeaconState(
# Misc
@ -234,19 +218,13 @@ func get_genesis_beacon_state*(
for i in 0 ..< SHARD_COUNT:
state.latest_crosslinks[i] = Crosslink(
epoch: GENESIS_EPOCH, shard_block_root: ZERO_HASH)
epoch: GENESIS_EPOCH, crosslink_data_root: ZERO_HASH)
# Process initial deposits
for deposit in initial_validator_deposits:
process_deposit(
state,
deposit.deposit_data.deposit_input.pubkey,
deposit.deposit_data.amount,
deposit.deposit_data.deposit_input.proof_of_possession,
deposit.deposit_data.deposit_input.withdrawal_credentials,
)
# Process genesis deposits
for deposit in genesis_validator_deposits:
process_deposit(state, deposit)
# Process initial activations
# Process genesis activations
for validator_index in 0 ..< state.validator_registry.len:
let vi = validator_index.ValidatorIndex
if get_effective_balance(state, vi) >= MAX_DEPOSIT_AMOUNT:
@ -258,6 +236,7 @@ func get_genesis_beacon_state*(
state.latest_active_index_roots[index] = genesis_active_index_root
state.current_shuffling_seed = generate_seed(state, GENESIS_EPOCH)
# Not in spec.
update_shuffling_cache(state)
state
@ -317,15 +296,13 @@ func get_attestation_participants*(state: BeaconState,
if aggregation_bit == 1:
result.add(validator_index)
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#ejections
func process_ejections*(state: var BeaconState, active_validator_indices: auto) =
# https://github.com/ethereum/eth2.0-specs/blob/0.4.0/specs/core/0_beacon-chain.md#ejections
func process_ejections*(state: var BeaconState) =
## Iterate through the validator registry and eject active validators with
## balance below ``EJECTION_BALANCE``
##
## `active_validator_indices` was already computed in `processEpoch`. Reuse.
## Spec recomputes. This is called before validator reshuffling, so use that
## cached version from beginning of `processEpoch`.
for index in active_validator_indices:
for index in get_active_validator_indices(
# Spec bug in 0.4.0: is just current_epoch(state)
state.validator_registry, get_current_epoch(state)):
if state.validator_balances[index] < EJECTION_BALANCE:
exit_validator(state, index)
@ -334,7 +311,7 @@ func get_total_balance*(state: BeaconState, validators: auto): Gwei =
# Return the combined effective balance of an array of validators.
foldl(validators, a + get_effective_balance(state, b), 0'u64)
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#validator-registry-and-shuffling-seed-data
# https://github.com/ethereum/eth2.0-specs/blob/0.4.0/specs/core/0_beacon-chain.md#validator-registry-and-shuffling-seed-data
func update_validator_registry*(state: var BeaconState) =
## Update validator registry.
## Note that this function mutates ``state``.
@ -355,7 +332,7 @@ func update_validator_registry*(state: var BeaconState) =
# Activate validators within the allowable balance churn
var balance_churn = 0'u64
for index, validator in state.validator_registry:
if validator.activation_epoch > get_entry_exit_effect_epoch(current_epoch) and
if validator.activation_epoch == FAR_FUTURE_EPOCH and
state.validator_balances[index] >= MAX_DEPOSIT_AMOUNT:
# Check the balance churn would be within the allowance
balance_churn += get_effective_balance(state, index.ValidatorIndex)
@ -368,8 +345,8 @@ func update_validator_registry*(state: var BeaconState) =
# Exit validators within the allowable balance churn
balance_churn = 0
for index, validator in state.validator_registry:
if validator.exit_epoch > get_entry_exit_effect_epoch(current_epoch) and
((validator.status_flags and INITIATED_EXIT) == INITIATED_EXIT):
if validator.activation_epoch == FAR_FUTURE_EPOCH and
validator.initiated_exit:
# Check the balance churn would be within the allowance
balance_churn += get_effective_balance(state, index.ValidatorIndex)
if balance_churn > max_balance_churn:
@ -380,25 +357,26 @@ func update_validator_registry*(state: var BeaconState) =
state.validator_registry_update_epoch = current_epoch
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#attestations-1
# https://github.com/ethereum/eth2.0-specs/blob/0.4.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
## at the current slot. When acting as a proposer, the same rules need to
## be followed!
# Can't underflow, because GENESIS_SLOT > MIN_ATTESTATION_INCLUSION_DELAY
doAssert GENESIS_SLOT > MIN_ATTESTATION_INCLUSION_DELAY
if not (attestation.data.slot >= GENESIS_SLOT):
warn("Attestation predates genesis slot",
attestation_slot = attestation.data.slot,
state_slot = humaneSlotNum(state.slot))
return
if not (attestation.data.slot <= state.slot - MIN_ATTESTATION_INCLUSION_DELAY):
if not (attestation.data.slot + MIN_ATTESTATION_INCLUSION_DELAY <= state.slot):
warn("Attestation too new",
attestation_slot = humaneSlotNum(attestation.data.slot),
state_slot = humaneSlotNum(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):
if not (state.slot < attestation.data.slot + SLOTS_PER_EPOCH):
warn("Attestation too old",
attestation_slot = humaneSlotNum(attestation.data.slot),
state_slot = humaneSlotNum(state.slot))
@ -428,14 +406,14 @@ proc checkAttestation*(
if not (state.latest_crosslinks[attestation.data.shard] in [
attestation.data.latest_crosslink,
Crosslink(
shard_block_root: attestation.data.shard_block_root,
crosslink_data_root: attestation.data.crosslink_data_root,
epoch: slot_to_epoch(attestation.data.slot))]):
warn("Unexpected crosslink shard",
state_latest_crosslinks_attestation_data_shard =
state.latest_crosslinks[attestation.data.shard],
attestation_data_latest_crosslink = attestation.data.latest_crosslink,
epoch = humaneEpochNum(slot_to_epoch(attestation.data.slot)),
shard_block_root = attestation.data.shard_block_root)
crosslink_data_root = attestation.data.crosslink_data_root)
return
assert allIt(attestation.custody_bitfield, it == 0) #TO BE REMOVED IN PHASE 1
@ -478,9 +456,6 @@ proc checkAttestation*(
custody_bit_1_participants: seq[ValidatorIndex] = @[]
custody_bit_0_participants = participants
group_public_key = bls_aggregate_pubkeys(
participants.mapIt(state.validator_registry[it].pubkey))
if skipValidation notin flags:
# Verify that aggregate_signature verifies using the group pubkey.
assert bls_verify_multiple(
@ -502,8 +477,8 @@ proc checkAttestation*(
)
# To be removed in Phase1:
if attestation.data.shard_block_root != ZERO_HASH:
warn("Invalid shard block root")
if attestation.data.crosslink_data_root != ZERO_HASH:
warn("Invalid crosslink data root")
return
true
@ -517,4 +492,4 @@ func prepare_validator_for_withdrawal*(state: var BeaconState, index: ValidatorI
# Bug in 0.3.0 spec; constant got renamed. Use 0.3.0 name.
validator.withdrawable_epoch = get_current_epoch(state) +
MIN_VALIDATOR_WITHDRAWAL_DELAY
MIN_VALIDATOR_WITHDRAWABILITY_DELAY

View File

@ -40,12 +40,12 @@ import
# TODO Many of these constants should go into a config object that can be used
# to run.. well.. a chain with different constants!
const
SPEC_VERSION* = "0.3.0" ## \
SPEC_VERSION* = "0.4.0" ## \
## Spec version we're aiming to be compatible with, right now
## TODO: improve this scheme once we can negotiate versions in protocol
# Misc
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#misc
# https://github.com/ethereum/eth2.0-specs/blob/0.4.0/specs/core/0_beacon-chain.md#misc
SHARD_COUNT* {.intdefine.} = 1024 ##\
## Number of shards supported by the network - validators will jump around
## between these shards and provide attestations to their state.
@ -71,18 +71,16 @@ const
MAX_INDICES_PER_SLASHABLE_VOTE* = 2^12 ##\
## votes
MAX_WITHDRAWALS_PER_EPOCH* = 4 # withdrawals
MAX_EXIT_DEQUEUES_PER_EPOCH* = 4
SHUFFLE_ROUND_COUNT* = 90
# Deposit contract
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#deposit-contract
# https://github.com/ethereum/eth2.0-specs/blob/0.4.0/specs/core/0_beacon-chain.md#deposit-contract
DEPOSIT_CONTRACT_TREE_DEPTH* = 2^5
# Gwei values
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#gwei-values
# https://github.com/ethereum/eth2.0-specs/blob/0.4.0/specs/core/0_beacon-chain.md#gwei-values
MIN_DEPOSIT_AMOUNT* = 2'u64^0 * 10'u64^9 ##\
## Minimum amounth of ETH that can be deposited in one call - deposits can
## be used either to top up an existing validator or commit to a new one
@ -103,9 +101,9 @@ const
## Compile with -d:SLOTS_PER_EPOCH=4 for shorter epochs
# Initial values
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#initial-values
# https://github.com/ethereum/eth2.0-specs/blob/0.4.0/specs/core/0_beacon-chain.md#initial-values
GENESIS_FORK_VERSION* = 0'u64
GENESIS_SLOT* = 2'u64^63
GENESIS_SLOT* = 2'u64^32
GENESIS_EPOCH* = GENESIS_SLOT div SLOTS_PER_EPOCH # slot_to_epoch(GENESIS_SLOT)
GENESIS_START_SHARD* = 0'u64
FAR_FUTURE_EPOCH* = not 0'u64 # 2^64 - 1 in spec
@ -114,7 +112,7 @@ const
BLS_WITHDRAWAL_PREFIX_BYTE* = 0'u8
# Time parameters
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#time-parameters
# https://github.com/ethereum/eth2.0-specs/blob/0.4.0/specs/core/0_beacon-chain.md#time-parameters
SECONDS_PER_SLOT*{.intdefine.} = 6'u64 # Compile with -d:SECONDS_PER_SLOT=1 for 6x faster slots
## TODO consistent time unit across projects, similar to C++ chrono?
@ -130,6 +128,8 @@ const
## wait towards the end of the slot and still have time to publish the
## attestation.
# SLOTS_PER_EPOCH is defined above.
MIN_SEED_LOOKAHEAD* = 1 ##\
## epochs (~6.4 minutes)
@ -139,18 +139,18 @@ const
EPOCHS_PER_ETH1_VOTING_PERIOD* = 2'u64^4 ##\
## epochs (~1.7 hours)
MIN_VALIDATOR_WITHDRAWAL_DELAY* = 2'u64^8 ##\
MIN_VALIDATOR_WITHDRAWABILITY_DELAY* = 2'u64^8 ##\
## epochs (~27 hours)
# State list lengths
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#state-list-lengths
# https://github.com/ethereum/eth2.0-specs/blob/0.4.0/specs/core/0_beacon-chain.md#state-list-lengths
LATEST_BLOCK_ROOTS_LENGTH* = 2'u64^13
LATEST_RANDAO_MIXES_LENGTH* = 2'u64^13
LATEST_ACTIVE_INDEX_ROOTS_LENGTH* = 8192 # 2'u64^13, epochs
LATEST_SLASHED_EXIT_LENGTH* = 8192 # epochs
# Reward and penalty quotients
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#reward-and-penalty-quotients
# https://github.com/ethereum/eth2.0-specs/blob/0.4.0/specs/core/0_beacon-chain.md#reward-and-penalty-quotients
BASE_REWARD_QUOTIENT* = 2'u64^5 ##\
## The `BASE_REWARD_QUOTIENT` parameter dictates the per-epoch reward. It
## corresponds to ~2.54% annual interest assuming 10 million participating
@ -160,12 +160,8 @@ const
INACTIVITY_PENALTY_QUOTIENT* = 2'u64^24
MIN_PENALTY_QUOTIENT* = 32 # 2^5
# Status flags
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#status-flags
INITIATED_EXIT* = 1'u64
# Max transactions per block
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#max-transactions-per-block
# https://github.com/ethereum/eth2.0-specs/blob/0.4.0/specs/core/0_beacon-chain.md#max-transactions-per-block
MAX_PROPOSER_SLASHINGS* = 2^4
MAX_ATTESTER_SLASHINGS* = 2^0
MAX_ATTESTATIONS* = 2^7
@ -184,15 +180,18 @@ type
Epoch* = uint64
Gwei* = uint64
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#proposerslashing
# https://github.com/ethereum/eth2.0-specs/blob/0.4.0/specs/core/0_beacon-chain.md#proposerslashing
ProposerSlashing* = object
proposer_index*: uint64
proposal_data_1*: ProposalSignedData
proposal_signature_1*: ValidatorSig
proposal_data_2*: ProposalSignedData
proposal_signature_2*: ValidatorSig
proposer_index*: uint64 ##\
## Proposer index
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#attesterslashing
proposal_1*: Proposal ##\
# First proposal
proposal_2*: Proposal ##\
# Second proposal
# https://github.com/ethereum/eth2.0-specs/blob/0.4.0/specs/core/0_beacon-chain.md#attesterslashing
AttesterSlashing* = object
slashable_attestation_1*: SlashableAttestation ## \
## First slashable attestation
@ -227,7 +226,7 @@ type
aggregate_signature*: ValidatorSig ##\
## BLS aggregate signature
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#attestationdata
# https://github.com/ethereum/eth2.0-specs/blob/0.4.0/specs/core/0_beacon-chain.md#attestationdata
AttestationData* = object
slot*: uint64 ##\
## Slot number
@ -241,8 +240,8 @@ type
epoch_boundary_root*: Eth2Digest ##\
## Hash of root of the ancestor at the epoch boundary
shard_block_root*: Eth2Digest ##\
## Shard block's hash of root
crosslink_data_root*: Eth2Digest ##\
## Data from the shard since the last attestation
latest_crosslink*: Crosslink ##\
## Last crosslink
@ -253,7 +252,7 @@ type
justified_block_root*: Eth2Digest ##\
## Hash of the last justified beacon block
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#attestationdataandcustodybit
# https://github.com/ethereum/eth2.0-specs/blob/0.4.0/specs/core/0_beacon-chain.md#attestationdataandcustodybit
AttestationDataAndCustodyBit* = object
data*: AttestationData
custody_bit*: bool
@ -360,6 +359,20 @@ type
voluntary_exits*: seq[VoluntaryExit]
transfers*: seq[Transfer]
# https://github.com/ethereum/eth2.0-specs/blob/0.4.0/specs/core/0_beacon-chain.md#proposal
Proposal* = object
slot*: uint64 ##\
## Slot number
shard*: uint64 ##\
## Shard number (`BEACON_CHAIN_SHARD_NUMBER` for beacon chain)
block_root*: Eth2Digest ##\
## Block root
signature*: ValidatorSig ##\
## Signature
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#proposalsigneddata
ProposalSignedData* = object
slot*: uint64
@ -420,7 +433,7 @@ type
# Not in spec. TODO: don't serialize or deserialize this.
shuffling_cache*: ShufflingCache
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#validator
# https://github.com/ethereum/eth2.0-specs/blob/0.4.0/specs/core/0_beacon-chain.md#validator
Validator* = object
pubkey*: ValidatorPubKey ##\
## BLS public key
@ -437,18 +450,19 @@ type
withdrawable_epoch*: uint64 ##\
## Epoch when validator is eligible to withdraw
slashed_epoch*: uint64 ##\
## Epoch when validator slashed
initiated_exit*: bool ##\
## Did the validator initiate an exit
status_flags*: uint64
slashed*: bool ##\
## Was the validator slashed
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#crosslink
# https://github.com/ethereum/eth2.0-specs/blob/0.4.0/specs/core/0_beacon-chain.md#crosslink
Crosslink* = object
epoch*: uint64 ##\
## Epoch number
shard_block_root*: Eth2Digest ##\
## Shard block root
crosslink_data_root*: Eth2Digest ##\
## Shard data since the previous crosslink
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#pendingattestation
PendingAttestation* = object
@ -485,7 +499,7 @@ type
Activation = 0
Exit = 1
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#signature-domains
# https://github.com/ethereum/eth2.0-specs/blob/0.4.0/specs/core/0_beacon-chain.md#signature-domains
SignatureDomain* {.pure.} = enum
DOMAIN_DEPOSIT = 0
DOMAIN_ATTESTATION = 1

View File

@ -138,7 +138,7 @@ func is_double_vote*(attestation_data_1: AttestationData,
target_epoch_2 = slot_to_epoch(attestation_data_2.slot)
target_epoch_1 == target_epoch_2
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#is_surround_vote
# https://github.com/ethereum/eth2.0-specs/blob/0.4.0/specs/core/0_beacon-chain.md#is_surround_vote
func is_surround_vote*(attestation_data_1: AttestationData,
attestation_data_2: AttestationData): bool =
## Check if ``attestation_data_1`` surrounds ``attestation_data_2``.

View File

@ -290,6 +290,21 @@ func hash_tree_root*[T: object|tuple](x: T): array[32, byte] =
for field in x.fields:
h.update hash_tree_root(field)
# https://github.com/ethereum/eth2.0-specs/blob/0.4.0/specs/simple-serialize.md#signed-roots
func signed_root*[T: object](x: T, field_name: string): array[32, byte] =
# TODO write tests for this (check vs hash_tree_root)
var found_field_name = false
withHash:
for name, field in x.fieldPairs:
if name == field_name:
found_field_name = true
break
h.update hash_tree_root(field)
doAssert found_field_name
# #################################
# hash_tree_root not part of official spec
func hash_tree_root*(x: enum): array[8, byte] =

View File

@ -106,32 +106,7 @@ func processDepositRoot(state: var BeaconState, blck: BeaconBlock) =
vote_count: 1
)
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#slashValidator
func slashValidator(state: var BeaconState, index: ValidatorIndex) =
## Slash the validator of the given ``index``.
## Note that this function mutates ``state``.
var validator = addr state.validator_registry[index]
doAssert state.slot < get_epoch_start_slot(validator.withdrawable_epoch) ##\
## [TO BE REMOVED IN PHASE 2]
exit_validator(state, index)
state.latest_slashed_balances[(get_current_epoch(state) mod
LATEST_SLASHED_EXIT_LENGTH).int] += get_effective_balance(state,
index.ValidatorIndex)
let
whistleblower_index = get_beacon_proposer_index(state, state.slot)
whistleblower_reward = get_effective_balance(state, index) div
WHISTLEBLOWER_REWARD_QUOTIENT
state.validator_balances[whistleblower_index] += whistleblower_reward
reduce_balance(state.validator_balances[index], whistleblower_reward)
validator.slashed_epoch = get_current_epoch(state)
# v0.3.0 spec bug, fixed later, involving renamed constants. Use v0.3.0 name.
validator.withdrawable_epoch = get_current_epoch(state) + LATEST_SLASHED_EXIT_LENGTH
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#proposer-slashings-1
# https://github.com/ethereum/eth2.0-specs/blob/0.4.0/specs/core/0_beacon-chain.md#proposer-slashings-1
proc processProposerSlashings(
state: var BeaconState, blck: BeaconBlock, flags: UpdateFlags): bool =
if len(blck.body.proposer_slashings) > MAX_PROPOSER_SLASHINGS:
@ -141,48 +116,49 @@ proc processProposerSlashings(
for proposer_slashing in blck.body.proposer_slashings:
let proposer = state.validator_registry[proposer_slashing.proposer_index.int]
if not (proposer_slashing.proposal_1.slot ==
proposer_slashing.proposal_2.slot):
notice "PropSlash: slot mismatch"
return false
if not (proposer_slashing.proposal_1.shard ==
proposer_slashing.proposal_2.shard):
notice "PropSlash: shard mismatch"
return false
if not (proposer_slashing.proposal_1.block_root !=
proposer_slashing.proposal_2.block_root):
notice "PropSlash: block root mismatch"
return false
if not (proposer.slashed == false):
notice "PropSlash: slashed proposer"
return false
if skipValidation notin flags:
if not bls_verify(
proposer.pubkey,
hash_tree_root_final(proposer_slashing.proposal_data_1).data,
proposer_slashing.proposal_signature_1,
signed_root(proposer_slashing.proposal_1, "signature"),
proposer_slashing.proposal_1.signature,
get_domain(
state.fork, slot_to_epoch(proposer_slashing.proposal_data_1.slot),
state.fork, slot_to_epoch(proposer_slashing.proposal_1.slot),
DOMAIN_PROPOSAL)):
notice "PropSlash: invalid signature 1"
return false
if not bls_verify(
proposer.pubkey,
hash_tree_root_final(proposer_slashing.proposal_data_2).data,
proposer_slashing.proposal_signature_2,
signed_root(proposer_slashing.proposal_2, "signature"),
proposer_slashing.proposal_2.signature,
get_domain(
state.fork, slot_to_epoch(proposer_slashing.proposal_data_2.slot),
state.fork, slot_to_epoch(proposer_slashing.proposal_2.slot),
DOMAIN_PROPOSAL)):
notice "PropSlash: invalid signature 2"
return false
if not (proposer_slashing.proposal_data_1.slot ==
proposer_slashing.proposal_data_2.slot):
notice "PropSlash: slot mismatch"
return false
if not (proposer_slashing.proposal_data_1.shard ==
proposer_slashing.proposal_data_2.shard):
notice "PropSlash: shard mismatch"
return false
if not (proposer_slashing.proposal_data_1.block_root ==
proposer_slashing.proposal_data_2.block_root):
notice "PropSlash: block root mismatch"
return false
if not (proposer.slashed_epoch > get_current_epoch(state)):
notice "PropSlash: penalized slot"
return false
slashValidator(state, proposer_slashing.proposer_index.ValidatorIndex)
return true
true
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#verify_slashable_attestation
func verify_slashable_attestation(state: BeaconState, slashable_attestation: SlashableAttestation): bool =
@ -233,7 +209,7 @@ func verify_slashable_attestation(state: BeaconState, slashable_attestation: Sla
),
)
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#attester-slashings-1
# https://github.com/ethereum/eth2.0-specs/blob/0.4.0/specs/core/0_beacon-chain.md#attester-slashings-1
proc processAttesterSlashings(state: var BeaconState, blck: BeaconBlock): bool =
if len(blck.body.attester_slashings) > MAX_ATTESTER_SLASHINGS:
notice "CaspSlash: too many!"
@ -263,7 +239,7 @@ proc processAttesterSlashings(state: var BeaconState, blck: BeaconBlock): bool =
return false
let
indices2 = slashable_attestation_2.validator_indices
indices2 = toSet(slashable_attestation_2.validator_indices)
slashable_indices =
slashable_attestation_1.validator_indices.filterIt(it in indices2)
@ -272,7 +248,7 @@ proc processAttesterSlashings(state: var BeaconState, blck: BeaconBlock): bool =
return false
for index in slashable_indices:
if state.validator_registry[index.int].slashed_epoch > get_current_epoch(state):
if state.validator_registry[index.int].slashed:
slash_validator(state, index.ValidatorIndex)
true
@ -413,7 +389,7 @@ func processSlot(state: var BeaconState, previous_block_root: Eth2Digest) =
## chain at that time. In case the proposer is missing, it may happen that
## the no block is produced during the slot.
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#slot
# https://github.com/ethereum/eth2.0-specs/blob/0.4.0/specs/core/0_beacon-chain.md#slot
state.slot += 1
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#block-roots
@ -503,13 +479,13 @@ func boundary_attestations(
func lowerThan(candidate, current: Eth2Digest): bool =
# return true iff candidate is "lower" than current, per spec rule:
# "ties broken by favoring lower `shard_block_root` values"
# "ties broken by favoring lower `crosslink_data_root` values"
# TODO spec - clarify hash ordering..
for i, v in current.data:
if v > candidate.data[i]: return true
false
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#validator-registry-and-shuffling-seed-data
# https://github.com/ethereum/eth2.0-specs/blob/0.4.0/specs/core/0_beacon-chain.md#validator-registry-and-shuffling-seed-data
func process_slashings(state: var BeaconState) =
## Process the slashings.
## Note that this function mutates ``state``.
@ -517,12 +493,12 @@ func process_slashings(state: var BeaconState) =
current_epoch = get_current_epoch(state)
active_validator_indices = get_active_validator_indices(
state.validator_registry, current_epoch)
# TODO 0.3.0 spec doesn't use this helper function?
# 0.4.0 spec doesn't use this helper function?
total_balance = get_total_balance(state, active_validator_indices)
for index, validator in state.validator_registry:
if current_epoch ==
validator.slashed_epoch + LATEST_SLASHED_EXIT_LENGTH div 2:
if validator.slashed and current_epoch == validator.withdrawable_epoch -
LATEST_SLASHED_EXIT_LENGTH div 2:
let
epoch_index = current_epoch mod LATEST_SLASHED_EXIT_LENGTH
total_at_start = state.latest_slashed_balances[
@ -543,14 +519,12 @@ func process_exit_queue(state: var BeaconState) =
func eligible(index: ValidatorIndex): bool =
let validator = state.validator_registry[index]
# Filter out dequeued validators
if validator.withdrawable_epoch < FAR_FUTURE_EPOCH:
if validator.withdrawable_epoch != FAR_FUTURE_EPOCH:
return false
# Dequeue if the minimum amount of time has passed
else:
return get_current_epoch(state) >= validator.exit_epoch +
# TODO in future versions, remove workaround for 0.3.0 spec bug
# but for 0.3.0 use MIN_VALIDATOR_WITHDRAWAL_DELAY constant
MIN_VALIDATOR_WITHDRAWAL_DELAY
MIN_VALIDATOR_WITHDRAWABILITY_DELAY
# TODO try again with filterIt
var eligible_indices: seq[ValidatorIndex]
@ -661,22 +635,22 @@ func processEpoch(state: var BeaconState) =
# these closures outside this scope, but still..
let statePtr = state.addr
func attesting_validator_indices(
crosslink_committee: CrosslinkCommittee, shard_block_root: Eth2Digest): seq[ValidatorIndex] =
crosslink_committee: CrosslinkCommittee, crosslink_data_root: Eth2Digest): seq[ValidatorIndex] =
let shard_block_attestations =
concat(current_epoch_attestations, previous_epoch_attestations).
filterIt(it.data.shard == crosslink_committee.shard and
it.data.shard_block_root == shard_block_root)
it.data.crosslink_data_root == crosslink_data_root)
get_attester_indices(statePtr[], shard_block_attestations)
func winning_root(crosslink_committee: CrosslinkCommittee): Eth2Digest =
# * Let `winning_root(crosslink_committee)` be equal to the value of
# `shard_block_root` such that
# `sum([get_effective_balance(state, i) for i in attesting_validator_indices(crosslink_committee, shard_block_root)])`
# is maximized (ties broken by favoring lower `shard_block_root` values).
# `crosslink_data_root` such that
# `sum([get_effective_balance(state, i) for i in attesting_validator_indices(crosslink_committee, crosslink_data_root)])`
# is maximized (ties broken by favoring lower `crosslink_data_root` values).
let candidates =
concat(current_epoch_attestations, previous_epoch_attestations).
filterIt(it.data.shard == crosslink_committee.shard).
mapIt(it.data.shard_block_root)
mapIt(it.data.crosslink_data_root)
# TODO not covered by spec!
if candidates.len == 0:
@ -750,7 +724,7 @@ func processEpoch(state: var BeaconState) =
2'u64 * get_total_balance(state, crosslink_committee.committee):
state.latest_crosslinks[crosslink_committee.shard] = Crosslink(
epoch: slot_to_epoch(slot),
shard_block_root: winning_root(crosslink_committee))
crosslink_data_root: winning_root(crosslink_committee))
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#rewards-and-penalties
## First, we define some additional helpers
@ -768,22 +742,7 @@ func processEpoch(state: var BeaconState) =
get_effective_balance(state, index) * epochs_since_finality div
INACTIVITY_PENALTY_QUOTIENT div 2
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#justification-and-finalization
## TODO remove inclusion_{slot,distance} when fully replaced, but note
## intentional absence, for spec sync purposes. Both positively invite
## quadratic behavior.
func inclusion_slot(state: BeaconState, v: ValidatorIndex): uint64 =
for a in previous_epoch_attestations:
if v in get_attestation_participants(state, a.data, a.aggregation_bitfield):
return a.inclusion_slot
doAssert false
func inclusion_distance(state: BeaconState, v: ValidatorIndex): uint64 =
for a in previous_epoch_attestations:
if v in get_attestation_participants(state, a.data, a.aggregation_bitfield):
return a.inclusion_slot - a.data.slot
doAssert false
# https://github.com/ethereum/eth2.0-specs/blob/0.4.0/specs/core/0_beacon-chain.md#justification-and-finalization
func inclusion_distances(state: BeaconState): auto =
result = initTable[ValidatorIndex, uint64]()
@ -797,6 +756,12 @@ func processEpoch(state: var BeaconState) =
let
epochs_since_finality = next_epoch - state.finalized_epoch
## Note: Rewards and penalties are for participation in the previous
## epoch, so the "active validator" set is drawn from
## get_active_validator_indices(state.validator_registry, previous_epoch).
active_validator_indices =
get_active_validator_indices(state.validator_registry, previous_epoch)
proc update_balance(attesters: HashSet[ValidatorIndex], attesting_balance: uint64) =
# TODO Spec - add helper?
for v in attesters:
@ -827,18 +792,17 @@ func processEpoch(state: var BeaconState) =
previous_epoch_head_attesting_balance)
# Inclusion distance
let distances = inclusion_distances(state)
# Strange plural (non)convention, but match spec name.
let inclusion_distance = inclusion_distances(state)
for v in previous_epoch_attester_indices:
statePtr.validator_balances[v] +=
base_reward(state, v) *
MIN_ATTESTATION_INCLUSION_DELAY div distances[v]
when false:
doAssert inclusion_distance(state, v) == distances[v]
MIN_ATTESTATION_INCLUSION_DELAY div inclusion_distance[v]
else:
# Case 2: epochs_since_finality > 4
let distances =
let inclusion_distance =
if previous_epoch_attester_indices.len > 0:
inclusion_distances(state)
else:
@ -858,7 +822,7 @@ func processEpoch(state: var BeaconState) =
if index notin previous_epoch_head_attester_indices:
reduce_balance(
state.validator_balances[index], base_reward(state, index))
if state.validator_registry[index].slashed_epoch <= current_epoch:
if state.validator_registry[index].slashed:
reduce_balance(
state.validator_balances[index],
2'u64 * inactivity_penalty(
@ -866,11 +830,10 @@ func processEpoch(state: var BeaconState) =
if index in previous_epoch_attester_indices:
reduce_balance(
state.validator_balances[index],
# TODO spec issue? depends on left/right associativity of * and /
base_reward(state, index) -
base_reward(state, index) * MIN_ATTESTATION_INCLUSION_DELAY div
distances[index])
when false:
doAssert inclusion_distance(state, index) == distances[index]
inclusion_distance[index])
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#attestation-inclusion
block:
@ -935,8 +898,8 @@ func processEpoch(state: var BeaconState) =
reduce_balance(
state.validator_balances[index], base_reward(state, index))
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#ejections
process_ejections(state, active_validator_indices)
# https://github.com/ethereum/eth2.0-specs/blob/0.4.0/specs/core/0_beacon-chain.md#ejections
process_ejections(state)
# https://github.com/ethereum/eth2.0-specs/blob/v0.3.0/specs/core/0_beacon-chain.md#validator-registry-and-shuffling-seed-data
block:

View File

@ -175,7 +175,7 @@ proc makeAttestation*(
beacon_block_root: beacon_block_root,
epoch_boundary_root: Eth2Digest(), # TODO
latest_crosslink: state.latest_crosslinks[sac.shard],
shard_block_root: Eth2Digest(), # TODO
crosslink_data_root: Eth2Digest(), # TODO
justified_epoch: state.justified_epoch,
justified_block_root: get_block_root(state, get_epoch_start_slot(state.justified_epoch)),
)