spec updates (#34)

* spec updates

* make several constants uint64 to help minimize casting
* document data type woes - will have to revisit these
* change comment style on fields and constants to make room for better
comments
* add BLSVerify and BLSAddPbkeys facades to insulate spec code from
milagro
* fix proof of possession type
* drop explicitly ordered container fields from ssz - there's an issue
open to sort this out before committing it to the spec
This commit is contained in:
Jacek Sieka 2018-12-11 11:55:45 -06:00 committed by GitHub
parent d18ca32ff5
commit 7ea51d5b0b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 564 additions and 392 deletions

View File

@ -99,7 +99,7 @@ proc getAttachedValidator(node: BeaconNode, idx: int): AttachedValidator =
proc makeAttestation(node: BeaconNode,
validator: AttachedValidator) {.async.} =
var attestation: Attestation
var attestation: AttestationCandidate
attestation.validator = validator.idx
# TODO: Populate attestation.data
@ -126,21 +126,22 @@ proc proposeBlock(node: BeaconNode,
node.mainchainMonitor.getBeaconBlockRef()
for a in node.attestations.each(firstSlot = node.headBlock.slot.int + 1,
lastSlot = slot - MIN_ATTESTATION_INCLUSION_DELAY):
lastSlot = slot - MIN_ATTESTATION_INCLUSION_DELAY.int):
# TODO: this is not quite right,
# the attestations from individual validators have to be merged.
# proposal.attestations.add a
discard
for r in node.mainchainMonitor.getValidatorActions(
node.headBlock.candidate_pow_receipt_root,
proposal.candidate_pow_receipt_root):
proposal.specials.add r
# TODO update after spec change removed specials
# for r in node.mainchainMonitor.getValidatorActions(
# node.headBlock.candidate_pow_receipt_root,
# proposal.candidate_pow_receipt_root):
# proposal.specials.add r
var signedData: ProposalSignedData
# TODO: populate the signed data
proposal.proposer_signature = await validator.signBlockProposal(signedData)
proposal.signature = await validator.signBlockProposal(signedData)
await node.network.broadcast(topicBeaconBlocks, proposal)
proc scheduleCycleActions(node: BeaconNode) =
@ -153,7 +154,7 @@ proc scheduleCycleActions(node: BeaconNode) =
let
slot = cycleStart + i
proposerIdx = get_beacon_proposer_index(node.beaconState, slot.uint64)
attachedValidator = node.getAttachedValidator(proposerIdx.int)
attachedValidator = node.getAttachedValidator(proposerIdx)
if attachedValidator != nil:
# TODO:

View File

@ -19,6 +19,6 @@ type
## Eth1 validator registration contract output
pubkey*: ValidatorPubKey
deposit_size*: uint64
proof_of_possession*: seq[byte]
proof_of_possession*: ValidatorSig
withdrawal_credentials*: Eth2Digest
randao_commitment*: Eth2Digest

View File

@ -3,17 +3,17 @@ import
spec/[datatypes, crypto]
type
Attestation* = object
AttestationCandidate* = object
validator*: int
data*: AttestationData
signature*: ValidatorSig
AttestationPool* = object
attestations: Deque[seq[Attestation]]
attestations: Deque[seq[AttestationCandidate]]
startingSlot: int
proc init*(T: type AttestationPool, startingSlot: int): T =
result.attestationsPerSlot = initDeque[seq[Attestation]]()
result.attestationsPerSlot = initDeque[seq[AttestationCandidate]]()
result.startingSlot = startingSlot
proc setLen*[T](d: var Deque[T], len: int) =
@ -27,7 +27,7 @@ proc setLen*[T](d: var Deque[T], len: int) =
d.shrink(fromLast = delta)
proc add*(pool: var AttestationPool,
attestation: Attestation,
attestation: AttestationCandidate,
beaconState: BeaconState) =
# The caller of this function is responsible for ensuring that
# the attestations will be given in a strictly slot increasing order:
@ -40,7 +40,7 @@ proc add*(pool: var AttestationPool,
pool.attestations[slotIdxInPool].add attestation
iterator each*(pool: AttestationPool,
firstSlot, lastSlot: int): Attestation =
firstSlot, lastSlot: int): AttestationCandidate =
## Both indices are treated inclusively
## TODO: this should return a lent value
doAssert firstSlot <= lastSlot

View File

@ -21,9 +21,10 @@ proc getBeaconBlockRef*(m: MainchainMonitor): Eth2Digest =
# This should be a simple accessor for the reference kept above
discard
iterator getValidatorActions*(m: MainchainMonitor,
fromBlock, toBlock: Eth2Digest): SpecialRecord =
# It's probably better if this doesn't return a SpecialRecord, but
# rather a more readable description of the change that can be packed
# in a SpecialRecord by the client of the API.
discard
# TODO update after spec change removed Specials
# iterator getValidatorActions*(m: MainchainMonitor,
# fromBlock, toBlock: Eth2Digest): SpecialRecord =
# # It's probably better if this doesn't return a SpecialRecord, but
# # rather a more readable description of the change that can be packed
# # in a SpecialRecord by the client of the API.
# discard

View File

@ -7,8 +7,8 @@
import
math, sequtils,
../extras,
./datatypes, ./digest, ./helpers, ./validator
../extras, ../ssz,
./crypto, ./datatypes, ./digest, ./helpers, ./validator
func on_startup*(initial_validator_entries: openArray[InitialValidator],
genesis_time: uint64,
@ -96,14 +96,12 @@ func on_startup*(initial_validator_entries: openArray[InitialValidator],
)
func get_block_hash*(state: BeaconState,
current_block: BeaconBlock,
slot: uint64): Eth2Digest =
let earliest_slot_in_array =
current_block.slot.int - state.latest_block_hashes.len
assert earliest_slot_in_array <= slot.int
assert slot < current_block.slot
state.latest_block_hashes[slot.int - earliest_slot_in_array]
state.slot - len(state.latest_block_hashes).uint64
assert earliest_slot_in_array <= slot
assert slot < state.slot
state.latest_block_hashes[(slot - earliest_slot_in_array).int]
func append_to_recent_block_hashes*(old_block_hashes: seq[Eth2Digest],
parent_slot, current_slot: uint64,
@ -139,15 +137,76 @@ func get_attestation_participants*(state: BeaconState,
result.add(vindex)
return # found the shard, we're done
func change_validators*(state: var BeaconState,
current_slot: uint64) =
## Change validator registry.
func process_ejections*(state: var BeaconState) =
## Iterate through the validator registry
## and eject active validators with balance below ``EJECTION_BALANCE``.
let res = get_changed_validators(
state.validator_registry,
for i, v in state.validator_registry.mpairs():
if is_active_validator(v) and v.balance < EJECTION_BALANCE:
exit_validator(i.Uint24, state, EXITED_WITHOUT_PENALTY)
func update_validator_registry*(state: var BeaconState) =
# Update validator registry.
# Note that this function mutates ``state``.
(state.validator_registry,
state.latest_penalized_exit_balances,
state.validator_registry_delta_chain_tip,
current_slot
)
state.validator_registry = res.validators
state.latest_penalized_exit_balances = res.latest_penalized_exit_balances
state.validator_registry_delta_chain_tip) =
get_updated_validator_registry(
state.validator_registry,
state.latest_penalized_exit_balances,
state.validator_registry_delta_chain_tip,
state.slot
)
func checkAttestation*(state: BeaconState, attestation: Attestation): bool =
## Check that an attestation follows the rules of being included in the state
## at the current slot.
##
## https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#attestations-1
if attestation.data.slot + MIN_ATTESTATION_INCLUSION_DELAY <= state.slot:
return
if attestation.data.slot + EPOCH_LENGTH >= state.slot:
return
let expected_justified_slot =
if attestation.data.slot >= state.slot - (state.slot mod EPOCH_LENGTH):
state.justified_slot
else:
state.previous_justified_slot
if attestation.data.justified_slot != expected_justified_slot:
return
let expected_justified_block_hash =
get_block_hash(state, attestation.data.justified_slot)
if attestation.data.justified_block_hash != expected_justified_block_hash:
return
if state.latest_crosslinks[attestation.data.shard].shard_block_hash notin [
attestation.data.latest_crosslink_hash,
attestation.data.shard_block_hash]:
return
let
participants = get_attestation_participants(
state, attestation.data, attestation.participation_bitfield)
group_public_key = BLSAddPubkeys(mapIt(
participants, state.validator_registry[it].pubkey))
# Verify that aggregate_signature verifies using the group pubkey.
let msg = hashSSZ(attestation.data)
if not BLSVerify(
group_public_key, @msg & @[0'u8], attestation.aggregate_signature,
get_domain(state.fork_data, attestation.data.slot, DOMAIN_ATTESTATION)
):
return
# To be removed in Phase1:
if attestation.data.shard_block_hash != ZERO_HASH:
return
true

View File

@ -20,5 +20,22 @@ type
template hash*(k: ValidatorPubKey|ValidatorPrivKey): Hash =
hash(k.getRaw)
proc pubKey*(pk: ValidatorPrivKey): ValidatorPubKey = fromSigKey(pk)
func pubKey*(pk: ValidatorPrivKey): ValidatorPubKey = fromSigKey(pk)
func BLSAddPubkeys*(keys: openArray[ValidatorPubKey]): ValidatorPubKey =
# name from spec!
var empty = false
for key in keys:
if empty:
result = key
empty = false
else:
result.combine(key)
func BLSVerify*(
pubkey: ValidatorPubKey, msg: openArray[byte], sig: ValidatorSig,
domain: uint64): bool =
# name from spec!
# TODO domain!
sig.verifyMessage(msg, pubkey)

View File

@ -27,86 +27,224 @@ import
intsets, eth_common, math,
./crypto, ./digest
const
SHARD_COUNT* = 1024 # a constant referring to the number of shards
TARGET_COMMITTEE_SIZE* = 2^8 # validators
MAX_ATTESTATIONS_PER_BLOCK* = 2^7 # attestations
MIN_BALANCE* = 2^4 # ETH
MAX_BALANCE_CHURN_QUOTIENT* = 2^5 # ETH
GWEI_PER_ETH* = 10^9 # Gwei/ETH
BEACON_CHAIN_SHARD_NUMBER* = not 0'u64
# TODO Data types:
# Presently, we're reusing the data types from the serialization (uint64) in the
# objects we pass around to the beacon chain logic, thus keeping the two
# similar. This is convenient for keeping up with the specification, but
# will eventually need a more robust approach such that we don't run into
# over- and underflows.
# Some of the open questions are being tracked here:
# https://github.com/ethereum/eth2.0-specs/issues/224
#
# The present approach causes some problems due to how Nim treats unsigned
# integers - here's no high(uint64), arithmetic support is incomplete, there's
# no over/underflow checking available
#
# Eventually, we could also differentiate between user/tainted data and
# internal state that's gone through sanity checks already.
DEPOSIT_CONTRACT_TREE_DEPTH* = 2^5 #
MIN_DEPOSIT* = 2^0 #
MAX_DEPOSIT* = 2^5 #
const
SHARD_COUNT* = 1024 ##\
## Number of shards supported by the network - validators will jump around
## between these shards and provide attestations to their state.
TARGET_COMMITTEE_SIZE* = 2^8 ##\
## Number of validators in the committee attesting to one shard
EJECTION_BALANCE* = 2'u64^4 ##\
## Once the balance of a validator drops below this, it will be ejected from
## the validator pool
MAX_ATTESTATIONS_PER_BLOCK* = 2^7 # attestations
MIN_BALANCE* = 2'u64^4 ##\
## Minimum balance in ETH before a validator is removed from the validator
## pool
MAX_BALANCE_CHURN_QUOTIENT* = 2^5 ##\
## At most `1/MAX_BALANCE_CHURN_QUOTIENT` of the validators can change during
## each validator registry change.
GWEI_PER_ETH* = 10'u64^9 # Gwei/ETH
BEACON_CHAIN_SHARD_NUMBER* = not 0'u64 # 2^64 - 1 in spec
DEPOSIT_CONTRACT_TREE_DEPTH* = 2^5
MIN_DEPOSIT* = 2'u64^0 ##\
## 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
MAX_DEPOSIT* = 2'u64^5 ##\
## Maximum amounth of ETH that can be deposited in one call
# Initial values
INITIAL_FORK_VERSION* = 0 #
INITIAL_SLOT_NUMBER* = 0 #
ZERO_HASH* = Eth2Digest()
INITIAL_FORK_VERSION* = 0'u64
INITIAL_SLOT_NUMBER* = 0'u64
ZERO_HASH* = Eth2Digest()
# Time constants
SLOT_DURATION* = 6 # seconds
MIN_ATTESTATION_INCLUSION_DELAY* = 4 # slots (~25 minutes)
EPOCH_LENGTH* = 64 # slots (~6.4 minutes)
MIN_VALIDATOR_REGISTRY_CHANGE_INTERVAL* = 2^8 # slots (~25.6 minutes)
POW_RECEIPT_ROOT_VOTING_PERIOD* = 2^10 # slots (~1.7 hours)
SHARD_PERSISTENT_COMMITTEE_CHANGE_PERIOD* = 2^17 # slots (~9 days)
COLLECTIVE_PENALTY_CALCULATION_PERIOD* = 2^20 # slots (~2.4 months)
ZERO_BALANCE_VALIDATOR_TTL* = 2^22 # slots (~290 days)
SLOT_DURATION* = 6 ## \
## TODO consistent time unit across projects, similar to C++ chrono?
MIN_ATTESTATION_INCLUSION_DELAY* = 4'u64 ##\
## (24 seconds)
## Number of slots that attestations stay in the attestation
## pool before being added to a block
EPOCH_LENGTH* = 64 ##\
## (~6.4 minutes)
## slots that make up an epoch, at the end of which more heavy
## processing is done
MIN_VALIDATOR_REGISTRY_CHANGE_INTERVAL* = 2'u64^8 ##
## slots (~25.6 minutes)
POW_RECEIPT_ROOT_VOTING_PERIOD* = 2'u64^10 ##\
## slots (~1.7 hours)
SHARD_PERSISTENT_COMMITTEE_CHANGE_PERIOD* = 2'u64^17 ##\
## slots (~9 days)
COLLECTIVE_PENALTY_CALCULATION_PERIOD* = 2'u64^20 ##\
## slots (~73 days)
ZERO_BALANCE_VALIDATOR_TTL* = 2'u64^22 ##\
## slots (~291 days)
# Quotients
BASE_REWARD_QUOTIENT* = 2^11 # per-cycle interest rate assuming all validators are
# participating, assuming total deposits of 1 ETH. It
# corresponds to ~2.57% annual interest assuming 10
# million participating ETH.
WHISTLEBLOWER_REWARD_QUOTIENT* = 2^9 # ?
INCLUDER_REWARD_QUOTIENT* = 2^3 #
INACTIVITY_PENALTY_QUOTIENT* = 2^34 #
BASE_REWARD_QUOTIENT* = 2'u64^11 ##\
## per-cycle interest rate assuming all validators are participating, assuming
## total deposits of 1 ETH. It corresponds to ~2.57% annual interest assuming
## 10 million participating ETH.
WHISTLEBLOWER_REWARD_QUOTIENT* = 2'u64^9
INCLUDER_REWARD_QUOTIENT* = 2'u64^3
INACTIVITY_PENALTY_QUOTIENT* = 2'u64^34
type
Uint24* = range[0'u32 .. 0xFFFFFF'u32] # TODO: wrap-around
BeaconBlock* = object
slot*: uint64 # Slot number
randao_reveal*: Eth2Digest # Proposer RANDAO reveal
candidate_pow_receipt_root*: Eth2Digest # Recent PoW receipt root
ancestor_hashes*: seq[Eth2Digest] # Skip list of previous beacon block hashes
# i'th item is most recent ancestor whose
# slot is a multiple of 2**i for
# i == 0, ..., 31
state_root*: Eth2Digest # State root
attestations*: seq[AttestationRecord] # Attestations
specials*: seq[SpecialRecord] # Specials (e.g. logouts, penalties)
proposer_signature*: ValidatorSig # Proposer signature
# https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#data-structures
ProposerSlashing* = object
proposer_index*: Uint24
proposal_data_1*: ProposalSignedData
proposal_signature_1*: ValidatorSig
proposal_data_2*: ProposalSignedData
proposal_signature_2*: ValidatorSig
AttestationRecord* = object
CasperSlashing* = object
votes_1*: SlashableVoteData
votes_2*: SlashableVoteData
SlashableVoteData* = object
aggregate_signature_poc_0_indices*: seq[Uint24] ##\
## Proof-of-custody indices (0 bits)
aggregate_signature_poc_1_indices*: seq[Uint24] ##\
## Proof-of-custody indices (1 bits)
data*: AttestationData
aggregate_signature*: ValidatorSig
Attestation* = object
data*: AttestationData
participation_bitfield*: seq[byte] # Attester participation bitfield
custody_bitfield*: seq[byte] # Proof of custody bitfield
aggregate_signature*: ValidatorSig # BLS aggregate signature
AttestationData* = object
slot*: uint64 # Slot number
shard*: uint64 # Shard number
beacon_block_hash*: Eth2Digest # Hash of the block we're signing
epoch_boundary_hash*: Eth2Digest # Hash of the ancestor at the cycle boundary
shard_block_hash*: Eth2Digest # Shard block hash being attested to
latest_crosslink_hash*: Eth2Digest # Last crosslink hash
justified_slot*: uint64 # Slot of last justified beacon block
justified_block_hash*: Eth2Digest # Hash of last justified beacon block
slot*: uint64
shard*: uint64
beacon_block_hash*: Eth2Digest ##\
## Hash of the block we're signing
epoch_boundary_hash*: Eth2Digest ##\
## Hash of the ancestor at the cycle boundary
shard_block_hash*: Eth2Digest ##\
## Shard block hash being attested to
latest_crosslink_hash*: Eth2Digest ##\
## Last crosslink hash
justified_slot*: uint64 ##\
## Slot of last justified beacon block
justified_block_hash*: Eth2Digest ##\
## Hash of last justified beacon block
Deposit* = object
merkle_branch*: seq[Eth2Digest] ##\
## Receipt Merkle branch
merkle_tree_index*: uint64
## Deposit data
deposit_data*: DepositData
DepositData* = object
deposit_parameters*: DepositParameters
value*: uint64 ## Value in Gwei
timestamp*: uint64 # Timestamp from deposit contract
DepositParameters* = object
pubkey*: ValidatorPubKey
proof_of_possession*: ValidatorSig ##\
## BLS proof of possession (a BLS signature)
withdrawal_credentials*: Eth2Digest
randao_commitment*: Eth2Digest # Initial RANDAO commitment
Exit* = object
# Minimum slot for processing exit
slot*: uint64
# Index of the exiting validator
validator_index*: uint64
# Validator signature
signature*: ValidatorSig
BeaconBlock* = object
## For each slot, a proposer is chosen from the validator pool to propose
## a new block. Once the block as been proposed, it is transmitted to
## validators that will have a chance to vote on it through attestations.
## Each block collects attestations, or votes, on past blocks, thus a chain
## is formed.
slot*: uint64
ancestor_hashes*: seq[Eth2Digest] ##\
## Skip list of previous beacon block hashes i'th item is most recent
## ancestor whose slot is a multiple of 2**i for i == 0, ..., 31
state_root*: Eth2Digest
randao_reveal*: Eth2Digest ##\
## Proposer RANDAO reveal
candidate_pow_receipt_root*: Eth2Digest
signature*: ValidatorSig ##\
## Proposer signature
body*: BeaconBlockBody
BeaconBlockBody* = object
attestations*: seq[Attestation]
proposer_slashings*: seq[ProposerSlashing]
casper_slashings*: seq[CasperSlashing]
deposits*: seq[Deposit]
exits*: seq[Exit]
ProposalSignedData* = object
slot*: uint64 # Slot number
shard*: uint64 # Shard number (or `BEACON_CHAIN_SHARD_NUMBER` for beacon chain)
block_hash*: Eth2Digest # Block hash
SpecialRecord* = object
kind*: SpecialRecordType # Kind
data*: seq[byte] # Data
slot*: uint64
shard*: uint64 ##\
## Shard number (or `BEACON_CHAIN_SHARD_NUMBER` for beacon chain)
block_hash*: Eth2Digest
BeaconState* = object
slot*: uint64
genesis_time*: uint64
fork_data*: ForkData ##\
## For versioning hard forks
# Validator registry
validator_registry*: seq[ValidatorRecord]
validator_registry_latest_change_slot*: uint64
@ -126,7 +264,7 @@ type
# Finality
previous_justified_slot*: uint64
justified_slot*: uint64
justified_slot_bitfield*: uint64
justification_bitfield*: uint64
finalized_slot*: uint64
latest_crosslinks*: array[SHARD_COUNT, CrosslinkRecord]
@ -140,10 +278,6 @@ type
processed_pow_receipt_root*: Eth2Digest
candidate_pow_receipt_roots*: seq[CandidatePoWReceiptRootRecord]
genesis_time*: uint64
fork_data*: ForkData ##\
## For versioning hard forks
ValidatorRecord* = object
pubkey*: ValidatorPubKey # Public key
withdrawal_credentials*: Eth2Digest # Withdrawal credentials
@ -200,6 +334,12 @@ type
Activation = 0
Exit = 1
SignatureDomain* {.pure.} = enum
DOMAIN_DEPOSIT = 0
DOMAIN_ATTESTATION = 1
DOMAIN_PROPOSAL = 2
DOMAIN_EXIT = 3
# Note:
# We use IntSet from Nim Standard library which are efficient sparse bitsets.
# See: https://nim-lang.org/docs/intsets.html
@ -236,4 +376,3 @@ when true:
proc read*(rlp: var Rlp, T: type ValidatorSig): T {.inline.} =
discard

View File

@ -130,8 +130,17 @@ func get_fork_version*(fork_data: ForkData, slot: uint64): uint64 =
if slot < fork_data.fork_slot: fork_data.pre_fork_version
else: fork_data.post_fork_version
func get_domain*(fork_data: ForkData, slot: uint64, domain_type: uint64): uint64 =
func get_domain*(
fork_data: ForkData, slot: uint64, domain_type: SignatureDomain): uint64 =
# TODO Slot overflow? Or is slot 32 bits for all intents and purposes?
(get_fork_version(fork_data, slot) shl 32) + domain_type
(get_fork_version(fork_data, slot) shl 32) + domain_type.uint32
func is_power_of_2*(v: uint64): bool = (v and (v-1)) == 0
func get_updated_ancestor_hashes*(latest_block: BeaconBlock,
latest_hash: Eth2Digest): seq[Eth2Digest] =
var new_ancestor_hashes = latest_block.ancestor_hashes
for i in 0..<32:
if latest_block.slot mod 2'u64^i == 0:
new_ancestor_hashes[i] = latest_hash
new_ancestor_hashes

View File

@ -12,6 +12,9 @@ import
../ssz,
./crypto, ./datatypes, ./digest, ./helpers
func is_active_validator*(validator: ValidatorRecord): bool =
validator.status in {ACTIVE, ACTIVE_PENDING_EXIT}
func min_empty_validator_index(validators: seq[ValidatorRecord], current_slot: uint64): Option[int] =
for i, v in validators:
if v.balance == 0 and
@ -23,7 +26,7 @@ func get_new_validators*(current_validators: seq[ValidatorRecord],
fork_data: ForkData,
pubkey: ValidatorPubKey,
deposit: uint64,
proof_of_possession: seq[byte],
proof_of_possession: ValidatorSig,
withdrawal_credentials: Eth2Digest,
randao_commitment: Eth2Digest,
status: ValidatorStatusCodes,
@ -32,26 +35,20 @@ func get_new_validators*(current_validators: seq[ValidatorRecord],
# TODO Spec candidate: inefficient API
#
# Check that validator really did register
# let signed_message = signed_message = bytes32(pubkey) + withdrawal_credentials + randao_commitment
# assert BLSVerify(pub=pubkey,
# msg=hash(signed_message),
# sig=proof_of_possession,
# domain=get_domain(
# fork_data,
# current_slot,
# DOMAIN_DEPOSIT
# ))
# TODO fix tests and enable (nightmare)
# let msg = hashSSZ((pubkey, withdrawal_credentials, randao_commitment))
# assert BLSVerify(
# pubkey, msg, proof_of_possession,
# get_domain(fork_data, current_slot, DOMAIN_DEPOSIT))
var new_validators = current_validators
var validators_copy = current_validators
for index, val in new_validators.mpairs():
if val.pubkey == pubkey:
# assert deposit_size >= MIN_TOPUP_SIZE
# assert val.status != WITHDRAWN
# assert val.withdrawal_credentials == withdrawal_credentials
for index, validator in validators_copy.mpairs():
if validator.pubkey == pubkey:
assert validator.withdrawal_credentials == withdrawal_credentials
val.balance.inc(deposit.int)
return (new_validators, index)
validator.balance += deposit
return (validators_copy, index)
# new validator
let
@ -66,18 +63,18 @@ func get_new_validators*(current_validators: seq[ValidatorRecord],
exit_count: 0
)
let index = min_empty_validator_index(new_validators, current_slot)
let index = min_empty_validator_index(validators_copy, current_slot)
if index.isNone:
new_validators.add(rec)
(new_validators, len(new_validators) - 1)
validators_copy.add(rec)
(validators_copy, len(validators_copy) - 1)
else:
new_validators[index.get()] = rec
(new_validators, index.get())
validators_copy[index.get()] = rec
(validators_copy, index.get())
func get_active_validator_indices*(validators: openArray[ValidatorRecord]): seq[Uint24] =
## Select the active validators
for idx, val in validators:
if val.status in {ACTIVE, ACTIVE_PENDING_EXIT}:
if is_active_validator(val):
result.add idx.Uint24
func get_new_shuffling*(seed: Eth2Digest,
@ -109,7 +106,7 @@ func get_new_shuffling*(seed: Eth2Digest,
var committees = newSeq[ShardCommittee](shard_indices.len)
for shard_position, indices in shard_indices:
committees[shard_position].shard =
uint64(shard_id_start + shard_position.uint64) mod SHARD_COUNT.uint64
shard_id_start + shard_position.uint64 mod SHARD_COUNT.uint64
committees[shard_position].committee = indices
committees[shard_position].total_validator_count =
len(active_validator_indices).uint64
@ -127,24 +124,24 @@ func get_new_validator_registry_delta_chain_tip(
h.update hashSSZ(current_validator_registry_delta_chain_tip)
h.update hashSSZ(flag.uint8)
h.update hashSSZ(index)
# TODO h.update hashSSZ(pubkey)
h.update hashSSZ(pubkey)
func get_effective_balance*(validator: ValidatorRecord): uint64 =
min(validator.balance, MAX_DEPOSIT.uint64)
min(validator.balance, MAX_DEPOSIT)
func exit_validator*(index: Uint24,
state: var BeaconState,
penalize: bool,
current_slot: uint64) =
new_status: ValidatorStatusCodes) =
## Remove the validator with the given `index` from `state`.
## Note that this function mutates `state`.
state.validator_registry_exit_count.inc()
state.validator_registry_exit_count += 1
var
validator = state.validator_registry[index]
validator = addr state.validator_registry[index]
validator.latest_status_change_slot = current_slot
validator.status = new_status
validator.latest_status_change_slot = state.slot
validator.exit_count = state.validator_registry_exit_count
# Remove validator from persistent committees
@ -154,34 +151,30 @@ func exit_validator*(index: Uint24,
committee.delete(i)
break
if penalize:
validator.status = EXITED_WITH_PENALTY
if new_status == EXITED_WITH_PENALTY:
state.latest_penalized_exit_balances[
(current_slot div COLLECTIVE_PENALTY_CALCULATION_PERIOD.uint64).int].inc(
get_effective_balance(validator).int)
(state.slot div COLLECTIVE_PENALTY_CALCULATION_PERIOD).int] +=
get_effective_balance(validator[])
var
whistleblower =
state.validator_registry[get_beacon_proposer_index(state, current_slot).int]
whistleblower = addr state.validator_registry[
get_beacon_proposer_index(state, state.slot)]
whistleblower_reward =
validator.balance div WHISTLEBLOWER_REWARD_QUOTIENT.uint64
whistleblower.balance.inc(whistleblower_reward.int)
validator.balance.dec(whistleblower_reward.int)
else:
validator.status = ACTIVE_PENDING_EXIT
validator.balance div WHISTLEBLOWER_REWARD_QUOTIENT
whistleblower.balance += whistleblower_reward
validator.balance -= whistleblower_reward
state.validator_registry_delta_chain_tip =
get_new_validator_registry_delta_chain_tip(
state.validator_registry_delta_chain_tip,
index,
validator.pubkey,
EXIT,
)
state.validator_registry_delta_chain_tip, index, validator.pubkey,
ValidatorSetDeltaFlags.EXIT)
func get_changed_validators*(validators: seq[ValidatorRecord],
latest_penalized_exit_balances: seq[uint64],
validator_registry_delta_chain_tip: Eth2Digest,
current_slot: uint64):
func get_updated_validator_registry*(
validator_registry: seq[ValidatorRecord],
latest_penalized_exit_balances: seq[uint64],
validator_registry_delta_chain_tip: Eth2Digest,
current_slot: uint64):
tuple[
validators: seq[ValidatorRecord],
latest_penalized_exit_balances: seq[uint64],
@ -190,77 +183,85 @@ func get_changed_validators*(validators: seq[ValidatorRecord],
## `validator_registry_delta_chain_tip`.
# TODO inefficient
var validators = validators
var
validator_registry = validator_registry
latest_penalized_exit_balances = latest_penalized_exit_balances
# The active validators
let active_validator_indices = get_active_validator_indices(validators)
let active_validator_indices =
get_active_validator_indices(validator_registry)
# The total effective balance of active validators
let total_balance = sum(mapIt(
active_validator_indices, get_effective_balance(validators[it])))
active_validator_indices, get_effective_balance(validator_registry[it])))
# The maximum balance churn in Gwei (for deposits and exits separately)
let max_balance_churn = max(
(MAX_DEPOSIT * GWEI_PER_ETH).uint64,
MAX_DEPOSIT * GWEI_PER_ETH,
total_balance div (2 * MAX_BALANCE_CHURN_QUOTIENT)
)
# Activate validators within the allowable balance churn
var balance_churn = 0'u64
var validator_registry_delta_chain_tip = validator_registry_delta_chain_tip
for i in 0..<len(validators):
if validators[i].status == PENDING_ACTIVATION and
validators[i].balance >= MAX_DEPOSIT.uint64:
for i in 0..<len(validator_registry):
if validator_registry[i].status == PENDING_ACTIVATION and
validator_registry[i].balance >= MAX_DEPOSIT:
# Check the balance churn would be within the allowance
balance_churn.inc(get_effective_balance(validators[i]).int)
balance_churn += get_effective_balance(validator_registry[i])
if balance_churn > max_balance_churn:
break
# Activate validator
validators[i].status = ACTIVE
validators[i].latest_status_change_slot = current_slot
validator_registry[i].status = ACTIVE
validator_registry[i].latest_status_change_slot = current_slot
validator_registry_delta_chain_tip =
get_new_validator_registry_delta_chain_tip(
validator_registry_delta_chain_tip,
i.Uint24,
validators[i].pubkey,
validator_registry[i].pubkey,
ACTIVATION,
)
# Exit validators within the allowable balance churn
balance_churn = 0
for i in 0..<len(validators):
if validators[i].status == ACTIVE_PENDING_EXIT:
for i in 0..<len(validator_registry):
if validator_registry[i].status == ACTIVE_PENDING_EXIT:
# Check the balance churn would be within the allowance
balance_churn.inc(get_effective_balance(validators[i]).int)
balance_churn += get_effective_balance(validator_registry[i])
if balance_churn > max_balance_churn:
break
# Exit validator
validators[i].status = EXITED_WITHOUT_PENALTY
validators[i].latest_status_change_slot = current_slot
validator_registry[i].status = EXITED_WITHOUT_PENALTY
validator_registry[i].latest_status_change_slot = current_slot
validator_registry_delta_chain_tip =
get_new_validator_registry_delta_chain_tip(
validator_registry_delta_chain_tip,
i.Uint24,
validators[i].pubkey,
EXIT,
validator_registry[i].pubkey,
ValidatorSetDeltaFlags.EXIT,
)
# Calculate the total ETH that has been penalized in the last ~2-3 withdrawal periods
let period_index = current_slot.int div COLLECTIVE_PENALTY_CALCULATION_PERIOD
# Calculate the total ETH that has been penalized in the last ~2-3 withdrawal
# periods
let period_index =
(current_slot div COLLECTIVE_PENALTY_CALCULATION_PERIOD).int
let total_penalties = (
(latest_penalized_exit_balances[period_index]) +
(if period_index >= 1: latest_penalized_exit_balances[period_index - 1] else: 0) +
(if period_index >= 2: latest_penalized_exit_balances[period_index - 2] else: 0)
(if period_index >= 1:
latest_penalized_exit_balances[period_index - 1] else: 0) +
(if period_index >= 2:
latest_penalized_exit_balances[period_index - 2] else: 0)
)
# Calculate penalties for slashed validators
func to_penalize(v: ValidatorRecord): bool =
v.status == EXITED_WITH_PENALTY
var validators_to_penalize = filter(validators, to_penalize)
for v in validators_to_penalize.mitems():
v.balance.dec(
for v in validator_registry.mitems():
if not to_penalize(v): continue
v.balance -=
(get_effective_balance(v) * min(total_penalties * 3, total_balance) div
total_balance).int)
total_balance)
(validators, latest_penalized_exit_balances, validator_registry_delta_chain_tip)
(validator_registry, latest_penalized_exit_balances,
validator_registry_delta_chain_tip)

View File

@ -209,6 +209,7 @@ func hashSSZ*[T: not enum](x: T): array[32, byte] =
else:
## Containers have their fields recursively hashed, concatenated and hashed
# TODO could probaby compile-time-macro-sort fields...
# TODO or.. https://github.com/ethereum/eth2.0-specs/issues/275
var fields: seq[tuple[name: string, value: seq[byte]]]
for name, field in x.fieldPairs:
fields.add (name, @(hashSSZ(field)))
@ -227,36 +228,17 @@ func hashSSZ*(x: enum): array[32, byte] =
withHash:
h.update [uint8 x]
func hashSSZ*(x: ValidatorSig): array[32, byte] =
func hashSSZ*(x: ValidatorPubKey): array[32, byte] =
## TODO - Warning ⚠️: not part of the spec
## as of https://github.com/ethereum/beacon_chain/pull/133/files
## This is a "stub" needed for BeaconBlock hashing
x.getRaw().hash()
func hashSSZ*(x: AttestationRecord): array[32, byte] =
func hashSSZ*(x: ValidatorSig): array[32, byte] =
## TODO - Warning ⚠️: not part of the spec
## as of https://github.com/ethereum/beacon_chain/pull/133/files
## This is a "stub" needed for BeaconBlock hashing
withHash:
# h.update hashSSZ(x.data) # TODO this is now a sub-object of its own
# h.update hashSSZ(attester_bitfield) # TODO - the bitfield as a specific serialisation format
# h.update hashSSZ(x.poc_bitfield) # TODO - same serialization format
h.update hashSSZ(x.aggregate_signature)
func hashSSZ*(x: BeaconBlock): array[32, byte] =
## TODO - Warning ⚠️: not part of the spec
## as of https://github.com/ethereum/beacon_chain/pull/133/files
## This is a "stub" needed for fork_choice_rule
## and networking
withHash:
h.update hashSSZ(x.slot)
h.update hashSSZ(x.randao_reveal)
h.update hashSSZ(x.candidate_pow_receipt_root)
h.update hashSSZ(x.ancestor_hashes)
h.update hashSSZ(x.state_root)
h.update hashSSZ(x.attestations)
h.update hashSSZ(x.specials)
h.update hashSSZ(x.proposer_signature)
x.getRaw().hash()
# ################### Tree hash ###################################

View File

@ -40,104 +40,55 @@ import
milagro_crypto
func processAttestations(state: var BeaconState,
blck: BeaconBlock,
parent_slot: uint64): bool =
blck: BeaconBlock): bool =
# Each block includes a number of attestations that the proposer chose. Each
# attestation represents an update to a specific shard and is signed by a
# committee of validators.
# Here we make sanity checks for each attestation and it to the state - most
# updates will happen at the epoch boundary where state updates happen in
# bulk.
if blck.attestations.len > MAX_ATTESTATIONS_PER_BLOCK:
if blck.body.attestations.len > MAX_ATTESTATIONS_PER_BLOCK:
return
var res: seq[PendingAttestationRecord]
for attestation in blck.attestations:
if attestation.data.slot <= blck.slot - MIN_ATTESTATION_INCLUSION_DELAY:
return
if not allIt(blck.body.attestations, checkAttestation(state, it)):
return
# TODO spec - unsigned underflow
if attestation.data.slot >= max(parent_slot.int - EPOCH_LENGTH + 1, 0).uint64:
return
let expected_justified_slot =
if attestation.data.slot >= state.latest_state_recalculation_slot:
state.justified_slot
else:
state.previous_justified_slot
if attestation.data.justified_slot != expected_justified_slot:
return
let expected_justified_block_hash =
get_block_hash(state, blck, attestation.data.justified_slot)
if attestation.data.justified_block_hash != expected_justified_block_hash:
return
if state.latest_crosslinks[attestation.data.shard].shard_block_hash notin [
attestation.data.latest_crosslink_hash, attestation.data.shard_block_hash]:
return
let attestation_participants = get_attestation_participants(
state, attestation.data, attestation.participation_bitfield)
var
agg_pubkey: ValidatorPubKey
empty = true
for attester_idx in attestation_participants:
let validator = state.validator_registry[attester_idx]
if empty:
agg_pubkey = validator.pubkey
empty = false
else:
agg_pubkey.combine(validator.pubkey)
# Verify that aggregate_signature verifies using the group pubkey.
let msg = hashSSZ(attestation.data)
# For now only check compilation
# doAssert attestation.aggregate_signature.verifyMessage(msg, agg_pubkey)
debugEcho "Aggregate sig verify message: ",
attestation.aggregate_signature.verifyMessage(msg, agg_pubkey)
# All checks passed - update state
# TODO no rollback in case of errors
state.latest_attestations.add PendingAttestationRecord(
data: attestation.data,
participation_bitfield: attestation.participation_bitfield,
custody_bitfield: attestation.custody_bitfield,
slot_included: blck.slot
# All checks passed - update state
state.latest_attestations.add mapIt(blck.body.attestations,
PendingAttestationRecord(
data: it.data,
participation_bitfield: it.participation_bitfield,
custody_bitfield: it.custody_bitfield,
slot_included: state.slot
)
)
true
func verifyProposerSignature(state: BeaconState, blck: BeaconBlock): bool =
## https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#proposer-signature
var blck_without_sig = blck
blck_without_sig.proposer_signature = ValidatorSig()
blck_without_sig.signature = ValidatorSig()
let
proposal_hash = hashSSZ(ProposalSignedData(
slot: blck.slot,
slot: state.slot,
shard: BEACON_CHAIN_SHARD,
block_hash: Eth2Digest(data: hashSSZ(blck_without_sig))
))
verifyMessage(
blck.proposer_signature, proposal_hash,
state.validator_registry[get_beacon_proposer_index(state, blck.slot).int].pubkey)
let validator_idx = get_beacon_proposer_index(state, state.slot)
BLSVerify(
state.validator_registry[validator_idx].pubkey,
proposal_hash, blck.signature,
get_domain(state.fork_data, state.slot, DOMAIN_PROPOSAL))
func processRandaoReveal(state: var BeaconState,
blck: BeaconBlock,
parent_slot: uint64): bool =
# Update randao skips
for slot in parentslot + 1 ..< blck.slot:
let proposer_index = get_beacon_proposer_index(state, slot)
state.validator_registry[proposer_index.int].randao_skips.inc()
blck: BeaconBlock): bool =
let
proposer_index = get_beacon_proposer_index(state, blck.slot)
proposer = state.validator_registry[proposer_index.int].addr
proposer_index = get_beacon_proposer_index(state, state.slot)
proposer = addr state.validator_registry[proposer_index]
# Check that proposer commit and reveal match
if repeat_hash(blck.randao_reveal, proposer.randao_skips + 1) !=
@ -156,7 +107,7 @@ func processRandaoReveal(state: var BeaconState,
func processPoWReceiptRoot(state: var BeaconState, blck: BeaconBlock): bool =
for x in state.candidate_pow_receipt_roots.mitems():
if blck.candidate_pow_receipt_root == x.candidate_pow_receipt_root:
x.votes.inc
x.votes += 1
return true
state.candidate_pow_receipt_roots.add CandidatePoWReceiptRootRecord(
@ -165,42 +116,50 @@ func processPoWReceiptRoot(state: var BeaconState, blck: BeaconBlock): bool =
)
return true
func processSpecials(state: var BeaconState, blck: BeaconBlock): bool =
# TODO incoming spec changes here..
true
func processBlock(state: var BeaconState, blck: BeaconBlock): bool =
## When a new block is received, all participants must verify that the block
## makes sense and update their state accordingly. This function will return
## the new state, unless something breaks along the way
if not processAttestations(state, blck):
false
elif not verifyProposerSignature(state, blck):
false
elif not processRandaoReveal(state, blck):
false
elif not processPoWReceiptRoot(state, blck):
false
else:
true
func processSlot(state: var BeaconState, latest_block: BeaconBlock): bool =
## Time on the beacon chain moves in slots. Every time we make it to a new
## slot, a proposer cleates a block to represent the state of the beacon
## 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/master/specs/core/0_beacon-chain.md#per-slot-processing
# TODO state not rolled back in case of failure
let
parent_hash = blck.ancestor_hashes[0]
slot = blck.slot
parent_slot = slot - 1 # TODO Not!! can skip slots...
# TODO actually get parent block, which means fixing up BeaconState refs above;
latest_hash = Eth2Digest(data: hashSSZ(latest_block))
state.latest_block_hashes =
append_to_recent_block_hashes(state.latest_block_hashes, parent_slot, slot,
parent_hash)
state.slot += 1
state.latest_block_hashes.add latest_hash
if not processAttestations(state, blck, parent_slot):
return
if not verifyProposerSignature(state, blck):
return
if not processRandaoReveal(state, blck, parent_slot):
return
if not processPoWReceiptRoot(state, blck):
return
if not processSpecials(state, blck):
return
true
if state.latest_block_hashes.len < 2 or
state.latest_block_hashes[^2] != state.latest_block_hashes[^1]:
# TODO a bit late for the following checks?
# https://github.com/ethereum/eth2.0-specs/issues/284
if latest_block.slot != state.slot:
false
elif latest_block.ancestor_hashes !=
get_updated_ancestor_hashes(latest_block, latest_hash):
false
else:
processBlock(state, latest_block)
else:
state.validator_registry[get_beacon_proposer_index(state, state.slot)].randao_skips += 1
# Skip all other per-slot processing. Move directly to epoch processing
# prison. Do not do any slot updates when passing go.
true
func flatten[T](v: openArray[seq[T]]): seq[T] =
# TODO not in nim - doh.
@ -255,13 +214,13 @@ func processEpoch(state: var BeaconState, blck: BeaconBlock): bool =
active_validator_indices =
get_active_validator_indices(state.validator_registry)
total_balance = sum_effective_balances(state, active_validator_indices)
total_balance_in_eth = total_balance.int div GWEI_PER_ETH
total_balance_in_eth = total_balance div GWEI_PER_ETH
# The per-slot maximum interest rate is `2/reward_quotient`.)
reward_quotient = BASE_REWARD_QUOTIENT * int_sqrt(total_balance_in_eth)
# TODO not in spec, convenient
epoch_boundary_hash = get_block_hash(state, blck, s)
epoch_boundary_hash = get_block_hash(state, s)
proc base_reward(v: ValidatorRecord): uint64 =
get_effective_balance(v) div reward_quotient.uint64
@ -350,61 +309,65 @@ func processEpoch(state: var BeaconState, blck: BeaconBlock): bool =
return a.slot_included - a.data.slot
assert false # shouldn't happen..
block: # Adjust justified slots and crosslink status
var new_justified_slot: Option[uint64]
# TODO where's that bitfield type when you need it?
# TODO what happens with the bits that drop off..?
state.justified_slot_bitfield = state.justified_slot_bitfield shl 1
if 3'u64 * previous_epoch_boundary_attesting_balance >= 2'u64 * total_balance:
# TODO spec says "flip the second lowest bit to 1" and does "AND", wrong?
state.justified_slot_bitfield = state.justified_slot_bitfield or 2
new_justified_slot = some(s - EPOCH_LENGTH)
if 3'u64 * this_epoch_boundary_attesting_balance >= 2'u64 * total_balance:
# TODO spec says "flip the second lowest bit to 1" and does "AND", wrong?
state.justified_slot_bitfield = state.justified_slot_bitfield or 1
new_justified_slot = some(s)
if state.justified_slot == s - EPOCH_LENGTH and
state.justified_slot_bitfield mod 4 == 3:
state.finalized_slot = state.justified_slot
if state.justified_slot == s - EPOCH_LENGTH - EPOCH_LENGTH and
state.justified_slot_bitfield mod 8 == 7:
state.finalized_slot = state.justified_slot
if state.justified_slot == s - EPOCH_LENGTH - 2 * EPOCH_LENGTH and
state.justified_slot_bitfield mod 16 in [15'u64, 14]:
state.finalized_slot = state.justified_slot
block: # Receipt roots
if state.slot mod POW_RECEIPT_ROOT_VOTING_PERIOD == 0:
for x in state.candidate_pow_receipt_roots:
if x.votes * 2 >= POW_RECEIPT_ROOT_VOTING_PERIOD:
state.processed_pow_receipt_root = x.candidate_pow_receipt_root
break
state.candidate_pow_receipt_roots = @[]
block: # Justification
state.previous_justified_slot = state.justified_slot
if new_justified_slot.isSome():
state.justified_slot = new_justified_slot.get()
# TODO where's that bitfield type when you need it?
# TODO why are all bits kept?
state.justification_bitfield = state.justification_bitfield shl 1
if 3'u64 * previous_epoch_boundary_attesting_balance >=
2'u64 * total_balance:
state.justification_bitfield = state.justification_bitfield or 2
state.justified_slot = state.slot - 2 * EPOCH_LENGTH
if 3'u64 * this_epoch_boundary_attesting_balance >=
2'u64 * total_balance:
state.justification_bitfield = state.justification_bitfield or 1
state.justified_slot = state.slot - 1 * EPOCH_LENGTH
block: # Finalization
if
(state.previous_justified_slot == state.slot - 2 * EPOCH_LENGTH and
state.justification_bitfield mod 4 == 3) or
(state.previous_justified_slot == state.slot - 3 * EPOCH_LENGTH and
state.justification_bitfield mod 8 == 7) or
(state.previous_justified_slot == state.slot - 4 * EPOCH_LENGTH and
state.justification_bitfield mod 16 in [15'u64, 14]):
state.finalized_slot = state.justified_slot
block: # Crosslinks
for sac in state.shard_committees_at_slots:
# TODO or just state.shard_committees_at_slots[s]?
for obj in sac:
if 3'u64 * total_attesting_balance(obj) >= 2'u64 * total_balance_sac(obj):
if 3'u64 * total_attesting_balance(obj) >=
2'u64 * total_balance_sac(obj):
state.latest_crosslinks[obj.shard] = CrosslinkRecord(
slot: state.latest_state_recalculation_slot + EPOCH_LENGTH,
shard_block_hash: winning_hash(obj))
block: # Balance recalculations related to FFG rewards
block: # Justification and finalization rewards and penalties
let
time_since_finality = blck.slot - state.finalized_slot
slots_since_finality = blck.slot - state.finalized_slot
if time_since_finality <= 4'u64 * EPOCH_LENGTH:
if slots_since_finality <= 4'u64 * EPOCH_LENGTH:
for v in previous_epoch_boundary_attesters:
state.validator_registry[v].balance.inc(adjust_for_inclusion_distance(
state.validator_registry[v].balance += adjust_for_inclusion_distance(
base_reward(state.validator_registry[v]) *
previous_epoch_boundary_attesting_balance div total_balance,
inclusion_distance(v)).int)
inclusion_distance(v))
for v in active_validator_indices:
if v notin previous_epoch_boundary_attesters:
state.validator_registry[v].balance.dec(
base_reward(state.validator_registry[v]).int)
state.validator_registry[v].balance -=
base_reward(state.validator_registry[v])
else:
# Any validator in `prev_cycle_boundary_attesters` sees their balance
# unchanged.
@ -413,63 +376,63 @@ func processEpoch(state: var BeaconState, blck: BeaconBlock): bool =
if (v.status == ACTIVE and
vindex.Uint24 notin previous_epoch_boundary_attesters) or
v.status == EXITED_WITH_PENALTY:
v.balance.dec(
(base_reward(v) + get_effective_balance(v) * time_since_finality div
INACTIVITY_PENALTY_QUOTIENT.uint64).int)
v.balance -= base_reward(v) +
get_effective_balance(v) * slots_since_finality div
INACTIVITY_PENALTY_QUOTIENT
for v in previous_epoch_boundary_attesters:
let proposer_index = get_beacon_proposer_index(state, inclusion_slot(v))
state.validator_registry[proposer_index].balance.inc(
(base_reward(state.validator_registry[v]) div INCLUDER_REWARD_QUOTIENT.uint64).int)
let proposer_index =
get_beacon_proposer_index(state, inclusion_slot(v))
state.validator_registry[proposer_index].balance +=
base_reward(state.validator_registry[v]) div
INCLUDER_REWARD_QUOTIENT
block: # Balance recalculations related to crosslink rewards
block: # Crosslink rewards and penalties
for sac in state.shard_committees_at_slots[0 ..< EPOCH_LENGTH]:
for obj in sac:
for vindex in obj.committee:
let v = state.validator_registry[vindex].addr
if vindex in attesting_validators(obj):
v.balance.inc(adjust_for_inclusion_distance(
v.balance += adjust_for_inclusion_distance(
base_reward(v[]) * total_attesting_balance(obj) div total_balance_sac(obj),
inclusion_distance(vindex)).int)
inclusion_distance(vindex))
else:
v.balance.dec(base_reward(v[]).int)
v.balance -= base_reward(v[])
block: # Ethereum 1.0 chain related rules
if state.latest_state_recalculation_slot mod
POW_RECEIPT_ROOT_VOTING_PERIOD.uint64 == 0:
for x in state.candidate_pow_receipt_roots:
if x.votes * 2 >= POW_RECEIPT_ROOT_VOTING_PERIOD.uint64:
state.processed_pow_receipt_root = x.candidate_pow_receipt_root
break
state.candidate_pow_receipt_roots = @[]
block: # Validator registry
block: # Validator registry change
if state.finalized_slot > state.validator_registry_latest_change_slot and
allIt(state.shard_committees_at_slots,
allIt(it,
state.latest_crosslinks[it.shard].slot >
state.validator_registry_latest_change_slot)):
state.change_validators(s)
state.validator_registry_latest_change_slot = s + EPOCH_LENGTH
update_validator_registry(state)
state.validator_registry_latest_change_slot = state.slot
for i in 0..<EPOCH_LENGTH:
state.shard_committees_at_slots[i] =
state.shard_committees_at_slots[EPOCH_LENGTH + i]
# https://github.com/ethereum/eth2.0-specs/issues/223
let next_start_shard = (state.shard_committees_at_slots[^1][^1].shard + 1) mod SHARD_COUNT
let next_start_shard =
(state.shard_committees_at_slots[^1][^1].shard + 1) mod SHARD_COUNT
for i, v in get_new_shuffling(
state.next_seed, state.validator_registry, next_start_shard):
state.shard_committees_at_slots[i + EPOCH_LENGTH] = v
state.next_seed = state.randao_mix
else:
# If a validator registry change does NOT happen
for i in 0..<EPOCH_LENGTH:
state.shard_committees_at_slots[i] =
state.shard_committees_at_slots[EPOCH_LENGTH + i]
let time_since_finality = blck.slot - state.validator_registry_latest_change_slot
let slots_since_finality =
state.slot - state.validator_registry_latest_change_slot
let start_shard = state.shard_committees_at_slots[0][0].shard
if time_since_finality * EPOCH_LENGTH <= MIN_VALIDATOR_REGISTRY_CHANGE_INTERVAL.uint64 or
is_power_of_2(time_since_finality):
if slots_since_finality * EPOCH_LENGTH <=
MIN_VALIDATOR_REGISTRY_CHANGE_INTERVAL or
is_power_of_2(slots_since_finality):
for i, v in get_new_shuffling(
state.next_seed, state.validator_registry, start_shard):
state.shard_committees_at_slots[i + EPOCH_LENGTH] = v
@ -478,7 +441,9 @@ func processEpoch(state: var BeaconState, blck: BeaconBlock): bool =
block: # Proposer reshuffling
let active_validator_indices = get_active_validator_indices(state.validator_registry)
let num_validators_to_reshuffle = len(active_validator_indices) div SHARD_PERSISTENT_COMMITTEE_CHANGE_PERIOD
let num_validators_to_reshuffle =
len(active_validator_indices) div
SHARD_PERSISTENT_COMMITTEE_CHANGE_PERIOD.int
for i in 0..<num_validators_to_reshuffle:
# Multiplying i to 2 to ensure we have different input to all the required hashes in the shuffling
# and none of the hashes used for entropy in this loop will be the same
@ -487,7 +452,7 @@ func processEpoch(state: var BeaconState, blck: BeaconBlock): bool =
let shard_reassignment_record = ShardReassignmentRecord(
validator_index: validator_index,
shard: new_shard,
slot: s + SHARD_PERSISTENT_COMMITTEE_CHANGE_PERIOD.uint64
slot: s + SHARD_PERSISTENT_COMMITTEE_CHANGE_PERIOD
)
state.persistent_committee_reassignments.add(shard_reassignment_record)
@ -501,17 +466,13 @@ func processEpoch(state: var BeaconState, blck: BeaconBlock): bool =
state.persistent_committees[reassignment.shard.int].add(
reassignment.validator_index)
block: # Finally...
# Remove all attestation records older than slot `s`.
for i, v in state.validator_registry:
if v.balance < MIN_BALANCE.uint64 and v.status == ACTIVE:
exit_validator(i.Uint24, state, penalize=false, current_slot=blck.slot)
block: # Final updates
# TODO Remove all attestation records older than slot `s`.
state.latest_block_hashes = state.latest_block_hashes[EPOCH_LENGTH..^1]
state.latest_state_recalculation_slot.inc(EPOCH_LENGTH)
true
func updateState*(state: BeaconState, blck: BeaconBlock): Option[BeaconState] =
func updateState*(state: BeaconState, latest_block: BeaconBlock): Option[BeaconState] =
## Adjust `state` according to the information in `blck`.
## Returns the new state, or `none` if the block is invalid.
@ -523,14 +484,14 @@ func updateState*(state: BeaconState, blck: BeaconBlock): Option[BeaconState] =
# bool return values...)
var state = state
# Block processing is split up into two phases - lightweight updates done
# for each block, and bigger updates done for each epoch.
# Slot processing is split up into two phases - lightweight updates done
# for each slot, and bigger updates done for each epoch.
# Lightweight updates that happen for every block
if not processBlock(state, blck): return
# Lightweight updates that happen for every slot
if not processSlot(state, latest_block): return
# Heavy updates that happen for every epoch
if not processEpoch(state, blck): return
if not processEpoch(state, latest_block): return
# All good, we can return the new state
some(state)

View File

@ -57,7 +57,7 @@ iterator changes*(log: ChangeLog): ChangeLogEntry =
yield if log.order.getBit(i):
ChangeLogEntry(kind: Activation, pubkey: nextItem(added))
else:
ChangeLogEntry(kind: Exit, index: nextItem(removed))
ChangeLogEntry(kind: ValidatorSetDeltaFlags.Exit, index: nextItem(removed))
proc getValidatorChangeLog*(node: EthereumNode, changeLogHead: Eth2Digest):
Future[(Peer, ChangeLog)] {.async.} =

View File

@ -6,14 +6,16 @@
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import
milagro_crypto,
../beacon_chain/extras,
../beacon_chain/spec/[crypto, datatypes]
func makeValidatorPubKey(n: int): ValidatorPubKey =
result.point.x.a.g[0] = n
func makeValidatorPrivKey(n: int): ValidatorPrivKey =
result.x[0] = n
func makeInitialValidators*(n = EPOCH_LENGTH): seq[InitialValidator] =
for i in 0..<n:
for i in 0..<n.int:
let key = makeValidatorPrivKey(i)
result.add InitialValidator(
pubkey: makeValidatorPubKey(i)
pubkey: key.fromSigKey()
)