mirror of
https://github.com/status-im/nimbus-eth2.git
synced 2025-01-20 19:40:52 +00:00
begin refactoring 0.5.0 epoch transition (#185)
* begin refactoring 0.5.0 epoch transition at justification and finalization * update processTransfers to 0.5.0 spec * update get_crosslink_committees_at_slot to 0.5.0 except for noted workaround for pre-0.5.0 spec bug related to epoch boundary conditions, to be flipped in more coordinated way with other epoch processing changes * mark ejection processing as 0.5.0 * 0.5.0 epoch update finalization * rm BEACON_CHAIN_SHARD_NUMBER to complete updating miscellaneous constants to 0.5.0 * mark verify_slashable_attestation as 0.5.0 * update process_attester_slashing to 0.5.0 * update process_slashings(...) to 0.5.0 and mark process_exit_queue as 0.5.0 * refactor epoch processing by implementing 0.5.0's update_registry_and_shuffling_data(...)
This commit is contained in:
parent
9930fa5bef
commit
13c7f7fcec
@ -423,7 +423,6 @@ proc proposeBlock(node: BeaconNode,
|
|||||||
|
|
||||||
let proposal = Proposal(
|
let proposal = Proposal(
|
||||||
slot: slot.uint64,
|
slot: slot.uint64,
|
||||||
shard: BEACON_CHAIN_SHARD_NUMBER,
|
|
||||||
block_root: Eth2Digest(data: signed_root(newBlock)),
|
block_root: Eth2Digest(data: signed_root(newBlock)),
|
||||||
signature: ValidatorSig(),
|
signature: ValidatorSig(),
|
||||||
)
|
)
|
||||||
|
@ -372,7 +372,7 @@ func get_attestation_participants*(state: BeaconState,
|
|||||||
if aggregation_bit == 1:
|
if aggregation_bit == 1:
|
||||||
result.add(validator_index)
|
result.add(validator_index)
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/0.4.0/specs/core/0_beacon-chain.md#ejections
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#ejections
|
||||||
func process_ejections*(state: var BeaconState) =
|
func process_ejections*(state: var BeaconState) =
|
||||||
## Iterate through the validator registry and eject active validators with
|
## Iterate through the validator registry and eject active validators with
|
||||||
## balance below ``EJECTION_BALANCE``
|
## balance below ``EJECTION_BALANCE``
|
||||||
|
@ -51,7 +51,7 @@ const
|
|||||||
## TODO: improve this scheme once we can negotiate versions in protocol
|
## TODO: improve this scheme once we can negotiate versions in protocol
|
||||||
|
|
||||||
# Misc
|
# Misc
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/0.4.0/specs/core/0_beacon-chain.md#misc
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#misc
|
||||||
SHARD_COUNT* {.intdefine.} = 1024 ##\
|
SHARD_COUNT* {.intdefine.} = 1024 ##\
|
||||||
## Number of shards supported by the network - validators will jump around
|
## Number of shards supported by the network - validators will jump around
|
||||||
## between these shards and provide attestations to their state.
|
## between these shards and provide attestations to their state.
|
||||||
@ -72,8 +72,6 @@ const
|
|||||||
## At most `1/MAX_BALANCE_CHURN_QUOTIENT` of the validators can change during
|
## At most `1/MAX_BALANCE_CHURN_QUOTIENT` of the validators can change during
|
||||||
## each validator registry change.
|
## each validator registry change.
|
||||||
|
|
||||||
BEACON_CHAIN_SHARD_NUMBER* = not 0'u64 # 2^64 - 1 in spec
|
|
||||||
|
|
||||||
MAX_INDICES_PER_SLASHABLE_VOTE* = 2^12 ##\
|
MAX_INDICES_PER_SLASHABLE_VOTE* = 2^12 ##\
|
||||||
## votes
|
## votes
|
||||||
|
|
||||||
@ -237,6 +235,8 @@ type
|
|||||||
|
|
||||||
# FFG vote
|
# FFG vote
|
||||||
source_epoch*: Epoch
|
source_epoch*: Epoch
|
||||||
|
target_root*: Eth2Digest
|
||||||
|
|
||||||
## TODO epoch_boundary_root and justified_block_root are creatures of new
|
## TODO epoch_boundary_root and justified_block_root are creatures of new
|
||||||
## epoch processing and don't function quite as straightforwardly as just
|
## epoch processing and don't function quite as straightforwardly as just
|
||||||
## renamings, so do that as part of epoch processing change.
|
## renamings, so do that as part of epoch processing change.
|
||||||
@ -369,9 +369,6 @@ type
|
|||||||
slot*: uint64 ##\
|
slot*: uint64 ##\
|
||||||
## Slot number
|
## Slot number
|
||||||
|
|
||||||
shard*: uint64 ##\
|
|
||||||
## Shard number (`BEACON_CHAIN_SHARD_NUMBER` for beacon chain)
|
|
||||||
|
|
||||||
block_root*: Eth2Digest ##\
|
block_root*: Eth2Digest ##\
|
||||||
## Block root
|
## Block root
|
||||||
|
|
||||||
@ -479,6 +476,14 @@ type
|
|||||||
custody_bitfield*: seq[byte] # Custody bitfield
|
custody_bitfield*: seq[byte] # Custody bitfield
|
||||||
inclusion_slot*: Slot # Inclusion slot
|
inclusion_slot*: Slot # Inclusion slot
|
||||||
|
|
||||||
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#historicalbatch
|
||||||
|
HistoricalBatch* = object
|
||||||
|
block_roots* : array[SLOTS_PER_HISTORICAL_ROOT, Eth2Digest] ##\
|
||||||
|
## Block roots
|
||||||
|
|
||||||
|
state_roots* : array[SLOTS_PER_HISTORICAL_ROOT, Eth2Digest] ##\
|
||||||
|
## State roots
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#fork
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#fork
|
||||||
Fork* = object
|
Fork* = object
|
||||||
previous_version*: array[4, byte] ##\
|
previous_version*: array[4, byte] ##\
|
||||||
|
@ -127,7 +127,7 @@ func get_previous_epoch*(state: BeaconState): Epoch =
|
|||||||
## Return the previous epoch of the given ``state``.
|
## Return the previous epoch of the given ``state``.
|
||||||
max(get_current_epoch(state) - 1, GENESIS_EPOCH)
|
max(get_current_epoch(state) - 1, GENESIS_EPOCH)
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/0.4.0/specs/core/0_beacon-chain.md#get_crosslink_committees_at_slot
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#get_crosslink_committees_at_slot
|
||||||
func get_crosslink_committees_at_slot*(state: BeaconState, slot: Slot|uint64,
|
func get_crosslink_committees_at_slot*(state: BeaconState, slot: Slot|uint64,
|
||||||
registry_change: bool = false):
|
registry_change: bool = false):
|
||||||
seq[CrosslinkCommittee] =
|
seq[CrosslinkCommittee] =
|
||||||
@ -140,6 +140,8 @@ func get_crosslink_committees_at_slot*(state: BeaconState, slot: Slot|uint64,
|
|||||||
# TODO: the + 1 here works around a bug, remove when upgrading to
|
# TODO: the + 1 here works around a bug, remove when upgrading to
|
||||||
# some more recent version:
|
# some more recent version:
|
||||||
# https://github.com/ethereum/eth2.0-specs/pull/732
|
# https://github.com/ethereum/eth2.0-specs/pull/732
|
||||||
|
# TODO remove +1 along with rest of epoch reorganization, then
|
||||||
|
# remove this 0.4.0 tag.
|
||||||
epoch = slot_to_epoch(slot + 1)
|
epoch = slot_to_epoch(slot + 1)
|
||||||
current_epoch = get_current_epoch(state)
|
current_epoch = get_current_epoch(state)
|
||||||
previous_epoch = get_previous_epoch(state)
|
previous_epoch = get_previous_epoch(state)
|
||||||
@ -174,21 +176,29 @@ func get_crosslink_committees_at_slot*(state: BeaconState, slot: Slot|uint64,
|
|||||||
doAssert epoch == next_epoch
|
doAssert epoch == next_epoch
|
||||||
|
|
||||||
let
|
let
|
||||||
current_committees_per_epoch = get_current_epoch_committee_count(state)
|
|
||||||
committees_per_epoch = get_next_epoch_committee_count(state)
|
|
||||||
shuffling_epoch = next_epoch
|
shuffling_epoch = next_epoch
|
||||||
|
|
||||||
epochs_since_last_registry_update = current_epoch - state.validator_registry_update_epoch
|
epochs_since_last_registry_update =
|
||||||
|
current_epoch - state.validator_registry_update_epoch
|
||||||
condition = epochs_since_last_registry_update > 1'u64 and
|
condition = epochs_since_last_registry_update > 1'u64 and
|
||||||
is_power_of_2(epochs_since_last_registry_update)
|
is_power_of_2(epochs_since_last_registry_update)
|
||||||
seed = if registry_change or condition:
|
use_next = registry_change or condition
|
||||||
|
committees_per_epoch =
|
||||||
|
if use_next:
|
||||||
|
get_next_epoch_committee_count(state)
|
||||||
|
else:
|
||||||
|
get_current_epoch_committee_count(state)
|
||||||
|
seed =
|
||||||
|
if use_next:
|
||||||
generate_seed(state, next_epoch)
|
generate_seed(state, next_epoch)
|
||||||
else:
|
else:
|
||||||
state.current_shuffling_seed
|
state.current_shuffling_seed
|
||||||
|
shuffling_epoch =
|
||||||
|
if use_next: next_epoch else: state.current_shuffling_epoch
|
||||||
shuffling_start_shard =
|
shuffling_start_shard =
|
||||||
if registry_change:
|
if registry_change:
|
||||||
(state.current_shuffling_start_shard +
|
(state.current_shuffling_start_shard +
|
||||||
current_committees_per_epoch) mod SHARD_COUNT
|
get_current_epoch_committee_count(state)) mod SHARD_COUNT
|
||||||
else:
|
else:
|
||||||
state.current_shuffling_start_shard
|
state.current_shuffling_start_shard
|
||||||
(committees_per_epoch, seed, shuffling_epoch, shuffling_start_shard)
|
(committees_per_epoch, seed, shuffling_epoch, shuffling_start_shard)
|
||||||
|
@ -50,7 +50,6 @@ func verifyBlockSignature(state: BeaconState, blck: BeaconBlock): bool =
|
|||||||
state.validator_registry[get_beacon_proposer_index(state, state.slot)]
|
state.validator_registry[get_beacon_proposer_index(state, state.slot)]
|
||||||
proposal = Proposal(
|
proposal = Proposal(
|
||||||
slot: blck.slot.uint64,
|
slot: blck.slot.uint64,
|
||||||
shard: BEACON_CHAIN_SHARD_NUMBER,
|
|
||||||
block_root: Eth2Digest(data: signed_root(blck)),
|
block_root: Eth2Digest(data: signed_root(blck)),
|
||||||
signature: blck.signature)
|
signature: blck.signature)
|
||||||
bls_verify(
|
bls_verify(
|
||||||
@ -179,7 +178,7 @@ proc processProposerSlashings(
|
|||||||
|
|
||||||
true
|
true
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/0.4.0/specs/core/0_beacon-chain.md#verify_slashable_attestation
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#verify_slashable_attestation
|
||||||
func verify_slashable_attestation(state: BeaconState, slashable_attestation: SlashableAttestation): bool =
|
func verify_slashable_attestation(state: BeaconState, slashable_attestation: SlashableAttestation): bool =
|
||||||
# Verify validity of ``slashable_attestation`` fields.
|
# Verify validity of ``slashable_attestation`` fields.
|
||||||
|
|
||||||
@ -228,8 +227,10 @@ func verify_slashable_attestation(state: BeaconState, slashable_attestation: Sla
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/0.4.0/specs/core/0_beacon-chain.md#attester-slashings-1
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#attester-slashings
|
||||||
proc processAttesterSlashings(state: var BeaconState, blck: BeaconBlock): bool =
|
proc processAttesterSlashings(state: var BeaconState, blck: BeaconBlock): bool =
|
||||||
|
## Process ``AttesterSlashing`` transaction.
|
||||||
|
## Note that this function mutates ``state``.
|
||||||
if len(blck.body.attester_slashings) > MAX_ATTESTER_SLASHINGS:
|
if len(blck.body.attester_slashings) > MAX_ATTESTER_SLASHINGS:
|
||||||
notice "CaspSlash: too many!"
|
notice "CaspSlash: too many!"
|
||||||
return false
|
return false
|
||||||
@ -239,6 +240,7 @@ proc processAttesterSlashings(state: var BeaconState, blck: BeaconBlock): bool =
|
|||||||
slashable_attestation_1 = attester_slashing.slashable_attestation_1
|
slashable_attestation_1 = attester_slashing.slashable_attestation_1
|
||||||
slashable_attestation_2 = attester_slashing.slashable_attestation_2
|
slashable_attestation_2 = attester_slashing.slashable_attestation_2
|
||||||
|
|
||||||
|
# Check that the attestations are conflicting
|
||||||
if not (slashable_attestation_1.data != slashable_attestation_2.data):
|
if not (slashable_attestation_1.data != slashable_attestation_2.data):
|
||||||
notice "CaspSlash: invalid data"
|
notice "CaspSlash: invalid data"
|
||||||
return false
|
return false
|
||||||
@ -260,14 +262,14 @@ proc processAttesterSlashings(state: var BeaconState, blck: BeaconBlock): bool =
|
|||||||
let
|
let
|
||||||
indices2 = toSet(slashable_attestation_2.validator_indices)
|
indices2 = toSet(slashable_attestation_2.validator_indices)
|
||||||
slashable_indices =
|
slashable_indices =
|
||||||
slashable_attestation_1.validator_indices.filterIt(it in indices2)
|
slashable_attestation_1.validator_indices.filterIt(
|
||||||
|
it in indices2 and not state.validator_registry[it.int].slashed)
|
||||||
|
|
||||||
if not (len(slashable_indices) >= 1):
|
if not (len(slashable_indices) >= 1):
|
||||||
notice "CaspSlash: no intersection"
|
notice "CaspSlash: no intersection"
|
||||||
return false
|
return false
|
||||||
|
|
||||||
for index in slashable_indices:
|
for index in slashable_indices:
|
||||||
if state.validator_registry[index.int].slashed:
|
|
||||||
slash_validator(state, index.ValidatorIndex)
|
slash_validator(state, index.ValidatorIndex)
|
||||||
|
|
||||||
true
|
true
|
||||||
@ -334,7 +336,40 @@ proc processExits(
|
|||||||
|
|
||||||
true
|
true
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/0.4.0/specs/core/0_beacon-chain.md#transfers-1
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#validator-registry-and-shuffling-seed-data
|
||||||
|
func update_registry_and_shuffling_data(state: var BeaconState) =
|
||||||
|
# First set previous shuffling data to current shuffling data
|
||||||
|
state.previous_shuffling_epoch = state.current_shuffling_epoch
|
||||||
|
state.previous_shuffling_start_shard = state.current_shuffling_start_shard
|
||||||
|
state.previous_shuffling_seed = state.current_shuffling_seed
|
||||||
|
|
||||||
|
let
|
||||||
|
current_epoch = get_current_epoch(state)
|
||||||
|
next_epoch = current_epoch + 1
|
||||||
|
|
||||||
|
# Check if we should update, and if so, update
|
||||||
|
if should_update_validator_registry(state):
|
||||||
|
update_validator_registry(state)
|
||||||
|
# If we update the registry, update the shuffling data and shards as well
|
||||||
|
state.current_shuffling_epoch = next_epoch
|
||||||
|
state.current_shuffling_start_shard = (
|
||||||
|
state.current_shuffling_start_shard +
|
||||||
|
get_current_epoch_committee_count(state) mod SHARD_COUNT
|
||||||
|
)
|
||||||
|
state.current_shuffling_seed =
|
||||||
|
generate_seed(state, state.current_shuffling_epoch)
|
||||||
|
else:
|
||||||
|
## If processing at least one crosslink keeps failing, then reshuffle every
|
||||||
|
## power of two, but don't update the current_shuffling_start_shard
|
||||||
|
let epochs_since_last_registry_update =
|
||||||
|
current_epoch - state.validator_registry_update_epoch
|
||||||
|
if epochs_since_last_registry_update > 1'u64 and
|
||||||
|
is_power_of_2(epochs_since_last_registry_update):
|
||||||
|
state.current_shuffling_epoch = next_epoch
|
||||||
|
state.current_shuffling_seed =
|
||||||
|
generate_seed(state, state.current_shuffling_epoch)
|
||||||
|
|
||||||
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#transfers
|
||||||
proc processTransfers(state: var BeaconState, blck: BeaconBlock,
|
proc processTransfers(state: var BeaconState, blck: BeaconBlock,
|
||||||
flags: UpdateFlags): bool =
|
flags: UpdateFlags): bool =
|
||||||
## Note: Transfers are a temporary functionality for phases 0 and 1, to be
|
## Note: Transfers are a temporary functionality for phases 0 and 1, to be
|
||||||
@ -344,33 +379,37 @@ proc processTransfers(state: var BeaconState, blck: BeaconBlock,
|
|||||||
return false
|
return false
|
||||||
|
|
||||||
for transfer in blck.body.transfers:
|
for transfer in blck.body.transfers:
|
||||||
let from_balance = state.validator_balances[transfer.sender.int]
|
let sender_balance = state.validator_balances[transfer.sender.int]
|
||||||
|
|
||||||
if not (from_balance >= transfer.amount):
|
## Verify the amount and fee aren't individually too big (for anti-overflow
|
||||||
notice "Transfer: source balance too low for amount"
|
## purposes)
|
||||||
|
if not (sender_balance >= max(transfer.amount, transfer.fee)):
|
||||||
|
notice "Transfer: sender balance too low for transfer amount or fee"
|
||||||
return false
|
return false
|
||||||
|
|
||||||
if not (from_balance >= transfer.fee):
|
## Verify that we have enough ETH to send, and that after the transfer the
|
||||||
notice "Transfer: source balance too low for fee"
|
## balance will be either exactly zero or at least MIN_DEPOSIT_AMOUNT
|
||||||
return false
|
if not (
|
||||||
|
sender_balance == transfer.amount + transfer.fee or
|
||||||
if not (from_balance == transfer.amount + transfer.fee or from_balance >=
|
sender_balance >= transfer.amount + transfer.fee + MIN_DEPOSIT_AMOUNT):
|
||||||
transfer.amount + transfer.fee + MIN_DEPOSIT_AMOUNT):
|
notice "Transfer: sender balance too low for amount + fee"
|
||||||
notice "Transfer: source balance too low for amount + fee"
|
|
||||||
return false
|
return false
|
||||||
|
|
||||||
|
# A transfer is valid in only one slot
|
||||||
if not (state.slot == transfer.slot):
|
if not (state.slot == transfer.slot):
|
||||||
notice "Transfer: slot mismatch"
|
notice "Transfer: slot mismatch"
|
||||||
return false
|
return false
|
||||||
|
|
||||||
|
# Only withdrawn or not-yet-deposited accounts can transfer
|
||||||
if not (get_current_epoch(state) >=
|
if not (get_current_epoch(state) >=
|
||||||
state.validator_registry[
|
state.validator_registry[
|
||||||
transfer.sender.int].withdrawable_epoch or
|
transfer.sender.int].withdrawable_epoch or
|
||||||
state.validator_registry[transfer.sender.int].activation_epoch ==
|
state.validator_registry[transfer.sender.int].activation_epoch ==
|
||||||
FAR_FUTURE_EPOCH):
|
FAR_FUTURE_EPOCH):
|
||||||
notice "Transfer: epoch mismatch"
|
notice "Transfer: only withdrawn or not-deposited accounts can transfer"
|
||||||
return false
|
return false
|
||||||
|
|
||||||
|
# Verify that the pubkey is valid
|
||||||
let wc = state.validator_registry[transfer.sender.int].
|
let wc = state.validator_registry[transfer.sender.int].
|
||||||
withdrawal_credentials
|
withdrawal_credentials
|
||||||
if not (wc.data[0] == BLS_WITHDRAWAL_PREFIX_BYTE and
|
if not (wc.data[0] == BLS_WITHDRAWAL_PREFIX_BYTE and
|
||||||
@ -378,10 +417,10 @@ proc processTransfers(state: var BeaconState, blck: BeaconBlock,
|
|||||||
notice "Transfer: incorrect withdrawal credentials"
|
notice "Transfer: incorrect withdrawal credentials"
|
||||||
return false
|
return false
|
||||||
|
|
||||||
|
# Verify that the signature is valid
|
||||||
if skipValidation notin flags:
|
if skipValidation notin flags:
|
||||||
let transfer_message = signed_root(transfer)
|
|
||||||
if not bls_verify(
|
if not bls_verify(
|
||||||
pubkey=transfer.pubkey, transfer_message, transfer.signature,
|
pubkey=transfer.pubkey, signed_root(transfer), transfer.signature,
|
||||||
get_domain(
|
get_domain(
|
||||||
state.fork, slot_to_epoch(transfer.slot), DOMAIN_TRANSFER)):
|
state.fork, slot_to_epoch(transfer.slot), DOMAIN_TRANSFER)):
|
||||||
notice "Transfer: incorrect signature"
|
notice "Transfer: incorrect signature"
|
||||||
@ -498,17 +537,6 @@ proc processBlock(
|
|||||||
|
|
||||||
true
|
true
|
||||||
|
|
||||||
func get_attester_indices(
|
|
||||||
state: BeaconState,
|
|
||||||
attestations: openArray[PendingAttestation]): seq[ValidatorIndex] =
|
|
||||||
# Union of attesters that participated in some attestations
|
|
||||||
# TODO spec - add as helper?
|
|
||||||
attestations.
|
|
||||||
mapIt(
|
|
||||||
get_attestation_participants(state, it.data, it.aggregation_bitfield)).
|
|
||||||
flatten().
|
|
||||||
deduplicate()
|
|
||||||
|
|
||||||
func boundary_attestations(
|
func boundary_attestations(
|
||||||
state: BeaconState, boundary_hash: Eth2Digest,
|
state: BeaconState, boundary_hash: Eth2Digest,
|
||||||
attestations: openArray[PendingAttestation]
|
attestations: openArray[PendingAttestation]
|
||||||
@ -524,7 +552,226 @@ func lowerThan(candidate, current: Eth2Digest): bool =
|
|||||||
if v > candidate.data[i]: return true
|
if v > candidate.data[i]: return true
|
||||||
false
|
false
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/0.4.0/specs/core/0_beacon-chain.md#validator-registry-and-shuffling-seed-data
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#helper-functions-1
|
||||||
|
func get_previous_total_balance(state: BeaconState): Gwei =
|
||||||
|
get_total_balance(
|
||||||
|
state,
|
||||||
|
get_active_validator_indices(state.validator_registry,
|
||||||
|
get_previous_epoch(state)))
|
||||||
|
|
||||||
|
func get_attesting_indices(
|
||||||
|
state: BeaconState,
|
||||||
|
attestations: openArray[PendingAttestation]): seq[ValidatorIndex] =
|
||||||
|
# Union of attesters that participated in some attestations
|
||||||
|
attestations.
|
||||||
|
mapIt(
|
||||||
|
get_attestation_participants(state, it.data, it.aggregation_bitfield)).
|
||||||
|
flatten().
|
||||||
|
deduplicate().
|
||||||
|
sorted(system.cmp)
|
||||||
|
|
||||||
|
func get_attesting_balance(state: BeaconState,
|
||||||
|
attestations: seq[PendingAttestation]): Gwei =
|
||||||
|
get_total_balance(state, get_attesting_indices(state, attestations))
|
||||||
|
|
||||||
|
func get_previous_epoch_boundary_attestations(state: BeaconState):
|
||||||
|
seq[PendingAttestation] =
|
||||||
|
filterIt(
|
||||||
|
state.previous_epoch_attestations,
|
||||||
|
it.data.target_root ==
|
||||||
|
get_block_root(state, get_epoch_start_slot(get_previous_epoch(state))))
|
||||||
|
|
||||||
|
func get_previous_epoch_matching_head_attestations(state: BeaconState):
|
||||||
|
seq[PendingAttestation] =
|
||||||
|
filterIt(
|
||||||
|
state.previous_epoch_attestations,
|
||||||
|
it.data.beacon_block_root == get_block_root(state, it.data.slot))
|
||||||
|
|
||||||
|
# Combination of earliest_attestation and inclusion_slot avoiding O(n^2)
|
||||||
|
# TODO merge/refactor these two functions, which differ only very slightly.
|
||||||
|
func inclusion_slots(state: BeaconState): auto =
|
||||||
|
result = initTable[ValidatorIndex, Slot]()
|
||||||
|
|
||||||
|
let previous_epoch_attestations =
|
||||||
|
state.latest_attestations.filterIt(
|
||||||
|
get_previous_epoch(state) == slot_to_epoch(it.data.slot))
|
||||||
|
|
||||||
|
## TODO switch previous_epoch_attestations to state.foo,
|
||||||
|
## when implemented finish_epoch_update
|
||||||
|
for a in sorted(previous_epoch_attestations,
|
||||||
|
func (x, y: PendingAttestation): auto =
|
||||||
|
system.cmp(x.inclusion_slot, y.inclusion_slot)):
|
||||||
|
for v in get_attestation_participants(
|
||||||
|
state, a.data, a.aggregation_bitfield):
|
||||||
|
if v notin result:
|
||||||
|
result[v] = a.inclusion_slot
|
||||||
|
|
||||||
|
# Combination of earliest_attestation and inclusion_distance avoiding O(n^2)
|
||||||
|
func inclusion_distances(state: BeaconState): auto =
|
||||||
|
result = initTable[ValidatorIndex, Slot]()
|
||||||
|
|
||||||
|
let previous_epoch_attestations =
|
||||||
|
state.latest_attestations.filterIt(
|
||||||
|
get_previous_epoch(state) == slot_to_epoch(it.data.slot))
|
||||||
|
|
||||||
|
## TODO switch previous_epoch_attestations to state.foo,
|
||||||
|
## when implemented finish_epoch_update
|
||||||
|
for a in sorted(previous_epoch_attestations,
|
||||||
|
func (x, y: PendingAttestation): auto =
|
||||||
|
system.cmp(x.inclusion_slot, y.inclusion_slot)):
|
||||||
|
for v in get_attestation_participants(
|
||||||
|
state, a.data, a.aggregation_bitfield):
|
||||||
|
if v notin result:
|
||||||
|
result[v] = Slot(a.inclusion_slot - a.data.slot)
|
||||||
|
|
||||||
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#rewards-and-penalties
|
||||||
|
func get_base_reward(state: BeaconState, index: ValidatorIndex): uint64 =
|
||||||
|
if get_previous_total_balance(state) == 0:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
let adjusted_quotient =
|
||||||
|
integer_squareroot(get_previous_total_balance(state)) div
|
||||||
|
BASE_REWARD_QUOTIENT
|
||||||
|
get_effective_balance(state, index) div adjusted_quotient.uint64 div 5
|
||||||
|
|
||||||
|
func get_inactivity_penalty(
|
||||||
|
state: BeaconState, index: ValidatorIndex,
|
||||||
|
epochs_since_finality: uint64): uint64 =
|
||||||
|
# TODO Left/right associativity sensitivity on * and div?
|
||||||
|
get_base_reward(state, index) +
|
||||||
|
get_effective_balance(state, index) * epochs_since_finality div
|
||||||
|
INACTIVITY_PENALTY_QUOTIENT div 2
|
||||||
|
|
||||||
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#justification-and-finalization
|
||||||
|
func compute_normal_justification_and_finalization_deltas(state: BeaconState):
|
||||||
|
tuple[a: seq[Gwei], b: seq[Gwei]] =
|
||||||
|
# deltas[0] for rewards
|
||||||
|
# deltas[1] for penalties
|
||||||
|
var deltas = (
|
||||||
|
repeat(0'u64, len(state.validator_registry)),
|
||||||
|
repeat(0'u64, len(state.validator_registry))
|
||||||
|
)
|
||||||
|
# Some helper variables
|
||||||
|
let
|
||||||
|
boundary_attestations = get_previous_epoch_boundary_attestations(state)
|
||||||
|
boundary_attesting_balance =
|
||||||
|
get_attesting_balance(state, boundary_attestations)
|
||||||
|
total_balance = get_previous_total_balance(state)
|
||||||
|
total_attesting_balance =
|
||||||
|
get_attesting_balance(state, state.previous_epoch_attestations)
|
||||||
|
matching_head_attestations =
|
||||||
|
get_previous_epoch_matching_head_attestations(state)
|
||||||
|
matching_head_balance =
|
||||||
|
get_attesting_balance(state, matching_head_attestations)
|
||||||
|
|
||||||
|
let
|
||||||
|
inclusion_distance = inclusion_distances(state)
|
||||||
|
inclusion_slot = inclusion_slots(state)
|
||||||
|
# Process rewards or penalties for all validators
|
||||||
|
for index in get_active_validator_indices(
|
||||||
|
state.validator_registry, get_previous_epoch(state)):
|
||||||
|
# Expected FFG source
|
||||||
|
## TODO accidentally quadratic or worse, along with rest of
|
||||||
|
## (not)in get_attesting_indices(...)
|
||||||
|
if index in get_attesting_indices(
|
||||||
|
state, state.previous_epoch_attestations):
|
||||||
|
deltas[0][index] +=
|
||||||
|
get_base_reward(state, index) * total_attesting_balance div
|
||||||
|
total_balance
|
||||||
|
# Inclusion speed bonus
|
||||||
|
deltas[0][index] += (
|
||||||
|
get_base_reward(state, index) * MIN_ATTESTATION_INCLUSION_DELAY div
|
||||||
|
inclusion_distance[index]
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
deltas[1][index] += get_base_reward(state, index)
|
||||||
|
# Expected FFG target
|
||||||
|
if index in get_attesting_indices(state, boundary_attestations):
|
||||||
|
deltas[0][index] +=
|
||||||
|
get_base_reward(state, index) * boundary_attesting_balance div
|
||||||
|
total_balance
|
||||||
|
else:
|
||||||
|
deltas[1][index] += get_base_reward(state, index)
|
||||||
|
# Expected head
|
||||||
|
if index in get_attesting_indices(state, matching_head_attestations):
|
||||||
|
deltas[0][index] +=
|
||||||
|
get_base_reward(state, index) *
|
||||||
|
matching_head_balance div total_balance
|
||||||
|
else:
|
||||||
|
deltas[1][index] += get_base_reward(state, index)
|
||||||
|
# Proposer bonus
|
||||||
|
if index in get_attesting_indices(state, state.previous_epoch_attestations):
|
||||||
|
let proposer_index =
|
||||||
|
get_beacon_proposer_index(state, inclusion_slot[index])
|
||||||
|
deltas[0][proposer_index] +=
|
||||||
|
get_base_reward(state, index) div ATTESTATION_INCLUSION_REWARD_QUOTIENT
|
||||||
|
deltas
|
||||||
|
|
||||||
|
func compute_inactivity_leak_deltas(state: BeaconState):
|
||||||
|
tuple[a: seq[Gwei], b: seq[Gwei]] =
|
||||||
|
# When blocks are not finalizing normally
|
||||||
|
# deltas[0] for rewards
|
||||||
|
# deltas[1] for penalties
|
||||||
|
var deltas = (
|
||||||
|
repeat(0'u64, len(state.validator_registry)),
|
||||||
|
repeat(0'u64, len(state.validator_registry))
|
||||||
|
)
|
||||||
|
let
|
||||||
|
boundary_attestations = get_previous_epoch_boundary_attestations(state)
|
||||||
|
matching_head_attestations =
|
||||||
|
get_previous_epoch_matching_head_attestations(state)
|
||||||
|
active_validator_indices = get_active_validator_indices(
|
||||||
|
state.validator_registry, get_previous_epoch(state))
|
||||||
|
epochs_since_finality =
|
||||||
|
get_current_epoch(state) + 1 - state.finalized_epoch
|
||||||
|
let
|
||||||
|
inclusion_distance = inclusion_distances(state)
|
||||||
|
for index in active_validator_indices:
|
||||||
|
# TODO more >= quadratics
|
||||||
|
if index notin get_attesting_indices(
|
||||||
|
state, state.previous_epoch_attestations):
|
||||||
|
deltas[1][index] +=
|
||||||
|
get_inactivity_penalty(state, index, epochs_since_finality)
|
||||||
|
else:
|
||||||
|
## If a validator did attest, apply a small penalty for getting
|
||||||
|
## attestations included late
|
||||||
|
deltas[0][index] += (
|
||||||
|
get_base_reward(state, index) * MIN_ATTESTATION_INCLUSION_DELAY div
|
||||||
|
inclusion_distance[index]
|
||||||
|
)
|
||||||
|
deltas[1][index] += get_base_reward(state, index)
|
||||||
|
if index notin get_attesting_indices(state, boundary_attestations):
|
||||||
|
deltas[1][index] +=
|
||||||
|
get_inactivity_penalty(state, index, epochs_since_finality)
|
||||||
|
if index notin get_attesting_indices(state, matching_head_attestations):
|
||||||
|
deltas[1][index] += get_base_reward(state, index)
|
||||||
|
## Penalize slashed-but-inactive validators as though they were active but
|
||||||
|
## offline
|
||||||
|
for index in 0 ..< len(state.validator_registry):
|
||||||
|
let eligible = (
|
||||||
|
index.ValidatorIndex notin active_validator_indices and
|
||||||
|
state.validator_registry[index].slashed and
|
||||||
|
get_current_epoch(state) <
|
||||||
|
state.validator_registry[index].withdrawable_epoch
|
||||||
|
)
|
||||||
|
if eligible:
|
||||||
|
deltas[1][index] += (
|
||||||
|
2'u64 * get_inactivity_penalty(
|
||||||
|
state, index.ValidatorIndex, epochs_since_finality) +
|
||||||
|
get_base_reward(state, index.ValidatorIndex)
|
||||||
|
)
|
||||||
|
deltas
|
||||||
|
|
||||||
|
func get_justification_and_finalization_deltas(state: BeaconState):
|
||||||
|
tuple[a: seq[Gwei], b: seq[Gwei]] =
|
||||||
|
let epochs_since_finality =
|
||||||
|
get_current_epoch(state) + 1 - state.finalized_epoch
|
||||||
|
if epochs_since_finality <= 4:
|
||||||
|
compute_normal_justification_and_finalization_deltas(state)
|
||||||
|
else:
|
||||||
|
compute_inactivity_leak_deltas(state)
|
||||||
|
|
||||||
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#slashings-and-exit-queue
|
||||||
func process_slashings(state: var BeaconState) =
|
func process_slashings(state: var BeaconState) =
|
||||||
## Process the slashings.
|
## Process the slashings.
|
||||||
## Note that this function mutates ``state``.
|
## Note that this function mutates ``state``.
|
||||||
@ -534,15 +781,18 @@ func process_slashings(state: var BeaconState) =
|
|||||||
state.validator_registry, current_epoch)
|
state.validator_registry, current_epoch)
|
||||||
total_balance = get_total_balance(state, active_validator_indices)
|
total_balance = get_total_balance(state, active_validator_indices)
|
||||||
|
|
||||||
|
# Compute `total_penalties`
|
||||||
|
total_at_start = state.latest_slashed_balances[
|
||||||
|
(current_epoch + 1) mod LATEST_SLASHED_EXIT_LENGTH]
|
||||||
|
total_at_end =
|
||||||
|
state.latest_slashed_balances[current_epoch mod
|
||||||
|
LATEST_SLASHED_EXIT_LENGTH]
|
||||||
|
total_penalties = total_at_end - total_at_start
|
||||||
|
|
||||||
for index, validator in state.validator_registry:
|
for index, validator in state.validator_registry:
|
||||||
if validator.slashed and current_epoch == validator.withdrawable_epoch -
|
if validator.slashed and current_epoch == validator.withdrawable_epoch -
|
||||||
LATEST_SLASHED_EXIT_LENGTH div 2:
|
LATEST_SLASHED_EXIT_LENGTH div 2:
|
||||||
let
|
let
|
||||||
epoch_index = current_epoch mod LATEST_SLASHED_EXIT_LENGTH
|
|
||||||
total_at_start = state.latest_slashed_balances[
|
|
||||||
(epoch_index + 1) mod LATEST_SLASHED_EXIT_LENGTH]
|
|
||||||
total_at_end = state.latest_slashed_balances[epoch_index]
|
|
||||||
total_penalties = total_at_end - total_at_start
|
|
||||||
penalty = max(
|
penalty = max(
|
||||||
get_effective_balance(state, index.ValidatorIndex) *
|
get_effective_balance(state, index.ValidatorIndex) *
|
||||||
min(total_penalties * 3, total_balance) div total_balance,
|
min(total_penalties * 3, total_balance) div total_balance,
|
||||||
@ -564,14 +814,13 @@ func process_exit_queue(state: var BeaconState) =
|
|||||||
return get_current_epoch(state) >= validator.exit_epoch +
|
return get_current_epoch(state) >= validator.exit_epoch +
|
||||||
MIN_VALIDATOR_WITHDRAWABILITY_DELAY
|
MIN_VALIDATOR_WITHDRAWABILITY_DELAY
|
||||||
|
|
||||||
# TODO try again with filterIt
|
|
||||||
var eligible_indices: seq[ValidatorIndex]
|
var eligible_indices: seq[ValidatorIndex]
|
||||||
for vi in 0 ..< len(state.validator_registry):
|
for vi in 0 ..< len(state.validator_registry):
|
||||||
if eligible(vi.ValidatorIndex):
|
if eligible(vi.ValidatorIndex):
|
||||||
eligible_indices.add vi.ValidatorIndex
|
eligible_indices.add vi.ValidatorIndex
|
||||||
let
|
let
|
||||||
# Sort in order of exit epoch, and validators that exit within the same
|
## Sort in order of exit epoch, and validators that exit within the same
|
||||||
# epoch exit in order of validator index
|
## epoch exit in order of validator index
|
||||||
sorted_indices = sorted(
|
sorted_indices = sorted(
|
||||||
eligible_indices,
|
eligible_indices,
|
||||||
func(x, y: ValidatorIndex): int =
|
func(x, y: ValidatorIndex): int =
|
||||||
@ -584,6 +833,41 @@ func process_exit_queue(state: var BeaconState) =
|
|||||||
break
|
break
|
||||||
prepare_validator_for_withdrawal(state, index)
|
prepare_validator_for_withdrawal(state, index)
|
||||||
|
|
||||||
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#final-updates
|
||||||
|
func finish_epoch_update(state: var BeaconState) =
|
||||||
|
let
|
||||||
|
current_epoch = get_current_epoch(state)
|
||||||
|
next_epoch = current_epoch + 1
|
||||||
|
|
||||||
|
# Set active index root
|
||||||
|
let index_root_position =
|
||||||
|
(next_epoch + ACTIVATION_EXIT_DELAY) mod LATEST_ACTIVE_INDEX_ROOTS_LENGTH
|
||||||
|
state.latest_active_index_roots[index_root_position] =
|
||||||
|
Eth2Digest(data: hash_tree_root(get_active_validator_indices(
|
||||||
|
state.validator_registry, next_epoch + ACTIVATION_EXIT_DELAY))
|
||||||
|
)
|
||||||
|
|
||||||
|
# Set total slashed balances
|
||||||
|
state.latest_slashed_balances[next_epoch mod LATEST_SLASHED_EXIT_LENGTH] = (
|
||||||
|
state.latest_slashed_balances[current_epoch mod LATEST_SLASHED_EXIT_LENGTH]
|
||||||
|
)
|
||||||
|
|
||||||
|
# Set randao mix
|
||||||
|
state.latest_randao_mixes[next_epoch mod LATEST_RANDAO_MIXES_LENGTH] =
|
||||||
|
get_randao_mix(state, current_epoch)
|
||||||
|
|
||||||
|
# Set historical root accumulator
|
||||||
|
if next_epoch mod (SLOTS_PER_HISTORICAL_ROOT div SLOTS_PER_EPOCH).uint64 == 0:
|
||||||
|
let historical_batch = HistoricalBatch(
|
||||||
|
block_roots: state.latest_block_roots,
|
||||||
|
state_roots: state.latest_state_roots,
|
||||||
|
)
|
||||||
|
state.historical_roots.add (hash_tree_root_final(historical_batch))
|
||||||
|
|
||||||
|
# Rotate current/previous epoch attestations
|
||||||
|
state.previous_epoch_attestations = state.current_epoch_attestations
|
||||||
|
state.current_epoch_attestations = @[]
|
||||||
|
|
||||||
func processEpoch(state: var BeaconState) =
|
func processEpoch(state: var BeaconState) =
|
||||||
if (state.slot + 1) mod SLOTS_PER_EPOCH != 0:
|
if (state.slot + 1) mod SLOTS_PER_EPOCH != 0:
|
||||||
return
|
return
|
||||||
@ -612,7 +896,7 @@ func processEpoch(state: var BeaconState) =
|
|||||||
current_epoch_attestations)
|
current_epoch_attestations)
|
||||||
|
|
||||||
current_epoch_boundary_attester_indices =
|
current_epoch_boundary_attester_indices =
|
||||||
get_attester_indices(state, current_epoch_boundary_attestations)
|
get_attesting_indices(state, current_epoch_boundary_attestations)
|
||||||
|
|
||||||
current_epoch_boundary_attesting_balance =
|
current_epoch_boundary_attesting_balance =
|
||||||
get_total_balance(state, current_epoch_boundary_attester_indices)
|
get_total_balance(state, current_epoch_boundary_attester_indices)
|
||||||
@ -631,7 +915,7 @@ func processEpoch(state: var BeaconState) =
|
|||||||
previous_epoch == slot_to_epoch(it.data.slot))
|
previous_epoch == slot_to_epoch(it.data.slot))
|
||||||
|
|
||||||
previous_epoch_attester_indices =
|
previous_epoch_attester_indices =
|
||||||
toSet(get_attester_indices(state, previous_epoch_attestations))
|
toSet(get_attesting_indices(state, previous_epoch_attestations))
|
||||||
|
|
||||||
previous_epoch_attesting_balance =
|
previous_epoch_attesting_balance =
|
||||||
get_total_balance(state, previous_epoch_attester_indices)
|
get_total_balance(state, previous_epoch_attester_indices)
|
||||||
@ -645,7 +929,7 @@ func processEpoch(state: var BeaconState) =
|
|||||||
previous_epoch_attestations)
|
previous_epoch_attestations)
|
||||||
|
|
||||||
previous_epoch_boundary_attester_indices =
|
previous_epoch_boundary_attester_indices =
|
||||||
toSet(get_attester_indices(state, previous_epoch_boundary_attestations))
|
toSet(get_attesting_indices(state, previous_epoch_boundary_attestations))
|
||||||
|
|
||||||
previous_epoch_boundary_attesting_balance =
|
previous_epoch_boundary_attesting_balance =
|
||||||
get_total_balance(state, previous_epoch_boundary_attester_indices)
|
get_total_balance(state, previous_epoch_boundary_attester_indices)
|
||||||
@ -658,7 +942,7 @@ func processEpoch(state: var BeaconState) =
|
|||||||
it.data.beacon_block_root == get_block_root(state, it.data.slot))
|
it.data.beacon_block_root == get_block_root(state, it.data.slot))
|
||||||
|
|
||||||
previous_epoch_head_attester_indices =
|
previous_epoch_head_attester_indices =
|
||||||
toSet(get_attester_indices(state, previous_epoch_head_attestations))
|
toSet(get_attesting_indices(state, previous_epoch_head_attestations))
|
||||||
|
|
||||||
previous_epoch_head_attesting_balance =
|
previous_epoch_head_attesting_balance =
|
||||||
get_total_balance(state, previous_epoch_head_attester_indices)
|
get_total_balance(state, previous_epoch_head_attester_indices)
|
||||||
@ -673,7 +957,7 @@ func processEpoch(state: var BeaconState) =
|
|||||||
concat(current_epoch_attestations, previous_epoch_attestations).
|
concat(current_epoch_attestations, previous_epoch_attestations).
|
||||||
filterIt(it.data.shard == crosslink_committee.shard and
|
filterIt(it.data.shard == crosslink_committee.shard and
|
||||||
it.data.crosslink_data_root == crosslink_data_root)
|
it.data.crosslink_data_root == crosslink_data_root)
|
||||||
get_attester_indices(statePtr[], shard_block_attestations)
|
get_attesting_indices(statePtr[], shard_block_attestations)
|
||||||
|
|
||||||
func winning_root(crosslink_committee: CrosslinkCommittee): Eth2Digest =
|
func winning_root(crosslink_committee: CrosslinkCommittee): Eth2Digest =
|
||||||
# * Let `winning_root(crosslink_committee)` be equal to the value of
|
# * Let `winning_root(crosslink_committee)` be equal to the value of
|
||||||
@ -711,9 +995,6 @@ func processEpoch(state: var BeaconState) =
|
|||||||
get_total_balance(
|
get_total_balance(
|
||||||
statePtr[], attesting_validator_indices(crosslink_committee))
|
statePtr[], attesting_validator_indices(crosslink_committee))
|
||||||
|
|
||||||
## Regarding inclusion_slot and inclusion_distance, as defined they result in
|
|
||||||
## O(n^2) behavior, so implement slightly differently.
|
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/0.4.0/specs/core/0_beacon-chain.md#eth1-data-1
|
# https://github.com/ethereum/eth2.0-specs/blob/0.4.0/specs/core/0_beacon-chain.md#eth1-data-1
|
||||||
block:
|
block:
|
||||||
if next_epoch mod EPOCHS_PER_ETH1_VOTING_PERIOD == 0:
|
if next_epoch mod EPOCHS_PER_ETH1_VOTING_PERIOD == 0:
|
||||||
@ -764,115 +1045,15 @@ func processEpoch(state: var BeaconState) =
|
|||||||
epoch: slot_to_epoch(slot),
|
epoch: slot_to_epoch(slot),
|
||||||
crosslink_data_root: winning_root(crosslink_committee))
|
crosslink_data_root: winning_root(crosslink_committee))
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/0.4.0/specs/core/0_beacon-chain.md#rewards-and-penalties
|
|
||||||
## First, we define some additional helpers
|
|
||||||
## Note: When applying penalties in the following balance recalculations
|
|
||||||
## implementers should make sure the uint64 does not underflow
|
|
||||||
let base_reward_quotient =
|
|
||||||
integer_squareroot(previous_total_balance) div BASE_REWARD_QUOTIENT
|
|
||||||
|
|
||||||
func base_reward(state: BeaconState, index: ValidatorIndex): uint64 =
|
|
||||||
get_effective_balance(state, index) div base_reward_quotient.uint64 div 5
|
|
||||||
|
|
||||||
func inactivity_penalty(
|
|
||||||
state: BeaconState, index: ValidatorIndex, epochs_since_finality: uint64): uint64 =
|
|
||||||
# TODO Left/right associativity sensitivity on * and div?
|
|
||||||
base_reward(state, index) +
|
|
||||||
get_effective_balance(state, index) * epochs_since_finality div
|
|
||||||
INACTIVITY_PENALTY_QUOTIENT div 2
|
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/0.4.0/specs/core/0_beacon-chain.md#justification-and-finalization
|
# 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 =
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#apply-rewards
|
||||||
result = initTable[ValidatorIndex, Slot]()
|
## TODO this is incomplete, since get_crosslink_deltas isn't there, but worth
|
||||||
|
## beginning to switch to something which does not affect structural aspects.
|
||||||
for a in previous_epoch_attestations:
|
## also, this is `apply_rewards` in spec. TOD
|
||||||
for v in get_attestation_participants(
|
let deltas1 = get_justification_and_finalization_deltas(state)
|
||||||
state, a.data, a.aggregation_bitfield):
|
for i in 0 ..< len(state.validator_registry):
|
||||||
if v notin result:
|
state.validator_balances[i] = max(
|
||||||
result[v] = Slot(a.inclusion_slot - a.data.slot)
|
0'u64, state.validator_balances[i] + deltas1[0][i] - deltas1[1][i])
|
||||||
|
|
||||||
block: # Justification and finalization
|
|
||||||
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:
|
|
||||||
statePtr.validator_balances[v] +=
|
|
||||||
base_reward(statePtr[], v) *
|
|
||||||
attesting_balance div previous_total_balance
|
|
||||||
|
|
||||||
for v in active_validator_indices:
|
|
||||||
if v notin attesters:
|
|
||||||
reduce_balance(
|
|
||||||
statePtr.validator_balances[v], base_reward(statePtr[], v))
|
|
||||||
|
|
||||||
if epochs_since_finality <= 4'u64:
|
|
||||||
# Case 1: epochs_since_finality <= 4
|
|
||||||
# Expected FFG source
|
|
||||||
update_balance(
|
|
||||||
previous_epoch_attester_indices,
|
|
||||||
previous_epoch_attesting_balance)
|
|
||||||
|
|
||||||
# Expected FFG target:
|
|
||||||
update_balance(
|
|
||||||
previous_epoch_boundary_attester_indices,
|
|
||||||
previous_epoch_boundary_attesting_balance)
|
|
||||||
|
|
||||||
# Expected beacon chain head:
|
|
||||||
update_balance(
|
|
||||||
previous_epoch_head_attester_indices,
|
|
||||||
previous_epoch_head_attesting_balance)
|
|
||||||
|
|
||||||
# Inclusion distance
|
|
||||||
# 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 inclusion_distance[v]
|
|
||||||
|
|
||||||
else:
|
|
||||||
# Case 2: epochs_since_finality > 4
|
|
||||||
let inclusion_distance =
|
|
||||||
if previous_epoch_attester_indices.len > 0:
|
|
||||||
inclusion_distances(state)
|
|
||||||
else:
|
|
||||||
## Last case will not occur. If this assumption becomes false,
|
|
||||||
## indexing into distances will fail.
|
|
||||||
initTable[ValidatorIndex, Slot]()
|
|
||||||
|
|
||||||
for index in active_validator_indices:
|
|
||||||
if index notin previous_epoch_attester_indices:
|
|
||||||
reduce_balance(
|
|
||||||
state.validator_balances[index],
|
|
||||||
inactivity_penalty(state, index, epochs_since_finality))
|
|
||||||
if index notin previous_epoch_boundary_attester_indices:
|
|
||||||
reduce_balance(
|
|
||||||
state.validator_balances[index],
|
|
||||||
inactivity_penalty(state, index, epochs_since_finality))
|
|
||||||
if index notin previous_epoch_head_attester_indices:
|
|
||||||
reduce_balance(
|
|
||||||
state.validator_balances[index], base_reward(state, index))
|
|
||||||
if state.validator_registry[index].slashed:
|
|
||||||
reduce_balance(
|
|
||||||
state.validator_balances[index],
|
|
||||||
2'u64 * inactivity_penalty(
|
|
||||||
state, index, epochs_since_finality) + base_reward(state, index))
|
|
||||||
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
|
|
||||||
inclusion_distance[index])
|
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/0.4.0/specs/core/0_beacon-chain.md#attestation-inclusion
|
# https://github.com/ethereum/eth2.0-specs/blob/0.4.0/specs/core/0_beacon-chain.md#attestation-inclusion
|
||||||
block:
|
block:
|
||||||
@ -903,7 +1084,7 @@ func processEpoch(state: var BeaconState) =
|
|||||||
get_beacon_proposer_index(state, proposer_indexes[v])
|
get_beacon_proposer_index(state, proposer_indexes[v])
|
||||||
|
|
||||||
state.validator_balances[proposer_index] +=
|
state.validator_balances[proposer_index] +=
|
||||||
base_reward(state, v) div ATTESTATION_INCLUSION_REWARD_QUOTIENT
|
get_base_reward(state, v) div ATTESTATION_INCLUSION_REWARD_QUOTIENT
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/0.4.0/specs/core/0_beacon-chain.md#crosslinks-1
|
# https://github.com/ethereum/eth2.0-specs/blob/0.4.0/specs/core/0_beacon-chain.md#crosslinks-1
|
||||||
block:
|
block:
|
||||||
@ -927,47 +1108,17 @@ func processEpoch(state: var BeaconState) =
|
|||||||
for index in crosslink_committee.committee:
|
for index in crosslink_committee.committee:
|
||||||
if index in committee_attesting_validators:
|
if index in committee_attesting_validators:
|
||||||
state.validator_balances[index.int] +=
|
state.validator_balances[index.int] +=
|
||||||
base_reward(state, index) * committee_attesting_balance div
|
get_base_reward(state, index) * committee_attesting_balance div
|
||||||
committee_total_balance
|
committee_total_balance
|
||||||
else:
|
else:
|
||||||
reduce_balance(
|
reduce_balance(
|
||||||
state.validator_balances[index], base_reward(state, index))
|
state.validator_balances[index], get_base_reward(state, index))
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/0.4.0/specs/core/0_beacon-chain.md#ejections
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#ejections
|
||||||
process_ejections(state)
|
process_ejections(state)
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/0.4.0/specs/core/0_beacon-chain.md#validator-registry-and-shuffling-seed-data
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#validator-registry-and-shuffling-seed-data
|
||||||
block:
|
update_registry_and_shuffling_data(state)
|
||||||
state.previous_shuffling_epoch = state.current_shuffling_epoch
|
|
||||||
state.previous_shuffling_start_shard = state.current_shuffling_start_shard
|
|
||||||
state.previous_shuffling_seed = state.current_shuffling_seed
|
|
||||||
|
|
||||||
if state.finalized_epoch > state.validator_registry_update_epoch and
|
|
||||||
allIt(
|
|
||||||
0 ..< get_current_epoch_committee_count(state).int,
|
|
||||||
state.latest_crosslinks[
|
|
||||||
(state.current_shuffling_start_shard + it.uint64) mod
|
|
||||||
SHARD_COUNT].epoch > state.validator_registry_update_epoch):
|
|
||||||
|
|
||||||
# update the validator registry and associated fields by running
|
|
||||||
update_validator_registry(state)
|
|
||||||
|
|
||||||
# and perform the following updates
|
|
||||||
state.current_shuffling_epoch = next_epoch
|
|
||||||
state.current_shuffling_start_shard =
|
|
||||||
(state.current_shuffling_start_shard +
|
|
||||||
get_current_epoch_committee_count(state)) mod SHARD_COUNT
|
|
||||||
state.current_shuffling_seed = generate_seed(
|
|
||||||
state, state.current_shuffling_epoch)
|
|
||||||
else:
|
|
||||||
# If a validator registry change does NOT happen
|
|
||||||
let epochs_since_last_registry_update =
|
|
||||||
current_epoch - state.validator_registry_update_epoch
|
|
||||||
if epochs_since_last_registry_update > 1'u64 and
|
|
||||||
is_power_of_2(epochs_since_last_registry_update):
|
|
||||||
state.current_shuffling_epoch = next_epoch
|
|
||||||
state.current_shuffling_seed = generate_seed(state, state.current_shuffling_epoch)
|
|
||||||
# /Note/ that state.current_shuffling_start_shard is left unchanged
|
|
||||||
|
|
||||||
# Not in spec.
|
# Not in spec.
|
||||||
updateShufflingCache(state)
|
updateShufflingCache(state)
|
||||||
@ -977,22 +1128,8 @@ func processEpoch(state: var BeaconState) =
|
|||||||
process_slashings(state)
|
process_slashings(state)
|
||||||
process_exit_queue(state)
|
process_exit_queue(state)
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/0.4.0/specs/core/0_beacon-chain.md#final-updates
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#final-updates
|
||||||
block:
|
finish_epoch_update(state)
|
||||||
state.latest_active_index_roots[
|
|
||||||
(next_epoch + ACTIVATION_EXIT_DELAY) mod
|
|
||||||
LATEST_ACTIVE_INDEX_ROOTS_LENGTH] =
|
|
||||||
Eth2Digest(data: hash_tree_root(get_active_validator_indices(
|
|
||||||
state.validator_registry, next_epoch + ACTIVATION_EXIT_DELAY)))
|
|
||||||
state.latest_slashed_balances[(next_epoch mod
|
|
||||||
LATEST_SLASHED_EXIT_LENGTH).int] =
|
|
||||||
state.latest_slashed_balances[
|
|
||||||
(current_epoch mod LATEST_SLASHED_EXIT_LENGTH).int]
|
|
||||||
state.latest_randao_mixes[next_epoch mod LATEST_RANDAO_MIXES_LENGTH] =
|
|
||||||
get_randao_mix(state, current_epoch)
|
|
||||||
state.latest_attestations.keepItIf(
|
|
||||||
not (slot_to_epoch(it.data.slot) < current_epoch)
|
|
||||||
)
|
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#state-root-verification
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#state-root-verification
|
||||||
proc verifyStateRoot(state: BeaconState, blck: BeaconBlock): bool =
|
proc verifyStateRoot(state: BeaconState, blck: BeaconBlock): bool =
|
||||||
@ -1060,6 +1197,7 @@ proc updateState*(
|
|||||||
state = old_state
|
state = old_state
|
||||||
false
|
false
|
||||||
|
|
||||||
|
# 0.4.0 to flag that the slot/epoch processing order flips in 0.5
|
||||||
proc advanceState*(
|
proc advanceState*(
|
||||||
state: var BeaconState, previous_block_root: Eth2Digest) =
|
state: var BeaconState, previous_block_root: Eth2Digest) =
|
||||||
## Sometimes we need to update the state even though we don't have a block at
|
## Sometimes we need to update the state even though we don't have a block at
|
||||||
|
@ -125,7 +125,6 @@ proc addBlock*(
|
|||||||
# some book-keeping values
|
# some book-keeping values
|
||||||
signed_data = Proposal(
|
signed_data = Proposal(
|
||||||
slot: new_block.slot.uint64,
|
slot: new_block.slot.uint64,
|
||||||
shard: BEACON_CHAIN_SHARD_NUMBER,
|
|
||||||
block_root: Eth2Digest(data: signed_root(new_block)),
|
block_root: Eth2Digest(data: signed_root(new_block)),
|
||||||
signature: ValidatorSig(),
|
signature: ValidatorSig(),
|
||||||
)
|
)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user