nimbus-eth2/beacon_chain/beacon_node.nim

210 lines
6.7 KiB
Nim
Raw Normal View History

import
2018-12-05 13:58:41 +00:00
os, net, sequtils,
2018-11-29 01:08:34 +00:00
asyncdispatch2, chronicles, confutils, eth_p2p, eth_keys,
2018-12-05 13:58:41 +00:00
spec/[beaconstate, datatypes, helpers, crypto], conf, time, fork_choice,
2018-11-26 13:33:06 +00:00
beacon_chain_db, validator_pool, mainchain_monitor,
sync_protocol, gossipsub_protocol, trusted_state_snapshots
type
BeaconNode* = ref object
beaconState*: BeaconState
network*: EthereumNode
db*: BeaconChainDB
2018-11-29 01:08:34 +00:00
config*: BeaconNodeConf
keys*: KeyPair
2018-11-26 13:33:06 +00:00
attachedValidators: ValidatorPool
attestations: AttestationPool
headBlock: BeaconBlock
mainchainMonitor: MainchainMonitor
const
2018-11-29 01:08:34 +00:00
version = "v0.1" # TODO: read this from the nimble file
clientId = "nimbus beacon node " & version
2018-11-26 13:33:06 +00:00
topicBeaconBlocks = "ethereum/2.1/beacon_chain/blocks"
topicAttestations = "ethereum/2.1/beacon_chain/attestations"
proc ensureNetworkKeys*(dataDir: string): KeyPair =
# TODO:
# 1. Check if keys already exist in the data dir
# 2. Generate new ones and save them in the directory
# if necessary
return newKeyPair()
2018-11-29 01:08:34 +00:00
proc init*(T: type BeaconNode, conf: BeaconNodeConf): T =
new result
result.config = conf
2018-11-29 01:08:34 +00:00
result.db = BeaconChainDB.init(string conf.dataDir)
result.keys = ensureNetworkKeys(string conf.dataDir)
var address: Address
address.ip = parseIpAddress("0.0.0.0")
address.tcpPort = Port(conf.tcpPort)
address.udpPort = Port(conf.udpPort)
result.network = newEthereumNode(result.keys, address, 0, nil, clientId)
proc sync*(node: BeaconNode): Future[bool] {.async.} =
let persistedState = node.db.lastFinalizedState()
if persistedState.isNil or
2018-11-29 01:08:34 +00:00
persistedState[].slotDistanceFromNow() > WEAK_SUBJECTVITY_PERIOD:
node.beaconState = await obtainTrustedStateSnapshot(node.db)
else:
node.beaconState = persistedState[]
var targetSlot = toSlot timeSinceGenesis(node.beaconState)
while node.beaconState.finalized_slot.int < targetSlot:
2018-11-29 01:08:34 +00:00
var (peer, changeLog) = await node.network.getValidatorChangeLog(
node.beaconState.validator_registry_delta_chain_tip)
if peer == nil:
error "Failed to sync with any peer"
return false
if applyValidatorChangeLog(changeLog, node.beaconState):
2018-11-29 01:08:34 +00:00
node.db.persistBlock(node.beaconState, changeLog.signedBlock)
else:
warn "Ignoring invalid validator change log", sentFrom = peer
return true
2018-12-05 13:58:41 +00:00
template findIt(s: openarray, predicate: untyped): int =
var res = -1
for i, it {.inject.} in s:
if predicate:
res = i
break
res
proc addLocalValidators*(node: BeaconNode) =
for validator in node.config.validatorKeys:
# 1. Parse the validator keys
2018-12-05 13:58:41 +00:00
let privKey = loadPrivKey(validator)
let pubKey = privKey.pubKey()
# 2. Check whether the validators exist in the beacon state.
# (Report a warning otherwise)
2018-12-05 13:58:41 +00:00
let idx = node.beaconState.validator_registry.findIt(it.pubKey == pubKey)
if idx == -1:
warn "Validator not in registry", pubKey
else:
# 3. Add the validators to node.attachedValidators
# TODO: Parse randao secret
node.attachedValidators.addLocalValidator(idx, pubKey, privKey, @[])
2018-11-26 13:33:06 +00:00
proc getAttachedValidator(node: BeaconNode, idx: int): AttachedValidator =
let validatorKey = node.beaconState.validator_registry[idx].pubkey
2018-11-26 13:33:06 +00:00
return node.attachedValidators.getValidator(validatorKey)
2018-11-29 01:08:34 +00:00
proc makeAttestation(node: BeaconNode,
validator: AttachedValidator) {.async.} =
var attestation: Attestation
attestation.validator = validator.idx
2018-11-26 13:33:06 +00:00
2018-11-29 01:08:34 +00:00
# TODO: Populate attestation.data
2018-11-26 13:33:06 +00:00
2018-11-29 01:08:34 +00:00
attestation.signature = await validator.signAttestation(attestation.data)
await node.network.broadcast(topicAttestations, attestation)
2018-11-26 13:33:06 +00:00
2018-11-29 01:08:34 +00:00
proc proposeBlock(node: BeaconNode,
validator: AttachedValidator,
slot: int) {.async.} =
var proposal: BeaconBlock
2018-11-26 13:33:06 +00:00
2018-11-29 01:08:34 +00:00
# TODO:
# 1. Produce a RANDAO reveal from attachedVadalidator.randaoSecret
# and its matching ValidatorRecord.
2018-11-26 13:33:06 +00:00
2018-11-29 01:08:34 +00:00
# 2. Get ancestors from the beacon_db
2018-11-26 13:33:06 +00:00
2018-11-29 01:08:34 +00:00
# 3. Calculate the correct state hash
2018-11-26 13:33:06 +00:00
2018-11-29 01:08:34 +00:00
proposal.candidate_pow_receipt_root =
node.mainchainMonitor.getBeaconBlockRef()
2018-11-26 13:33:06 +00:00
2018-11-29 01:08:34 +00:00
for a in node.attestations.each(firstSlot = node.headBlock.slot.int + 1,
lastSlot = slot - MIN_ATTESTATION_INCLUSION_DELAY):
# TODO: this is not quite right,
# the attestations from individual validators have to be merged.
# proposal.attestations.add a
discard
2018-11-26 13:33:06 +00:00
2018-11-29 01:08:34 +00:00
for r in node.mainchainMonitor.getValidatorActions(
node.headBlock.candidate_pow_receipt_root,
proposal.candidate_pow_receipt_root):
proposal.specials.add r
2018-11-26 13:33:06 +00:00
2018-11-29 01:08:34 +00:00
var signedData: ProposalSignedData
# TODO: populate the signed data
2018-11-26 13:33:06 +00:00
2018-11-29 01:08:34 +00:00
proposal.proposer_signature = await validator.signBlockProposal(signedData)
await node.network.broadcast(topicBeaconBlocks, proposal)
2018-11-26 13:33:06 +00:00
2018-11-29 01:08:34 +00:00
proc scheduleCycleActions(node: BeaconNode) =
## This schedules the required block proposals and
## attestations from our attached validators.
let cycleStart = node.beaconState.latest_state_recalculation_slot.int
for i in 0 ..< EPOCH_LENGTH:
2018-11-26 13:33:06 +00:00
# Schedule block proposals
let
2018-11-26 13:33:06 +00:00
slot = cycleStart + i
proposerIdx = get_beacon_proposer_index(node.beaconState, slot.uint64)
attachedValidator = node.getAttachedValidator(proposerIdx.int)
2018-11-26 13:33:06 +00:00
if attachedValidator != nil:
# TODO:
# Warm-up the proposer earlier to try to obtain previous
# missing blocks if necessary
2018-11-29 01:08:34 +00:00
addTimer(node.beaconState.slotStart(slot)) do (p: pointer):
asyncCheck proposeBlock(node, attachedValidator, slot)
2018-11-26 13:33:06 +00:00
# Schedule attestations
let
committeesIdx = get_shard_and_committees_index(node.beaconState, slot.uint64)
2018-11-26 13:33:06 +00:00
2018-11-29 01:08:34 +00:00
for shard in node.beaconState.shard_and_committee_for_slots[committees_idx]:
for validatorIdx in shard.committee:
let attachedValidator = node.getAttachedValidator(validatorIdx)
if attachedValidator != nil:
addTimer(node.beaconState.slotMiddle(slot)) do (p: pointer):
asyncCheck makeAttestation(node, attachedValidator)
proc processBlocks*(node: BeaconNode) {.async.} =
node.scheduleCycleActions()
node.network.subscribe(topicBeaconBlocks) do (b: BeaconBlock):
# TODO:
#
# 1. Check for missing blocks and obtain them
2018-11-26 13:33:06 +00:00
#
# 2. Apply fork-choice rule (update node.headBlock)
#
# 3. Peform block processing / state recalculation / etc
#
if b.slot mod EPOCH_LENGTH == 0:
node.scheduleCycleActions()
2018-11-26 13:33:06 +00:00
node.attestations.discardHistoryToSlot(b.slot)
node.network.subscribe(topicAttestations) do (a: Attestation):
# TODO
#
# 1. Validate the attestation
node.attestations.add(a, node.beaconState)
when isMainModule:
2018-11-29 01:08:34 +00:00
let config = BeaconNodeConf.load()
waitFor syncrhronizeClock()
2018-11-29 01:08:34 +00:00
var node = BeaconNode.init config
if not waitFor node.sync():
quit 1
node.addLocalValidators()
waitFor node.processBlocks()