mirror of
https://github.com/status-im/nimbus-eth2.git
synced 2025-01-23 21:12:52 +00:00
first batch of work towards the VC/BN split:
- we have a new binary which connects via RPC to the respective BN and has an internal clock - waking it up on every slot - the BN has a new option called --external-validators and currently in order to have the VC binaries to run we need to pass EXTERNAL_VALIDATORS=yes to make - factored some code out of beacon_node.nim for easier reuse in validator_api.nim and validator_client.nim - the VC loads its associated private keys from the datadir for its BN - most of the validator API calls have been implemented as a stub. - the VC polls its BN at the start of each epoch - getting a list of all active validators for the current epoch - and then continues to request blocks and sign them with its appropriate validators when necessary
This commit is contained in:
parent
f41d823d07
commit
8760494c72
3
.gitignore
vendored
3
.gitignore
vendored
@ -9,6 +9,9 @@ build/
|
|||||||
# ntags/ctags output
|
# ntags/ctags output
|
||||||
/tags
|
/tags
|
||||||
|
|
||||||
|
# vscode
|
||||||
|
.vscode
|
||||||
|
|
||||||
# Ignore dynamic, static libs and libtool archive files
|
# Ignore dynamic, static libs and libtool archive files
|
||||||
*.so
|
*.so
|
||||||
*.dylib
|
*.dylib
|
||||||
|
1
Makefile
1
Makefile
@ -15,6 +15,7 @@ BUILD_SYSTEM_DIR := vendor/nimbus-build-system
|
|||||||
|
|
||||||
# unconditionally built by the default Make target
|
# unconditionally built by the default Make target
|
||||||
TOOLS := \
|
TOOLS := \
|
||||||
|
validator_client \
|
||||||
beacon_node \
|
beacon_node \
|
||||||
inspector \
|
inspector \
|
||||||
logtrace \
|
logtrace \
|
||||||
|
@ -12,7 +12,7 @@ import
|
|||||||
# Nimble packages
|
# Nimble packages
|
||||||
stew/[objects, bitseqs, byteutils], stew/shims/macros,
|
stew/[objects, bitseqs, byteutils], stew/shims/macros,
|
||||||
chronos, confutils, metrics, json_rpc/[rpcserver, jsonmarshal],
|
chronos, confutils, metrics, json_rpc/[rpcserver, jsonmarshal],
|
||||||
chronicles, chronicles/helpers as chroniclesHelpers,
|
chronicles,
|
||||||
json_serialization/std/[options, sets, net], serialization/errors,
|
json_serialization/std/[options, sets, net], serialization/errors,
|
||||||
eth/db/kvstore, eth/db/kvstore_sqlite3,
|
eth/db/kvstore, eth/db/kvstore_sqlite3,
|
||||||
eth/p2p/enode, eth/[keys, async_utils], eth/p2p/discoveryv5/[protocol, enr],
|
eth/p2p/enode, eth/[keys, async_utils], eth/p2p/discoveryv5/[protocol, enr],
|
||||||
@ -22,14 +22,14 @@ import
|
|||||||
spec/presets/custom,
|
spec/presets/custom,
|
||||||
conf, time, beacon_chain_db, validator_pool, extras,
|
conf, time, beacon_chain_db, validator_pool, extras,
|
||||||
attestation_pool, block_pool, eth2_network, eth2_discovery,
|
attestation_pool, block_pool, eth2_network, eth2_discovery,
|
||||||
beacon_node_common, beacon_node_types, sszdump,
|
beacon_node_common, beacon_node_types,
|
||||||
|
nimbus_binary_common,
|
||||||
mainchain_monitor, version, ssz, ssz/dynamic_navigator,
|
mainchain_monitor, version, ssz, ssz/dynamic_navigator,
|
||||||
sync_protocol, request_manager, validator_keygen, interop, statusbar,
|
sync_protocol, request_manager, validator_keygen, interop, statusbar,
|
||||||
sync_manager, state_transition,
|
sync_manager, state_transition,
|
||||||
validator_duties
|
validator_duties, validator_api
|
||||||
|
|
||||||
const
|
const
|
||||||
genesisFile = "genesis.ssz"
|
|
||||||
hasPrompt = not defined(withoutPrompt)
|
hasPrompt = not defined(withoutPrompt)
|
||||||
|
|
||||||
type
|
type
|
||||||
@ -55,68 +55,12 @@ declareGauge beacon_head_slot,
|
|||||||
# Metrics for tracking attestation and beacon block loss
|
# Metrics for tracking attestation and beacon block loss
|
||||||
declareCounter beacon_attestations_received,
|
declareCounter beacon_attestations_received,
|
||||||
"Number of beacon chain attestations received by this peer"
|
"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,
|
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]
|
"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"
|
logScope: topics = "beacnde"
|
||||||
|
|
||||||
proc onBeaconBlock*(node: BeaconNode, signedBlock: SignedBeaconBlock) {.gcsafe.}
|
|
||||||
|
|
||||||
proc getStateFromSnapshot(conf: BeaconNodeConf): NilableBeaconStateRef =
|
|
||||||
var
|
|
||||||
genesisPath = conf.dataDir/genesisFile
|
|
||||||
snapshotContents: TaintedString
|
|
||||||
writeGenesisFile = false
|
|
||||||
|
|
||||||
if conf.stateSnapshot.isSome:
|
|
||||||
let
|
|
||||||
snapshotPath = conf.stateSnapshot.get.string
|
|
||||||
snapshotExt = splitFile(snapshotPath).ext
|
|
||||||
|
|
||||||
if cmpIgnoreCase(snapshotExt, ".ssz") != 0:
|
|
||||||
error "The supplied state snapshot must be a SSZ file",
|
|
||||||
suppliedPath = snapshotPath
|
|
||||||
quit 1
|
|
||||||
|
|
||||||
snapshotContents = readFile(snapshotPath)
|
|
||||||
if fileExists(genesisPath):
|
|
||||||
let genesisContents = readFile(genesisPath)
|
|
||||||
if snapshotContents != genesisContents:
|
|
||||||
error "Data directory not empty. Existing genesis state differs from supplied snapshot",
|
|
||||||
dataDir = conf.dataDir.string, snapshot = snapshotPath
|
|
||||||
quit 1
|
|
||||||
else:
|
|
||||||
debug "No previous genesis state. Importing snapshot",
|
|
||||||
genesisPath, dataDir = conf.dataDir.string
|
|
||||||
writeGenesisFile = true
|
|
||||||
genesisPath = snapshotPath
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
snapshotContents = readFile(genesisPath)
|
|
||||||
except CatchableError as err:
|
|
||||||
error "Failed to read genesis file", err = err.msg
|
|
||||||
quit 1
|
|
||||||
|
|
||||||
result = try:
|
|
||||||
newClone(SSZ.decode(snapshotContents, BeaconState))
|
|
||||||
except SerializationError:
|
|
||||||
error "Failed to import genesis file", path = genesisPath
|
|
||||||
quit 1
|
|
||||||
|
|
||||||
info "Loaded genesis state", path = genesisPath
|
|
||||||
|
|
||||||
if writeGenesisFile:
|
|
||||||
try:
|
|
||||||
notice "Writing genesis to data directory", path = conf.dataDir/genesisFile
|
|
||||||
writeFile(conf.dataDir/genesisFile, snapshotContents.string)
|
|
||||||
except CatchableError as err:
|
|
||||||
error "Failed to persist genesis file to data dir",
|
|
||||||
err = err.msg, genesisFile = conf.dataDir/genesisFile
|
|
||||||
quit 1
|
|
||||||
|
|
||||||
proc enrForkIdFromState(state: BeaconState): ENRForkID =
|
proc enrForkIdFromState(state: BeaconState): ENRForkID =
|
||||||
let
|
let
|
||||||
forkVer = state.fork.current_version
|
forkVer = state.fork.current_version
|
||||||
@ -256,72 +200,6 @@ proc connectToNetwork(node: BeaconNode) {.async.} =
|
|||||||
let addressFile = node.config.dataDir / "beacon_node.address"
|
let addressFile = node.config.dataDir / "beacon_node.address"
|
||||||
writeFile(addressFile, node.network.announcedENR.toURI)
|
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()
|
|
||||||
discard ? node.blockPool.add(blockRoot, signedBlock)
|
|
||||||
|
|
||||||
# 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)
|
|
||||||
|
|
||||||
func verifyFinalization(node: BeaconNode, slot: Slot) =
|
func verifyFinalization(node: BeaconNode, slot: Slot) =
|
||||||
# Epoch must be >= 4 to check finalization
|
# Epoch must be >= 4 to check finalization
|
||||||
const SETTLING_TIME_OFFSET = 1'u64
|
const SETTLING_TIME_OFFSET = 1'u64
|
||||||
@ -567,16 +445,6 @@ proc runSyncLoop(node: BeaconNode) {.async.} =
|
|||||||
|
|
||||||
await syncman.sync()
|
await syncman.sync()
|
||||||
|
|
||||||
# TODO: Should we move these to other modules?
|
|
||||||
# This would require moving around other type definitions
|
|
||||||
proc installValidatorApiHandlers(rpcServer: RpcServer, node: BeaconNode) =
|
|
||||||
discard
|
|
||||||
|
|
||||||
func slotOrZero(time: BeaconTime): Slot =
|
|
||||||
let exSlot = time.toSlot
|
|
||||||
if exSlot.afterGenesis: exSlot.slot
|
|
||||||
else: Slot(0)
|
|
||||||
|
|
||||||
proc currentSlot(node: BeaconNode): Slot =
|
proc currentSlot(node: BeaconNode): Slot =
|
||||||
node.beaconClock.now.slotOrZero
|
node.beaconClock.now.slotOrZero
|
||||||
|
|
||||||
@ -683,7 +551,9 @@ proc installDebugApiHandlers(rpcServer: RpcServer, node: BeaconNode) =
|
|||||||
return res
|
return res
|
||||||
|
|
||||||
proc installRpcHandlers(rpcServer: RpcServer, node: BeaconNode) =
|
proc installRpcHandlers(rpcServer: RpcServer, node: BeaconNode) =
|
||||||
rpcServer.installValidatorApiHandlers(node)
|
# TODO: remove this if statement later - here just to test the config option for now
|
||||||
|
if node.config.externalValidators:
|
||||||
|
rpcServer.installValidatorApiHandlers(node)
|
||||||
rpcServer.installBeaconApiHandlers(node)
|
rpcServer.installBeaconApiHandlers(node)
|
||||||
rpcServer.installDebugApiHandlers(node)
|
rpcServer.installDebugApiHandlers(node)
|
||||||
|
|
||||||
@ -754,9 +624,7 @@ proc run*(node: BeaconNode) =
|
|||||||
installAttestationHandlers(node)
|
installAttestationHandlers(node)
|
||||||
|
|
||||||
let
|
let
|
||||||
t = node.beaconClock.now().toSlot()
|
curSlot = node.beaconClock.now().slotOrZero()
|
||||||
curSlot = if t.afterGenesis: t.slot
|
|
||||||
else: GENESIS_SLOT
|
|
||||||
nextSlot = curSlot + 1 # No earlier than GENESIS_SLOT + 1
|
nextSlot = curSlot + 1 # No earlier than GENESIS_SLOT + 1
|
||||||
fromNow = saturate(node.beaconClock.fromNow(nextSlot))
|
fromNow = saturate(node.beaconClock.fromNow(nextSlot))
|
||||||
|
|
||||||
@ -954,30 +822,7 @@ programMain:
|
|||||||
banner = clientId & "\p" & copyrights & "\p\p" & nimBanner
|
banner = clientId & "\p" & copyrights & "\p\p" & nimBanner
|
||||||
config = BeaconNodeConf.load(version = banner, copyrightBanner = banner)
|
config = BeaconNodeConf.load(version = banner, copyrightBanner = banner)
|
||||||
|
|
||||||
when compiles(defaultChroniclesStream.output.writer):
|
setupMainProc(config.logLevel)
|
||||||
defaultChroniclesStream.output.writer =
|
|
||||||
proc (logLevel: LogLevel, msg: LogOutputStr) {.gcsafe, raises: [Defect].} =
|
|
||||||
try:
|
|
||||||
stdout.write(msg)
|
|
||||||
except IOError as err:
|
|
||||||
logLoggingFailure(cstring(msg), err)
|
|
||||||
|
|
||||||
randomize()
|
|
||||||
|
|
||||||
try:
|
|
||||||
let directives = config.logLevel.split(";")
|
|
||||||
try:
|
|
||||||
setLogLevel(parseEnum[LogLevel](directives[0]))
|
|
||||||
except ValueError:
|
|
||||||
raise (ref ValueError)(msg: "Please specify one of TRACE, DEBUG, INFO, NOTICE, WARN, ERROR or FATAL")
|
|
||||||
|
|
||||||
if directives.len > 1:
|
|
||||||
for topicName, settings in parseTopicDirectives(directives[1..^1]):
|
|
||||||
if not setTopicState(topicName, settings.state, settings.logLevel):
|
|
||||||
warn "Unrecognized logging topic", topic = topicName
|
|
||||||
except ValueError as err:
|
|
||||||
stderr.write "Invalid value for --log-level. " & err.msg
|
|
||||||
quit 1
|
|
||||||
|
|
||||||
case config.cmd
|
case config.cmd
|
||||||
of createTestnet:
|
of createTestnet:
|
||||||
@ -1063,14 +908,7 @@ programMain:
|
|||||||
|
|
||||||
var node = waitFor BeaconNode.init(config)
|
var node = waitFor BeaconNode.init(config)
|
||||||
|
|
||||||
## Ctrl+C handling
|
ctrlCHandling: status = BeaconNodeStatus.Stopping
|
||||||
proc controlCHandler() {.noconv.} =
|
|
||||||
when defined(windows):
|
|
||||||
# workaround for https://github.com/nim-lang/Nim/issues/4057
|
|
||||||
setupForeignThreadGc()
|
|
||||||
info "Shutting down after having received SIGINT"
|
|
||||||
status = BeaconNodeStatus.Stopping
|
|
||||||
setControlCHook(controlCHandler)
|
|
||||||
|
|
||||||
when hasPrompt:
|
when hasPrompt:
|
||||||
initPrompt(node)
|
initPrompt(node)
|
||||||
|
@ -8,15 +8,23 @@
|
|||||||
# Common routines for a BeaconNode and a BeaconValidator node
|
# Common routines for a BeaconNode and a BeaconValidator node
|
||||||
|
|
||||||
import
|
import
|
||||||
|
# Standard library
|
||||||
|
os, tables,
|
||||||
|
|
||||||
# Nimble packages
|
# Nimble packages
|
||||||
chronos, json_rpc/rpcserver, metrics,
|
chronos, json_rpc/rpcserver, metrics,
|
||||||
|
chronicles,
|
||||||
|
|
||||||
# Local modules
|
# Local modules
|
||||||
spec/[datatypes, crypto],
|
spec/[datatypes, crypto, helpers],
|
||||||
conf, time, beacon_chain_db,
|
conf, time, beacon_chain_db, sszdump,
|
||||||
attestation_pool, block_pool, eth2_network,
|
attestation_pool, block_pool, eth2_network,
|
||||||
beacon_node_types, mainchain_monitor, request_manager
|
beacon_node_types, mainchain_monitor, request_manager
|
||||||
|
|
||||||
|
# TODO figure out how to silence the `unused pragma` warning for specific builds of this
|
||||||
|
# https://discordapp.com/channels/613988663034118151/614014714590134292/713053239297179668
|
||||||
|
import spec/digest
|
||||||
|
|
||||||
type
|
type
|
||||||
RpcServer* = RpcHttpServer
|
RpcServer* = RpcHttpServer
|
||||||
|
|
||||||
@ -38,12 +46,82 @@ type
|
|||||||
topicAggregateAndProofs*: string
|
topicAggregateAndProofs*: string
|
||||||
syncLoop*: Future[void]
|
syncLoop*: Future[void]
|
||||||
|
|
||||||
const MaxEmptySlotCount* = uint64(10*60) div SECONDS_PER_SLOT
|
const
|
||||||
|
MaxEmptySlotCount* = uint64(10*60) div SECONDS_PER_SLOT
|
||||||
|
|
||||||
# Metrics
|
# Metrics
|
||||||
declareGauge beacon_head_root,
|
declareGauge beacon_head_root,
|
||||||
"Root of the head block of the beacon chain"
|
"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()
|
||||||
|
discard ? node.blockPool.add(blockRoot, signedBlock)
|
||||||
|
|
||||||
|
# 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 =
|
proc updateHead*(node: BeaconNode): BlockRef =
|
||||||
# Check pending attestations - maybe we found some blocks for them
|
# Check pending attestations - maybe we found some blocks for them
|
||||||
node.attestationPool.resolve()
|
node.attestationPool.resolve()
|
||||||
|
@ -13,13 +13,16 @@ export
|
|||||||
type
|
type
|
||||||
ValidatorKeyPath* = TypedInputFile[ValidatorPrivKey, Txt, "privkey"]
|
ValidatorKeyPath* = TypedInputFile[ValidatorPrivKey, Txt, "privkey"]
|
||||||
|
|
||||||
StartUpCmd* = enum
|
BNStartUpCmd* = enum
|
||||||
noCommand
|
noCommand
|
||||||
importValidator
|
importValidator
|
||||||
createTestnet
|
createTestnet
|
||||||
makeDeposits
|
makeDeposits
|
||||||
query
|
query
|
||||||
|
|
||||||
|
VCStartUpCmd* = enum
|
||||||
|
VCNoCommand
|
||||||
|
|
||||||
QueryCmd* = enum
|
QueryCmd* = enum
|
||||||
nimQuery
|
nimQuery
|
||||||
get
|
get
|
||||||
@ -64,7 +67,7 @@ type
|
|||||||
|
|
||||||
case cmd* {.
|
case cmd* {.
|
||||||
command
|
command
|
||||||
defaultValue: noCommand }: StartUpCmd
|
defaultValue: noCommand }: BNStartUpCmd
|
||||||
|
|
||||||
of noCommand:
|
of noCommand:
|
||||||
bootstrapNodes* {.
|
bootstrapNodes* {.
|
||||||
@ -108,6 +111,11 @@ type
|
|||||||
abbr: "v"
|
abbr: "v"
|
||||||
name: "validator" }: seq[ValidatorKeyPath]
|
name: "validator" }: seq[ValidatorKeyPath]
|
||||||
|
|
||||||
|
externalValidators* {.
|
||||||
|
defaultValue: false
|
||||||
|
desc: "Specify whether validators should be in an external process (a validator client) which communicates with the beacon node or they should be embedded."
|
||||||
|
name: "external-validators" }: bool
|
||||||
|
|
||||||
stateSnapshot* {.
|
stateSnapshot* {.
|
||||||
desc: "Json file specifying a recent state snapshot."
|
desc: "Json file specifying a recent state snapshot."
|
||||||
abbr: "s"
|
abbr: "s"
|
||||||
@ -278,7 +286,51 @@ type
|
|||||||
argument
|
argument
|
||||||
desc: "REST API path to evaluate" }: string
|
desc: "REST API path to evaluate" }: string
|
||||||
|
|
||||||
proc defaultDataDir*(conf: BeaconNodeConf): string =
|
ValidatorClientConf* = object
|
||||||
|
logLevel* {.
|
||||||
|
defaultValue: "DEBUG"
|
||||||
|
desc: "Sets the log level."
|
||||||
|
name: "log-level" }: string
|
||||||
|
|
||||||
|
dataDir* {.
|
||||||
|
defaultValue: config.defaultDataDir()
|
||||||
|
desc: "The directory where nimbus will store all blockchain data."
|
||||||
|
abbr: "d"
|
||||||
|
name: "data-dir" }: OutDir
|
||||||
|
|
||||||
|
case cmd* {.
|
||||||
|
command
|
||||||
|
defaultValue: VCNoCommand }: VCStartUpCmd
|
||||||
|
|
||||||
|
of VCNoCommand:
|
||||||
|
rpcPort* {.
|
||||||
|
defaultValue: defaultEth2RpcPort
|
||||||
|
desc: "HTTP port of the server to connect to for RPC."
|
||||||
|
name: "rpc-port" }: Port
|
||||||
|
|
||||||
|
rpcAddress* {.
|
||||||
|
defaultValue: defaultAdminListenAddress(config)
|
||||||
|
desc: "Address of the server to connect to for RPC."
|
||||||
|
name: "rpc-address" }: IpAddress
|
||||||
|
|
||||||
|
validators* {.
|
||||||
|
required
|
||||||
|
desc: "Path to a validator private key, as generated by makeDeposits."
|
||||||
|
abbr: "v"
|
||||||
|
name: "validator" }: seq[ValidatorKeyPath]
|
||||||
|
|
||||||
|
stateSnapshot* {.
|
||||||
|
desc: "Json file specifying a recent state snapshot."
|
||||||
|
abbr: "s"
|
||||||
|
name: "state-snapshot" }: Option[InputFile]
|
||||||
|
|
||||||
|
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):
|
let dataDir = when defined(windows):
|
||||||
"AppData" / "Roaming" / "Nimbus"
|
"AppData" / "Roaming" / "Nimbus"
|
||||||
elif defined(macosx):
|
elif defined(macosx):
|
||||||
@ -295,24 +347,24 @@ proc validatorFileBaseName*(validatorIdx: int): string =
|
|||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
raiseAssert e.msg
|
raiseAssert e.msg
|
||||||
|
|
||||||
func dumpDir*(conf: BeaconNodeConf): string =
|
func dumpDir*(conf: BeaconNodeConf|ValidatorClientConf): string =
|
||||||
conf.dataDir / "dump"
|
conf.dataDir / "dump"
|
||||||
|
|
||||||
func localValidatorsDir*(conf: BeaconNodeConf): string =
|
func localValidatorsDir*(conf: BeaconNodeConf|ValidatorClientConf): string =
|
||||||
conf.dataDir / "validators"
|
conf.dataDir / "validators"
|
||||||
|
|
||||||
func databaseDir*(conf: BeaconNodeConf): string =
|
func databaseDir*(conf: BeaconNodeConf|ValidatorClientConf): string =
|
||||||
conf.dataDir / "db"
|
conf.dataDir / "db"
|
||||||
|
|
||||||
func defaultListenAddress*(conf: BeaconNodeConf): IpAddress =
|
func defaultListenAddress*(conf: BeaconNodeConf|ValidatorClientConf): IpAddress =
|
||||||
# TODO: How should we select between IPv4 and IPv6
|
# TODO: How should we select between IPv4 and IPv6
|
||||||
# Maybe there should be a config option for this.
|
# Maybe there should be a config option for this.
|
||||||
return static: parseIpAddress("0.0.0.0")
|
return static: parseIpAddress("0.0.0.0")
|
||||||
|
|
||||||
func defaultAdminListenAddress*(conf: BeaconNodeConf): IpAddress =
|
func defaultAdminListenAddress*(conf: BeaconNodeConf|ValidatorClientConf): IpAddress =
|
||||||
return static: parseIpAddress("127.0.0.1")
|
return static: parseIpAddress("127.0.0.1")
|
||||||
|
|
||||||
iterator validatorKeys*(conf: BeaconNodeConf): ValidatorPrivKey =
|
iterator validatorKeys*(conf: BeaconNodeConf|ValidatorClientConf): ValidatorPrivKey =
|
||||||
for validatorKeyFile in conf.validators:
|
for validatorKeyFile in conf.validators:
|
||||||
try:
|
try:
|
||||||
yield validatorKeyFile.load
|
yield validatorKeyFile.load
|
||||||
|
44
beacon_chain/eth2_json_rpc_serialization.nim
Normal file
44
beacon_chain/eth2_json_rpc_serialization.nim
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import
|
||||||
|
# Standard library
|
||||||
|
tables, json,
|
||||||
|
|
||||||
|
# Nimble packages
|
||||||
|
stew/[bitseqs],
|
||||||
|
json_rpc/jsonmarshal,
|
||||||
|
|
||||||
|
# Local modules
|
||||||
|
spec/[datatypes, digest, crypto]
|
||||||
|
|
||||||
|
proc fromJson*(n: JsonNode, argName: string, result: var ValidatorPubKey) =
|
||||||
|
result = ValidatorPubKey.fromHex(n.getStr()).tryGet()
|
||||||
|
|
||||||
|
proc `%`*(pubkey: ValidatorPubKey): JsonNode =
|
||||||
|
result = newJString($pubkey)
|
||||||
|
|
||||||
|
proc fromJson*(n: JsonNode, argName: string, result: var List) =
|
||||||
|
fromJson(n, argName, asSeq result)
|
||||||
|
|
||||||
|
proc `%`*(list: List): JsonNode = %(asSeq(list))
|
||||||
|
|
||||||
|
proc fromJson*(n: JsonNode, argName: string, result: var BitList) =
|
||||||
|
fromJson(n, argName, seq[byte](BitSeq(result)))
|
||||||
|
|
||||||
|
proc `%`*(bitlist: BitList): JsonNode = %(seq[byte](BitSeq(bitlist)))
|
||||||
|
|
||||||
|
proc fromJson*(n: JsonNode, argName: string, result: var ValidatorSig) =
|
||||||
|
result = ValidatorSig.fromHex(n.getStr()).tryGet()
|
||||||
|
|
||||||
|
proc `%`*(value: ValidatorSig): JsonNode =
|
||||||
|
result = newJString($value)
|
||||||
|
|
||||||
|
template genFromJsonForIntType(t: untyped) =
|
||||||
|
proc fromJson*(n: JsonNode, argName: string, result: var t) =
|
||||||
|
n.kind.expect(JInt, argName)
|
||||||
|
result = n.getInt().t
|
||||||
|
|
||||||
|
genFromJsonForIntType(Epoch)
|
||||||
|
genFromJsonForIntType(Slot)
|
||||||
|
genFromJsonForIntType(CommitteeIndex)
|
||||||
|
|
||||||
|
proc `%`*(value: CommitteeIndex): JsonNode =
|
||||||
|
result = newJInt(value.int)
|
112
beacon_chain/nimbus_binary_common.nim
Normal file
112
beacon_chain/nimbus_binary_common.nim
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
# 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, random, strutils,
|
||||||
|
|
||||||
|
# Nimble packages
|
||||||
|
chronos,
|
||||||
|
chronicles, chronicles/helpers as chroniclesHelpers,
|
||||||
|
|
||||||
|
# Local modules
|
||||||
|
spec/[datatypes, crypto],
|
||||||
|
conf,
|
||||||
|
block_pool, eth2_network
|
||||||
|
|
||||||
|
const
|
||||||
|
genesisFile* = "genesis.ssz"
|
||||||
|
|
||||||
|
proc getStateFromSnapshot*(conf: BeaconNodeConf|ValidatorClientConf): NilableBeaconStateRef =
|
||||||
|
var
|
||||||
|
genesisPath = conf.dataDir/genesisFile
|
||||||
|
snapshotContents: TaintedString
|
||||||
|
writeGenesisFile = false
|
||||||
|
|
||||||
|
if conf.stateSnapshot.isSome:
|
||||||
|
let
|
||||||
|
snapshotPath = conf.stateSnapshot.get.string
|
||||||
|
snapshotExt = splitFile(snapshotPath).ext
|
||||||
|
|
||||||
|
if cmpIgnoreCase(snapshotExt, ".ssz") != 0:
|
||||||
|
error "The supplied state snapshot must be a SSZ file",
|
||||||
|
suppliedPath = snapshotPath
|
||||||
|
quit 1
|
||||||
|
|
||||||
|
snapshotContents = readFile(snapshotPath)
|
||||||
|
if fileExists(genesisPath):
|
||||||
|
let genesisContents = readFile(genesisPath)
|
||||||
|
if snapshotContents != genesisContents:
|
||||||
|
error "Data directory not empty. Existing genesis state differs from supplied snapshot",
|
||||||
|
dataDir = conf.dataDir.string, snapshot = snapshotPath
|
||||||
|
quit 1
|
||||||
|
else:
|
||||||
|
debug "No previous genesis state. Importing snapshot",
|
||||||
|
genesisPath, dataDir = conf.dataDir.string
|
||||||
|
writeGenesisFile = true
|
||||||
|
genesisPath = snapshotPath
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
snapshotContents = readFile(genesisPath)
|
||||||
|
except CatchableError as err:
|
||||||
|
error "Failed to read genesis file", err = err.msg
|
||||||
|
quit 1
|
||||||
|
|
||||||
|
result = try:
|
||||||
|
newClone(SSZ.decode(snapshotContents, BeaconState))
|
||||||
|
except SerializationError:
|
||||||
|
error "Failed to import genesis file", path = genesisPath
|
||||||
|
quit 1
|
||||||
|
|
||||||
|
info "Loaded genesis state", path = genesisPath
|
||||||
|
|
||||||
|
if writeGenesisFile:
|
||||||
|
try:
|
||||||
|
notice "Writing genesis to data directory", path = conf.dataDir/genesisFile
|
||||||
|
writeFile(conf.dataDir/genesisFile, snapshotContents.string)
|
||||||
|
except CatchableError as err:
|
||||||
|
error "Failed to persist genesis file to data dir",
|
||||||
|
err = err.msg, genesisFile = conf.dataDir/genesisFile
|
||||||
|
quit 1
|
||||||
|
|
||||||
|
proc setupMainProc*(logLevel: string) =
|
||||||
|
when compiles(defaultChroniclesStream.output.writer):
|
||||||
|
defaultChroniclesStream.output.writer =
|
||||||
|
proc (logLevel: LogLevel, msg: LogOutputStr) {.gcsafe, raises: [Defect].} =
|
||||||
|
try:
|
||||||
|
stdout.write(msg)
|
||||||
|
except IOError as err:
|
||||||
|
logLoggingFailure(cstring(msg), err)
|
||||||
|
|
||||||
|
randomize()
|
||||||
|
|
||||||
|
try:
|
||||||
|
let directives = logLevel.split(";")
|
||||||
|
try:
|
||||||
|
setLogLevel(parseEnum[LogLevel](directives[0]))
|
||||||
|
except ValueError:
|
||||||
|
raise (ref ValueError)(msg: "Please specify one of TRACE, DEBUG, INFO, NOTICE, WARN, ERROR or FATAL")
|
||||||
|
|
||||||
|
if directives.len > 1:
|
||||||
|
for topicName, settings in parseTopicDirectives(directives[1..^1]):
|
||||||
|
if not setTopicState(topicName, settings.state, settings.logLevel):
|
||||||
|
warn "Unrecognized logging topic", topic = topicName
|
||||||
|
except ValueError as err:
|
||||||
|
stderr.write "Invalid value for --log-level. " & err.msg
|
||||||
|
quit 1
|
||||||
|
|
||||||
|
template ctrlCHandling*(extraCode: untyped) =
|
||||||
|
## Ctrl+C handling
|
||||||
|
proc controlCHandler() {.noconv.} =
|
||||||
|
when defined(windows):
|
||||||
|
# workaround for https://github.com/nim-lang/Nim/issues/4057
|
||||||
|
setupForeignThreadGc()
|
||||||
|
info "Shutting down after having received SIGINT"
|
||||||
|
extraCode
|
||||||
|
setControlCHook(controlCHandler)
|
@ -1,36 +1,31 @@
|
|||||||
import
|
import
|
||||||
|
# Standard library
|
||||||
options,
|
options,
|
||||||
../datatypes
|
# Local modules
|
||||||
|
../[datatypes, digest, crypto],
|
||||||
|
json_rpc/jsonmarshal,
|
||||||
|
validator_callsigs_types
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-APIs/tree/master/apis/validator
|
# TODO check which arguments are part of the path in the REST API
|
||||||
|
|
||||||
type
|
proc get_v1_validator_blocks(slot: Slot, graffiti: Eth2Digest, randao_reveal: ValidatorSig): BeaconBlock
|
||||||
SyncStatus* = object
|
|
||||||
starting_slot*: Slot
|
|
||||||
current_slot*: Slot
|
|
||||||
highest_slot*: Slot
|
|
||||||
|
|
||||||
SyncingStatusResponse* = object
|
# TODO this doesn't have "validator" in it's path but is used by the validators nonetheless
|
||||||
is_syncing*: bool
|
proc post_v1_beacon_blocks(body: SignedBeaconBlock)
|
||||||
sync_status*: SyncStatus
|
|
||||||
|
|
||||||
ValidatorDuty* = object
|
proc get_v1_validator_attestation_data(slot: Slot, committee_index: CommitteeIndex): AttestationData
|
||||||
validator_pubkey: ValidatorPubKey
|
|
||||||
attestation_slot: Slot
|
|
||||||
attestation_shard: uint
|
|
||||||
block_proposal_slot: Slot
|
|
||||||
|
|
||||||
proc getNodeVersion(): string
|
proc get_v1_validator_aggregate_attestation(query: Eth2Digest): Attestation
|
||||||
proc getGenesisTime(): uint64
|
|
||||||
proc getSyncingStatus(): SyncingStatusResponse
|
|
||||||
proc getValidator(key: ValidatorPubKey): Validator
|
|
||||||
proc getValidatorDuties(validators: openarray[ValidatorPubKey], epoch: Epoch): seq[ValidatorDuty]
|
|
||||||
proc getBlockForSigning(slot: Slot, randaoReveal: string): BeaconBlock
|
|
||||||
proc postBlock(blk: BeaconBlock)
|
|
||||||
proc getAttestationForSigning(validatorKey: ValidatorPubKey, pocBit: int, slot: Slot, shard: uint): Attestation
|
|
||||||
proc postAttestation(attestation: Attestation)
|
|
||||||
|
|
||||||
# Optional RPCs
|
proc post_v1_validator_aggregate_and_proof(payload: SignedAggregateAndProof)
|
||||||
|
|
||||||
proc getForkId()
|
# TODO this should perhaps be a GET instead of a POST?
|
||||||
|
proc post_v1_validator_duties_attester(epoch: Epoch, public_keys: seq[ValidatorPubKey]): seq[AttesterDuties]
|
||||||
|
|
||||||
|
proc get_v1_validator_duties_proposer(epoch: Epoch): seq[ValidatorPubkeySlotPair]
|
||||||
|
|
||||||
|
proc post_v1_validator_beacon_committee_subscription(committee_index: CommitteeIndex,
|
||||||
|
slot: Slot,
|
||||||
|
aggregator: bool,
|
||||||
|
validator_pubkey: ValidatorPubKey,
|
||||||
|
slot_signature: ValidatorSig)
|
||||||
|
21
beacon_chain/spec/eth2_apis/validator_callsigs_types.nim
Normal file
21
beacon_chain/spec/eth2_apis/validator_callsigs_types.nim
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import
|
||||||
|
# Standard library
|
||||||
|
options,
|
||||||
|
# Local modules
|
||||||
|
# TODO for some reason "../[datatypes, digest, crypto]" results in "Error: cannot open file"
|
||||||
|
../datatypes,
|
||||||
|
../digest,
|
||||||
|
../crypto
|
||||||
|
|
||||||
|
type
|
||||||
|
AttesterDuties* = object
|
||||||
|
public_key*: ValidatorPubKey
|
||||||
|
committee_index*: CommitteeIndex
|
||||||
|
committee_length*: uint64
|
||||||
|
validator_committee_index*: uint64
|
||||||
|
slot*: Slot
|
||||||
|
|
||||||
|
# TODO do we even need this? how about a simple tuple?
|
||||||
|
ValidatorPubkeySlotPair* = object
|
||||||
|
public_key*: ValidatorPubKey
|
||||||
|
slot*: Slot
|
@ -15,7 +15,7 @@ import
|
|||||||
# Third-party
|
# Third-party
|
||||||
stew/endians2,
|
stew/endians2,
|
||||||
# Internal
|
# Internal
|
||||||
./datatypes, ./digest, ../ssz
|
./datatypes, ./digest, ./crypto, ../ssz
|
||||||
|
|
||||||
type
|
type
|
||||||
# This solves an ambiguous identifier Error in some contexts
|
# This solves an ambiguous identifier Error in some contexts
|
||||||
|
@ -187,14 +187,14 @@ func compute_proposer_index(state: BeaconState, indices: seq[ValidatorIndex],
|
|||||||
i += 1
|
i += 1
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.3/specs/phase0/beacon-chain.md#get_beacon_proposer_index
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.3/specs/phase0/beacon-chain.md#get_beacon_proposer_index
|
||||||
func get_beacon_proposer_index*(state: BeaconState, stateCache: var StateCache):
|
func get_beacon_proposer_index*(state: BeaconState, stateCache: var StateCache, slot: Slot):
|
||||||
Option[ValidatorIndex] =
|
Option[ValidatorIndex] =
|
||||||
# Return the beacon proposer index at the current slot.
|
# Return the beacon proposer index at the current slot.
|
||||||
let epoch = get_current_epoch(state)
|
let epoch = get_current_epoch(state)
|
||||||
|
|
||||||
var buffer: array[32 + 8, byte]
|
var buffer: array[32 + 8, byte]
|
||||||
buffer[0..31] = get_seed(state, epoch, DOMAIN_BEACON_PROPOSER).data
|
buffer[0..31] = get_seed(state, epoch, DOMAIN_BEACON_PROPOSER).data
|
||||||
buffer[32..39] = int_to_bytes8(state.slot.uint64)
|
buffer[32..39] = int_to_bytes8(slot.uint64)
|
||||||
|
|
||||||
# TODO fixme; should only be run once per slot and cached
|
# TODO fixme; should only be run once per slot and cached
|
||||||
# There's exactly one beacon proposer per slot.
|
# There's exactly one beacon proposer per slot.
|
||||||
@ -204,6 +204,21 @@ func get_beacon_proposer_index*(state: BeaconState, stateCache: var StateCache):
|
|||||||
|
|
||||||
compute_proposer_index(state, indices, seed, stateCache)
|
compute_proposer_index(state, indices, seed, stateCache)
|
||||||
|
|
||||||
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#get_beacon_proposer_index
|
||||||
|
func get_beacon_proposer_index*(state: BeaconState, stateCache: var StateCache):
|
||||||
|
Option[ValidatorIndex] =
|
||||||
|
return get_beacon_proposer_index(state, stateCache, state.slot)
|
||||||
|
|
||||||
|
# Not from spec
|
||||||
|
# TODO: cache the results from this and reuse in subsequent calls to get_beacon_proposer_index
|
||||||
|
func get_beacon_proposer_indexes_for_epoch*(state: BeaconState, epoch: Epoch, stateCache: var StateCache):
|
||||||
|
seq[tuple[s: Slot, i: ValidatorIndex]] =
|
||||||
|
for i in 0 ..< SLOTS_PER_EPOCH:
|
||||||
|
let currSlot = (compute_start_slot_at_epoch(epoch).int + i).Slot
|
||||||
|
let idx = get_beacon_proposer_index(state, stateCache, currSlot)
|
||||||
|
if idx.isSome:
|
||||||
|
result.add (currSlot, idx.get)
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/validator.md#validator-assignments
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/validator.md#validator-assignments
|
||||||
func get_committee_assignment(
|
func get_committee_assignment(
|
||||||
state: BeaconState, epoch: Epoch, validator_index: ValidatorIndex):
|
state: BeaconState, epoch: Epoch, validator_index: ValidatorIndex):
|
||||||
|
@ -53,6 +53,11 @@ func toSlot*(t: BeaconTime): tuple[afterGenesis: bool, slot: Slot] =
|
|||||||
else:
|
else:
|
||||||
(false, Slot(uint64(-ti) div SECONDS_PER_SLOT))
|
(false, Slot(uint64(-ti) div SECONDS_PER_SLOT))
|
||||||
|
|
||||||
|
func slotOrZero*(time: BeaconTime): Slot =
|
||||||
|
let exSlot = time.toSlot
|
||||||
|
if exSlot.afterGenesis: exSlot.slot
|
||||||
|
else: Slot(0)
|
||||||
|
|
||||||
func toBeaconTime*(c: BeaconClock, t: Time): BeaconTime =
|
func toBeaconTime*(c: BeaconClock, t: Time): BeaconTime =
|
||||||
BeaconTime(times.inSeconds(t - c.genesis))
|
BeaconTime(times.inSeconds(t - c.genesis))
|
||||||
|
|
||||||
|
73
beacon_chain/validator_api.nim
Normal file
73
beacon_chain/validator_api.nim
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
import
|
||||||
|
# Standard library
|
||||||
|
tables, strutils, sequtils,
|
||||||
|
|
||||||
|
# Nimble packages
|
||||||
|
stew/[objects, bitseqs],
|
||||||
|
chronos, metrics, json_rpc/[rpcserver, jsonmarshal],
|
||||||
|
|
||||||
|
# Local modules
|
||||||
|
spec/[datatypes, digest, crypto, validator],
|
||||||
|
block_pool,
|
||||||
|
beacon_node_common,
|
||||||
|
validator_duties,
|
||||||
|
spec/eth2_apis/validator_callsigs_types,
|
||||||
|
eth2_json_rpc_serialization
|
||||||
|
|
||||||
|
type
|
||||||
|
RpcServer* = RpcHttpServer
|
||||||
|
|
||||||
|
proc installValidatorApiHandlers*(rpcServer: RpcServer, node: BeaconNode) =
|
||||||
|
|
||||||
|
rpcServer.rpc("get_v1_validator_blocks") do (slot: Slot, graffiti: Eth2Digest, randao_reveal: ValidatorSig) -> BeaconBlock:
|
||||||
|
|
||||||
|
var head = node.updateHead()
|
||||||
|
|
||||||
|
let proposer = node.blockPool.getProposer(head, slot)
|
||||||
|
# TODO how do we handle the case when we cannot return a meaningful block? 404...
|
||||||
|
doAssert(proposer.isSome())
|
||||||
|
|
||||||
|
let valInfo = ValidatorInfoForMakeBeaconBlock(kind: viRandao_reveal, randao_reveal: randao_reveal)
|
||||||
|
let res = makeBeaconBlockForHeadAndSlot(node, valInfo, proposer.get()[0], graffiti, head, slot)
|
||||||
|
|
||||||
|
# TODO how do we handle the case when we cannot return a meaningful block? 404...
|
||||||
|
doAssert(res.message.isSome())
|
||||||
|
return res.message.get()
|
||||||
|
|
||||||
|
rpcServer.rpc("post_v1_beacon_blocks") do (body: SignedBeaconBlock):
|
||||||
|
onBeaconBlock(node, body)
|
||||||
|
|
||||||
|
rpcServer.rpc("get_v1_validator_attestation_data") do (slot: Slot, committee_index: CommitteeIndex) -> AttestationData:
|
||||||
|
discard
|
||||||
|
|
||||||
|
rpcServer.rpc("get_v1_validator_aggregate_attestation") do (query: Eth2Digest)-> Attestation:
|
||||||
|
# TODO look at attestation.data.beacon_block_root
|
||||||
|
discard
|
||||||
|
|
||||||
|
rpcServer.rpc("post_v1_validator_aggregate_and_proof") do (payload: SignedAggregateAndProof):
|
||||||
|
discard
|
||||||
|
|
||||||
|
rpcServer.rpc("post_v1_validator_duties_attester") do (epoch: Epoch, public_keys: seq[ValidatorPubKey]) -> seq[AttesterDuties]:
|
||||||
|
discard
|
||||||
|
|
||||||
|
rpcServer.rpc("get_v1_validator_duties_proposer") do (epoch: Epoch) -> seq[ValidatorPubkeySlotPair]:
|
||||||
|
var cache = get_empty_per_epoch_cache()
|
||||||
|
return get_beacon_proposer_indexes_for_epoch(node.blockPool.headState.data.data, epoch, cache).mapIt(ValidatorPubkeySlotPair(
|
||||||
|
public_key: node.blockPool.headState.data.data.validators[it.i].pubkey,
|
||||||
|
slot: it.s
|
||||||
|
))
|
||||||
|
|
||||||
|
rpcServer.rpc("post_v1_validator_beacon_committee_subscription") do (
|
||||||
|
committee_index: CommitteeIndex,
|
||||||
|
slot: Slot,
|
||||||
|
aggregator: bool,
|
||||||
|
validator_pubkey: ValidatorPubKey,
|
||||||
|
slot_signature: ValidatorSig):
|
||||||
|
discard
|
148
beacon_chain/validator_client.nim
Normal file
148
beacon_chain/validator_client.nim
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
import
|
||||||
|
# Standard library
|
||||||
|
os, strutils, json, times,
|
||||||
|
|
||||||
|
# Nimble packages
|
||||||
|
stew/shims/[tables, macros],
|
||||||
|
chronos, confutils, metrics, json_rpc/[rpcclient, jsonmarshal],
|
||||||
|
blscurve, json_serialization/std/[options, sets, net],
|
||||||
|
|
||||||
|
# Local modules
|
||||||
|
spec/[datatypes, digest, crypto, helpers, network],
|
||||||
|
conf, time,
|
||||||
|
eth2_network, eth2_discovery, validator_pool, beacon_node_types,
|
||||||
|
nimbus_binary_common,
|
||||||
|
version, ssz, ssz/dynamic_navigator,
|
||||||
|
sync_manager,
|
||||||
|
spec/eth2_apis/validator_callsigs_types,
|
||||||
|
eth2_json_rpc_serialization
|
||||||
|
|
||||||
|
template sourceDir: string = currentSourcePath.rsplit(DirSep, 1)[0]
|
||||||
|
|
||||||
|
## Generate client convenience marshalling wrappers from forward declarations
|
||||||
|
createRpcSigs(RpcClient, sourceDir & DirSep & "spec" & DirSep & "eth2_apis" & DirSep & "validator_callsigs.nim")
|
||||||
|
|
||||||
|
type
|
||||||
|
ValidatorClient = ref object
|
||||||
|
config: ValidatorClientConf
|
||||||
|
client: RpcHttpClient
|
||||||
|
beaconClock: BeaconClock
|
||||||
|
attachedValidators: ValidatorPool
|
||||||
|
validatorDutiesForEpoch: Table[Slot, ValidatorPubKey]
|
||||||
|
|
||||||
|
proc onSlotStart(vc: ValidatorClient, lastSlot, scheduledSlot: Slot) {.gcsafe, async.} =
|
||||||
|
|
||||||
|
let
|
||||||
|
# The slot we should be at, according to the clock
|
||||||
|
beaconTime = vc.beaconClock.now()
|
||||||
|
wallSlot = beaconTime.toSlot()
|
||||||
|
|
||||||
|
let
|
||||||
|
slot = wallSlot.slot # afterGenesis == true!
|
||||||
|
nextSlot = slot + 1
|
||||||
|
|
||||||
|
try:
|
||||||
|
# TODO think about handling attestations in addition to block proposals - is waitFor OK...?
|
||||||
|
|
||||||
|
# at the start of each epoch - request all validators which should propose
|
||||||
|
# during this epoch and match that against the validators in this VC instance
|
||||||
|
if scheduledSlot.isEpoch:
|
||||||
|
let validatorDutiesForEpoch = waitFor vc.client.get_v1_validator_duties_proposer(scheduledSlot.compute_epoch_at_slot)
|
||||||
|
# update the duties (block proposals) this VC client should do during this epoch
|
||||||
|
vc.validatorDutiesForEpoch.clear()
|
||||||
|
for curr in validatorDutiesForEpoch:
|
||||||
|
if vc.attachedValidators.validators.contains curr.public_key:
|
||||||
|
vc.validatorDutiesForEpoch.add(curr.slot, curr.public_key)
|
||||||
|
# check if we have a validator which needs to propose on this slot
|
||||||
|
if vc.validatorDutiesForEpoch.contains slot:
|
||||||
|
let pubkey = vc.validatorDutiesForEpoch[slot]
|
||||||
|
let validator = vc.attachedValidators.validators[pubkey]
|
||||||
|
|
||||||
|
# TODO get these from the BN and store them in the ValidatorClient
|
||||||
|
let fork = Fork()
|
||||||
|
let genesis_validators_root = Eth2Digest()
|
||||||
|
|
||||||
|
|
||||||
|
let randao_reveal = validator.genRandaoReveal(fork, genesis_validators_root, slot)
|
||||||
|
|
||||||
|
var newBlock = SignedBeaconBlock(
|
||||||
|
message: waitFor vc.client.get_v1_validator_blocks(slot, Eth2Digest(), randao_reveal)
|
||||||
|
)
|
||||||
|
|
||||||
|
let blockRoot = hash_tree_root(newBlock.message)
|
||||||
|
newBlock.signature = waitFor validator.signBlockProposal(fork, genesis_validators_root, slot, blockRoot)
|
||||||
|
|
||||||
|
discard waitFor vc.client.post_v1_beacon_blocks(newBlock)
|
||||||
|
|
||||||
|
except CatchableError as err:
|
||||||
|
echo err.msg
|
||||||
|
|
||||||
|
let
|
||||||
|
nextSlotStart = saturate(vc.beaconClock.fromNow(nextSlot))
|
||||||
|
|
||||||
|
# it's much easier to wake up on every slot compared to scheduling the start of each
|
||||||
|
# epoch and only the precise slots when the VC should sign/propose/attest with a key
|
||||||
|
addTimer(nextSlotStart) do (p: pointer):
|
||||||
|
asyncCheck vc.onSlotStart(slot, nextSlot)
|
||||||
|
|
||||||
|
programMain:
|
||||||
|
let
|
||||||
|
clientIdVC = "Nimbus validator client v" & fullVersionStr
|
||||||
|
banner = clientIdVC & "\p" & copyrights & "\p\p" & nimBanner
|
||||||
|
config = ValidatorClientConf.load(version = banner, copyrightBanner = banner)
|
||||||
|
|
||||||
|
sleep(config.delayStart * 1000)
|
||||||
|
|
||||||
|
setupMainProc(config.logLevel)
|
||||||
|
|
||||||
|
# TODO figure out how to re-enable this without the VCs continuing
|
||||||
|
# to run when `make eth2_network_simulation` is killed with CTRL+C
|
||||||
|
#ctrlCHandling: discard
|
||||||
|
|
||||||
|
case config.cmd
|
||||||
|
of VCNoCommand:
|
||||||
|
debug "Launching validator client",
|
||||||
|
version = fullVersionStr,
|
||||||
|
cmdParams = commandLineParams(),
|
||||||
|
config
|
||||||
|
|
||||||
|
# TODO: the genesis time should be obtained through calls to the beacon node
|
||||||
|
# this applies also for genesis_validators_root... and the fork!
|
||||||
|
var genesisState = config.getStateFromSnapshot()
|
||||||
|
|
||||||
|
var vc = ValidatorClient(
|
||||||
|
config: config,
|
||||||
|
client: newRpcHttpClient(),
|
||||||
|
beaconClock: BeaconClock.init(genesisState[]),
|
||||||
|
attachedValidators: ValidatorPool.init()
|
||||||
|
)
|
||||||
|
vc.validatorDutiesForEpoch.init()
|
||||||
|
|
||||||
|
for curr in vc.config.validatorKeys:
|
||||||
|
vc.attachedValidators.addLocalValidator(curr.toPubKey, curr)
|
||||||
|
|
||||||
|
waitFor vc.client.connect("localhost", Port(config.rpcPort)) # TODO: use config.rpcAddress
|
||||||
|
echo "connected to beacon node running on port ", config.rpcPort
|
||||||
|
|
||||||
|
let
|
||||||
|
curSlot = vc.beaconClock.now().slotOrZero()
|
||||||
|
nextSlot = curSlot + 1 # No earlier than GENESIS_SLOT + 1
|
||||||
|
fromNow = saturate(vc.beaconClock.fromNow(nextSlot))
|
||||||
|
|
||||||
|
info "Scheduling first slot action",
|
||||||
|
beaconTime = shortLog(vc.beaconClock.now()),
|
||||||
|
nextSlot = shortLog(nextSlot),
|
||||||
|
fromNow = shortLog(fromNow),
|
||||||
|
cat = "scheduling"
|
||||||
|
|
||||||
|
addTimer(fromNow) do (p: pointer) {.gcsafe.}:
|
||||||
|
asyncCheck vc.onSlotStart(curSlot, nextSlot)
|
||||||
|
|
||||||
|
runForever()
|
8
beacon_chain/validator_client.nim.cfg
Normal file
8
beacon_chain/validator_client.nim.cfg
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
-d:"chronicles_runtime_filtering=on"
|
||||||
|
-d:"chronicles_default_output_device=dynamic"
|
||||||
|
|
||||||
|
@if testnet_servers_image:
|
||||||
|
-d:"chronicles_sinks=json"
|
||||||
|
-d:"withoutPrompt"
|
||||||
|
@end
|
||||||
|
|
@ -136,6 +136,74 @@ proc sendAttestation(node: BeaconNode,
|
|||||||
|
|
||||||
beacon_attestations_sent.inc()
|
beacon_attestations_sent.inc()
|
||||||
|
|
||||||
|
type
|
||||||
|
ValidatorInfoForMakeBeaconBlockType* = enum
|
||||||
|
viValidator
|
||||||
|
viRandao_reveal
|
||||||
|
ValidatorInfoForMakeBeaconBlock* = object
|
||||||
|
case kind*: ValidatorInfoForMakeBeaconBlockType
|
||||||
|
of viValidator: validator*: AttachedValidator
|
||||||
|
else: randao_reveal*: ValidatorSig
|
||||||
|
|
||||||
|
proc makeBeaconBlockForHeadAndSlot*(node: BeaconNode,
|
||||||
|
val_info: ValidatorInfoForMakeBeaconBlock,
|
||||||
|
validator_index: ValidatorIndex,
|
||||||
|
graffiti: Eth2Digest,
|
||||||
|
head: BlockRef,
|
||||||
|
slot: Slot):
|
||||||
|
tuple[message: Option[BeaconBlock], fork: Fork, genesis_validators_root: Eth2Digest] =
|
||||||
|
# Advance state to the slot that we're proposing for - this is the equivalent
|
||||||
|
# of running `process_slots` up to the slot of the new block.
|
||||||
|
node.blockPool.withState(
|
||||||
|
node.blockPool.tmpState, head.atSlot(slot)):
|
||||||
|
let (eth1data, deposits) =
|
||||||
|
if node.mainchainMonitor.isNil:
|
||||||
|
(get_eth1data_stub(state.eth1_deposit_index, slot.compute_epoch_at_slot()),
|
||||||
|
newSeq[Deposit]())
|
||||||
|
else:
|
||||||
|
node.mainchainMonitor.getBlockProposalData(state)
|
||||||
|
|
||||||
|
# TODO perhaps just making the enclosing function accept 2 different types at the
|
||||||
|
# same time and doing some compile-time branching logic is cleaner (without the
|
||||||
|
# need for the discriminated union)... but we need the `state` from `withState`
|
||||||
|
# in order to get the fork/root for the specific head/slot for the randao_reveal
|
||||||
|
# and it's causing problems when the function becomes a generic for 2 types...
|
||||||
|
proc getRandaoReveal(val_info: ValidatorInfoForMakeBeaconBlock): ValidatorSig =
|
||||||
|
if val_info.kind == viValidator:
|
||||||
|
val_info.validator.genRandaoReveal(state.fork, state.genesis_validators_root, slot)
|
||||||
|
else:
|
||||||
|
val_info.randao_reveal
|
||||||
|
|
||||||
|
let
|
||||||
|
poolPtr = unsafeAddr node.blockPool.dag # safe because restore is short-lived
|
||||||
|
|
||||||
|
func restore(v: var HashedBeaconState) =
|
||||||
|
# TODO address this ugly workaround - there should probably be a
|
||||||
|
# `state_transition` that takes a `StateData` instead and updates
|
||||||
|
# the block as well
|
||||||
|
doAssert v.addr == addr poolPtr.tmpState.data
|
||||||
|
poolPtr.tmpState = poolPtr.headState
|
||||||
|
|
||||||
|
let message = makeBeaconBlock(
|
||||||
|
hashedState,
|
||||||
|
validator_index,
|
||||||
|
head.root,
|
||||||
|
getRandaoReveal(val_info),
|
||||||
|
eth1data,
|
||||||
|
graffiti,
|
||||||
|
node.attestationPool.getAttestationsForBlock(state),
|
||||||
|
deposits,
|
||||||
|
restore)
|
||||||
|
|
||||||
|
if message.isSome():
|
||||||
|
# TODO this restore is needed because otherwise tmpState will be internally
|
||||||
|
# inconsistent - it's blck will not be pointing to the block that
|
||||||
|
# created this state - we have to reset it here before `await` to avoid
|
||||||
|
# races.
|
||||||
|
restore(poolPtr.tmpState.data)
|
||||||
|
|
||||||
|
return (message, state.fork, state.genesis_validators_root)
|
||||||
|
|
||||||
proc proposeBlock(node: BeaconNode,
|
proc proposeBlock(node: BeaconNode,
|
||||||
validator: AttachedValidator,
|
validator: AttachedValidator,
|
||||||
validator_index: ValidatorIndex,
|
validator_index: ValidatorIndex,
|
||||||
@ -153,61 +221,22 @@ proc proposeBlock(node: BeaconNode,
|
|||||||
cat = "fastforward"
|
cat = "fastforward"
|
||||||
return head
|
return head
|
||||||
|
|
||||||
# Advance state to the slot that we're proposing for - this is the equivalent
|
let valInfo = ValidatorInfoForMakeBeaconBlock(kind: viValidator, validator: validator)
|
||||||
# of running `process_slots` up to the slot of the new block.
|
let beaconBlockTuple = makeBeaconBlockForHeadAndSlot(node, valInfo, validator_index, Eth2Digest(), head, slot)
|
||||||
let (nroot, nblck) = node.blockPool.withState(
|
|
||||||
node.blockPool.tmpState, head.atSlot(slot)):
|
|
||||||
let (eth1data, deposits) =
|
|
||||||
if node.mainchainMonitor.isNil:
|
|
||||||
(get_eth1data_stub(state.eth1_deposit_index, slot.compute_epoch_at_slot()),
|
|
||||||
newSeq[Deposit]())
|
|
||||||
else:
|
|
||||||
node.mainchainMonitor.getBlockProposalData(state)
|
|
||||||
|
|
||||||
let
|
if not beaconBlockTuple.message.isSome():
|
||||||
poolPtr = unsafeAddr node.blockPool.dag # safe because restore is short-lived
|
return head # already logged elsewhere!
|
||||||
|
var
|
||||||
|
newBlock = SignedBeaconBlock(
|
||||||
|
message: beaconBlockTuple.message.get()
|
||||||
|
)
|
||||||
|
|
||||||
func restore(v: var HashedBeaconState) =
|
let blockRoot = hash_tree_root(newBlock.message)
|
||||||
# TODO address this ugly workaround - there should probably be a
|
|
||||||
# `state_transition` that takes a `StateData` instead and updates
|
|
||||||
# the block as well
|
|
||||||
doAssert v.addr == addr poolPtr.tmpState.data
|
|
||||||
poolPtr.tmpState = poolPtr.headState
|
|
||||||
|
|
||||||
let message = makeBeaconBlock(
|
newBlock.signature = await validator.signBlockProposal(
|
||||||
hashedState,
|
beaconBlockTuple.fork, beaconBlockTuple.genesis_validators_root, slot, blockRoot)
|
||||||
validator_index,
|
|
||||||
head.root,
|
|
||||||
validator.genRandaoReveal(state.fork, state.genesis_validators_root, slot),
|
|
||||||
eth1data,
|
|
||||||
Eth2Digest(),
|
|
||||||
node.attestationPool.getAttestationsForBlock(state),
|
|
||||||
deposits,
|
|
||||||
restore)
|
|
||||||
|
|
||||||
if not message.isSome():
|
let newBlockRef = node.blockPool.add(blockRoot, newBlock)
|
||||||
return head # already logged elsewhere!
|
|
||||||
|
|
||||||
# TODO this restore is needed because otherwise tmpState will be internally
|
|
||||||
# inconsistent - it's blck will not be pointing to the block that
|
|
||||||
# created this state - we have to reset it here before `await` to avoid
|
|
||||||
# races.
|
|
||||||
restore(poolPtr.tmpState.data)
|
|
||||||
|
|
||||||
var
|
|
||||||
newBlock = SignedBeaconBlock(
|
|
||||||
message: message.get()
|
|
||||||
)
|
|
||||||
|
|
||||||
let blockRoot = hash_tree_root(newBlock.message)
|
|
||||||
|
|
||||||
# Careful, state no longer valid after here because of the await..
|
|
||||||
newBlock.signature = await validator.signBlockProposal(
|
|
||||||
state.fork, state.genesis_validators_root, slot, blockRoot)
|
|
||||||
|
|
||||||
(blockRoot, newBlock)
|
|
||||||
|
|
||||||
let newBlockRef = node.blockPool.add(nroot, nblck)
|
|
||||||
if newBlockRef.isErr:
|
if newBlockRef.isErr:
|
||||||
warn "Unable to add proposed block to block pool",
|
warn "Unable to add proposed block to block pool",
|
||||||
newBlock = shortLog(newBlock.message),
|
newBlock = shortLog(newBlock.message),
|
||||||
|
@ -59,8 +59,26 @@ if [ -f "${SNAPSHOT_FILE}" ]; then
|
|||||||
SNAPSHOT_ARG="--state-snapshot=${SNAPSHOT_FILE}"
|
SNAPSHOT_ARG="--state-snapshot=${SNAPSHOT_FILE}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
cd "$DATA_DIR"
|
||||||
|
|
||||||
|
# uncomment to force always using an external VC binary for VC duties
|
||||||
|
# TODO remove this when done with implementing the VC - here just for convenience during dev
|
||||||
|
#EXTERNAL_VALIDATORS="yes"
|
||||||
|
|
||||||
|
EXTERNAL_VALIDATORS_ARG=""
|
||||||
|
if [ "${EXTERNAL_VALIDATORS:-}" == "yes" ]; then
|
||||||
|
EXTERNAL_VALIDATORS_ARG="--external-validators"
|
||||||
|
# we lass a few seconds as delay for the start ==> that way we can start the
|
||||||
|
# beacon node before the VC - otherwise we would have to add "&" conditionally to
|
||||||
|
# the command which starts the BN - makes the shell script much more complicated
|
||||||
|
$VALIDATOR_CLIENT_BIN \
|
||||||
|
--data-dir=$DATA_DIR \
|
||||||
|
--rpc-port="$(( $BASE_RPC_PORT + $NODE_ID ))" \
|
||||||
|
--delay-start=5 &
|
||||||
|
fi
|
||||||
|
|
||||||
# if you want tracing messages, add "--log-level=TRACE" below
|
# if you want tracing messages, add "--log-level=TRACE" below
|
||||||
cd "$DATA_DIR" && $BEACON_NODE_BIN \
|
$BEACON_NODE_BIN \
|
||||||
--log-level=${LOG_LEVEL:-DEBUG} \
|
--log-level=${LOG_LEVEL:-DEBUG} \
|
||||||
--bootstrap-file=$BOOTSTRAP_ADDRESS_FILE \
|
--bootstrap-file=$BOOTSTRAP_ADDRESS_FILE \
|
||||||
--data-dir=$DATA_DIR \
|
--data-dir=$DATA_DIR \
|
||||||
@ -68,6 +86,7 @@ cd "$DATA_DIR" && $BEACON_NODE_BIN \
|
|||||||
--tcp-port=$PORT \
|
--tcp-port=$PORT \
|
||||||
--udp-port=$PORT \
|
--udp-port=$PORT \
|
||||||
$SNAPSHOT_ARG \
|
$SNAPSHOT_ARG \
|
||||||
|
$EXTERNAL_VALIDATORS_ARG \
|
||||||
$NAT_ARG \
|
$NAT_ARG \
|
||||||
$WEB3_ARG \
|
$WEB3_ARG \
|
||||||
--deposit-contract=$DEPOSIT_CONTRACT_ADDRESS \
|
--deposit-contract=$DEPOSIT_CONTRACT_ADDRESS \
|
||||||
|
@ -110,7 +110,7 @@ if [[ "$USE_TMUX" != "no" ]]; then
|
|||||||
$TMUX select-window -t "${TMUX_SESSION_NAME}:sim"
|
$TMUX select-window -t "${TMUX_SESSION_NAME}:sim"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
$MAKE -j3 --no-print-directory NIMFLAGS="$CUSTOM_NIMFLAGS $DEFS" LOG_LEVEL="${LOG_LEVEL:-DEBUG}" beacon_node process_dashboard deposit_contract
|
$MAKE -j3 --no-print-directory NIMFLAGS="$CUSTOM_NIMFLAGS $DEFS" LOG_LEVEL="${LOG_LEVEL:-DEBUG}" beacon_node validator_client process_dashboard deposit_contract
|
||||||
|
|
||||||
if [ ! -f "${LAST_VALIDATOR}" ]; then
|
if [ ! -f "${LAST_VALIDATOR}" ]; then
|
||||||
if [ "$WEB3_ARG" != "" ]; then
|
if [ "$WEB3_ARG" != "" ]; then
|
||||||
|
@ -31,6 +31,7 @@ VALIDATORS_DIR="${SIM_ROOT}/validators"
|
|||||||
SNAPSHOT_FILE="${SIMULATION_DIR}/state_snapshot.ssz"
|
SNAPSHOT_FILE="${SIMULATION_DIR}/state_snapshot.ssz"
|
||||||
NETWORK_BOOTSTRAP_FILE="${SIMULATION_DIR}/bootstrap_nodes.txt"
|
NETWORK_BOOTSTRAP_FILE="${SIMULATION_DIR}/bootstrap_nodes.txt"
|
||||||
BEACON_NODE_BIN="${GIT_ROOT}/build/beacon_node"
|
BEACON_NODE_BIN="${GIT_ROOT}/build/beacon_node"
|
||||||
|
VALIDATOR_CLIENT_BIN="${GIT_ROOT}/build/validator_client"
|
||||||
DEPLOY_DEPOSIT_CONTRACT_BIN="${GIT_ROOT}/build/deposit_contract"
|
DEPLOY_DEPOSIT_CONTRACT_BIN="${GIT_ROOT}/build/deposit_contract"
|
||||||
MASTER_NODE_ADDRESS_FILE="${SIMULATION_DIR}/node-${MASTER_NODE}/beacon_node.address"
|
MASTER_NODE_ADDRESS_FILE="${SIMULATION_DIR}/node-${MASTER_NODE}/beacon_node.address"
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user