160 lines
5.6 KiB
Nim
160 lines
5.6 KiB
Nim
# beacon_chain
|
|
# Copyright (c) 2018-2020 Status Research & Development GmbH
|
|
# Licensed and distributed under either of
|
|
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
|
|
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
|
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
|
|
|
# Common routines for a BeaconNode and a BeaconValidator node
|
|
|
|
import
|
|
# Standard library
|
|
os, tables,
|
|
|
|
# Nimble packages
|
|
chronos, json_rpc/rpcserver, metrics,
|
|
chronicles,
|
|
|
|
# Local modules
|
|
spec/[datatypes, crypto, digest, helpers],
|
|
conf, time, beacon_chain_db, sszdump,
|
|
ssz/merkleization,
|
|
attestation_pool, block_pool, eth2_network,
|
|
beacon_node_types, mainchain_monitor, request_manager,
|
|
sync_manager
|
|
|
|
# This removes an invalid Nim warning that the digest module is unused here
|
|
# It's currently used for `shortLog(head.blck.root)`
|
|
type Eth2Digest = digest.Eth2Digest
|
|
|
|
type
|
|
RpcServer* = RpcHttpServer
|
|
|
|
BeaconNode* = ref object
|
|
nickname*: string
|
|
network*: Eth2Node
|
|
netKeys*: KeyPair
|
|
requestManager*: RequestManager
|
|
db*: BeaconChainDB
|
|
config*: BeaconNodeConf
|
|
attachedValidators*: ValidatorPool
|
|
blockPool*: BlockPool
|
|
attestationPool*: AttestationPool
|
|
mainchainMonitor*: MainchainMonitor
|
|
beaconClock*: BeaconClock
|
|
rpcServer*: RpcServer
|
|
forkDigest*: ForkDigest
|
|
syncManager*: SyncManager[Peer, PeerID]
|
|
topicBeaconBlocks*: string
|
|
topicAggregateAndProofs*: string
|
|
forwardSyncLoop*: Future[void]
|
|
onSecondLoop*: Future[void]
|
|
|
|
const
|
|
MaxEmptySlotCount* = uint64(10*60) div SECONDS_PER_SLOT
|
|
|
|
# Metrics
|
|
declareGauge beacon_head_root,
|
|
"Root of the head block of the beacon chain"
|
|
|
|
# Metrics for tracking attestation and beacon block loss
|
|
declareCounter beacon_blocks_received,
|
|
"Number of beacon chain blocks received by this peer"
|
|
|
|
proc onAttestation*(node: BeaconNode, attestation: Attestation) =
|
|
# We received an attestation from the network but don't know much about it
|
|
# yet - in particular, we haven't verified that it belongs to particular chain
|
|
# we're on, or that it follows the rules of the protocol
|
|
logScope: pcs = "on_attestation"
|
|
|
|
let
|
|
wallSlot = node.beaconClock.now().toSlot()
|
|
head = node.blockPool.head
|
|
|
|
debug "Attestation received",
|
|
attestation = shortLog(attestation),
|
|
headRoot = shortLog(head.blck.root),
|
|
headSlot = shortLog(head.blck.slot),
|
|
wallSlot = shortLog(wallSlot.slot),
|
|
cat = "consensus" # Tag "consensus|attestation"?
|
|
|
|
if not wallSlot.afterGenesis or wallSlot.slot < head.blck.slot:
|
|
warn "Received attestation before genesis or head - clock is wrong?",
|
|
afterGenesis = wallSlot.afterGenesis,
|
|
wallSlot = shortLog(wallSlot.slot),
|
|
headSlot = shortLog(head.blck.slot),
|
|
cat = "clock_drift" # Tag "attestation|clock_drift"?
|
|
return
|
|
|
|
if attestation.data.slot > head.blck.slot and
|
|
(attestation.data.slot - head.blck.slot) > MaxEmptySlotCount:
|
|
warn "Ignoring attestation, head block too old (out of sync?)",
|
|
attestationSlot = attestation.data.slot, headSlot = head.blck.slot
|
|
return
|
|
|
|
node.attestationPool.add(attestation)
|
|
|
|
proc storeBlock*(
|
|
node: BeaconNode, signedBlock: SignedBeaconBlock): Result[void, BlockError] =
|
|
let blockRoot = hash_tree_root(signedBlock.message)
|
|
debug "Block received",
|
|
signedBlock = shortLog(signedBlock.message),
|
|
blockRoot = shortLog(blockRoot),
|
|
cat = "block_listener",
|
|
pcs = "receive_block"
|
|
|
|
if node.config.dumpEnabled:
|
|
dump(node.config.dumpDir / "incoming", signedBlock, blockRoot)
|
|
|
|
beacon_blocks_received.inc()
|
|
let blck = node.blockPool.add(blockRoot, signedBlock)
|
|
if blck.isErr:
|
|
if blck.error == Invalid and node.config.dumpEnabled:
|
|
let parent = node.blockPool.getRef(signedBlock.message.parent_root)
|
|
if parent != nil:
|
|
node.blockPool.withState(
|
|
node.blockPool.tmpState, parent.atSlot(signedBlock.message.slot - 1)):
|
|
dump(node.config.dumpDir / "invalid", hashedState, parent)
|
|
dump(node.config.dumpDir / "invalid", signedBlock, blockRoot)
|
|
|
|
return err(blck.error)
|
|
|
|
# The block we received contains attestations, and we might not yet know about
|
|
# all of them. Let's add them to the attestation pool - in case they block
|
|
# is not yet resolved, neither will the attestations be!
|
|
# But please note that we only care about recent attestations.
|
|
# TODO shouldn't add attestations if the block turns out to be invalid..
|
|
let currentSlot = node.beaconClock.now.toSlot
|
|
if currentSlot.afterGenesis and
|
|
signedBlock.message.slot.epoch + 1 >= currentSlot.slot.epoch:
|
|
for attestation in signedBlock.message.body.attestations:
|
|
node.onAttestation(attestation)
|
|
ok()
|
|
|
|
proc onBeaconBlock*(node: BeaconNode, signedBlock: SignedBeaconBlock) =
|
|
# We received a block but don't know much about it yet - in particular, we
|
|
# don't know if it's part of the chain we're currently building.
|
|
discard node.storeBlock(signedBlock)
|
|
|
|
proc updateHead*(node: BeaconNode): BlockRef =
|
|
# Check pending attestations - maybe we found some blocks for them
|
|
node.attestationPool.resolve()
|
|
|
|
# Grab the new head according to our latest attestation data
|
|
let newHead = node.attestationPool.selectHead()
|
|
|
|
# Store the new head in the block pool - this may cause epochs to be
|
|
# justified and finalized
|
|
node.blockPool.updateHead(newHead)
|
|
beacon_head_root.set newHead.root.toGaugeValue
|
|
|
|
newHead
|
|
|
|
template findIt*(s: openarray, predicate: untyped): int64 =
|
|
var res = -1
|
|
for i, it {.inject.} in s:
|
|
if predicate:
|
|
res = i
|
|
break
|
|
res
|