Implement the 'deposits exit' command; Remove 'deposits create'

This commit is contained in:
Zahary Karadjov 2020-11-27 21:48:33 +02:00 committed by zah
parent a2364ce1bc
commit 3c0dfc2fbe
12 changed files with 274 additions and 47 deletions

View File

@ -1,7 +1,7 @@
{.push raises: [Defect].} {.push raises: [Defect].}
import import
strutils, os, options, unicode, strutils, os, options, unicode, uri,
chronicles, chronicles/options as chroniclesOptions, chronicles, chronicles/options as chroniclesOptions,
confutils, confutils/defs, confutils/std/net, stew/shims/net as stewNet, confutils, confutils/defs, confutils/std/net, stew/shims/net as stewNet,
stew/io2, unicodedb/properties, normalize, stew/io2, unicodedb/properties, normalize,
@ -12,12 +12,11 @@ import
network_metadata, filepath network_metadata, filepath
export export
uri,
defaultEth2TcpPort, enabledLogLevel, ValidIpAddress, defaultEth2TcpPort, enabledLogLevel, ValidIpAddress,
defs, parseCmdArg, completeCmdArg, network_metadata defs, parseCmdArg, completeCmdArg, network_metadata
type type
ValidatorKeyPath* = TypedInputFile[ValidatorPrivKey, Txt, "privkey"]
BNStartUpCmd* = enum BNStartUpCmd* = enum
noCommand noCommand
createTestnet createTestnet
@ -31,9 +30,10 @@ type
list = "Lists details about all wallets" list = "Lists details about all wallets"
DepositsCmd* {.pure.} = enum DepositsCmd* {.pure.} = enum
create = "Creates validator keystores and deposits" # create = "Creates validator keystores and deposits"
`import` = "Imports password-protected keystores interactively" `import` = "Imports password-protected keystores interactively"
status = "Displays status information about all deposits" # status = "Displays status information about all deposits"
exit = "Submits a validator voluntary exit"
VCStartUpCmd* = enum VCStartUpCmd* = enum
VCNoCommand VCNoCommand
@ -325,6 +325,7 @@ type
of deposits: of deposits:
case depositsCmd* {.command.}: DepositsCmd case depositsCmd* {.command.}: DepositsCmd
#[
of DepositsCmd.create: of DepositsCmd.create:
totalDeposits* {. totalDeposits* {.
defaultValue: 1 defaultValue: 1
@ -357,13 +358,28 @@ type
desc: "Output wallet file" desc: "Output wallet file"
name: "new-wallet-file" }: Option[OutFile] name: "new-wallet-file" }: Option[OutFile]
of DepositsCmd.status:
discard
#]#
of DepositsCmd.`import`: of DepositsCmd.`import`:
importedDepositsDir* {. importedDepositsDir* {.
argument argument
desc: "A directory with keystores to import" }: Option[InputDir] desc: "A directory with keystores to import" }: Option[InputDir]
of DepositsCmd.status: of DepositsCmd.exit:
discard exitedValidator* {.
name: "validator"
desc: "Validator index or a public key of the exited validator" }: string
rpcUrlForExit* {.
name: "rpc-url"
defaultValue: parseUri("wss://localhost:" & $defaultEth2RpcPort)
desc: "URL of the beacon node JSON-RPC service" }: Uri
exitAtEpoch* {.
name: "epoch"
desc: "The desired exit epoch" }: Option[uint64]
of record: of record:
case recordCmd* {.command.}: RecordCmd case recordCmd* {.command.}: RecordCmd
@ -501,6 +517,13 @@ func parseCmdArg*(T: type BlockHashOrNumber, input: TaintedString): T
func completeCmdArg*(T: type BlockHashOrNumber, input: TaintedString): seq[string] = func completeCmdArg*(T: type BlockHashOrNumber, input: TaintedString): seq[string] =
return @[] return @[]
func parseCmdArg*(T: type Uri, input: TaintedString): T
{.raises: [ValueError, Defect].} =
parseUri(input.string)
func completeCmdArg*(T: type Uri, input: TaintedString): seq[string] =
return @[]
func parseCmdArg*(T: type Checkpoint, input: TaintedString): T func parseCmdArg*(T: type Checkpoint, input: TaintedString): T
{.raises: [ValueError, Defect].} = {.raises: [ValueError, Defect].} =
let sepIdx = find(input.string, ':') let sepIdx = find(input.string, ':')
@ -569,9 +592,11 @@ func outWalletName*(conf: BeaconNodeConf): Option[WalletName] =
of WalletsCmd.restore: conf.restoredWalletNameFlag of WalletsCmd.restore: conf.restoredWalletNameFlag
of WalletsCmd.list: fail() of WalletsCmd.list: fail()
of deposits: of deposits:
case conf.depositsCmd # TODO: Uncomment when the deposits create command is restored
of DepositsCmd.create: conf.newWalletNameFlag #case conf.depositsCmd
else: fail() #of DepositsCmd.create: conf.newWalletNameFlag
#else: fail()
fail()
else: else:
fail() fail()
@ -586,9 +611,11 @@ func outWalletFile*(conf: BeaconNodeConf): Option[OutFile] =
of WalletsCmd.restore: conf.restoredWalletFileFlag of WalletsCmd.restore: conf.restoredWalletFileFlag
of WalletsCmd.list: fail() of WalletsCmd.list: fail()
of deposits: of deposits:
case conf.depositsCmd # TODO: Uncomment when the deposits create command is restored
of DepositsCmd.create: conf.newWalletFileFlag #case conf.depositsCmd
else: fail() #of DepositsCmd.create: conf.newWalletFileFlag
#else: fail()
fail()
else: else:
fail() fail()

View File

@ -48,9 +48,19 @@ template genFromJsonForIntType(T: untyped) =
proc fromJson*(n: JsonNode, argName: string, result: var T) = proc fromJson*(n: JsonNode, argName: string, result: var T) =
n.kind.expect(JInt, argName) n.kind.expect(JInt, argName)
let asInt = n.getBiggestInt() let asInt = n.getBiggestInt()
# signed -> unsigned conversions are unchecked when T is Epoch:
# https://github.com/nim-lang/RFCs/issues/175 if asInt == -1:
# TODO: This is a major hack here. Since the json library
# cannot handle properly 0xffffffff when serializing and
# deserializing uint64 values, we detect one known wrong
# result, appering in most `Validator` records. To fix
# this issue, we'll have to switch to nim-json-serialization
# in nim-json-rpc or work towards implementing a fix upstream.
result = FAR_FUTURE_EPOCH
return
if asInt < 0: if asInt < 0:
# signed -> unsigned conversions are unchecked
# https://github.com/nim-lang/RFCs/issues/175
raise newException( raise newException(
ValueError, "JSON-RPC input is an unexpected negative value") ValueError, "JSON-RPC input is an unexpected negative value")
result = T(asInt) result = T(asInt)

View File

@ -213,7 +213,7 @@ proc validateProposerSlashing*(
# https://github.com/ethereum/eth2.0-specs/blob/v1.0.0/specs/phase0/p2p-interface.md#voluntary_exit # https://github.com/ethereum/eth2.0-specs/blob/v1.0.0/specs/phase0/p2p-interface.md#voluntary_exit
proc validateVoluntaryExit*( proc validateVoluntaryExit*(
pool: var ExitPool, signed_voluntary_exit: SignedVoluntaryExit): pool: var ExitPool, signed_voluntary_exit: SignedVoluntaryExit):
Result[bool, (ValidationResult, cstring)] = Result[void, (ValidationResult, cstring)] =
# [IGNORE] The voluntary exit is the first valid voluntary exit received for # [IGNORE] The voluntary exit is the first valid voluntary exit received for
# the validator with index signed_voluntary_exit.message.validator_index. # the validator with index signed_voluntary_exit.message.validator_index.
if signed_voluntary_exit.message.validator_index >= if signed_voluntary_exit.message.validator_index >=
@ -241,4 +241,4 @@ proc validateVoluntaryExit*(
pool.voluntary_exits.addExitMessage( pool.voluntary_exits.addExitMessage(
signed_voluntary_exit, VOLUNTARY_EXITS_BOUND) signed_voluntary_exit, VOLUNTARY_EXITS_BOUND)
ok(true) ok()

View File

@ -38,7 +38,7 @@ const
"../vendor/nimbus-security-resources/passwords/10-million-password-list-top-100000.txt", "../vendor/nimbus-security-resources/passwords/10-million-password-list-top-100000.txt",
minWordLen = minPasswordLen) minWordLen = minPasswordLen)
proc echoP(msg: string) = proc echoP*(msg: string) =
## Prints a paragraph aligned to 80 columns ## Prints a paragraph aligned to 80 columns
echo "" echo ""
echo wrapWords(msg, 80) echo wrapWords(msg, 80)
@ -213,8 +213,8 @@ proc keyboardGetPassword[T](prompt: string, attempts: int,
dec(remainingAttempts) dec(remainingAttempts)
err("Failed to decrypt keystore") err("Failed to decrypt keystore")
proc loadKeystore(validatorsDir, secretsDir, keyName: string, proc loadKeystore*(validatorsDir, secretsDir, keyName: string,
nonInteractive: bool): Option[ValidatorPrivKey] = nonInteractive: bool): Option[ValidatorPrivKey] =
let let
keystorePath = validatorsDir / keyName / keystoreFileName keystorePath = validatorsDir / keyName / keystoreFileName
keystore = keystore =

View File

@ -4,10 +4,10 @@ import
eth/common/eth_types as commonEthTypes, eth/common/eth_types as commonEthTypes,
web3/[ethtypes, conversions], web3/[ethtypes, conversions],
chronicles, chronicles,
spec/presets,
spec/datatypes,
json_serialization, json_serialization,
json_serialization/std/[options, sets, net], serialization/errors json_serialization/std/[options, sets, net], serialization/errors,
ssz/navigator,
spec/[presets, datatypes, digest]
# ATTENTION! This file will produce a large C file, because we are inlining # ATTENTION! This file will produce a large C file, because we are inlining
# genesis states as C literals in the generated code (and blobs in the final # genesis states as C literals in the generated code (and blobs in the final
@ -219,3 +219,7 @@ proc getRuntimePresetForNetwork*(eth2Network: Option[string]): RuntimePreset =
if eth2Network.isSome: if eth2Network.isSome:
return getMetadataForNetwork(eth2Network.get).runtimePreset return getMetadataForNetwork(eth2Network.get).runtimePreset
return defaultRuntimePreset return defaultRuntimePreset
proc extractGenesisValidatorRootFromSnapshop*(snapshot: string): Eth2Digest =
sszMount(snapshot, BeaconState).genesis_validators_root[]

View File

@ -7,12 +7,12 @@
import import
# Standard library # Standard library
std/[os, tables, strutils, strformat, sequtils, times, math, terminal, osproc, std/[os, tables, strutils, strformat, sequtils, times, math,
random], terminal, osproc, random],
# Nimble packages # Nimble packages
stew/[objects, byteutils, endians2, io2], stew/shims/macros, stew/[objects, byteutils, endians2, io2], stew/shims/macros,
chronos, confutils, metrics, json_rpc/[rpcserver, jsonmarshal], chronos, confutils, metrics, json_rpc/[rpcclient, rpcserver, jsonmarshal],
chronicles, bearssl, blscurve, chronicles, bearssl, blscurve,
json_serialization/std/[options, sets, net], serialization/errors, json_serialization/std/[options, sets, net], serialization/errors,
@ -24,13 +24,14 @@ import
./rpc/[beacon_api, config_api, debug_api, event_api, nimbus_api, node_api, ./rpc/[beacon_api, config_api, debug_api, event_api, nimbus_api, node_api,
validator_api], validator_api],
spec/[datatypes, digest, crypto, beaconstate, helpers, network, presets], spec/[datatypes, digest, crypto, beaconstate, helpers, network, presets],
spec/[weak_subjectivity], spec/[weak_subjectivity, signatures],
spec/eth2_apis/beacon_rpc_client,
conf, time, beacon_chain_db, validator_pool, extras, conf, time, beacon_chain_db, validator_pool, extras,
attestation_pool, exit_pool, eth2_network, eth2_discovery, attestation_pool, exit_pool, eth2_network, eth2_discovery,
beacon_node_common, beacon_node_types, beacon_node_status, beacon_node_common, beacon_node_types, beacon_node_status,
block_pools/[chain_dag, quarantine, clearance, block_pools_types], block_pools/[chain_dag, quarantine, clearance, block_pools_types],
nimbus_binary_common, network_metadata, nimbus_binary_common, network_metadata,
eth1_monitor, version, ssz/[navigator, merkleization], eth1_monitor, version, ssz/merkleization,
sync_protocol, request_manager, keystore_management, interop, statusbar, sync_protocol, request_manager, keystore_management, interop, statusbar,
sync_manager, validator_duties, filepath, sync_manager, validator_duties, filepath,
validator_slashing_protection, ./eth2_processor validator_slashing_protection, ./eth2_processor
@ -220,7 +221,7 @@ proc init*(T: type BeaconNode,
if genesisStateContents != nil: if genesisStateContents != nil:
let let
networkGenesisValidatorsRoot = networkGenesisValidatorsRoot =
sszMount(genesisStateContents[], BeaconState).genesis_validators_root[] extractGenesisValidatorRootFromSnapshop(genesisStateContents[])
if networkGenesisValidatorsRoot != databaseGenesisValidatorsRoot: if networkGenesisValidatorsRoot != databaseGenesisValidatorsRoot:
fatal "The specified --data-dir contains data for a different network", fatal "The specified --data-dir contains data for a different network",
@ -1025,6 +1026,143 @@ when hasPrompt:
# var t: Thread[ptr Prompt] # var t: Thread[ptr Prompt]
# createThread(t, processPromptCommands, addr p) # createThread(t, processPromptCommands, addr p)
proc handleValidatorExitCommand(config: BeaconNodeConf) {.async.} =
let port = try:
let value = parseInt(config.rpcUrlForExit.port)
if value < Port.low.int or value > Port.high.int:
raise newException(ValueError,
"The port number must be between " & $Port.low & " and " & $Port.high)
Port value
except CatchableError as err:
fatal "Invalid port number", err = err.msg
quit 1
let rpcClient = newRpcHttpClient()
try:
await connect(rpcClient, config.rpcUrlForExit.hostname, port)
except CatchableError as err:
fatal "Failed to connect to the beacon node RPC service", err = err.msg
quit 1
let (validator, validatorIdx, status, balance) = try:
await rpcClient.get_v1_beacon_states_stateId_validators_validatorId(
"head", config.exitedValidator)
except CatchableError as err:
fatal "Failed to obtain information for validator", err = err.msg
quit 1
let exitAtEpoch = if config.exitAtEpoch.isSome:
Epoch config.exitAtEpoch.get
else:
let headSlot = try:
await rpcClient.getBeaconHead()
except CatchableError as err:
fatal "Failed to obtain the current head slot", err = err.msg
quit 1
headSlot.epoch
let
validatorsDir = config.validatorsDir
validatorKeyAsStr = "0x" & $validator.pubkey
keystoreDir = validatorsDir / validatorKeyAsStr
if not dirExists(keystoreDir):
echo "The validator keystores directory '" & config.validatorsDir.string &
"' does not contain a keystore for the selected validator with public " &
"key '" & validatorKeyAsStr & "'."
quit 1
let signingKey = loadKeystore(
validatorsDir,
config.secretsDir,
validatorKeyAsStr,
config.nonInteractive)
if signingKey.isNone:
fatal "Unable to continue without decrypted signing key"
quit 1
let fork = try:
await rpcClient.get_v1_beacon_states_fork("head")
except CatchableError as err:
fatal "Failed to obtain the fork id of the head state", err = err.msg
quit 1
let genesisValidatorsRoot = try:
(await rpcClient.get_v1_beacon_genesis()).genesis_validators_root
except CatchableError as err:
fatal "Failed to obtain the genesis validators root of the network",
err = err.msg
quit 1
var signedExit = SignedVoluntaryExit(
message: VoluntaryExit(
epoch: exitAtEpoch,
validator_index: validatorIdx))
signedExit.signature = get_voluntary_exit_signature(
fork, genesisValidatorsRoot, signedExit.message, signingKey.get)
template ask(prompt: string): string =
try:
stdout.write prompt, ": "
stdin.readLine()
except IOError as err:
fatal "Failed to read user input from stdin"
quit 1
try:
echoP "PLEASE BEWARE!"
echoP "Publishing a voluntary exit is an irreversible operation! " &
"You won't be able to restart again with the same validator."
echoP "By requesting an exit now, you'll be exempt from penalties " &
"stemming from not performing your validator duties, but you " &
"won't be able to withdraw your deposited funds at the time " &
"being. This means that your funds will be effectively frozen " &
"until withdrawals are enabled in a future phase of the Eth2 " &
"rollout."
echoP "To understand more about the Eth2 roadmap, we recommend you " &
"have a look at\n" &
"https://ethereum.org/en/eth2/#roadmap"
echoP "You must not shut down your validator for at least 5 epochs " &
"(32 minutes) after requesting a validator exit, as you will " &
"still be required to perform validator duties until your exit " &
"has been processed. The number of epochs could be significantly " &
"higher depending on how many other validators are queued to exit."
echoP "As such, we recommend you keep track of your validator's status " &
"using an Eth2 block explorer before shutting down your beacon node."
const
confirmation = "I understand the implications of submitting a voluntary exit"
while true:
echoP "To proceed to submitting your voluntary exit, please type '" &
confirmation & "' (without the quotes) in the prompt below and " &
"press ENTER or type 'q' to quit."
echo ""
let choice = ask "Your choice"
if choice == "q":
quit 0
elif choice == confirmation:
let success = await rpcClient.post_v1_beacon_pool_voluntary_exits(signedExit)
if success:
echo "Successfully published voluntary exit for validator " &
$validatorIdx & "(" & validatorKeyAsStr[0..9] & ")."
quit 0
else:
echo "The voluntary exit was not submitted successfully. Please try again."
quit 1
except CatchableError as err:
fatal "Failed to send the signed exit message to the beacon node RPC"
quit 1
programMain: programMain:
var var
config = makeBannerAndConfig(clientId, BeaconNodeConf) config = makeBannerAndConfig(clientId, BeaconNodeConf)
@ -1210,6 +1348,7 @@ programMain:
of deposits: of deposits:
case config.depositsCmd case config.depositsCmd
#[
of DepositsCmd.create: of DepositsCmd.create:
var seed: KeySeed var seed: KeySeed
defer: burnMem(seed) defer: burnMem(seed)
@ -1287,6 +1426,11 @@ programMain:
fatal "Failed to create launchpad deposit data file", err = err.msg fatal "Failed to create launchpad deposit data file", err = err.msg
quit 1 quit 1
of DepositsCmd.status:
echo "The status command is not implemented yet"
quit 1
#]#
of DepositsCmd.`import`: of DepositsCmd.`import`:
let validatorKeysDir = config.importedDepositsDir.get: let validatorKeysDir = config.importedDepositsDir.get:
let cwd = os.getCurrentDir() let cwd = os.getCurrentDir()
@ -1304,9 +1448,8 @@ programMain:
validatorKeysDir.string, validatorKeysDir.string,
config.validatorsDir, config.secretsDir) config.validatorsDir, config.secretsDir)
of DepositsCmd.status: of DepositsCmd.exit:
echo "The status command is not implemented yet" waitFor handleValidatorExitCommand(config)
quit 1
of wallets: of wallets:
case config.walletsCmd: case config.walletsCmd:

View File

@ -11,7 +11,7 @@ import
chronicles, chronicles,
../beacon_node_common, ../eth2_json_rpc_serialization, ../eth2_network, ../beacon_node_common, ../eth2_json_rpc_serialization, ../eth2_network,
../validator_duties, ../validator_duties,
../block_pools/chain_dag, ../block_pools/chain_dag, ../exit_pool,
../spec/[crypto, digest, datatypes, validator], ../spec/[crypto, digest, datatypes, validator],
../spec/eth2_apis/callsigs_types, ../spec/eth2_apis/callsigs_types,
../ssz/merkleization, ../ssz/merkleization,
@ -26,8 +26,11 @@ template unimplemented() =
raise (ref CatchableError)(msg: "Unimplemented") raise (ref CatchableError)(msg: "Unimplemented")
proc parsePubkey(str: string): ValidatorPubKey = proc parsePubkey(str: string): ValidatorPubKey =
if str.len != RawPubKeySize + 2: # +2 because of the `0x` prefix const expectedLen = RawPubKeySize * 2 + 2
raise newException(CatchableError, "Not a valid public key (too short)") if str.len != expectedLen: # +2 because of the `0x` prefix
raise newException(ValueError,
"A hex public key should be exactly " & $expectedLen & " characters. " &
$str.len & " provided")
let pubkeyRes = fromHex(ValidatorPubKey, str) let pubkeyRes = fromHex(ValidatorPubKey, str)
if pubkeyRes.isErr: if pubkeyRes.isErr:
raise newException(CatchableError, "Not a valid public key") raise newException(CatchableError, "Not a valid public key")
@ -46,19 +49,20 @@ proc getValidatorInfoFromValidatorId(
if status notin allowedStatuses: if status notin allowedStatuses:
raise newException(CatchableError, "Invalid status requested") raise newException(CatchableError, "Invalid status requested")
var validatorIdx: uint64
let validator = if validatorId.startsWith("0x"): let validator = if validatorId.startsWith("0x"):
let pubkey = parsePubkey(validatorId) let pubkey = parsePubkey(validatorId)
let idx = state.validators.asSeq.findIt(it.pubKey == pubkey) let idx = state.validators.asSeq.findIt(it.pubKey == pubkey)
if idx == -1: if idx == -1:
raise newException(CatchableError, "Could not find validator") raise newException(CatchableError, "Could not find validator")
validatorIdx = idx.uint64
state.validators[idx] state.validators[idx]
else: else:
var valIdx: BiggestUInt if parseBiggestUInt(validatorId, validatorIdx) != validatorId.len:
if parseBiggestUInt(validatorId, valIdx) != validatorId.len:
raise newException(CatchableError, "Not a valid index") raise newException(CatchableError, "Not a valid index")
if valIdx > state.validators.lenu64: if validatorIdx > state.validators.lenu64:
raise newException(CatchableError, "Index out of bounds") raise newException(CatchableError, "Index out of bounds")
state.validators[valIdx] state.validators[validatorIdx]
# time to determine the status of the validator - the code mimics # time to determine the status of the validator - the code mimics
# whatever is detailed here: https://hackmd.io/ofFJ5gOmQpu1jjHilHbdQQ # whatever is detailed here: https://hackmd.io/ofFJ5gOmQpu1jjHilHbdQQ
@ -102,8 +106,10 @@ proc getValidatorInfoFromValidatorId(
if status != "" and status notin actual_status: if status != "" and status notin actual_status:
return none(BeaconStatesValidatorsTuple) return none(BeaconStatesValidatorsTuple)
return some((validator: validator, status: actual_status, return some((validator: validator,
balance: validator.effective_balance)) index: validatorIdx,
status: actual_status,
balance: validator.effective_balance))
proc getBlockDataFromBlockId(node: BeaconNode, blockId: string): BlockData = proc getBlockDataFromBlockId(node: BeaconNode, blockId: string): BlockData =
result = case blockId: result = case blockId:
@ -254,5 +260,13 @@ proc installBeaconApiHandlers*(rpcServer: RpcServer, node: BeaconNode) =
rpcServer.rpc("get_v1_beacon_pool_voluntary_exits") do () -> JsonNode: rpcServer.rpc("get_v1_beacon_pool_voluntary_exits") do () -> JsonNode:
unimplemented() unimplemented()
rpcServer.rpc("post_v1_beacon_pool_voluntary_exits") do () -> JsonNode: rpcServer.rpc("post_v1_beacon_pool_voluntary_exits") do (
unimplemented() exit: SignedVoluntaryExit) -> bool:
doAssert node.exitPool != nil
let validity = node.exitPool[].validateVoluntaryExit(exit)
if validity.isOk:
node.sendVoluntaryExit(exit)
else:
raise newException(ValueError, $(validity.error[1]))
return true

View File

@ -54,8 +54,8 @@ proc get_v1_beacon_blocks_blockId_attestations(blockId: string): seq[Attestation
# TODO GET /v1/beacon/pool/attestations # TODO GET /v1/beacon/pool/attestations
proc post_v1_beacon_pool_attestations(attestation: Attestation): bool proc post_v1_beacon_pool_attestations(attestation: Attestation): bool
proc post_v1_beacon_pool_voluntary_exits(exit: SignedVoluntaryExit): bool
proc get_v1_config_fork_schedule(): seq[Fork] proc get_v1_config_fork_schedule(): seq[Fork]

View File

@ -0,0 +1,9 @@
import
os, json,
json_rpc/[rpcclient, jsonmarshal],
../../eth2_json_rpc_serialization,
../digest, ../datatypes,
callsigs_types
createRpcSigs(RpcClient, currentSourcePath.parentDir / "beacon_callsigs.nim")

View File

@ -30,6 +30,7 @@ type
BeaconStatesValidatorsTuple* = tuple BeaconStatesValidatorsTuple* = tuple
validator: Validator validator: Validator
index: uint64
status: string status: string
balance: uint64 balance: uint64

View File

@ -178,10 +178,24 @@ proc verify_deposit_signature*(preset: RuntimePreset,
blsVerify(deposit.pubkey, signing_root.data, deposit.signature) blsVerify(deposit.pubkey, signing_root.data, deposit.signature)
proc verify_voluntary_exit_signature*( func get_voluntary_exit_signature*(
fork: Fork, genesis_validators_root: Eth2Digest, fork: Fork,
genesis_validators_root: Eth2Digest,
voluntary_exit: VoluntaryExit, voluntary_exit: VoluntaryExit,
pubkey: ValidatorPubKey, signature: SomeSig): bool = privkey: ValidatorPrivKey): ValidatorSig =
let
domain = get_domain(
fork, DOMAIN_VOLUNTARY_EXIT, voluntary_exit.epoch, genesis_validators_root)
signing_root = compute_signing_root(voluntary_exit, domain)
blsSign(privKey, signing_root.data)
proc verify_voluntary_exit_signature*(
fork: Fork,
genesis_validators_root: Eth2Digest,
voluntary_exit: VoluntaryExit,
pubkey: ValidatorPubKey,
signature: SomeSig): bool =
withTrust(signature): withTrust(signature):
let let
domain = get_domain( domain = get_domain(

View File

@ -157,6 +157,11 @@ proc sendAttestation*(
beacon_attestations_sent.inc() beacon_attestations_sent.inc()
proc sendVoluntaryExit*(node: BeaconNode, exit: SignedVoluntaryExit) =
node.network.broadcast(
getVoluntaryExitsTopic(node.forkDIgest),
exit)
proc sendAttestation*(node: BeaconNode, attestation: Attestation) = proc sendAttestation*(node: BeaconNode, attestation: Attestation) =
# For the validator API, which doesn't supply num_active_validators. # For the validator API, which doesn't supply num_active_validators.
let attestationBlck = let attestationBlck =