nimbus-eth2/research/fork_choice_rule/lmd_ghost.nim

86 lines
3.9 KiB
Nim

# beacon_chain
# Copyright (c) 2018 Status Research & Development GmbH
# Licensed and distributed under either of
# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT).
# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0).
# at your option. This file may not be copied, modified, or distributed except according to those terms.
# This file contains the implementation of the beacon chain fork choice rule.
# The chosen rule is a hybrid that combines justification and finality
# with Latest Message Driven (LMD) Greediest Heaviest Observed SubTree (GHOST)
#
# The latest version can be seen here:
# https://github.com/ethereum/eth2.0-specs/blob/master/specs/beacon-chain.md
#
# How wrong the code is:
# https://github.com/ethereum/eth2.0-specs/compare/126a7abfa86448091a0e037f52966b6a9531a857...master
#
# A standalone research implementation can be found here:
# - https://github.com/ethereum/research/blob/94ac4e2100a808a7097715003d8ad1964df4dbd9/clock_disparity/lmd_node.py
# A minispec in mathematical notation including proofs can be found here:
# - https://ethresear.ch/t/beacon-chain-casper-ffg-rpj-mini-spec/2760
# Note that it might be out-of-sync with the official spec.
import
tables, hashes,
../../beacon_chain/spec/[datatypes, helpers, digest, crypto],
../../beacon_chain/ssz
type
AttesterIdx = int
BlockHash = Eth2Digest
Store = object
# This is private to each validator.
# It holds the set of attestations and blocks that a validator
# has observed and verified.
#
# We uniquely identify each block via it's block hash
# and each attester via it's attester index (from AttestationRecord object)
# TODO/Question: Should we use the public key? That would defeat the pubkey aggregation purpose
verified_attestations: Table[AttesterIdx, ref seq[AttestationData]]
# TODO: We assume that ref seq[AttestationData] is queue, ordered by
# a pair (slot, observation time).
verified_blocks: Table[BlockHash, BeaconBlock]
finalized_head: BeaconBlock
justified_head: BeaconBlock
func hash(x: BlockHash): Hash =
## Hash for Keccak digests for Nim hash tables
# We just slice the first 4 or 8 bytes of the block hash
# depending of if we are on a 32 or 64-bit platform
const size = x.sizeof
const num_hashes = size div sizeof(int)
result = cast[array[num_hashes, Hash]](x)[0]
func get_parent(store: Store, blck: BeaconBlock): BeaconBlock =
store.verified_blocks[blck.parent_root]
func get_ancestor(store: Store, blck: BeaconBlock, slot: uint64): BeaconBlock =
## Find the ancestor with a specific slot number
if blck.slot == slot:
return blck
else:
return store.get_ancestor(store.get_parent(blck), slot)
# TODO: what if the slot was never observed/verified?
func get_latest_attestation(store: Store, validatorIdx: AttesterIdx): AttestationData =
## Search for the attestation with the highest slot number
## If multiple attestation have the same slot number, keep the first one.
# We assume that in `verified_attestations: Table[AttesterIdx, seq[AttestationData]]`
# `seq[AttestationSignedData]` is a queue ordered by (slot, observation time)
let attestations = store.verified_attestations[validatorIdx]
result = attestations[^1] # Pick the last attestation
for idx in countdown(attestations[].len - 2, 0): # From the second to last attestation to 0, check if they have the same slot.
if attestations[idx].slot == result.slot: # if yes it was observed earlier
result = attestations[idx]
else: # otherwise we are at the first observed attestation with the highest slot
return
func get_latest_attestation_target(store: Store, validatorIdx: AttesterIdx): BlockHash =
store.get_latest_attestation(validatorIdx).beacon_block_root