2018-11-23 23:58:49 +00:00
|
|
|
import
|
2019-01-14 12:19:44 +00:00
|
|
|
std_shims/[os_shims, objects], net, sequtils, options, tables,
|
2019-02-06 17:56:04 +00:00
|
|
|
chronos, chronicles, confutils, eth/[p2p, keys],
|
2019-01-26 19:32:10 +00:00
|
|
|
spec/[datatypes, digest, crypto, beaconstate, helpers, validator], conf, time,
|
2018-12-28 16:51:40 +00:00
|
|
|
state_transition, fork_choice, ssz, beacon_chain_db, validator_pool, extras,
|
2019-02-19 23:35:02 +00:00
|
|
|
attestation_pool,
|
2019-01-08 17:28:21 +00:00
|
|
|
mainchain_monitor, sync_protocol, gossipsub_protocol, trusted_state_snapshots,
|
2019-02-05 19:21:18 +00:00
|
|
|
eth/trie/db, eth/trie/backends/rocksdb_backend
|
2018-11-23 23:58:49 +00:00
|
|
|
|
|
|
|
type
|
|
|
|
BeaconNode* = ref object
|
|
|
|
beaconState*: BeaconState
|
|
|
|
network*: EthereumNode
|
|
|
|
db*: BeaconChainDB
|
2018-11-29 01:08:34 +00:00
|
|
|
config*: BeaconNodeConf
|
2018-11-23 23:58:49 +00:00
|
|
|
keys*: KeyPair
|
2018-11-26 13:33:06 +00:00
|
|
|
attachedValidators: ValidatorPool
|
2018-12-28 16:51:40 +00:00
|
|
|
attestationPool: AttestationPool
|
2018-11-26 13:33:06 +00:00
|
|
|
mainchainMonitor: MainchainMonitor
|
2019-02-18 18:54:05 +00:00
|
|
|
lastScheduledEpoch: EpochNumber
|
2019-01-05 21:01:26 +00:00
|
|
|
headBlock: BeaconBlock
|
|
|
|
headBlockRoot: Eth2Digest
|
2019-01-08 17:28:21 +00:00
|
|
|
blocksChildren: Table[Eth2Digest, seq[Eth2Digest]]
|
2018-11-23 23:58:49 +00:00
|
|
|
|
|
|
|
const
|
2018-11-29 01:08:34 +00:00
|
|
|
version = "v0.1" # TODO: read this from the nimble file
|
2018-11-23 23:58:49 +00:00
|
|
|
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"
|
2018-11-23 23:58:49 +00:00
|
|
|
|
2019-02-14 19:32:33 +00:00
|
|
|
stateStoragePeriod = EPOCH_LENGTH.uint64 * 10 # Save states once per this number of slots. TODO: Find a good number.
|
2019-01-31 16:06:48 +00:00
|
|
|
|
2019-02-07 21:14:08 +00:00
|
|
|
func shortHash(x: auto): string =
|
|
|
|
($x)[0..7]
|
|
|
|
|
2019-01-25 17:35:22 +00:00
|
|
|
func shortValidatorKey(node: BeaconNode, validatorIdx: int): string =
|
|
|
|
($node.beaconState.validator_registry[validatorIdx].pubkey)[0..7]
|
2019-01-05 21:01:26 +00:00
|
|
|
|
2018-11-23 23:58:49 +00:00
|
|
|
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 =
|
2018-11-23 23:58:49 +00:00
|
|
|
new result
|
|
|
|
result.config = conf
|
2018-12-19 12:58:53 +00:00
|
|
|
|
2018-12-28 16:51:40 +00:00
|
|
|
result.attachedValidators = ValidatorPool.init
|
2019-02-19 23:35:02 +00:00
|
|
|
init result.attestationPool, 42 # TODO compile failure without the dummy int??
|
2018-12-28 16:51:40 +00:00
|
|
|
init result.mainchainMonitor, "", Port(0) # TODO: specify geth address and port
|
|
|
|
|
2019-01-14 12:19:44 +00:00
|
|
|
let trieDB = trieDB newChainDb(string conf.dataDir)
|
|
|
|
result.db = BeaconChainDB.init(trieDB)
|
2019-01-25 14:17:35 +00:00
|
|
|
|
|
|
|
if not result.db.isInitialized:
|
|
|
|
# Use stateSnapshot as genesis
|
|
|
|
info "Initializing DB"
|
|
|
|
result.db.persistState(result.config.stateSnapshot.get)
|
|
|
|
|
2018-11-29 01:08:34 +00:00
|
|
|
result.keys = ensureNetworkKeys(string conf.dataDir)
|
2018-11-23 23:58:49 +00:00
|
|
|
|
|
|
|
var address: Address
|
2018-12-28 16:51:40 +00:00
|
|
|
address.ip = parseIpAddress("127.0.0.1")
|
2018-11-23 23:58:49 +00:00
|
|
|
address.tcpPort = Port(conf.tcpPort)
|
|
|
|
address.udpPort = Port(conf.udpPort)
|
2018-12-19 12:58:53 +00:00
|
|
|
|
2018-12-28 16:51:40 +00:00
|
|
|
result.network = newEthereumNode(result.keys, address, 0, nil, clientId, minPeers = 1)
|
2018-11-23 23:58:49 +00:00
|
|
|
|
2018-12-19 12:58:53 +00:00
|
|
|
writeFile(string(conf.dataDir) / "beacon_node.address",
|
|
|
|
$result.network.listeningAddress)
|
|
|
|
|
|
|
|
proc connectToNetwork(node: BeaconNode) {.async.} =
|
|
|
|
var bootstrapNodes = newSeq[ENode]()
|
|
|
|
|
|
|
|
for node in node.config.bootstrapNodes:
|
|
|
|
bootstrapNodes.add initENode(node)
|
|
|
|
|
|
|
|
let bootstrapFile = string node.config.bootstrapNodesFile
|
|
|
|
if bootstrapFile.len > 0:
|
|
|
|
for ln in lines(bootstrapFile):
|
|
|
|
bootstrapNodes.add initENode(string ln)
|
|
|
|
|
|
|
|
if bootstrapNodes.len > 0:
|
2018-12-28 16:51:40 +00:00
|
|
|
info "Connecting to bootstrap nodes", bootstrapNodes
|
2018-12-19 12:58:53 +00:00
|
|
|
await node.network.connectToNetwork(bootstrapNodes)
|
|
|
|
else:
|
2018-12-28 16:51:40 +00:00
|
|
|
info "Waiting for connections"
|
2018-12-19 12:58:53 +00:00
|
|
|
node.network.startListening()
|
|
|
|
|
2018-11-23 23:58:49 +00:00
|
|
|
proc sync*(node: BeaconNode): Future[bool] {.async.} =
|
|
|
|
let persistedState = node.db.lastFinalizedState()
|
2019-01-25 14:17:35 +00:00
|
|
|
if persistedState.slotDistanceFromNow() > WEAK_SUBJECTVITY_PERIOD.int64:
|
|
|
|
node.beaconState = await obtainTrustedStateSnapshot(node.db)
|
2018-11-23 23:58:49 +00:00
|
|
|
else:
|
2019-01-25 14:17:35 +00:00
|
|
|
node.beaconState = persistedState
|
2019-02-07 15:38:46 +00:00
|
|
|
var targetSlot = node.beaconState.getSlotFromTime()
|
2018-11-23 23:58:49 +00:00
|
|
|
|
2019-01-16 23:01:15 +00:00
|
|
|
let t = now()
|
|
|
|
if t < node.beaconState.genesisTime * 1000:
|
|
|
|
await sleepAsync int(node.beaconState.genesisTime * 1000 - t)
|
|
|
|
|
2019-02-15 16:33:32 +00:00
|
|
|
# TODO: change this to a full sync / block download
|
|
|
|
info "Syncing state from remote peers",
|
|
|
|
finalized_epoch = humaneEpochNum(node.beaconState.finalized_epoch),
|
|
|
|
target_slot_epoch = humaneEpochNum(targetSlot.slot_to_epoch)
|
|
|
|
|
2019-01-29 03:22:22 +00:00
|
|
|
while node.beaconState.finalized_epoch < targetSlot.slot_to_epoch:
|
2018-11-29 01:08:34 +00:00
|
|
|
var (peer, changeLog) = await node.network.getValidatorChangeLog(
|
2018-12-03 21:41:24 +00:00
|
|
|
node.beaconState.validator_registry_delta_chain_tip)
|
2018-11-23 23:58:49 +00:00
|
|
|
|
|
|
|
if peer == nil:
|
|
|
|
error "Failed to sync with any peer"
|
|
|
|
return false
|
|
|
|
|
|
|
|
if applyValidatorChangeLog(changeLog, node.beaconState):
|
2019-01-31 16:06:48 +00:00
|
|
|
node.db.persistState(node.beaconState)
|
|
|
|
node.db.persistBlock(changeLog.signedBlock)
|
2018-11-23 23:58:49 +00:00
|
|
|
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
|
|
|
|
|
2018-11-23 23:58:49 +00:00
|
|
|
proc addLocalValidators*(node: BeaconNode) =
|
2018-12-19 12:58:53 +00:00
|
|
|
for validator in node.config.validators:
|
|
|
|
let
|
|
|
|
privKey = validator.privKey
|
|
|
|
pubKey = privKey.pubKey()
|
|
|
|
|
2019-02-07 10:51:21 +00:00
|
|
|
let idx = node.beaconState.validator_registry.findIt(it.pubKey == pubKey)
|
2018-12-05 13:58:41 +00:00
|
|
|
if idx == -1:
|
|
|
|
warn "Validator not in registry", pubKey
|
|
|
|
else:
|
2019-01-25 17:35:22 +00:00
|
|
|
debug "Attaching validator", validator = shortValidatorKey(node, idx),
|
|
|
|
idx, pubKey
|
2019-02-14 21:41:04 +00:00
|
|
|
node.attachedValidators.addLocalValidator(idx, pubKey, privKey)
|
2018-12-05 13:58:41 +00:00
|
|
|
|
2018-12-28 16:51:40 +00:00
|
|
|
info "Local validators attached ", count = node.attachedValidators.count
|
2018-11-23 23:58:49 +00:00
|
|
|
|
2018-11-26 13:33:06 +00:00
|
|
|
proc getAttachedValidator(node: BeaconNode, idx: int): AttachedValidator =
|
2018-12-03 21:41:24 +00:00
|
|
|
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,
|
2018-12-28 16:51:40 +00:00
|
|
|
validator: AttachedValidator,
|
2019-02-18 18:54:05 +00:00
|
|
|
slot: SlotNumber,
|
2018-12-28 16:51:40 +00:00
|
|
|
shard: uint64,
|
|
|
|
committeeLen: int,
|
|
|
|
indexInCommittee: int) {.async.} =
|
|
|
|
doAssert node != nil
|
|
|
|
doAssert validator != nil
|
|
|
|
|
2019-02-19 23:35:02 +00:00
|
|
|
var state = node.beaconState
|
2019-01-16 23:01:15 +00:00
|
|
|
|
2019-02-19 23:35:02 +00:00
|
|
|
if state.slot < slot:
|
|
|
|
info "Filling slot gap for attestation",
|
|
|
|
slot = humaneSlotNum(slot),
|
|
|
|
stateSlot = humaneSlotNum(state.slot)
|
2018-12-28 16:51:40 +00:00
|
|
|
|
2019-02-19 23:35:02 +00:00
|
|
|
for s in state.slot ..< slot:
|
|
|
|
let ok = updateState(
|
|
|
|
state, node.headBlockRoot, none[BeaconBlock](), {skipValidation})
|
|
|
|
doAssert ok
|
2018-12-28 16:51:40 +00:00
|
|
|
|
2019-02-19 23:35:02 +00:00
|
|
|
let
|
|
|
|
justifiedBlockRoot =
|
|
|
|
get_block_root(state, get_epoch_start_slot(state.justified_epoch))
|
|
|
|
|
|
|
|
attestationData = AttestationData(
|
|
|
|
slot: slot,
|
|
|
|
shard: shard,
|
|
|
|
beacon_block_root: node.headBlockRoot,
|
|
|
|
epoch_boundary_root: Eth2Digest(), # TODO
|
|
|
|
shard_block_root: Eth2Digest(), # TODO
|
|
|
|
latest_crosslink: Crosslink(), # TODO
|
|
|
|
justified_epoch: state.justified_epoch,
|
|
|
|
justified_block_root: justifiedBlockRoot)
|
|
|
|
|
|
|
|
validatorSignature = await validator.signAttestation(attestationData)
|
2018-12-28 16:51:40 +00:00
|
|
|
|
|
|
|
var participationBitfield = repeat(0'u8, ceil_div8(committeeLen))
|
|
|
|
bitSet(participationBitfield, indexInCommittee)
|
|
|
|
|
|
|
|
var attestation = Attestation(
|
|
|
|
data: attestationData,
|
|
|
|
aggregate_signature: validatorSignature,
|
2019-02-12 22:50:02 +00:00
|
|
|
aggregation_bitfield: participationBitfield,
|
|
|
|
# Stub in phase0
|
|
|
|
custody_bitfield: newSeq[byte](participationBitfield.len)
|
|
|
|
)
|
2018-11-26 13:33:06 +00:00
|
|
|
|
2018-11-29 01:08:34 +00:00
|
|
|
await node.network.broadcast(topicAttestations, attestation)
|
2018-11-26 13:33:06 +00:00
|
|
|
|
2019-02-19 23:35:02 +00:00
|
|
|
info "Attestation sent",
|
|
|
|
slot = humaneSlotNum(attestationData.slot),
|
|
|
|
shard = attestationData.shard,
|
|
|
|
validator = shortValidatorKey(node, validator.idx),
|
|
|
|
signature = shortHash(validatorSignature),
|
|
|
|
beaconBlockRoot = shortHash(attestationData.beacon_block_root)
|
2018-12-28 16:51:40 +00:00
|
|
|
|
2018-11-29 01:08:34 +00:00
|
|
|
proc proposeBlock(node: BeaconNode,
|
|
|
|
validator: AttachedValidator,
|
2019-02-18 18:54:05 +00:00
|
|
|
slot: SlotNumber) {.async.} =
|
2018-12-28 16:51:40 +00:00
|
|
|
doAssert node != nil
|
|
|
|
doAssert validator != nil
|
|
|
|
doAssert validator.idx < node.beaconState.validator_registry.len
|
|
|
|
|
2019-01-05 21:01:26 +00:00
|
|
|
var state = node.beaconState
|
|
|
|
|
2019-02-19 23:35:02 +00:00
|
|
|
if state.slot + 1 < slot:
|
|
|
|
info "Filling slot gap for block proposal",
|
2019-02-07 21:14:08 +00:00
|
|
|
slot = humaneSlotNum(slot),
|
2019-02-19 23:35:02 +00:00
|
|
|
stateSlot = humaneSlotNum(state.slot)
|
|
|
|
|
|
|
|
for s in state.slot + 1 ..< slot:
|
|
|
|
let ok = updateState(
|
|
|
|
state, node.headBlockRoot, none[BeaconBlock](), {skipValidation})
|
2019-01-25 17:35:22 +00:00
|
|
|
doAssert ok
|
2019-01-05 21:01:26 +00:00
|
|
|
|
2018-12-28 16:51:40 +00:00
|
|
|
var blockBody = BeaconBlockBody(
|
2019-02-19 23:35:02 +00:00
|
|
|
attestations: node.attestationPool.getAttestationsForBlock(state, slot))
|
2018-12-28 16:51:40 +00:00
|
|
|
|
|
|
|
var newBlock = BeaconBlock(
|
|
|
|
slot: slot,
|
2019-01-05 21:01:26 +00:00
|
|
|
parent_root: node.headBlockRoot,
|
2019-02-14 21:41:04 +00:00
|
|
|
randao_reveal: validator.genRandaoReveal(state, state.slot),
|
2019-01-18 00:14:22 +00:00
|
|
|
eth1_data: node.mainchainMonitor.getBeaconBlockRef(),
|
2018-12-28 16:51:40 +00:00
|
|
|
signature: ValidatorSig(), # we need the rest of the block first!
|
|
|
|
body: blockBody)
|
|
|
|
|
2019-02-19 23:35:02 +00:00
|
|
|
let ok =
|
|
|
|
updateState(state, node.headBlockRoot, some(newBlock), {skipValidation})
|
2018-12-28 16:51:40 +00:00
|
|
|
doAssert ok # TODO: err, could this fail somehow?
|
2018-11-26 13:33:06 +00:00
|
|
|
|
2018-12-28 16:51:40 +00:00
|
|
|
newBlock.state_root = Eth2Digest(data: hash_tree_root(state))
|
2018-11-26 13:33:06 +00:00
|
|
|
|
2018-12-28 16:51:40 +00:00
|
|
|
var signedData = ProposalSignedData(
|
|
|
|
slot: slot,
|
|
|
|
shard: BEACON_CHAIN_SHARD_NUMBER,
|
|
|
|
blockRoot: hash_tree_root_final(newBlock))
|
2018-11-26 13:33:06 +00:00
|
|
|
|
2019-02-19 23:35:02 +00:00
|
|
|
newBlock.signature = await validator.signBlockProposal(state.fork, signedData)
|
2018-11-26 13:33:06 +00:00
|
|
|
|
2018-12-28 16:51:40 +00:00
|
|
|
await node.network.broadcast(topicBeaconBlocks, newBlock)
|
2018-11-26 13:33:06 +00:00
|
|
|
|
2019-02-19 23:35:02 +00:00
|
|
|
info "Block proposed",
|
|
|
|
slot = humaneSlotNum(slot),
|
|
|
|
stateRoot = shortHash(newBlock.state_root),
|
|
|
|
validator = shortValidatorKey(node, validator.idx),
|
|
|
|
idx = validator.idx
|
2018-11-26 13:33:06 +00:00
|
|
|
|
2018-12-28 16:51:40 +00:00
|
|
|
proc scheduleBlockProposal(node: BeaconNode,
|
2019-02-18 18:54:05 +00:00
|
|
|
slot: SlotNumber,
|
2018-12-28 16:51:40 +00:00
|
|
|
validator: AttachedValidator) =
|
|
|
|
# TODO:
|
|
|
|
# This function exists only to hide a bug with Nim's closures.
|
2019-01-09 01:01:07 +00:00
|
|
|
# If you inline it in `scheduleEpochActions`, you'll see the
|
2018-12-28 16:51:40 +00:00
|
|
|
# internal `doAssert` starting to fail.
|
|
|
|
doAssert validator != nil
|
|
|
|
|
2019-02-19 23:35:02 +00:00
|
|
|
let
|
|
|
|
at = node.beaconState.slotStart(slot)
|
|
|
|
now = fastEpochTime()
|
|
|
|
|
|
|
|
if now > at:
|
|
|
|
warn "Falling behind on block proposals", at, now, slot
|
2019-01-25 17:35:22 +00:00
|
|
|
|
|
|
|
info "Scheduling block proposal",
|
|
|
|
validator = shortValidatorKey(node, validator.idx),
|
2019-02-12 15:56:58 +00:00
|
|
|
idx = validator.idx,
|
2019-02-07 21:14:08 +00:00
|
|
|
slot = humaneSlotNum(slot),
|
2019-02-19 23:35:02 +00:00
|
|
|
fromNow = (at - now) div 1000
|
2019-01-25 17:35:22 +00:00
|
|
|
|
2019-02-19 23:35:02 +00:00
|
|
|
addTimer(at) do (x: pointer) {.gcsafe.}:
|
2019-01-25 17:35:22 +00:00
|
|
|
# TODO timers are generally not accurate / guaranteed to fire at the right
|
|
|
|
# time - need to guard here against early / late firings
|
2018-12-28 16:51:40 +00:00
|
|
|
doAssert validator != nil
|
2019-02-18 18:54:05 +00:00
|
|
|
asyncCheck proposeBlock(node, validator, slot)
|
2018-12-28 16:51:40 +00:00
|
|
|
|
|
|
|
proc scheduleAttestation(node: BeaconNode,
|
|
|
|
validator: AttachedValidator,
|
2019-02-18 18:54:05 +00:00
|
|
|
slot: SlotNumber,
|
2018-12-28 16:51:40 +00:00
|
|
|
shard: uint64,
|
|
|
|
committeeLen: int,
|
|
|
|
indexInCommittee: int) =
|
|
|
|
# TODO:
|
|
|
|
# This function exists only to hide a bug with Nim's closures.
|
2019-01-09 01:01:07 +00:00
|
|
|
# If you inline it in `scheduleEpochActions`, you'll see the
|
2018-12-28 16:51:40 +00:00
|
|
|
# internal `doAssert` starting to fail.
|
|
|
|
doAssert validator != nil
|
|
|
|
|
2019-02-19 23:35:02 +00:00
|
|
|
let
|
|
|
|
at = node.beaconState.slotStart(slot)
|
|
|
|
now = fastEpochTime()
|
|
|
|
|
|
|
|
if now > at:
|
|
|
|
warn "Falling behind on attestations", at, now, slot
|
|
|
|
|
|
|
|
debug "Scheduling attestation",
|
|
|
|
validator = shortValidatorKey(node, validator.idx),
|
|
|
|
fromNow = (at - now) div 1000,
|
|
|
|
slot = humaneSlotNum(slot),
|
|
|
|
shard
|
|
|
|
|
|
|
|
addTimer(at) do (p: pointer) {.gcsafe.}:
|
2018-12-28 16:51:40 +00:00
|
|
|
doAssert validator != nil
|
2019-02-18 18:54:05 +00:00
|
|
|
asyncCheck makeAttestation(node, validator, slot,
|
2018-12-28 16:51:40 +00:00
|
|
|
shard, committeeLen, indexInCommittee)
|
2018-11-26 13:33:06 +00:00
|
|
|
|
2019-02-18 18:54:05 +00:00
|
|
|
proc scheduleEpochActions(node: BeaconNode, epoch: EpochNumber) =
|
2018-11-23 23:58:49 +00:00
|
|
|
## This schedules the required block proposals and
|
|
|
|
## attestations from our attached validators.
|
2018-12-28 16:51:40 +00:00
|
|
|
doAssert node != nil
|
2019-02-19 23:35:02 +00:00
|
|
|
doAssert epoch >= GENESIS_EPOCH,
|
|
|
|
"Epoch: " & $epoch & ", humane epoch: " & $humaneSlotNum(epoch)
|
2018-12-28 16:51:40 +00:00
|
|
|
|
2019-02-15 16:33:32 +00:00
|
|
|
debug "Scheduling epoch actions", epoch = humaneEpochNum(epoch)
|
2019-01-21 19:42:37 +00:00
|
|
|
|
2018-12-28 16:51:40 +00:00
|
|
|
# TODO: this copy of the state shouldn't be necessary, but please
|
|
|
|
# see the comments in `get_beacon_proposer_index`
|
|
|
|
var nextState = node.beaconState
|
2018-11-23 23:58:49 +00:00
|
|
|
|
2019-02-12 15:56:58 +00:00
|
|
|
let start = if epoch == GENESIS_EPOCH: 1.uint64 else: 0.uint64
|
|
|
|
|
|
|
|
for i in start ..< EPOCH_LENGTH:
|
|
|
|
let slot = epoch * EPOCH_LENGTH + i
|
2019-02-19 23:35:02 +00:00
|
|
|
nextState.slot = slot # ugly trick, see get_beacon_proposer_index
|
|
|
|
|
|
|
|
block: # Schedule block proposals
|
|
|
|
let proposerIdx = get_beacon_proposer_index(nextState, slot)
|
|
|
|
let validator = node.getAttachedValidator(proposerIdx)
|
|
|
|
|
|
|
|
if validator != nil:
|
|
|
|
# TODO:
|
|
|
|
# Warm-up the proposer earlier to try to obtain previous
|
|
|
|
# missing blocks if necessary
|
|
|
|
scheduleBlockProposal(node, slot, validator)
|
|
|
|
|
|
|
|
block: # Schedule attestations
|
|
|
|
for crosslink_committee in get_crosslink_committees_at_slot(
|
|
|
|
nextState, slot):
|
|
|
|
for i, validatorIdx in crosslink_committee.committee:
|
|
|
|
let validator = node.getAttachedValidator(validatorIdx)
|
|
|
|
if validator != nil:
|
|
|
|
scheduleAttestation(
|
|
|
|
node, validator, slot, crosslink_committee.shard,
|
|
|
|
crosslink_committee.committee.len, i)
|
2018-12-28 16:51:40 +00:00
|
|
|
|
2019-01-09 01:01:07 +00:00
|
|
|
node.lastScheduledEpoch = epoch
|
2019-01-25 17:35:22 +00:00
|
|
|
let
|
2019-02-18 15:58:34 +00:00
|
|
|
nextEpoch = epoch + 1
|
2019-01-25 17:35:22 +00:00
|
|
|
at = node.beaconState.slotMiddle(nextEpoch * EPOCH_LENGTH)
|
2018-11-23 23:58:49 +00:00
|
|
|
|
2019-01-25 17:35:22 +00:00
|
|
|
info "Scheduling next epoch update",
|
|
|
|
fromNow = (at - fastEpochTime()) div 1000,
|
2019-02-18 15:58:34 +00:00
|
|
|
epoch = humaneEpochNum(nextEpoch)
|
2019-01-25 17:35:22 +00:00
|
|
|
|
|
|
|
addTimer(at) do (p: pointer):
|
2019-01-09 01:01:07 +00:00
|
|
|
if node.lastScheduledEpoch != nextEpoch:
|
|
|
|
node.scheduleEpochActions(nextEpoch)
|
2018-11-23 23:58:49 +00:00
|
|
|
|
2019-01-31 16:06:48 +00:00
|
|
|
proc stateNeedsSaving(s: BeaconState): bool =
|
|
|
|
# TODO: Come up with a better predicate logic
|
|
|
|
s.slot mod stateStoragePeriod == 0
|
|
|
|
|
2018-12-28 16:51:40 +00:00
|
|
|
proc processBlocks*(node: BeaconNode) =
|
2019-01-05 21:01:26 +00:00
|
|
|
node.network.subscribe(topicBeaconBlocks) do (newBlock: BeaconBlock):
|
2019-01-25 17:35:22 +00:00
|
|
|
let stateSlot = node.beaconState.slot
|
2019-02-19 23:35:02 +00:00
|
|
|
info "Block received",
|
|
|
|
slot = humaneSlotNum(newBlock.slot),
|
|
|
|
stateRoot = shortHash(newBlock.state_root),
|
|
|
|
stateSlot = humaneSlotNum(stateSlot)
|
2019-01-21 19:42:37 +00:00
|
|
|
|
|
|
|
# TODO: This should be replaced with the real fork-choice rule
|
2019-01-25 17:35:22 +00:00
|
|
|
if newBlock.slot <= stateSlot:
|
2019-01-21 19:42:37 +00:00
|
|
|
debug "Ignoring block"
|
|
|
|
return
|
2019-01-05 21:01:26 +00:00
|
|
|
|
|
|
|
let newBlockRoot = hash_tree_root_final(newBlock)
|
|
|
|
|
2019-01-21 19:42:37 +00:00
|
|
|
var state = node.beaconState
|
2019-01-25 17:35:22 +00:00
|
|
|
if stateSlot + 1 < newBlock.slot:
|
|
|
|
info "Advancing state past slot gap",
|
2019-02-07 21:14:08 +00:00
|
|
|
blockSlot = humaneSlotNum(newBlock.slot),
|
2019-02-19 23:35:02 +00:00
|
|
|
stateSlot = humaneSlotNum(stateSlot)
|
|
|
|
|
2019-01-25 17:35:22 +00:00
|
|
|
for slot in stateSlot + 1 ..< newBlock.slot:
|
|
|
|
let ok = updateState(state, node.headBlockRoot, none[BeaconBlock](), {})
|
|
|
|
doAssert ok
|
2019-01-05 21:01:26 +00:00
|
|
|
|
2019-01-21 19:42:37 +00:00
|
|
|
let ok = updateState(state, node.headBlockRoot, some(newBlock), {})
|
|
|
|
if not ok:
|
|
|
|
debug "Ignoring non-validating block"
|
|
|
|
return
|
2019-01-05 21:01:26 +00:00
|
|
|
|
|
|
|
node.headBlock = newBlock
|
|
|
|
node.headBlockRoot = newBlockRoot
|
2019-01-21 19:42:37 +00:00
|
|
|
node.beaconState = state
|
2019-01-31 16:06:48 +00:00
|
|
|
|
|
|
|
if stateNeedsSaving(node.beaconState):
|
|
|
|
node.db.persistState(node.beaconState)
|
|
|
|
|
|
|
|
node.db.persistBlock(newBlock)
|
2018-12-28 16:51:40 +00:00
|
|
|
|
2018-11-23 23:58:49 +00:00
|
|
|
# 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
|
|
|
|
#
|
2018-11-23 23:58:49 +00:00
|
|
|
|
2019-01-09 01:01:07 +00:00
|
|
|
let epoch = newBlock.slot.epoch
|
|
|
|
if epoch != node.lastScheduledEpoch:
|
|
|
|
node.scheduleEpochActions(epoch)
|
2018-11-26 13:33:06 +00:00
|
|
|
|
|
|
|
node.network.subscribe(topicAttestations) do (a: Attestation):
|
2019-01-25 17:35:22 +00:00
|
|
|
let participants = get_attestation_participants(
|
2019-02-06 20:37:21 +00:00
|
|
|
node.beaconState, a.data, a.aggregation_bitfield).
|
2019-01-25 17:35:22 +00:00
|
|
|
mapIt(shortValidatorKey(node, it))
|
|
|
|
|
2019-02-19 23:35:02 +00:00
|
|
|
info "Attestation received",
|
|
|
|
slot = humaneSlotNum(a.data.slot),
|
|
|
|
shard = a.data.shard,
|
|
|
|
signature = shortHash(a.aggregate_signature),
|
|
|
|
participants,
|
|
|
|
beaconBlockRoot = shortHash(a.data.beacon_block_root)
|
2018-12-28 16:51:40 +00:00
|
|
|
|
|
|
|
node.attestationPool.add(a, node.beaconState)
|
|
|
|
|
2019-02-07 15:38:46 +00:00
|
|
|
let epoch = node.beaconState.getSlotFromTime div EPOCH_LENGTH
|
2019-01-25 17:35:22 +00:00
|
|
|
node.scheduleEpochActions(epoch)
|
2018-12-28 16:51:40 +00:00
|
|
|
|
2019-01-25 17:35:22 +00:00
|
|
|
runForever()
|
2018-11-23 23:58:49 +00:00
|
|
|
|
2018-12-19 12:58:53 +00:00
|
|
|
var gPidFile: string
|
|
|
|
proc createPidFile(filename: string) =
|
|
|
|
createDir splitFile(filename).dir
|
|
|
|
writeFile filename, $getCurrentProcessId()
|
|
|
|
gPidFile = filename
|
|
|
|
addQuitProc proc {.noconv.} = removeFile gPidFile
|
|
|
|
|
2018-11-23 23:58:49 +00:00
|
|
|
when isMainModule:
|
2018-12-28 16:51:40 +00:00
|
|
|
let config = load BeaconNodeConf
|
2019-01-21 19:42:37 +00:00
|
|
|
if config.logLevel != LogLevel.NONE:
|
|
|
|
setLogLevel(config.logLevel)
|
2019-01-16 23:01:15 +00:00
|
|
|
|
2018-12-19 12:58:53 +00:00
|
|
|
case config.cmd
|
|
|
|
of createChain:
|
2019-02-15 16:33:32 +00:00
|
|
|
createStateSnapshot(
|
|
|
|
config.chainStartupData, config.genesisOffset,
|
|
|
|
config.outputStateFile.string)
|
2018-12-19 12:58:53 +00:00
|
|
|
quit 0
|
|
|
|
|
|
|
|
of noCommand:
|
2019-01-25 17:35:22 +00:00
|
|
|
waitFor synchronizeClock()
|
2018-12-28 16:51:40 +00:00
|
|
|
createPidFile(config.dataDir.string / "beacon_node.pid")
|
2018-12-19 12:58:53 +00:00
|
|
|
|
|
|
|
var node = BeaconNode.init config
|
|
|
|
|
2019-01-25 17:35:22 +00:00
|
|
|
dynamicLogScope(node = node.config.tcpPort - 50000):
|
|
|
|
waitFor node.connectToNetwork()
|
|
|
|
|
|
|
|
if not waitFor node.sync():
|
|
|
|
quit 1
|
2018-12-19 12:58:53 +00:00
|
|
|
|
2019-01-25 17:35:22 +00:00
|
|
|
info "Starting beacon node",
|
|
|
|
slotsSinceFinalization = node.beaconState.slotDistanceFromNow(),
|
2019-02-14 19:32:33 +00:00
|
|
|
stateSlot = humaneSlotNum(node.beaconState.slot),
|
|
|
|
SHARD_COUNT,
|
|
|
|
EPOCH_LENGTH,
|
2019-02-19 23:07:56 +00:00
|
|
|
SECONDS_PER_SLOT,
|
2019-02-14 19:32:33 +00:00
|
|
|
SPEC_VERSION
|
2018-11-23 23:58:49 +00:00
|
|
|
|
2019-01-25 17:35:22 +00:00
|
|
|
node.addLocalValidators()
|
|
|
|
node.processBlocks()
|