mirror of
https://github.com/status-im/nimbus-eth2.git
synced 2025-01-23 04:50:59 +00:00
- work towards more REST API endpoints being implemented
- testnets can now be launched with a separate validator client - make altona SCRIPT_PARAMS="--separateVC" - reverted the ctrl+C signal handler code reuse - not necessary for the VC anyway (default is good enough) - added a bit more logging in the VC - removed unnecessary code in the VC - connect() just parses the address & port... - fixed a couple more VC issues - when fetching the duties for an epoch fails on the BN side ==> the VC shouldn't be left in a broken state - documented the currently supported json-rpc endpoints - added more checks on the BN side for the API - bounds-checking the requests & also checking if the BN itself is synced - other cleanup currently a local sim doesn't finalize, but participation in the altona network with a separate VC is painless and works just as well as with in-process validators in a BN
This commit is contained in:
parent
fc8502c54e
commit
1482b0430d
@ -1223,7 +1223,14 @@ programMain:
|
||||
|
||||
var node = waitFor BeaconNode.init(rng, config)
|
||||
|
||||
ctrlCHandling: status = BeaconNodeStatus.Stopping
|
||||
## 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"
|
||||
status = BeaconNodeStatus.Stopping
|
||||
setControlCHook(controlCHandler)
|
||||
|
||||
when hasPrompt:
|
||||
initPrompt(node)
|
||||
|
@ -44,16 +44,6 @@ proc setupMainProc*(logLevel: string) =
|
||||
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)
|
||||
|
||||
template makeBannerAndConfig*(clientId: string, ConfType: type): untyped =
|
||||
let
|
||||
version = clientId & "\p" & copyrights & "\p\p" &
|
||||
|
@ -12,6 +12,55 @@ proc get_v1_beacon_states_root(stateId: string): Eth2Digest
|
||||
# TODO stateId is part of the REST path
|
||||
proc get_v1_beacon_states_fork(stateId: string): Fork
|
||||
|
||||
# TODO stateId is part of the REST path
|
||||
proc get_v1_beacon_states_finality_checkpoints(
|
||||
stateId: string): BeaconStatesFinalityCheckpointsTuple
|
||||
|
||||
# TODO stateId is part of the REST path
|
||||
proc get_v1_beacon_states_stateId_validators(
|
||||
stateId: string, validatorIds: seq[string],
|
||||
status: string): seq[BeaconStatesValidatorsTuple]
|
||||
|
||||
# TODO stateId and validatorId are part of the REST path
|
||||
proc get_v1_beacon_states_stateId_validators_validatorId(
|
||||
stateId: string, validatorId: string): BeaconStatesValidatorsTuple
|
||||
|
||||
# TODO stateId and epoch are part of the REST path
|
||||
proc get_v1_beacon_states_stateId_committees_epoch(stateId: string,
|
||||
epoch: uint64, index: uint64, slot: uint64): seq[BeaconStatesCommitteesTuple]
|
||||
|
||||
proc get_v1_beacon_headers(slot: uint64, parent_root: Eth2Digest): seq[BeaconHeadersTuple]
|
||||
|
||||
# TODO blockId is part of the REST path
|
||||
proc get_v1_beacon_headers_blockId(blockId: string):
|
||||
tuple[canonical: bool, header: SignedBeaconBlockHeader]
|
||||
|
||||
# TODO blockId is part of the REST path
|
||||
proc get_v1_beacon_blocks_blockId(blockId: string): SignedBeaconBlock
|
||||
|
||||
# TODO blockId is part of the REST path
|
||||
proc get_v1_beacon_blocks_blockId_root(blockId: string): Eth2Digest
|
||||
|
||||
# TODO blockId is part of the REST path
|
||||
proc get_v1_beacon_blocks_blockId_attestations(blockId: string): seq[Attestation]
|
||||
|
||||
# TODO POST /v1/beacon/pool/attester_slashings
|
||||
# TODO GET /v1/beacon/pool/attester_slashings
|
||||
# TODO POST /v1/beacon/pool/proposer_slashings
|
||||
# TODO GET /v1/beacon/pool/proposer_slashings
|
||||
# TODO POST /v1/beacon/pool/voluntary_exits
|
||||
# TODO GET /v1/beacon/pool/voluntary_exits
|
||||
# TODO POST /v1/beacon/pool/attestations
|
||||
# TODO GET /v1/beacon/pool/attestations
|
||||
|
||||
|
||||
|
||||
proc post_v1_beacon_pool_attestations(attestation: Attestation): bool
|
||||
|
||||
proc get_v1_config_fork_schedule(): seq[tuple[epoch: uint64, version: Version]]
|
||||
|
||||
# TODO stateId is part of the REST path
|
||||
proc get_v1_debug_beacon_states_stateId(stateId: string): BeaconState
|
||||
|
||||
|
||||
# TODO: delete old stuff
|
||||
|
@ -21,3 +21,23 @@ type
|
||||
genesis_time: uint64
|
||||
genesis_validators_root: Eth2Digest
|
||||
genesis_fork_version: Version
|
||||
|
||||
BeaconStatesFinalityCheckpointsTuple* = tuple
|
||||
previous_justified: Checkpoint
|
||||
current_justified: Checkpoint
|
||||
finalized: Checkpoint
|
||||
|
||||
BeaconStatesValidatorsTuple* = tuple
|
||||
validator: Validator
|
||||
status: string
|
||||
balance: uint64
|
||||
|
||||
BeaconStatesCommitteesTuple* = tuple
|
||||
index: uint64
|
||||
slot: uint64
|
||||
validators: seq[uint64] # each object in the sequence should have an index field...
|
||||
|
||||
BeaconHeadersTuple* = tuple
|
||||
root: Eth2Digest
|
||||
canonical: bool
|
||||
header: SignedBeaconBlockHeader
|
||||
|
@ -10,19 +10,16 @@ import
|
||||
# calls that return a bool are actually without a return type in the main REST API
|
||||
# spec but nim-json-rpc requires that all RPC calls have a return type.
|
||||
|
||||
proc post_v1_beacon_pool_attestations(attestation: Attestation): bool
|
||||
proc get_v1_validator_block(slot: Slot, graffiti: Eth2Digest, randao_reveal: ValidatorSig): BeaconBlock
|
||||
|
||||
# TODO slot is part of the REST path
|
||||
proc get_v1_validator_blocks(slot: Slot, graffiti: Eth2Digest, randao_reveal: ValidatorSig): BeaconBlock
|
||||
proc post_v1_validator_block(body: SignedBeaconBlock): bool
|
||||
|
||||
proc post_v1_beacon_blocks(body: SignedBeaconBlock): bool
|
||||
|
||||
proc get_v1_validator_attestation_data(slot: Slot, committee_index: CommitteeIndex): AttestationData
|
||||
proc get_v1_validator_attestation(slot: Slot, committee_index: CommitteeIndex): AttestationData
|
||||
|
||||
# TODO at the time of writing (10.06.2020) the API specifies this call to have a hash of
|
||||
# the attestation data instead of the object itself but we also need the slot.. see here:
|
||||
# https://docs.google.com/spreadsheets/d/1kVIx6GvzVLwNYbcd-Fj8YUlPf4qGrWUlS35uaTnIAVg/edit?disco=AAAAGh7r_fQ
|
||||
proc get_v1_validator_aggregate_attestation(attestation_data: AttestationData): Attestation
|
||||
proc get_v1_validator_aggregate_and_proof(attestation_data: AttestationData): Attestation
|
||||
|
||||
proc post_v1_validator_aggregate_and_proof(payload: SignedAggregateAndProof): bool
|
||||
|
||||
|
@ -7,10 +7,10 @@
|
||||
|
||||
import
|
||||
# Standard library
|
||||
tables, strutils, parseutils,
|
||||
tables, strutils, parseutils, sequtils,
|
||||
|
||||
# Nimble packages
|
||||
stew/[objects],
|
||||
stew/[byteutils, objects],
|
||||
chronos, metrics, json_rpc/[rpcserver, jsonmarshal],
|
||||
chronicles,
|
||||
|
||||
@ -27,86 +27,281 @@ type
|
||||
|
||||
logScope: topics = "valapi"
|
||||
|
||||
proc toBlockSlot(blckRef: BlockRef): BlockSlot =
|
||||
blckRef.atSlot(blckRef.slot)
|
||||
|
||||
proc parseRoot(str: string): Eth2Digest =
|
||||
return Eth2Digest(data: hexToByteArray[32](str))
|
||||
|
||||
proc parsePubkey(str: string): ValidatorPubKey =
|
||||
let pubkeyRes = fromHex(ValidatorPubKey, str)
|
||||
if pubkeyRes.isErr:
|
||||
raise newException(CatchableError, "Not a valid public key")
|
||||
return pubkeyRes[]
|
||||
|
||||
proc doChecksAndGetCurrentHead(node: BeaconNode, slot: Slot): BlockRef =
|
||||
result = node.blockPool.head.blck
|
||||
if not node.isSynced(result):
|
||||
raise newException(CatchableError, "Cannot fulfill request until ndoe is synced")
|
||||
# TODO for now we limit the requests arbitrarily by up to 2 epochs into the future
|
||||
if result.slot + uint64(2 * SLOTS_PER_EPOCH) < slot:
|
||||
raise newException(CatchableError, "Requesting way ahead of the current head")
|
||||
|
||||
proc doChecksAndGetCurrentHead(node: BeaconNode, epoch: Epoch): BlockRef =
|
||||
node.doChecksAndGetCurrentHead(epoch.compute_start_slot_at_epoch)
|
||||
|
||||
# TODO currently this function throws if the validator isn't found - is this OK?
|
||||
proc getValidatorInfoFromValidatorId(
|
||||
state: BeaconState,
|
||||
current_epoch: Epoch,
|
||||
validatorId: string,
|
||||
status = ""):
|
||||
Option[BeaconStatesValidatorsTuple] =
|
||||
const allowedStatuses = ["", "pending", "pending_initialized", "pending_queued",
|
||||
"active", "active_ongoing", "active_exiting", "active_slashed", "exited",
|
||||
"exited_unslashed", "exited_slashed", "withdrawal", "withdrawal_possible",
|
||||
"withdrawal_done"]
|
||||
if status notin allowedStatuses:
|
||||
raise newException(CatchableError, "Invalid status requested")
|
||||
|
||||
let validator = if validatorId.startsWith("0x"):
|
||||
let pubkey = parsePubkey(validatorId)
|
||||
let idx = state.validators.asSeq.findIt(it.pubKey == pubkey)
|
||||
if idx == -1:
|
||||
raise newException(CatchableError, "Could not find validator")
|
||||
state.validators[idx]
|
||||
else:
|
||||
var valIdx: BiggestUInt
|
||||
if parseBiggestUInt(validatorId, valIdx) != validatorId.len:
|
||||
raise newException(CatchableError, "Not a valid index")
|
||||
if state.validators.len >= valIdx.int:
|
||||
raise newException(CatchableError, "Index out of bounds")
|
||||
state.validators[valIdx]
|
||||
|
||||
# time to determine the status of the validator - the code mimics
|
||||
# whatever is detailed here: https://hackmd.io/ofFJ5gOmQpu1jjHilHbdQQ
|
||||
let actual_status = if validator.activation_epoch > current_epoch:
|
||||
# pending
|
||||
if validator.activation_eligibility_epoch == FAR_FUTURE_EPOCH:
|
||||
"pending_initialized"
|
||||
else:
|
||||
# validator.activation_eligibility_epoch < FAR_FUTURE_EPOCH:
|
||||
"pending_queued"
|
||||
elif validator.activation_epoch <= current_epoch and
|
||||
current_epoch < validator.exit_epoch:
|
||||
# active
|
||||
if validator.exit_epoch == FAR_FUTURE_EPOCH:
|
||||
"active_ongoing"
|
||||
elif not validator.slashed:
|
||||
# validator.exit_epoch < FAR_FUTURE_EPOCH
|
||||
"active_exiting"
|
||||
else:
|
||||
# validator.exit_epoch < FAR_FUTURE_EPOCH and validator.slashed:
|
||||
"active_slashed"
|
||||
elif validator.exit_epoch <= current_epoch and
|
||||
current_epoch < validator.withdrawable_epoch:
|
||||
# exited
|
||||
if not validator.slashed:
|
||||
"exited_unslashed"
|
||||
else:
|
||||
# validator.slashed
|
||||
"exited_slashed"
|
||||
elif validator.withdrawable_epoch <= current_epoch:
|
||||
# withdrawal
|
||||
if validator.effective_balance != 0:
|
||||
"withdrawal_possible"
|
||||
else:
|
||||
# validator.effective_balance == 0
|
||||
"withdrawal_done"
|
||||
else:
|
||||
raise newException(CatchableError, "Invalid validator status")
|
||||
|
||||
# if the requested status doesn't match the actual status
|
||||
if status != "" and status notin actual_status:
|
||||
return none(BeaconStatesValidatorsTuple)
|
||||
|
||||
return some((validator: validator, status: actual_status,
|
||||
balance: validator.effective_balance))
|
||||
|
||||
proc getBlockSlotFromString(node: BeaconNode, slot: string): BlockSlot =
|
||||
var parsed: BiggestUInt
|
||||
if parseBiggestUInt(slot, parsed) != slot.len:
|
||||
raise newException(CatchableError, "Not a valid slot number")
|
||||
let head = node.doChecksAndGetCurrentHead(parsed.Slot)
|
||||
return head.atSlot(parsed.Slot)
|
||||
|
||||
proc getBlockDataFromBlockId(node: BeaconNode, blockId: string): BlockData =
|
||||
result = case blockId:
|
||||
of "head":
|
||||
node.blockPool.get(node.blockPool.head.blck)
|
||||
of "genesis":
|
||||
node.blockPool.get(node.blockPool.tail)
|
||||
of "finalized":
|
||||
node.blockPool.get(node.blockPool.finalizedHead.blck)
|
||||
else:
|
||||
if blockId.startsWith("0x"):
|
||||
let blckRoot = parseRoot(blockId)
|
||||
let blockData = node.blockPool.get(blckRoot)
|
||||
if blockData.isNone:
|
||||
raise newException(CatchableError, "Block not found")
|
||||
blockData.get()
|
||||
else:
|
||||
let blockSlot = node.getBlockSlotFromString(blockId)
|
||||
if blockSlot.blck.isNil:
|
||||
raise newException(CatchableError, "Block not found")
|
||||
node.blockPool.get(blockSlot.blck)
|
||||
|
||||
proc stateIdToBlockSlot(node: BeaconNode, stateId: string): BlockSlot =
|
||||
result = case stateId:
|
||||
of "head":
|
||||
node.blockPool.head.blck.toBlockSlot()
|
||||
of "genesis":
|
||||
node.blockPool.tail.toBlockSlot()
|
||||
of "finalized":
|
||||
node.blockPool.finalizedHead
|
||||
of "justified":
|
||||
node.blockPool.justifiedState.blck.toBlockSlot()
|
||||
else:
|
||||
if stateId.startsWith("0x"):
|
||||
let blckRoot = parseRoot(stateId)
|
||||
let blckRef = node.blockPool.getRef(blckRoot)
|
||||
if blckRef.isNil:
|
||||
raise newException(CatchableError, "Block not found")
|
||||
blckRef.toBlockSlot()
|
||||
else:
|
||||
node.getBlockSlotFromString(stateId)
|
||||
|
||||
# TODO Probably the `beacon` ones should be defined elsewhere...?
|
||||
|
||||
proc installValidatorApiHandlers*(rpcServer: RpcServer, node: BeaconNode) =
|
||||
|
||||
template withStateForSlot(stateId: string, body: untyped): untyped =
|
||||
var res: BiggestInt
|
||||
if parseBiggestInt(stateId, res) == stateId.len:
|
||||
raise newException(CatchableError, "Not a valid slot number")
|
||||
let head = node.updateHead()
|
||||
let blockSlot = head.atSlot(res.Slot)
|
||||
node.blockPool.withState(node.blockPool.tmpState, blockSlot):
|
||||
template withStateForStateId(stateId: string, body: untyped): untyped =
|
||||
node.blockPool.withState(node.blockPool.tmpState,
|
||||
node.stateIdToBlockSlot(stateId)):
|
||||
body
|
||||
|
||||
rpcServer.rpc("get_v1_beacon_genesis") do () -> BeaconGenesisTuple:
|
||||
debug "get_v1_beacon_genesis"
|
||||
return (genesis_time: node.blockPool.headState.data.data.genesis_time,
|
||||
genesis_validators_root:
|
||||
node.blockPool.headState.data.data.genesis_validators_root,
|
||||
genesis_fork_version: Version(GENESIS_FORK_VERSION))
|
||||
|
||||
rpcServer.rpc("get_v1_beacon_states_root") do (stateId: string) -> Eth2Digest:
|
||||
debug "get_v1_beacon_states_root", stateId = stateId
|
||||
# TODO do we need to call node.updateHead() before using headState?
|
||||
result = case stateId:
|
||||
of "head":
|
||||
node.blockPool.headState.blck.root
|
||||
of "genesis":
|
||||
node.blockPool.headState.data.data.genesis_validators_root
|
||||
of "finalized":
|
||||
node.blockPool.headState.data.data.finalized_checkpoint.root
|
||||
of "justified":
|
||||
node.blockPool.headState.data.data.current_justified_checkpoint.root
|
||||
else:
|
||||
if stateId.startsWith("0x"):
|
||||
# TODO not sure if `fromHex` is the right thing here...
|
||||
# https://github.com/ethereum/eth2.0-APIs/issues/37#issuecomment-638566144
|
||||
# we return whatever was passed to us (this is a nonsense request)
|
||||
fromHex(Eth2Digest, stateId[2..<stateId.len]) # skip first 2 chars
|
||||
else:
|
||||
withStateForSlot(stateId):
|
||||
hashedState.root
|
||||
withStateForStateId(stateId):
|
||||
return hashedState.root
|
||||
|
||||
rpcServer.rpc("get_v1_beacon_states_fork") do (stateId: string) -> Fork:
|
||||
debug "get_v1_beacon_states_fork", stateId = stateId
|
||||
result = case stateId:
|
||||
of "head":
|
||||
node.blockPool.headState.data.data.fork
|
||||
of "genesis":
|
||||
Fork(previous_version: Version(GENESIS_FORK_VERSION),
|
||||
current_version: Version(GENESIS_FORK_VERSION),
|
||||
epoch: GENESIS_EPOCH)
|
||||
of "finalized":
|
||||
node.blockPool.withState(node.blockPool.tmpState, node.blockPool.finalizedHead):
|
||||
state.fork
|
||||
of "justified":
|
||||
node.blockPool.justifiedState.data.data.fork
|
||||
else:
|
||||
if stateId.startsWith("0x"):
|
||||
# TODO not sure if `fromHex` is the right thing here...
|
||||
# https://github.com/ethereum/eth2.0-APIs/issues/37#issuecomment-638566144
|
||||
let blckRoot = fromHex(Eth2Digest, stateId[2..<stateId.len]) # skip first 2 chars
|
||||
let blckRef = node.blockPool.getRef(blckRoot)
|
||||
if blckRef.isNil:
|
||||
raise newException(CatchableError, "Block not found")
|
||||
let blckSlot = blckRef.atSlot(blckRef.slot)
|
||||
node.blockPool.withState(node.blockPool.tmpState, blckSlot):
|
||||
state.fork
|
||||
withStateForStateId(stateId):
|
||||
return state.fork
|
||||
|
||||
rpcServer.rpc("get_v1_beacon_states_finality_checkpoints") do (
|
||||
stateId: string) -> BeaconStatesFinalityCheckpointsTuple:
|
||||
withStateForStateId(stateId):
|
||||
return (previous_justified: state.previous_justified_checkpoint,
|
||||
current_justified: state.current_justified_checkpoint,
|
||||
finalized: state.finalized_checkpoint)
|
||||
|
||||
rpcServer.rpc("get_v1_beacon_states_stateId_validators") do (
|
||||
stateId: string, validatorIds: seq[string],
|
||||
status: string) -> seq[BeaconStatesValidatorsTuple]:
|
||||
let current_epoch = get_current_epoch(node.blockPool.headState.data.data)
|
||||
withStateForStateId(stateId):
|
||||
for validatorId in validatorIds:
|
||||
let res = state.getValidatorInfoFromValidatorId(
|
||||
current_epoch, validatorId, status)
|
||||
if res.isSome():
|
||||
result.add(res.get())
|
||||
|
||||
rpcServer.rpc("get_v1_beacon_states_stateId_validators_validatorId") do (
|
||||
stateId: string, validatorId: string) -> BeaconStatesValidatorsTuple:
|
||||
let current_epoch = get_current_epoch(node.blockPool.headState.data.data)
|
||||
withStateForStateId(stateId):
|
||||
let res = state.getValidatorInfoFromValidatorId(current_epoch, validatorId)
|
||||
if res.isNone:
|
||||
# TODO should we raise here? Maybe this is different from the array case...
|
||||
raise newException(CatchableError, "Validator status differs")
|
||||
return res.get()
|
||||
|
||||
rpcServer.rpc("get_v1_beacon_states_stateId_committees_epoch") do (
|
||||
stateId: string, epoch: uint64, index: uint64, slot: uint64) ->
|
||||
seq[BeaconStatesCommitteesTuple]:
|
||||
withStateForStateId(stateId):
|
||||
var cache = get_empty_per_epoch_cache() # TODO is this OK?
|
||||
|
||||
proc getCommittee(slot: Slot, index: CommitteeIndex): BeaconStatesCommitteesTuple =
|
||||
let vals = get_beacon_committee(state, slot, index, cache).mapIt(it.uint64)
|
||||
return (index: index.uint64, slot: slot.uint64, validators: vals)
|
||||
|
||||
proc forSlot(slot: Slot, res: var seq[BeaconStatesCommitteesTuple]) =
|
||||
if index == 0: # TODO this means if the parameter is missing (its optional)
|
||||
let committees_per_slot = get_committee_count_at_slot(state, slot)
|
||||
for committee_index in 0'u64..<committees_per_slot:
|
||||
res.add(getCommittee(slot, committee_index.CommitteeIndex))
|
||||
else:
|
||||
withStateForSlot(stateId):
|
||||
state.fork
|
||||
res.add(getCommittee(slot, index.CommitteeIndex))
|
||||
|
||||
if slot == 0: # TODO this means if the parameter is missing (its optional)
|
||||
for i in 0 ..< SLOTS_PER_EPOCH:
|
||||
forSlot((compute_start_slot_at_epoch(epoch.Epoch).int + i).Slot, result)
|
||||
else:
|
||||
forSlot(slot.Slot, result)
|
||||
|
||||
rpcServer.rpc("get_v1_beacon_headers") do (
|
||||
slot: uint64, parent_root: Eth2Digest) -> seq[BeaconHeadersTuple]:
|
||||
# @mratsim: I'm adding a toposorted iterator that returns all blocks from last finalization to all heads in the dual fork choice PR @viktor
|
||||
|
||||
# filterIt(dag.blocks.values(), it.blck.slot == slot_of_interest)
|
||||
# maybe usesBlockPool.heads ??? or getBlockRange ???
|
||||
|
||||
# https://discordapp.com/channels/613988663034118151/614014714590134292/726095138484518912
|
||||
|
||||
discard # raise newException(CatchableError, "Not implemented") # cannot compile...
|
||||
|
||||
rpcServer.rpc("get_v1_beacon_headers_blockId") do (
|
||||
blockId: string) -> tuple[canonical: bool, header: SignedBeaconBlockHeader]:
|
||||
let bd = node.getBlockDataFromBlockId(blockId)
|
||||
let tsbb = bd.data
|
||||
result.header.signature.blob = tsbb.signature.data
|
||||
|
||||
result.header.message.slot = tsbb.message.slot
|
||||
result.header.message.proposer_index = tsbb.message.proposer_index
|
||||
result.header.message.parent_root = tsbb.message.parent_root
|
||||
result.header.message.state_root = tsbb.message.state_root
|
||||
result.header.message.body_root = tsbb.message.body.hash_tree_root()
|
||||
|
||||
result.canonical = bd.refs.isAncestorOf(node.blockPool.head.blck)
|
||||
|
||||
rpcServer.rpc("get_v1_beacon_blocks_blockId") do (
|
||||
blockId: string) -> TrustedSignedBeaconBlock:
|
||||
return node.getBlockDataFromBlockId(blockId).data
|
||||
|
||||
rpcServer.rpc("get_v1_beacon_blocks_blockId_root") do (
|
||||
blockId: string) -> Eth2Digest:
|
||||
return node.getBlockDataFromBlockId(blockId).data.message.state_root
|
||||
|
||||
rpcServer.rpc("get_v1_beacon_blocks_blockId_attestations") do (
|
||||
blockId: string) -> seq[TrustedAttestation]:
|
||||
return node.getBlockDataFromBlockId(blockId).data.message.body.attestations.asSeq
|
||||
|
||||
rpcServer.rpc("post_v1_beacon_pool_attestations") do (
|
||||
attestation: Attestation) -> bool:
|
||||
node.sendAttestation(attestation)
|
||||
return true
|
||||
|
||||
rpcServer.rpc("get_v1_validator_blocks") do (
|
||||
rpcServer.rpc("get_v1_config_fork_schedule") do (
|
||||
) -> seq[tuple[epoch: uint64, version: Version]]:
|
||||
discard # raise newException(CatchableError, "Not implemented") # cannot compile...
|
||||
|
||||
rpcServer.rpc("get_v1_debug_beacon_states_stateId") do (
|
||||
stateId: string) -> BeaconState:
|
||||
withStateForStateId(stateId):
|
||||
return state
|
||||
|
||||
rpcServer.rpc("get_v1_validator_block") do (
|
||||
slot: Slot, graffiti: Eth2Digest, randao_reveal: ValidatorSig) -> BeaconBlock:
|
||||
debug "get_v1_validator_blocks", slot = slot
|
||||
let head = node.updateHead()
|
||||
debug "get_v1_validator_block", slot = slot
|
||||
let head = node.doChecksAndGetCurrentHead(slot)
|
||||
|
||||
let proposer = node.blockPool.getProposer(head, slot)
|
||||
if proposer.isNone():
|
||||
raise newException(CatchableError, "could not retrieve block for slot: " & $slot)
|
||||
@ -118,18 +313,13 @@ proc installValidatorApiHandlers*(rpcServer: RpcServer, node: BeaconNode) =
|
||||
raise newException(CatchableError, "could not retrieve block for slot: " & $slot)
|
||||
return res.message.get()
|
||||
|
||||
rpcServer.rpc("post_v1_beacon_blocks") do (body: SignedBeaconBlock) -> bool:
|
||||
debug "post_v1_beacon_blocks",
|
||||
rpcServer.rpc("post_v1_validator_block") do (body: SignedBeaconBlock) -> bool:
|
||||
debug "post_v1_validator_block",
|
||||
slot = body.message.slot,
|
||||
prop_idx = body.message.proposer_index
|
||||
let head = node.doChecksAndGetCurrentHead(body.message.slot)
|
||||
|
||||
let head = node.updateHead()
|
||||
if head.slot >= body.message.slot:
|
||||
warn "Skipping proposal, have newer head already",
|
||||
headSlot = shortLog(head.slot),
|
||||
headBlockRoot = shortLog(head.root),
|
||||
slot = shortLog(body.message.slot),
|
||||
cat = "fastforward"
|
||||
raise newException(CatchableError,
|
||||
"Proposal is for a past slot: " & $body.message.slot)
|
||||
if head == await proposeSignedBlock(node, head, AttachedValidator(),
|
||||
@ -137,26 +327,29 @@ proc installValidatorApiHandlers*(rpcServer: RpcServer, node: BeaconNode) =
|
||||
raise newException(CatchableError, "Could not propose block")
|
||||
return true
|
||||
|
||||
rpcServer.rpc("get_v1_validator_attestation_data") do (
|
||||
rpcServer.rpc("get_v1_validator_attestation") do (
|
||||
slot: Slot, committee_index: CommitteeIndex) -> AttestationData:
|
||||
let head = node.updateHead()
|
||||
let attestationHead = head.atSlot(slot)
|
||||
node.blockPool.withState(node.blockPool.tmpState, attestationHead):
|
||||
debug "get_v1_validator_attestation", slot = slot
|
||||
let head = node.doChecksAndGetCurrentHead(slot)
|
||||
|
||||
node.blockPool.withState(node.blockPool.tmpState, head.atSlot(slot)):
|
||||
return makeAttestationData(state, slot, committee_index.uint64, blck.root)
|
||||
|
||||
rpcServer.rpc("get_v1_validator_aggregate_attestation") do (
|
||||
rpcServer.rpc("get_v1_validator_aggregate_and_proof") do (
|
||||
attestation_data: AttestationData)-> Attestation:
|
||||
debug "get_v1_validator_aggregate_attestation"
|
||||
debug "get_v1_validator_aggregate_and_proof"
|
||||
raise newException(CatchableError, "Not implemented")
|
||||
|
||||
rpcServer.rpc("post_v1_validator_aggregate_and_proof") do (
|
||||
payload: SignedAggregateAndProof) -> bool:
|
||||
node.network.broadcast(node.topicAggregateAndProofs, payload)
|
||||
return true
|
||||
debug "post_v1_validator_aggregate_and_proof"
|
||||
raise newException(CatchableError, "Not implemented")
|
||||
|
||||
rpcServer.rpc("post_v1_validator_duties_attester") do (
|
||||
epoch: Epoch, public_keys: seq[ValidatorPubKey]) -> seq[AttesterDuties]:
|
||||
debug "post_v1_validator_duties_attester", epoch = epoch
|
||||
let head = node.updateHead()
|
||||
let head = node.doChecksAndGetCurrentHead(epoch)
|
||||
|
||||
let attestationHead = head.atSlot(compute_start_slot_at_epoch(epoch))
|
||||
node.blockPool.withState(node.blockPool.tmpState, attestationHead):
|
||||
for pubkey in public_keys:
|
||||
@ -174,7 +367,8 @@ proc installValidatorApiHandlers*(rpcServer: RpcServer, node: BeaconNode) =
|
||||
rpcServer.rpc("get_v1_validator_duties_proposer") do (
|
||||
epoch: Epoch) -> seq[ValidatorPubkeySlotPair]:
|
||||
debug "get_v1_validator_duties_proposer", epoch = epoch
|
||||
let head = node.updateHead()
|
||||
let head = node.doChecksAndGetCurrentHead(epoch)
|
||||
|
||||
for i in 0 ..< SLOTS_PER_EPOCH:
|
||||
let currSlot = (compute_start_slot_at_epoch(epoch).int + i).Slot
|
||||
let proposer = node.blockPool.getProposer(head, currSlot)
|
||||
|
@ -10,7 +10,7 @@ import
|
||||
os, strutils, json, times,
|
||||
|
||||
# Nimble packages
|
||||
stew/shims/[tables, macros],
|
||||
stew/byteutils, stew/shims/[tables, macros],
|
||||
chronos, confutils, metrics, json_rpc/[rpcclient, jsonmarshal],
|
||||
chronicles,
|
||||
blscurve, json_serialization/std/[options, sets, net],
|
||||
@ -44,18 +44,6 @@ type
|
||||
attestationsForEpoch: Table[Epoch, Table[Slot, seq[AttesterDuties]]]
|
||||
beaconGenesis: BeaconGenesisTuple
|
||||
|
||||
proc connectToBN(vc: ValidatorClient) {.gcsafe, async.} =
|
||||
while true:
|
||||
try:
|
||||
await vc.client.connect($vc.config.rpcAddress, Port(vc.config.rpcPort))
|
||||
info "Connected to BN",
|
||||
port = vc.config.rpcPort,
|
||||
address = vc.config.rpcAddress
|
||||
return
|
||||
except CatchableError as err:
|
||||
warn "Could not connect to the BN - retrying!", err = err.msg
|
||||
await sleepAsync(chronos.seconds(1)) # 1 second before retrying
|
||||
|
||||
template attemptUntilSuccess(vc: ValidatorClient, body: untyped) =
|
||||
while true:
|
||||
try:
|
||||
@ -63,9 +51,11 @@ template attemptUntilSuccess(vc: ValidatorClient, body: untyped) =
|
||||
break
|
||||
except CatchableError as err:
|
||||
warn "Caught an unexpected error", err = err.msg
|
||||
waitFor vc.connectToBN()
|
||||
waitFor sleepAsync(chronos.seconds(1)) # 1 second before retrying
|
||||
|
||||
proc getValidatorDutiesForEpoch(vc: ValidatorClient, epoch: Epoch) {.gcsafe, async.} =
|
||||
info "Getting validator duties for epoch", epoch = epoch
|
||||
|
||||
let proposals = await vc.client.get_v1_validator_duties_proposer(epoch)
|
||||
# update the block proposal duties this VC should do during this epoch
|
||||
vc.proposalsForCurrentEpoch.clear()
|
||||
@ -79,29 +69,35 @@ proc getValidatorDutiesForEpoch(vc: ValidatorClient, epoch: Epoch) {.gcsafe, asy
|
||||
validatorPubkeys.add key
|
||||
|
||||
proc getAttesterDutiesForEpoch(epoch: Epoch) {.gcsafe, async.} =
|
||||
let attestations = await vc.client.post_v1_validator_duties_attester(
|
||||
epoch, validatorPubkeys)
|
||||
# make sure there's an entry
|
||||
if not vc.attestationsForEpoch.contains epoch:
|
||||
vc.attestationsForEpoch.add(epoch, Table[Slot, seq[AttesterDuties]]())
|
||||
let attestations = await vc.client.post_v1_validator_duties_attester(
|
||||
epoch, validatorPubkeys)
|
||||
for a in attestations:
|
||||
if vc.attestationsForEpoch[epoch].hasKeyOrPut(a.slot, @[a]):
|
||||
vc.attestationsForEpoch[epoch][a.slot].add(a)
|
||||
|
||||
# clear both for the current epoch and the next because a change of
|
||||
# fork could invalidate the attester duties even the current epoch
|
||||
vc.attestationsForEpoch.clear()
|
||||
await getAttesterDutiesForEpoch(epoch)
|
||||
# obtain the attestation duties this VC should do during the next epoch
|
||||
# TODO currently we aren't making use of this but perhaps we should
|
||||
await getAttesterDutiesForEpoch(epoch + 1)
|
||||
# also get the attestation duties for the current epoch if missing
|
||||
if not vc.attestationsForEpoch.contains epoch:
|
||||
await getAttesterDutiesForEpoch(epoch)
|
||||
# cleanup old epoch attestation duties
|
||||
vc.attestationsForEpoch.del(epoch - 1)
|
||||
# TODO handle subscriptions to beacon committees for both the next epoch and
|
||||
# for the current if missing (beacon_committee_subscriptions from the REST api)
|
||||
|
||||
# for now we will get the fork each time we update the validator duties for each epoch
|
||||
# TODO should poll occasionally `/v1/config/fork_schedule`
|
||||
vc.fork = await vc.client.get_v1_beacon_states_fork("head")
|
||||
|
||||
var numAttestationsForEpoch = 0
|
||||
for _, dutiesForSlot in vc.attestationsForEpoch[epoch]:
|
||||
numAttestationsForEpoch += dutiesForSlot.len
|
||||
|
||||
info "Got validator duties for epoch",
|
||||
num_proposals = vc.proposalsForCurrentEpoch.len,
|
||||
num_attestations = numAttestationsForEpoch
|
||||
|
||||
proc onSlotStart(vc: ValidatorClient, lastSlot, scheduledSlot: Slot) {.gcsafe, async.} =
|
||||
|
||||
let
|
||||
@ -135,18 +131,22 @@ proc onSlotStart(vc: ValidatorClient, lastSlot, scheduledSlot: Slot) {.gcsafe, a
|
||||
let public_key = vc.proposalsForCurrentEpoch[slot]
|
||||
let validator = vc.attachedValidators.validators[public_key]
|
||||
|
||||
info "Proposing block", slot = slot, public_key = public_key
|
||||
|
||||
let randao_reveal = validator.genRandaoReveal(
|
||||
vc.fork, vc.beaconGenesis.genesis_validators_root, slot)
|
||||
|
||||
var graffiti: Eth2Digest
|
||||
graffiti.data[0..<5] = toBytes("quack")
|
||||
var newBlock = SignedBeaconBlock(
|
||||
message: await vc.client.get_v1_validator_blocks(slot, Eth2Digest(), randao_reveal)
|
||||
message: await vc.client.get_v1_validator_block(slot, graffiti, randao_reveal)
|
||||
)
|
||||
|
||||
let blockRoot = hash_tree_root(newBlock.message)
|
||||
newBlock.signature = await validator.signBlockProposal(
|
||||
vc.fork, vc.beaconGenesis.genesis_validators_root, slot, blockRoot)
|
||||
|
||||
discard await vc.client.post_v1_beacon_blocks(newBlock)
|
||||
discard await vc.client.post_v1_validator_block(newBlock)
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.12.1/specs/phase0/validator.md#attesting
|
||||
# A validator should create and broadcast the attestation to the associated
|
||||
@ -158,11 +158,13 @@ proc onSlotStart(vc: ValidatorClient, lastSlot, scheduledSlot: Slot) {.gcsafe, a
|
||||
seconds(int64(SECONDS_PER_SLOT)) div 3, slot, "Waiting to send attestations")
|
||||
|
||||
# check if we have validators which need to attest on this slot
|
||||
if vc.attestationsForEpoch[epoch].contains slot:
|
||||
if vc.attestationsForEpoch.contains(epoch) and
|
||||
vc.attestationsForEpoch[epoch].contains slot:
|
||||
for a in vc.attestationsForEpoch[epoch][slot]:
|
||||
let validator = vc.attachedValidators.validators[a.public_key]
|
||||
info "Attesting", slot = slot, public_key = a.public_key
|
||||
|
||||
let ad = await vc.client.get_v1_validator_attestation_data(slot, a.committee_index)
|
||||
let validator = vc.attachedValidators.validators[a.public_key]
|
||||
let ad = await vc.client.get_v1_validator_attestation(slot, a.committee_index)
|
||||
|
||||
# TODO I don't like these (u)int64-to-int conversions...
|
||||
let attestation = await validator.produceAndSignAttestation(
|
||||
@ -173,7 +175,6 @@ proc onSlotStart(vc: ValidatorClient, lastSlot, scheduledSlot: Slot) {.gcsafe, a
|
||||
|
||||
except CatchableError as err:
|
||||
warn "Caught an unexpected error", err = err.msg, slot = shortLog(slot)
|
||||
await vc.connectToBN()
|
||||
|
||||
let
|
||||
nextSlotStart = saturate(vc.beaconClock.fromNow(nextSlot))
|
||||
@ -202,10 +203,6 @@ programMain:
|
||||
|
||||
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",
|
||||
@ -222,7 +219,10 @@ programMain:
|
||||
for curr in vc.config.validatorKeys:
|
||||
vc.attachedValidators.addLocalValidator(curr.toPubKey, curr)
|
||||
|
||||
waitFor vc.connectToBN()
|
||||
waitFor vc.client.connect($vc.config.rpcAddress, Port(vc.config.rpcPort))
|
||||
info "Connected to BN",
|
||||
port = vc.config.rpcPort,
|
||||
address = vc.config.rpcAddress
|
||||
|
||||
vc.attemptUntilSuccess:
|
||||
# init the beacon clock
|
||||
|
@ -73,7 +73,7 @@ func getAttachedValidator*(node: BeaconNode,
|
||||
let validatorKey = state.validators[idx].pubkey
|
||||
node.attachedValidators.getValidator(validatorKey)
|
||||
|
||||
proc isSynced(node: BeaconNode, head: BlockRef): bool =
|
||||
proc isSynced*(node: BeaconNode, head: BlockRef): bool =
|
||||
## TODO This function is here as a placeholder for some better heurestics to
|
||||
## determine if we're in sync and should be producing blocks and
|
||||
## attestations. Generally, the problem is that slot time keeps advancing
|
||||
|
@ -19,6 +19,8 @@ Before you can access the API, make sure it's enabled using the RPC flag (`beaco
|
||||
--rpc-address Listening address of the RPC server.
|
||||
```
|
||||
|
||||
One difference is that currently endpoints that correspond to specific ones from the [spec](https://ethereum.github.io/eth2.0-APIs/) are named weirdly - for example an endpoint such as [`getGenesis`](https://ethereum.github.io/eth2.0-APIs/#/Beacon/getGenesis) is currently named `get_v1_beacon_genesis` which would map 1:1 to the actual REST path in the future - verbose but unambiguous.
|
||||
|
||||
## Beacon Node API
|
||||
|
||||
### getBeaconHead
|
||||
@ -53,10 +55,104 @@ curl -d '{"jsonrpc":"2.0","id":"id","method":"getChainHead","params":[] }' -H 'C
|
||||
curl -d '{"jsonrpc":"2.0","id":"id","method":"getNetworkEnr","params":[] }' -H 'Content-Type: application/json' localhost:9190 -s | jq
|
||||
```
|
||||
|
||||
### [`get_v1_beacon_genesis`](https://ethereum.github.io/eth2.0-APIs/#/Beacon/getGenesis)
|
||||
|
||||
```
|
||||
curl -d '{"jsonrpc":"2.0","method":"get_v1_beacon_genesis","params":[],"id":1}' -H 'Content-Type: application/json' localhost:9190 -s | jq
|
||||
```
|
||||
|
||||
### [`get_v1_beacon_states_root`](https://ethereum.github.io/eth2.0-APIs/#/Beacon/getStateRoot)
|
||||
|
||||
```
|
||||
curl -d '{"jsonrpc":"2.0","method":"get_v1_beacon_states_root","params":["finalized"],"id":1}' -H 'Content-Type: application/json' localhost:9190 -s | jq
|
||||
```
|
||||
|
||||
### [`get_v1_beacon_states_fork`](https://ethereum.github.io/eth2.0-APIs/#/Beacon/getStateFork)
|
||||
|
||||
```
|
||||
curl -d '{"jsonrpc":"2.0","method":"get_v1_beacon_states_fork","params":["finalized"],"id":1}' -H 'Content-Type: application/json' localhost:9190 -s | jq
|
||||
```
|
||||
|
||||
### [`get_v1_beacon_states_finality_checkpoints`](https://ethereum.github.io/eth2.0-APIs/#/Beacon/getStateFinalityCheckpoints)
|
||||
|
||||
```
|
||||
curl -d '{"jsonrpc":"2.0","method":"get_v1_beacon_states_finality_checkpoints","params":["finalized"],"id":1}' -H 'Content-Type: application/json' localhost:9190 -s | jq
|
||||
```
|
||||
|
||||
### [`get_v1_beacon_states_stateId_validators`](https://ethereum.github.io/eth2.0-APIs/#/Beacon/getStateValidators)
|
||||
|
||||
### [`get_v1_beacon_states_stateId_validators_validatorId`](https://ethereum.github.io/eth2.0-APIs/#/Beacon/getStateValidator)
|
||||
|
||||
### [`get_v1_beacon_states_stateId_committees_epoch`](https://ethereum.github.io/eth2.0-APIs/#/Beacon/getEpochCommittees)
|
||||
|
||||
### [`get_v1_beacon_headers`](https://ethereum.github.io/eth2.0-APIs/#/Beacon/getBlockHeaders)
|
||||
|
||||
### [`get_v1_beacon_headers_blockId`](https://ethereum.github.io/eth2.0-APIs/#/Beacon/getBlockHeader)
|
||||
|
||||
```
|
||||
curl -d '{"jsonrpc":"2.0","method":"get_v1_beacon_headers_blockId","params":["finalized"],"id":1}' -H 'Content-Type: application/json' localhost:9190 -s | jq
|
||||
```
|
||||
|
||||
### [`get_v1_beacon_blocks_blockId`](https://ethereum.github.io/eth2.0-APIs/#/Beacon/getBlock)
|
||||
|
||||
```
|
||||
curl -d '{"jsonrpc":"2.0","method":"get_v1_beacon_blocks_blockId","params":["finalized"],"id":1}' -H 'Content-Type: application/json' localhost:9190 -s | jq
|
||||
```
|
||||
|
||||
### [`get_v1_beacon_blocks_blockId_root`](https://ethereum.github.io/eth2.0-APIs/#/Beacon/getBlockRoot)
|
||||
|
||||
```
|
||||
curl -d '{"jsonrpc":"2.0","method":"get_v1_beacon_blocks_blockId_root","params":["finalized"],"id":1}' -H 'Content-Type: application/json' localhost:9190 -s | jq
|
||||
```
|
||||
|
||||
### [`get_v1_beacon_blocks_blockId_attestations`](https://ethereum.github.io/eth2.0-APIs/#/Beacon/getBlockAttestations)
|
||||
|
||||
```
|
||||
curl -d '{"jsonrpc":"2.0","method":"get_v1_beacon_blocks_blockId_attestations","params":["finalized"],"id":1}' -H 'Content-Type: application/json' localhost:9190 -s | jq
|
||||
```
|
||||
|
||||
### [`post_v1_beacon_pool_attestations`](https://ethereum.github.io/eth2.0-APIs/#/Beacon/submitPoolAttestations)
|
||||
|
||||
## Valdiator API
|
||||
|
||||
### [`get_v1_validator_block`](https://ethereum.github.io/eth2.0-APIs/#/ValidatorRequiredApi/produceBlock)
|
||||
|
||||
### [`post_v1_validator_block`](https://ethereum.github.io/eth2.0-APIs/#/ValidatorRequiredApi/publishBlock)
|
||||
|
||||
### [`get_v1_validator_attestation`](https://ethereum.github.io/eth2.0-APIs/#/ValidatorRequiredApi/produceAttestation)
|
||||
|
||||
```
|
||||
curl -d '{"jsonrpc":"2.0","method":"get_v1_validator_attestation_data","params":[0,3],"id":1}' -H 'Content-Type: application/json' localhost:9190 -s | jq
|
||||
```
|
||||
|
||||
### [`get_v1_validator_aggregate_and_proof`](https://ethereum.github.io/eth2.0-APIs/#/ValidatorRequiredApi/getAggregatedAttestation)
|
||||
|
||||
### [`post_v1_validator_aggregate_and_proof`](https://ethereum.github.io/eth2.0-APIs/#/ValidatorRequiredApi/publishAggregateAndProof)
|
||||
|
||||
### [`post_v1_validator_duties_attester`](https://ethereum.github.io/eth2.0-APIs/#/ValidatorRequiredApi/getAttesterDuties)
|
||||
|
||||
```
|
||||
curl -d '{"jsonrpc":"2.0","method":"post_v1_validator_duties_attester","params":[1,["a7a0502eae26043d1ac39a39457a6cdf68fae2055d89c7dc59092c25911e4ee55c4e7a31ade61c39480110a393be28e8","a1826dd94cd96c48a81102d316a2af4960d19ca0b574ae5695f2d39a88685a43997cef9a5c26ad911847674d20c46b75"]],"id":1}' -H 'Content-Type: application/json' localhost:9190 -s | jq
|
||||
```
|
||||
|
||||
### [`get_v1_validator_duties_proposer`](https://ethereum.github.io/eth2.0-APIs/#/ValidatorRequiredApi/getProposerDuties)
|
||||
|
||||
```
|
||||
curl -d '{"jsonrpc":"2.0","id":"id","method":"get_v1_validator_duties_proposer","params":[1] }' -H 'Content-Type: application/json' localhost:9190 -s | jq
|
||||
```
|
||||
|
||||
## Config
|
||||
|
||||
### [`get_v1_config_fork_schedule`](https://ethereum.github.io/eth2.0-APIs/#/Config/getForkSchedule)
|
||||
|
||||
## Administrative / Debug API
|
||||
|
||||
### `get_v1_debug_beacon_states_stateId` - returns an entire `BeaconState` object for the specified `stateId`
|
||||
|
||||
```
|
||||
curl -d '{"jsonrpc":"2.0","method":"get_v1_debug_beacon_states_stateId","params":["head"],"id":1}' -H 'Content-Type: application/json' localhost:9190 -s | jq
|
||||
```
|
||||
|
||||
### getNodeVersion
|
||||
|
||||
Show version of the software
|
||||
|
@ -40,10 +40,11 @@ proc updateTestnetsRepo(allTestnetsDir, buildDir: string) =
|
||||
|
||||
proc makePrometheusConfig(nodeID, baseMetricsPort: int, dataDir: string) =
|
||||
# macOS may not have gnu-getopts installed and in the PATH
|
||||
execIgnoringExitCode &"""./scripts/make_prometheus_config.sh --nodes """ & $(1 + nodeID) & &""" --base-metrics-port {baseMetricsPort} --config-file "{dataDir}/prometheus.yml""""
|
||||
execIgnoringExitCode &"""./scripts/make_prometheus_config.sh --nodes """ & $(1 + nodeID) & &""" --base-metrics-port {baseMetricsPort} --config-file "{dataDir}/prometheus.yml" """
|
||||
|
||||
proc buildNode(nimFlags, preset, beaconNodeBinary: string) =
|
||||
exec &"""nim c {nimFlags} -d:"const_preset={preset}" -o:"{beaconNodeBinary}" beacon_chain/beacon_node.nim"""
|
||||
proc buildBinary(nimFlags, preset, binary, nimFile: string) =
|
||||
if binary != "":
|
||||
exec &"""nim c {nimFlags} -d:"const_preset={preset}" -o:"{binary}" beacon_chain/{nimFile}"""
|
||||
|
||||
proc becomeValidator(validatorsDir, beaconNodeBinary, secretsDir, depositContractOpt, privateGoerliKey: string,
|
||||
becomeValidatorOnly: bool) =
|
||||
@ -75,8 +76,8 @@ proc becomeValidator(validatorsDir, beaconNodeBinary, secretsDir, depositContrac
|
||||
echo "\nDeposit sent, wait for confirmation then press enter to continue"
|
||||
discard readLineFromStdin()
|
||||
|
||||
proc runNode(dataDir, beaconNodeBinary, bootstrapFileOpt, depositContractOpt,
|
||||
genesisFileOpt, extraBeaconNodeOptions: string,
|
||||
proc runNode(dataDir, beaconNodeBinary, validatorClientBinary, bootstrapFileOpt,
|
||||
depositContractOpt, genesisFileOpt, extraBeaconNodeOptions: string,
|
||||
basePort, nodeID, baseMetricsPort, baseRpcPort: int,
|
||||
printCmdOnly: bool) =
|
||||
let logLevel = getEnv("LOG_LEVEL")
|
||||
@ -99,21 +100,36 @@ proc runNode(dataDir, beaconNodeBinary, bootstrapFileOpt, depositContractOpt,
|
||||
echo &"cd {dataDir}; exec {cmd}"
|
||||
else:
|
||||
cd dataDir
|
||||
mkDir dataDir & "/empty_dummy_folder"
|
||||
|
||||
# if launching a VC as well - send the BN looking nowhere for validators/secrets
|
||||
# TODO use `start` (or something else) on windows (instead of `&`) for the 2 processes
|
||||
let vcCommand = if validatorClientBinary == "": "" else:
|
||||
&"""
|
||||
--secrets-dir={dataDir}/empty_dummy_folder
|
||||
--validators-dir={dataDir}/empty_dummy_folder
|
||||
& {validatorClientBinary}
|
||||
--rpc-port={baseRpcPort + nodeID}
|
||||
--data-dir="{dataDir}"
|
||||
{logLevelOpt}
|
||||
"""
|
||||
|
||||
cmd = replace(&"""{beaconNodeBinary}
|
||||
--data-dir="{dataDir}"
|
||||
--dump
|
||||
--web3-url={web3Url}
|
||||
--tcp-port=""" & $(basePort + nodeID) & &"""
|
||||
--udp-port=""" & $(basePort + nodeID) & &"""
|
||||
--tcp-port={basePort + nodeID}
|
||||
--udp-port={basePort + nodeID}
|
||||
--metrics
|
||||
--metrics-port=""" & $(baseMetricsPort + nodeID) & &"""
|
||||
--metrics-port={baseMetricsPort + nodeID}
|
||||
--rpc
|
||||
--rpc-port=""" & $(baseRpcPort + nodeID) & &"""
|
||||
--rpc-port={baseRpcPort + nodeID}
|
||||
{bootstrapFileOpt}
|
||||
{logLevelOpt}
|
||||
{depositContractOpt}
|
||||
{genesisFileOpt}
|
||||
{extraBeaconNodeOptions}""", "\n", " ")
|
||||
{extraBeaconNodeOptions}
|
||||
{vcCommand} """, "\n", " ")
|
||||
execIgnoringExitCode cmd
|
||||
|
||||
cli do (skipGoerliKey {.
|
||||
@ -157,6 +173,9 @@ cli do (skipGoerliKey {.
|
||||
runOnly {.
|
||||
desc: "Just run it." .} = false,
|
||||
|
||||
separateVC {.
|
||||
desc: "use a separate validator client process." .} = false,
|
||||
|
||||
printCmdOnly {.
|
||||
desc: "Just print the commands (suitable for passing to 'eval'; might replace current shell)." .} = false,
|
||||
|
||||
@ -220,6 +239,11 @@ cli do (skipGoerliKey {.
|
||||
|
||||
doAssert specVersion in ["v0.11.3", "v0.12.1"]
|
||||
|
||||
if defined(windows) and separateVC:
|
||||
# TODO use `start` (or something else) on windows (instead of `&`) for the 2 processes
|
||||
echo "Cannot use a separate validator client process on Windows! (remove --separateVC)"
|
||||
quit(1)
|
||||
|
||||
let
|
||||
dataDirName = testnetName.replace("/", "_")
|
||||
.replace("(", "_")
|
||||
@ -228,12 +252,17 @@ cli do (skipGoerliKey {.
|
||||
validatorsDir = dataDir / "validators"
|
||||
secretsDir = dataDir / "secrets"
|
||||
beaconNodeBinary = buildDir / "beacon_node_" & dataDirName
|
||||
# using a separate VC is disabled on windows until we find a substitute for `&`
|
||||
validatorClientBinary = if separateVC: buildDir / "validator_client_" & dataDirName else: ""
|
||||
var
|
||||
nimFlags = &"-d:chronicles_log_level=TRACE " & getEnv("NIM_PARAMS")
|
||||
nimFlagsBN = &"-d:chronicles_log_level=TRACE " & getEnv("NIM_PARAMS")
|
||||
nimFlagsVC = nimFlagsBN
|
||||
|
||||
if writeLogFile:
|
||||
# write the logs to a file
|
||||
nimFlags.add """ -d:"chronicles_sinks=textlines,json[file(nbc""" & staticExec("date +\"%Y%m%d%H%M%S\"") & """.log)]" """
|
||||
let logFileNimParams = """ -d:"chronicles_sinks=textlines,json[file(placeholder_""" & staticExec("date +\"%Y%m%d%H%M%S\"") & """.log)]" """
|
||||
nimFlagsBN.add logFileNimParams.replace("placeholder_", "nbc_bn_")
|
||||
nimFlagsVC.add logFileNimParams.replace("placeholder_", "nbc_vc_")
|
||||
|
||||
let depositContractFile = testnetDir / depositContractFileName
|
||||
if system.fileExists(depositContractFile):
|
||||
@ -248,7 +277,8 @@ cli do (skipGoerliKey {.
|
||||
|
||||
if doBuild:
|
||||
makePrometheusConfig(nodeID, baseMetricsPort, dataDir)
|
||||
buildNode(nimFlags, preset, beaconNodeBinary)
|
||||
buildBinary(nimFlagsBN, preset, beaconNodeBinary, "beacon_node.nim")
|
||||
buildBinary(nimFlagsVC, preset, validatorClientBinary, "validator_client.nim")
|
||||
|
||||
if doBecomeValidator and depositContractOpt.len > 0 and not system.dirExists(validatorsDir):
|
||||
becomeValidator(validatorsDir, beaconNodeBinary, secretsDir, depositContractOpt, privateGoerliKey, becomeValidatorOnly)
|
||||
@ -256,7 +286,7 @@ cli do (skipGoerliKey {.
|
||||
echo &"extraBeaconNodeOptions = '{extraBeaconNodeOptions}'"
|
||||
|
||||
if doRun:
|
||||
runNode(dataDir, beaconNodeBinary, bootstrapFileOpt, depositContractOpt,
|
||||
genesisFileOpt, extraBeaconNodeOptions,
|
||||
runNode(dataDir, beaconNodeBinary, validatorClientBinary, bootstrapFileOpt,
|
||||
depositContractOpt, genesisFileOpt, extraBeaconNodeOptions,
|
||||
basePort, nodeID, baseMetricsPort, baseRpcPort,
|
||||
printCmdOnly)
|
||||
|
Loading…
x
Reference in New Issue
Block a user