mirror of
https://github.com/status-im/nimbus-eth2.git
synced 2025-01-14 16:47:21 +00:00
Spec update (#83)
* rm GWEI_PER_ETH * update EJECTION_BALANCE; rm MAX_CASPER_VOTES/replace with MAX_INDICES_PER_SLASHABLE_VOTE; modify GENESIS_SLOT * update various constants from slots to epochs; MAX_CASPER_SLASHINGS -> MAX_ATTESTER_SLASHINGS * AttesterSlashing field renamings; SlashableVote -> SlashableAttestation; add DOMAIN_RANDAO * rm aggregate_signature_poc_[01]_indices * centralize some bitfield functions
This commit is contained in:
parent
20e02d5615
commit
d1af069c55
@ -173,7 +173,7 @@ func getVoteCount(participation_bitfield: openarray[byte]): int =
|
||||
# https://github.com/status-im/nim-beacon-chain/issues/19
|
||||
|
||||
for validatorIdx in 0 ..< participation_bitfield.len * 8:
|
||||
result += int participation_bitfield.bitIsSet(validatorIdx)
|
||||
result += int participation_bitfield.get_bitfield_bit(validatorIdx)
|
||||
|
||||
func getAttestationVoteCount(pool: AttestationPool, current_slot: int): CountTable[Eth2Digest] =
|
||||
## Returns all blocks more recent that the current slot
|
||||
|
@ -62,7 +62,7 @@ const
|
||||
## with a Verifiable Delay Function (VDF) will improve committee robustness
|
||||
## and lower the safe minimum committee size.)
|
||||
|
||||
EJECTION_BALANCE* = 2'u64^4 ##\
|
||||
EJECTION_BALANCE* = 2'u64^4 * 10'u64^9 ##\
|
||||
## Once the balance of a validator drops below this, it will be ejected from
|
||||
## the validator pool
|
||||
|
||||
@ -70,20 +70,20 @@ const
|
||||
## 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
|
||||
|
||||
MAX_CASPER_VOTES* = 2^10
|
||||
MAX_INDICES_PER_SLASHABLE_VOTE* = 2^12 ##\
|
||||
## votes
|
||||
|
||||
MAX_WITHDRAWALS_PER_EPOCH* = 4 # withdrawals
|
||||
|
||||
# Deposit contract
|
||||
DEPOSIT_CONTRACT_TREE_DEPTH* = 2^5
|
||||
|
||||
MIN_DEPOSIT_AMOUNT* = 2'u64^0 * GWEI_PER_ETH ##\
|
||||
MIN_DEPOSIT_AMOUNT* = 2'u64^0 * 10'u64^9 ##\
|
||||
## Minimum amounth of ETH that can be deposited in one call - deposits can
|
||||
## be used either to top up an existing validator or commit to a new one
|
||||
MAX_DEPOSIT_AMOUNT* = 2'u64^5 * GWEI_PER_ETH ##\
|
||||
MAX_DEPOSIT_AMOUNT* = 2'u64^5 * 10'u64^9 ##\
|
||||
## Maximum amounth of ETH that can be deposited in one call
|
||||
|
||||
# Time parameter, here so that GENESIS_EPOCH can access it
|
||||
@ -93,9 +93,8 @@ const
|
||||
## processing is done
|
||||
|
||||
# Initial values
|
||||
|
||||
GENESIS_FORK_VERSION* = 0'u64
|
||||
GENESIS_SLOT* = 2'u64^19
|
||||
GENESIS_SLOT* = 2'u64^63
|
||||
GENESIS_EPOCH* = GENESIS_SLOT div EPOCH_LENGTH # slot_to_epoch(GENESIS_SLOT)
|
||||
GENESIS_START_SHARD* = 0'u64
|
||||
FAR_FUTURE_EPOCH* = not 0'u64 # 2^64 - 1 in spec
|
||||
@ -119,17 +118,17 @@ const
|
||||
## wait towards the end of the slot and still have time to publish the
|
||||
## attestation.
|
||||
|
||||
SEED_LOOKAHEAD* = 64 ##\
|
||||
## slots (~6.4 minutes)
|
||||
SEED_LOOKAHEAD* = 1 ##\
|
||||
## epochs (~6.4 minutes)
|
||||
|
||||
ENTRY_EXIT_DELAY* = 256 ##\
|
||||
## slots (~25.6 minutes)
|
||||
ENTRY_EXIT_DELAY* = 4 ##\
|
||||
## epochs (~25.6 minutes)
|
||||
|
||||
ETH1_DATA_VOTING_PERIOD* = 2'u64^10 ##\
|
||||
## slots (~1.7 hours)
|
||||
ETH1_DATA_VOTING_PERIOD* = 2'u64^4 ##\
|
||||
## epochs (~1.7 hours)
|
||||
|
||||
MIN_VALIDATOR_WITHDRAWAL_TIME* = 2'u64^14 ##\
|
||||
## slots (~27 hours)
|
||||
MIN_VALIDATOR_WITHDRAWAL_TIME* = 2'u64^8 ##\
|
||||
## epochs (~27 hours)
|
||||
|
||||
# State list lengths
|
||||
LATEST_BLOCK_ROOTS_LENGTH* = 2'u64^13
|
||||
@ -137,7 +136,8 @@ const
|
||||
LATEST_INDEX_ROOTS_LENGTH* = 2'u64^13
|
||||
LATEST_PENALIZED_EXIT_LENGTH* = 8192 # epochs
|
||||
|
||||
# Quotients
|
||||
# Reward and penalty quotients
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/dev/specs/core/0_beacon-chain.md#reward-and-penalty-quotients
|
||||
BASE_REWARD_QUOTIENT* = 2'u64^10 ##\
|
||||
## The `BASE_REWARD_QUOTIENT` parameter dictates the per-epoch reward. It
|
||||
## corresponds to ~2.54% annual interest assuming 10 million participating
|
||||
@ -153,7 +153,7 @@ const
|
||||
|
||||
# Max operations per block
|
||||
MAX_PROPOSER_SLASHINGS* = 2^4
|
||||
MAX_CASPER_SLASHINGS* = 2^4
|
||||
MAX_ATTESTER_SLASHINGS* = 2^0
|
||||
MAX_ATTESTATIONS* = 2^7
|
||||
MAX_DEPOSITS* = 2^4
|
||||
MAX_EXITS* = 2^4
|
||||
@ -163,7 +163,7 @@ type
|
||||
SlotNumber* = uint64
|
||||
EpochNumber* = uint64
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#data-structures
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/dev/specs/core/0_beacon-chain.md#proposerslashing
|
||||
ProposerSlashing* = object
|
||||
proposer_index*: ValidatorIndex
|
||||
proposal_data_1*: ProposalSignedData
|
||||
@ -171,26 +171,21 @@ type
|
||||
proposal_data_2*: ProposalSignedData
|
||||
proposal_signature_2*: ValidatorSig
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/dev/specs/core/0_beacon-chain.md#attesterslashing
|
||||
AttesterSlashing* = object
|
||||
slashable_vote_data_1*: SlashableVote ## \
|
||||
slashable_attestation_1*: SlashableAttestation ## \
|
||||
## First batch of votes
|
||||
slashable_vote_data_2*: SlashableVote ## \
|
||||
slashable_attestation_2*: SlashableAttestation ## \
|
||||
## Second batch of votes
|
||||
|
||||
SlashableVote* = object
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/dev/specs/core/0_beacon-chain.md#slashableattestation
|
||||
SlashableAttestation* = object
|
||||
validator_indices*: seq[uint64] ##\
|
||||
## Validator indices
|
||||
|
||||
custody_bitfield*: seq[byte] ##\
|
||||
## Custody bitfield
|
||||
|
||||
# TODO rm aggregate_signature_poc_0_indices, aggregate_signature_poc_1_indices
|
||||
aggregate_signature_poc_0_indices*: seq[ValidatorIndex] ##\
|
||||
## Proof-of-custody indices (0 bits)
|
||||
|
||||
aggregate_signature_poc_1_indices*: seq[ValidatorIndex] ##\
|
||||
## Proof-of-custody indices (1 bits)
|
||||
|
||||
data*: AttestationData ## \
|
||||
## Attestation data
|
||||
|
||||
@ -429,11 +424,13 @@ type
|
||||
Activation = 0
|
||||
Exit = 1
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/dev/specs/core/0_beacon-chain.md#signature-domains
|
||||
SignatureDomain* {.pure.} = enum
|
||||
DOMAIN_DEPOSIT = 0
|
||||
DOMAIN_ATTESTATION = 1
|
||||
DOMAIN_PROPOSAL = 2
|
||||
DOMAIN_EXIT = 3
|
||||
DOMAIN_RANDAO = 4
|
||||
|
||||
template epoch*(slot: int|uint64): auto =
|
||||
slot div EPOCH_LENGTH
|
||||
|
@ -11,12 +11,26 @@ import ./datatypes, ./digest, sequtils, math
|
||||
|
||||
# TODO spec candidate? there's bits in nim-ranges but that one has some API
|
||||
# issues regarding bit endianess that need resolving..
|
||||
func bitIsSet*(bitfield: openArray[byte], index: int): bool =
|
||||
(bitfield[index div 8] shr byte(7 - (index mod 8))) mod 2 > 0'u8
|
||||
|
||||
func bitSet*(bitfield: var openArray[byte], index: int) =
|
||||
bitfield[index div 8] = bitfield[index div 8] or 1'u8 shl (7 - (index mod 8))
|
||||
|
||||
func get_bitfield_bit*(bitfield: openarray[byte], i: int): byte =
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/dev/specs/core/0_beacon-chain.md#get_bitfield_bit
|
||||
# Extract the bit in ``bitfield`` at position ``i``.
|
||||
(bitfield[i div 8] shr (7 - (i mod 8))) mod 2
|
||||
|
||||
func verify_bitfield*(bitfield: openarray[byte], committee_size: int): bool =
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/dev/specs/core/0_beacon-chain.md#verify_bitfield
|
||||
# Verify ``bitfield`` against the ``committee_size``.
|
||||
if len(bitfield) != (committee_size + 7) div 8:
|
||||
return false
|
||||
|
||||
for i in committee_size + 1 ..< committee_size - (committee_size mod 8) + 8:
|
||||
if get_bitfield_bit(bitfield, i) == 0b1:
|
||||
return false
|
||||
|
||||
true
|
||||
|
||||
func mod_get[T](arr: openarray[T], pos: Natural): T =
|
||||
arr[pos mod arr.len]
|
||||
|
||||
|
@ -182,65 +182,73 @@ proc processProposerSlashings(
|
||||
|
||||
return true
|
||||
|
||||
func verify_slashable_vote_data(state: BeaconState, vote_data: SlashableVote): bool =
|
||||
if len(vote_data.aggregate_signature_poc_0_indices) +
|
||||
len(vote_data.aggregate_signature_poc_1_indices) > MAX_CASPER_VOTES:
|
||||
func verify_slashable_attestation(state: BeaconState, slashable_attestation: SlashableAttestation): bool =
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/dev/specs/core/0_beacon-chain.md#verify_slashable_attestation
|
||||
# Verify validity of ``slashable_attestation`` fields.
|
||||
|
||||
if anyIt(slashable_attestation.custody_bitfield, it != 0): # [TO BE REMOVED IN PHASE 1]
|
||||
return false
|
||||
|
||||
let pubs = [
|
||||
bls_aggregate_pubkeys(vote_data.aggregate_signature_poc_0_indices.
|
||||
mapIt(state.validator_registry[it].pubkey)),
|
||||
bls_aggregate_pubkeys(vote_data.aggregate_signature_poc_1_indices.
|
||||
mapIt(state.validator_registry[it].pubkey))]
|
||||
if len(slashable_attestation.validator_indices) == 0:
|
||||
return false
|
||||
|
||||
# TODO
|
||||
# return bls_verify_multiple(pubs, [hash_tree_root(votes)+bytes1(0), hash_tree_root(votes)+bytes1(1), signature=aggregate_signature)
|
||||
for i in 0 ..< (len(slashable_attestation.validator_indices) - 1):
|
||||
if slashable_attestation.validator_indices[i] >= slashable_attestation.validator_indices[i + 1]:
|
||||
return false
|
||||
|
||||
if not verify_bitfield(slashable_attestation.custody_bitfield, len(slashable_attestation.validator_indices)):
|
||||
return false
|
||||
|
||||
if len(slashable_attestation.validator_indices) > MAX_INDICES_PER_SLASHABLE_VOTE:
|
||||
return false
|
||||
|
||||
var
|
||||
custody_bit_0_indices: seq[uint64] = @[]
|
||||
custody_bit_1_indices: seq[uint64] = @[]
|
||||
|
||||
return true
|
||||
|
||||
proc indices(vote: SlashableVote): seq[ValidatorIndex] =
|
||||
vote.aggregate_signature_poc_0_indices &
|
||||
vote.aggregate_signature_poc_1_indices
|
||||
|
||||
proc processAttesterSlashings(state: var BeaconState, blck: BeaconBlock): bool =
|
||||
## https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#casper-slashings-1
|
||||
if len(blck.body.attester_slashings) > MAX_CASPER_SLASHINGS:
|
||||
## https://github.com/ethereum/eth2.0-specs/blob/dev/specs/core/0_beacon-chain.md#attester-slashings-1
|
||||
if len(blck.body.attester_slashings) > MAX_ATTESTER_SLASHINGS:
|
||||
notice "CaspSlash: too many!"
|
||||
return false
|
||||
|
||||
for casper_slashing in blck.body.attester_slashings:
|
||||
for attester_slashing in blck.body.attester_slashings:
|
||||
let
|
||||
slashable_vote_data_1 = casper_slashing.slashable_vote_data_1
|
||||
slashable_vote_data_2 = casper_slashing.slashable_vote_data_2
|
||||
indices2 = indices(slashable_vote_data_2)
|
||||
intersection =
|
||||
indices(slashable_vote_data_1).filterIt(it in indices2)
|
||||
slashable_attestation_1 = attester_slashing.slashable_attestation_1
|
||||
slashable_attestation_2 = attester_slashing.slashable_attestation_2
|
||||
|
||||
if not (slashable_vote_data_1.data != slashable_vote_data_2.data):
|
||||
if not (slashable_attestation_1.data != slashable_attestation_2.data):
|
||||
notice "CaspSlash: invalid data"
|
||||
return false
|
||||
|
||||
if not (len(intersection) >= 1):
|
||||
notice "CaspSlash: no intersection"
|
||||
return false
|
||||
|
||||
if not (
|
||||
is_double_vote(slashable_vote_data_1.data, slashable_vote_data_2.data) or
|
||||
is_surround_vote(slashable_vote_data_1.data, slashable_vote_data_2.data)):
|
||||
is_double_vote(slashable_attestation_1.data, slashable_attestation_2.data) or
|
||||
is_surround_vote(slashable_attestation_1.data, slashable_attestation_2.data)):
|
||||
notice "CaspSlash: surround or double vote check failed"
|
||||
return false
|
||||
|
||||
if not verify_slashable_vote_data(state, slashable_vote_data_1):
|
||||
if not verify_slashable_attestation(state, slashable_attestation_1):
|
||||
notice "CaspSlash: invalid votes 1"
|
||||
return false
|
||||
|
||||
if not verify_slashable_vote_data(state, slashable_vote_data_2):
|
||||
if not verify_slashable_attestation(state, slashable_attestation_2):
|
||||
notice "CaspSlash: invalid votes 2"
|
||||
return false
|
||||
|
||||
for i in intersection:
|
||||
if state.validator_registry[i].penalized_epoch > get_current_epoch(state):
|
||||
penalize_validator(state, i)
|
||||
let
|
||||
indices2 = slashable_attestation_2.validator_indices
|
||||
slashable_indices =
|
||||
slashable_attestation_1.validator_indices.filterIt(it in indices2)
|
||||
|
||||
if not (len(slashable_indices) >= 1):
|
||||
notice "CaspSlash: no intersection"
|
||||
return false
|
||||
|
||||
for index in slashable_indices:
|
||||
if state.validator_registry[index.int].penalized_epoch > get_current_epoch(state):
|
||||
penalize_validator(state, index.ValidatorIndex)
|
||||
|
||||
return true
|
||||
|
||||
@ -451,25 +459,11 @@ func processEpoch(state: var BeaconState) =
|
||||
active_validator_indices =
|
||||
get_active_validator_indices(state.validator_registry, state.slot)
|
||||
total_balance = sum_effective_balances(state, active_validator_indices)
|
||||
total_balance_in_eth = total_balance div GWEI_PER_ETH
|
||||
|
||||
# The per-slot maximum interest rate is `2/reward_quotient`.)
|
||||
base_reward_quotient =
|
||||
BASE_REWARD_QUOTIENT * integer_squareroot(total_balance_in_eth)
|
||||
|
||||
current_epoch = get_current_epoch(state)
|
||||
previous_epoch = if current_epoch > GENESIS_EPOCH: current_epoch - 1 else: current_epoch
|
||||
next_epoch = (current_epoch + 1).EpochNumber
|
||||
|
||||
func base_reward(state: BeaconState, index: ValidatorIndex): uint64 =
|
||||
get_effective_balance(state, index) div base_reward_quotient.uint64 div 4
|
||||
|
||||
func inactivity_penalty(
|
||||
state: BeaconState, index: ValidatorIndex, epochs_since_finality: uint64): uint64 =
|
||||
base_reward(state, index) +
|
||||
get_effective_balance(state, index) *
|
||||
epochs_since_finality div INACTIVITY_PENALTY_QUOTIENT div 2
|
||||
|
||||
# TODO doing this with iterators failed:
|
||||
# https://github.com/nim-lang/Nim/issues/9827
|
||||
let
|
||||
@ -640,7 +634,21 @@ func processEpoch(state: var BeaconState) =
|
||||
# state.latest_crosslinks[shard] = Crosslink(
|
||||
# slot=state.slot, shard_block_root=winning_root(crosslink_committee))
|
||||
|
||||
# TODO Rewards and penalties helpers
|
||||
# Rewards and penalties helpers
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/dev/specs/core/0_beacon-chain.md#rewards-and-penalties
|
||||
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 4
|
||||
|
||||
func inactivity_penalty(
|
||||
state: BeaconState, index: ValidatorIndex, epochs_since_finality: uint64): uint64 =
|
||||
base_reward(state, index) +
|
||||
get_effective_balance(state, index) *
|
||||
epochs_since_finality div INACTIVITY_PENALTY_QUOTIENT div 2
|
||||
|
||||
|
||||
block: # Justification and finalization
|
||||
let epochs_since_finality = next_epoch - state.finalized_epoch
|
||||
|
Loading…
x
Reference in New Issue
Block a user