Merge pull request #60 from status-im/SUS

More spec updates
This commit is contained in:
Dustin Brody 2019-01-17 08:17:54 +00:00 committed by GitHub
commit 187c832b72
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 128 additions and 73 deletions

View File

@ -51,11 +51,12 @@ func process_deposit(state: var BeaconState,
proof_of_possession: ValidatorSig,
withdrawal_credentials: Eth2Digest,
randao_commitment: Eth2Digest,
flags: UpdateFlags): Uint24 =
custody_commitment: Eth2Digest) : Uint24 =
## Process a deposit from Ethereum 1.0.
if skipValidation notin flags:
# TODO return error
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,
randao_commitment)
@ -65,13 +66,20 @@ func process_deposit(state: var BeaconState,
if pubkey notin validator_pubkeys:
# Add new validator
let validator = ValidatorRecord(
status: UNUSED,
pubkey: pubkey,
withdrawal_credentials: withdrawal_credentials,
randao_commitment: randao_commitment,
randao_layers: 0,
status: PENDING_ACTIVATION,
latest_status_change_slot: state.slot,
exit_count: 0
activation_slot: FAR_FUTURE_SLOT,
exit_slot: FAR_FUTURE_SLOT,
withdrawal_slot: FAR_FUTURE_SLOT,
penalized_slot: FAR_FUTURE_SLOT,
exit_count: 0,
status_flags: 0,
custody_commitment: custody_commitment,
latest_custody_reseed_slot: GENESIS_SLOT,
penultimate_custody_reseed_slot: GENESIS_SLOT
)
let index = min_empty_validator_index(
@ -85,7 +93,7 @@ func process_deposit(state: var BeaconState,
state.validator_balances[index.get()] = deposit
index.get().Uint24
else:
# Increase balance by deposit
# 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 ==
@ -95,20 +103,19 @@ func process_deposit(state: var BeaconState,
index.Uint24
func activate_validator(state: var BeaconState,
index: Uint24) =
index: Uint24,
genesis: bool) =
## Activate the validator with the given ``index``.
let validator = addr state.validator_registry[index]
if validator.status != PENDING_ACTIVATION:
return
validator.status = ACTIVE
validator.latest_status_change_slot = state.slot
validator.activation_slot = if genesis: GENESIS_SLOT else: state.slot + ENTRY_EXIT_DELAY
state.validator_registry_delta_chain_tip =
get_new_validator_registry_delta_chain_tip(
state.validator_registry_delta_chain_tip,
index,
validator.pubkey,
validator.activation_slot,
ACTIVATION,
)
@ -122,9 +129,9 @@ func initiate_validator_exit(state: var BeaconState,
validator.status = ACTIVE_PENDING_EXIT
validator.latest_status_change_slot = state.slot
func exit_validator(state: var BeaconState,
index: Uint24,
new_status: ValidatorStatusCodes) =
func exit_validator*(state: var BeaconState,
index: Uint24,
new_status: ValidatorStatusCodes) =
## Exit the validator with the given ``index``.
let
@ -162,6 +169,7 @@ func exit_validator(state: var BeaconState,
state.validator_registry_delta_chain_tip,
index,
validator.pubkey,
validator.exit_slot,
ValidatorSetDeltaFlags.EXIT
)
@ -172,17 +180,43 @@ func exit_validator(state: var BeaconState,
committee.delete(i)
break
func update_validator_status*(state: var BeaconState,
index: Uint24,
new_status: ValidatorStatusCodes) =
## Update the validator status with the given ``index`` to ``new_status``.
## Handle other general accounting related to this status update.
if new_status == ACTIVE:
activate_validator(state, index)
if new_status == ACTIVE_PENDING_EXIT:
initiate_validator_exit(state, index)
if new_status in [EXITED_WITH_PENALTY, EXITED_WITHOUT_PENALTY]:
exit_validator(state, index, new_status)
func process_penalties_and_exits_eligible(state: BeaconState, index: int): bool =
let validator = state.validator_registry[index]
if validator.penalized_slot <= state.slot:
# strangely uppercase variable-ish name
let PENALIZED_WITHDRAWAL_TIME = (LATEST_PENALIZED_EXIT_LENGTH * EPOCH_LENGTH div 2).uint64
return state.slot >= validator.penalized_slot + PENALIZED_WITHDRAWAL_TIME
else:
return state.slot >= validator.exit_slot + MIN_VALIDATOR_WITHDRAWAL_TIME
func process_penalties_and_exits(state: var BeaconState) =
# The active validators
let active_validator_indices = get_active_validator_indices(state.validator_registry, state.slot)
# The total effective balance of active validators
var total_balance : uint64 = 0
for i in active_validator_indices:
total_balance += get_effective_balance(state, i)
for index, validator in state.validator_registry:
if (state.slot div EPOCH_LENGTH) == (validator.penalized_slot div EPOCH_LENGTH) + LATEST_PENALIZED_EXIT_LENGTH div 2:
let
e = ((state.slot div EPOCH_LENGTH) mod LATEST_PENALIZED_EXIT_LENGTH).int
total_at_start = state.latest_penalized_exit_balances[(e + 1) mod LATEST_PENALIZED_EXIT_LENGTH]
total_at_end = state.latest_penalized_exit_balances[e]
total_penalties = total_at_end - total_at_start
penalty = get_effective_balance(state, index.Uint24) * min(total_penalties * 3, total_balance) div total_balance
state.validator_balances[index] -= penalty
## 'state' is of type <var BeaconState> which cannot be captured as it
## would violate memory safety, when using nested function approach in
## spec directly. That said, the spec approach evidently is not meant,
## based on its abundant and pointless memory copies, for production.
var eligible_indices : seq[Uint24] = @[]
for i in 0 ..< len(state.validator_registry):
eligible_indices.add i.Uint24
## TODO figure out that memory safety issue, which would come up again when
## sorting, and then actually do withdrawals
func get_initial_beacon_state*(
initial_validator_deposits: openArray[Deposit],
@ -219,16 +253,25 @@ func get_initial_beacon_state*(
validator_registry_exit_count: 0,
validator_registry_delta_chain_tip: ZERO_HASH,
# Randomness and committees
previous_epoch_start_shard: GENESIS_START_SHARD,
current_epoch_start_shard: GENESIS_START_SHARD,
previous_epoch_calculation_slot: GENESIS_SLOT,
current_epoch_calculation_slot: GENESIS_SLOT,
previous_epoch_randao_mix: ZERO_HASH,
current_epoch_randao_mix: ZERO_HASH,
# Finality
previous_justified_slot: GENESIS_SLOT,
justified_slot: GENESIS_SLOT,
justification_bitfield: 0,
finalized_slot: GENESIS_SLOT,
# PoW receipt root
# Deposit root
latest_deposit_root: latest_deposit_root,
)
# handle initial deposits and activations
# Process initial deposits
for deposit in initial_validator_deposits:
let validator_index = process_deposit(
state,
@ -237,10 +280,17 @@ func get_initial_beacon_state*(
deposit.deposit_data.deposit_input.proof_of_possession,
deposit.deposit_data.deposit_input.withdrawal_credentials,
deposit.deposit_data.deposit_input.randao_commitment,
flags
deposit.deposit_data.deposit_input.custody_commitment,
)
if state.validator_balances[validator_index] >= MAX_DEPOSIT:
update_validator_status(state, validator_index, ACTIVE)
activate_validator(state, validator_index, true)
# Process initial activations
#for validator_index in 0 ..< state.validator_registry.len:
# let vi = validator_index.Uint24
# if get_effective_balance(state, vi) > MAX_DEPOSIT * GWEI_PER_ETH:
# activate_validator(state, vi, true)
# set initial committee shuffling
let
@ -323,7 +373,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.status == PENDING_ACTIVATION and
if validator.activation_slot > state.slot + ENTRY_EXIT_DELAY and
state.validator_balances[index] >= MAX_DEPOSIT * GWEI_PER_ETH:
# Check the balance churn would be within the allowance
balance_churn += get_effective_balance(state, index.Uint24)
@ -331,37 +381,20 @@ func update_validator_registry*(state: var BeaconState) =
break
# Activate validator
update_validator_status(state, index.Uint24, ACTIVE)
activate_validator(state, index.Uint24, false)
# Exit validators within the allowable balance churn
balance_churn = 0
for index, validator in state.validator_registry:
if validator.status == ACTIVE_PENDING_EXIT:
if (validator.exit_slot > state.slot + ENTRY_EXIT_DELAY) and
((validator.status_flags and INITIATED_EXIT) == INITIATED_EXIT):
# Check the balance churn would be within the allowance
balance_churn += get_effective_balance(state, index.Uint24)
if balance_churn > max_balance_churn:
break
# Exit validator
update_validator_status(state, index.Uint24, EXITED_WITHOUT_PENALTY)
# Calculate the total ETH that has been penalized in the last ~2-3 withdrawal periods
let
period_index = (state.slot div COLLECTIVE_PENALTY_CALCULATION_PERIOD).int
total_penalties = (
(state.latest_penalized_exit_balances[period_index]) +
(if period_index >= 1:
state.latest_penalized_exit_balances[period_index - 1] else: 0) +
(if period_index >= 2:
state.latest_penalized_exit_balances[period_index - 2] else: 0)
)
# Calculate penalties for slashed validators
for index, validator in state.validator_registry:
if validator.status == EXITED_WITH_PENALTY:
state.validator_balances[index] -=
get_effective_balance(state, index.Uint24) *
min(total_penalties * 3, total_balance) div total_balance
exit_validator(state, index.Uint24, EXITED_WITHOUT_PENALTY)
# Perform additional updates
state.previous_epoch_calculation_slot = state.current_epoch_calculation_slot
@ -373,7 +406,7 @@ func update_validator_registry*(state: var BeaconState) =
# TODO "If a validator registry update does not happen do the following: ..."
#process_penalties_and_exits(state)
process_penalties_and_exits(state)
proc checkAttestation*(
state: BeaconState, attestation: Attestation, flags: UpdateFlags): bool =

View File

@ -78,7 +78,7 @@ const
LATEST_BLOCK_ROOTS_LENGTH* = 2'u64^13
LATEST_RANDAO_MIXES_LENGTH* = 2'u64^13
LATEST_PENALIZED_EXIT_LENGTH* = 8192 # epochs
MAX_WITHDRAWALS_PER_EPOCHS* = 4 # withdrawals
MAX_WITHDRAWALS_PER_EPOCH* = 4 # withdrawals
# Deposit contract
DEPOSIT_CONTRACT_TREE_DEPTH* = 2^5
@ -132,6 +132,9 @@ const
COLLECTIVE_PENALTY_CALCULATION_PERIOD* = 2'u64^20 ##\
## slots (~73 days)
ENTRY_EXIT_DELAY* = 256 ##\
## slots (~25.6 minutes)
ZERO_BALANCE_VALIDATOR_TTL* = 2'u64^22 ##\
## slots (~291 days)
@ -390,10 +393,15 @@ type
exit_count*: uint64 ##\
## Exit counter when validator exited (or 0)
status_flags*: uint64
custody_commitment*: Eth2Digest
last_poc_change_slot*: uint64
second_last_poc_change_slot*: uint64
latest_custody_reseed_slot*: uint64 ##\
## Slot of latest custody reseed
penultimate_custody_reseed_slot*: uint64 ##\
## Slot of second-latest custody reseed
CrosslinkRecord* = object
slot*: uint64
@ -438,10 +446,11 @@ type
latest_registry_delta_root*: Eth2Digest
validator_index*: Uint24
pubkey*: ValidatorPubKey
slot*: uint64
flag*: ValidatorSetDeltaFlags
ValidatorStatusCodes* {.pure.} = enum
PENDING_ACTIVATION = 0
UNUSED = 0
ACTIVE = 1
ACTIVE_PENDING_EXIT = 2
EXITED_WITHOUT_PENALTY = 3

View File

@ -68,6 +68,7 @@ func get_new_validator_registry_delta_chain_tip*(
current_validator_registry_delta_chain_tip: Eth2Digest,
index: Uint24,
pubkey: ValidatorPubKey,
slot: uint64,
flag: ValidatorSetDeltaFlags): Eth2Digest =
## Compute the next hash in the validator registry delta hash chain.
@ -75,5 +76,6 @@ func get_new_validator_registry_delta_chain_tip*(
latest_registry_delta_root: current_validator_registry_delta_chain_tip,
validator_index: index,
pubkey: pubkey,
slot: slot,
flag: flag
))

View File

@ -101,7 +101,7 @@ func processRandao(
return true
func processDepositRoot(state: var BeaconState, blck: BeaconBlock) =
## https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#pow-receipt-root
## https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#deposit-root
for x in state.deposit_roots.mitems():
if blck.deposit_root == x.deposit_root:
@ -113,6 +113,18 @@ func processDepositRoot(state: var BeaconState, blck: BeaconBlock) =
vote_count: 1
)
func penalizeValidator(state: var BeaconState, index: Uint24) =
exit_validator(state, index, EXITED_WITH_PENALTY)
var validator = state.validator_registry[index]
#state.latest_penalized_exit_balances[(state.slot div EPOCH_LENGTH) mod LATEST_PENALIZED_EXIT_LENGTH] += get_effective_balance(state, index.Uint24)
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
state.validator_balances[index] -= whistleblower_reward
validator.penalized_slot = state.slot
proc processProposerSlashings(
state: var BeaconState, blck: BeaconBlock, flags: UpdateFlags): bool =
## https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#proposer-slashings-1
@ -159,12 +171,11 @@ proc processProposerSlashings(
warn("PropSlash: block root mismatch")
return false
if not (proposer.status != EXITED_WITH_PENALTY):
warn("PropSlash: wrong status")
if not (proposer.penalized_slot > state.slot):
warn("PropSlash: penalized slot")
return false
update_validator_status(
state, proposer_slashing.proposer_index, EXITED_WITH_PENALTY)
penalizeValidator(state, proposer_slashing.proposer_index)
return true
@ -226,7 +237,7 @@ proc processCasperSlashings(state: var BeaconState, blck: BeaconBlock): bool =
for i in intersection:
if state.validator_registry[i].status != EXITED_WITH_PENALTY:
update_validator_status(state, i, EXITED_WITH_PENALTY)
exit_validator(state, i, EXITED_WITH_PENALTY)
return true
@ -264,6 +275,10 @@ proc processDeposits(state: var BeaconState, blck: BeaconBlock): bool =
# 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
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
@ -281,20 +296,16 @@ proc processExits(
warn("Exit: invalid signature")
return false
if not (validator.status == ACTIVE):
warn("Exit: validator not active")
if not (validator.exit_slot > state.slot + ENTRY_EXIT_DELAY):
warn("Exit: exit/entry too close")
return false
if not (state.slot >= exit.slot):
warn("Exit: bad slot")
return false
if not (state.slot >=
validator.latest_status_change_slot +
SHARD_PERSISTENT_COMMITTEE_CHANGE_PERIOD):
warn("Exit: not within committee change period")
update_validator_status(state, exit.validator_index, ACTIVE_PENDING_EXIT)
exit_validator(state, exit.validator_index, ACTIVE_PENDING_EXIT)
initiate_validator_exit(state, exit.validator_index)
return true
@ -307,7 +318,7 @@ proc process_ejections(state: var BeaconState) =
for index, validator in state.validator_registry:
if is_active_validator(validator) and
state.validator_balances[index] < EJECTION_BALANCE:
update_validator_status(state, index.Uint24, EXITED_WITHOUT_PENALTY)
exit_validator(state, index.Uint24, EXITED_WITHOUT_PENALTY)
func processSlot(state: var BeaconState, previous_block_root: Eth2Digest) =
## Time on the beacon chain moves in slots. Every time we make it to a new
@ -563,7 +574,7 @@ func processEpoch(state: var BeaconState) =
func total_balance_sac(shard_committee: ShardCommittee): uint64 =
sum_effective_balances(statePtr[], shard_committee.committee)
block: # Receipt roots
block: # Deposit roots
if state.slot mod POW_RECEIPT_ROOT_VOTING_PERIOD == 0:
for x in state.deposit_roots:
if x.vote_count * 2 >= POW_RECEIPT_ROOT_VOTING_PERIOD: