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:
Dustin Brody 2019-02-05 18:40:27 +00:00 committed by Jacek Sieka
parent 20e02d5615
commit d1af069c55
4 changed files with 101 additions and 82 deletions

View File

@ -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

View File

@ -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

View File

@ -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]

View File

@ -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