Skip to content

Commit

Permalink
- extracted the commands to run a VC into a separate run_validator.sh…
Browse files Browse the repository at this point in the history
… script

    - each BN gets half of its previous validators as inProcess and the other half goes to the respective VC for that BN - using separate data dirs where the keys are copied
    - also removed a few command line options which are no longer necessary
- block proposals originating from a VC are propagated from one BN to the rest properly
- other cleanup & moving code back to  since it is no longer used elsewhere
  • Loading branch information
onqtam committed Jun 10, 2020
1 parent ce897fe commit 3bae40a
Show file tree
Hide file tree
Showing 13 changed files with 361 additions and 273 deletions.
106 changes: 91 additions & 15 deletions beacon_chain/beacon_node.nim
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import
attestation_pool, block_pool, eth2_network, eth2_discovery,
beacon_node_common, beacon_node_types, block_pools/block_pools_types,
nimbus_binary_common,
mainchain_monitor, version, ssz/[merkleization],
mainchain_monitor, version, ssz/[merkleization], sszdump,
sync_protocol, request_manager, validator_keygen, interop, statusbar,
sync_manager, state_transition,
validator_duties, validator_api, attestation_aggregation
Expand Down Expand Up @@ -56,12 +56,16 @@ declareGauge beacon_head_slot,
# Metrics for tracking attestation and beacon block loss
declareCounter beacon_attestations_received,
"Number of beacon chain attestations received by this peer"
declareCounter beacon_blocks_received,
"Number of beacon chain blocks received by this peer"

declareHistogram beacon_attestation_received_seconds_from_slot_start,
"Interval between slot start and attestation receival", buckets = [2.0, 4.0, 6.0, 8.0, 10.0, 12.0, 14.0, Inf]

logScope: topics = "beacnde"

proc onBeaconBlock(node: BeaconNode, signedBlock: SignedBeaconBlock) {.gcsafe.}

proc getStateFromSnapshot(conf: BeaconNodeConf): NilableBeaconStateRef =
var
genesisPath = conf.dataDir/genesisFile
Expand Down Expand Up @@ -253,6 +257,81 @@ proc connectToNetwork(node: BeaconNode) {.async.} =
let addressFile = node.config.dataDir / "beacon_node.address"
writeFile(addressFile, node.network.announcedENR.toURI)

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 the 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)

func verifyFinalization(node: BeaconNode, slot: Slot) =
# Epoch must be >= 4 to check finalization
const SETTLING_TIME_OFFSET = 1'u64
Expand Down Expand Up @@ -621,9 +700,7 @@ proc installDebugApiHandlers(rpcServer: RpcServer, node: BeaconNode) =
return res

proc installRpcHandlers(rpcServer: RpcServer, node: BeaconNode) =
# TODO: remove this if statement later - here just to test the config option for now
if node.config.validatorApi:
rpcServer.installValidatorApiHandlers(node)
rpcServer.installValidatorApiHandlers(node)
rpcServer.installBeaconApiHandlers(node)
rpcServer.installDebugApiHandlers(node)

Expand All @@ -638,6 +715,14 @@ proc installAttestationHandlers(node: BeaconNode) =

node.onAttestation(attestation)

proc attestationValidator(attestation: Attestation,
committeeIndex: uint64): bool =
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/p2p-interface.md#attestation-subnets
let (afterGenesis, slot) = node.beaconClock.now().toSlot()
if not afterGenesis:
return false
node.attestationPool.isValidAttestation(attestation, slot, committeeIndex, {})

var attestationSubscriptions: seq[Future[void]] = @[]

# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/p2p-interface.md#mainnet-3
Expand All @@ -648,25 +733,16 @@ proc installAttestationHandlers(node: BeaconNode) =
getMainnetAttestationTopic(node.forkDigest, ci), attestationHandler,
# This proc needs to be within closureScope; don't lift out of loop.
proc(attestation: Attestation): bool =
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/p2p-interface.md#attestation-subnets
let (afterGenesis, slot) = node.beaconClock.now().toSlot()
if not afterGenesis:
return false
node.attestationPool.isValidAttestation(attestation, slot, ci)))
attestationValidator(attestation, ci)

when ETH2_SPEC == "v0.11.3":
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/p2p-interface.md#interop-3
attestationSubscriptions.add(node.network.subscribe(
getInteropAttestationTopic(node.forkDigest), attestationHandler,
proc(attestation: Attestation): bool =
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/p2p-interface.md#attestation-subnets
let (afterGenesis, slot) = node.beaconClock.now().toSlot()
if not afterGenesis:
return false
# isValidAttestation checks attestation.data.index == topicCommitteeIndex
# which doesn't make sense here, so rig that check to vacuously pass.
node.attestationPool.isValidAttestation(
attestation, slot, attestation.data.index)))
attestationValidator(attestation, attestation.data.index)

waitFor allFutures(attestationSubscriptions)

Expand Down
86 changes: 3 additions & 83 deletions beacon_chain/beacon_node_common.nim
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,15 @@

import
# Standard library
os, tables,
tables,

# Nimble packages
chronos, json_rpc/rpcserver, metrics,
chronicles,

# Local modules
spec/[datatypes, crypto, digest, helpers],
conf, time, beacon_chain_db, sszdump,
ssz/merkleization,
spec/[datatypes, crypto, digest],
conf, time, beacon_chain_db,
attestation_pool, block_pool, eth2_network,
beacon_node_types, mainchain_monitor, request_manager,
sync_manager
Expand Down Expand Up @@ -57,85 +56,6 @@ const
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()
Expand Down
11 changes: 0 additions & 11 deletions beacon_chain/conf.nim
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,6 @@ type
abbr: "v"
name: "validator" }: seq[ValidatorKeyPath]

validatorApi* {.
defaultValue: false
desc: "Specify whether the validator API should be enabled which would allow for external validators (validator clients) to use this beacon node."
name: "validator-api" }: bool

stateSnapshot* {.
desc: "Json file specifying a recent state snapshot."
abbr: "s"
Expand Down Expand Up @@ -299,12 +294,6 @@ type
abbr: "v"
name: "validator" }: seq[ValidatorKeyPath]

delayStart* {.
defaultValue: 0
desc: "Seconds from now to delay the starting of the validator client (useful for debug purposes when starting before the beacon node in a script)."
abbr: "g"
name: "delay-start" }: int

proc defaultDataDir*(conf: BeaconNodeConf|ValidatorClientConf): string =
let dataDir = when defined(windows):
"AppData" / "Roaming" / "Nimbus"
Expand Down
18 changes: 17 additions & 1 deletion beacon_chain/nimbus_binary_common.nim
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import
chronicles, chronicles/helpers as chroniclesHelpers,

# Local modules
spec/[datatypes, crypto], eth2_network
spec/[datatypes, crypto], eth2_network, time

proc setupMainProc*(logLevel: string) =
when compiles(defaultChroniclesStream.output.writer):
Expand Down Expand Up @@ -57,3 +57,19 @@ template ctrlCHandling*(extraCode: untyped) =
template makeBannerAndConfig*(clientId: string, ConfType: type): untyped =
let banner = clientId & "\p" & copyrights & "\p\p" & nimBanner
ConfType.load(version = banner, copyrightBanner = banner)

# TODO not sure if this belongs here but it doesn't belong in `time.nim` either
proc sleepToSlotOffset*(clock: BeaconClock, extra: chronos.Duration,
slot: Slot, msg: static string): Future[bool] {.async.} =
let
fromNow = clock.fromNow(slot.toBeaconTime(extra))

if fromNow.inFuture:
trace msg,
slot = shortLog(slot),
fromNow = shortLog(fromNow.offset),
cat = "scheduling"

await sleepAsync(fromNow.offset)
return true
return false
11 changes: 7 additions & 4 deletions beacon_chain/spec/eth2_apis/validator_callsigs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,22 @@ proc get_v1_beacon_states_fork(stateId: string): Fork
# TODO this doesn't have "validator" in it's path but is used by the validators nonetheless
proc get_v1_beacon_genesis(): BeaconGenesisTuple

# TODO returns a bool even though in the API there is no return type - because of nim-json-rpc
proc post_v1_beacon_pool_attestations(attestation: Attestation): bool

proc get_v1_validator_blocks(slot: Slot, graffiti: Eth2Digest, randao_reveal: ValidatorSig): BeaconBlock

# TODO this doesn't have "validator" in it's path but is used by the validators nonetheless
# TODO returns a bool even though in the API there is no return type - because of nim-json-rpc
proc post_v1_beacon_blocks(body: SignedBeaconBlock): bool

proc get_v1_validator_attestation_data(slot: Slot, committee_index: CommitteeIndex): AttestationData

proc get_v1_validator_aggregate_attestation(query: Eth2Digest): Attestation
proc get_v1_validator_aggregate_attestation(attestation_data_root: Eth2Digest): Attestation

proc post_v1_validator_aggregate_and_proof(payload: SignedAggregateAndProof)
# TODO returns a bool even though in the API there is no return type - because of nim-json-rpc
proc post_v1_validator_aggregate_and_proof(payload: SignedAggregateAndProof): bool

# TODO this should perhaps be a GET instead of a POST?
# this is a POST instead of a GET because of this: https://docs.google.com/spreadsheets/d/1kVIx6GvzVLwNYbcd-Fj8YUlPf4qGrWUlS35uaTnIAVg/edit?disco=AAAAJk5rbKA
proc post_v1_validator_duties_attester(epoch: Epoch, public_keys: seq[ValidatorPubKey]): seq[AttesterDuties]

proc get_v1_validator_duties_proposer(epoch: Epoch): seq[ValidatorPubkeySlotPair]
Expand Down
Loading

0 comments on commit 3bae40a

Please sign in to comment.