web3signer refactoring and test suite. (#4775)
* Refactor nimbus_signing_node to support Unix signals. * Fix SN unable to close REST server properly. * Fix `keys`, `deposit` and `validator_registration` endpoints issues. Add getValidatorExitSignature() and getDepositMessageSignature() to validator_pool. * Add /reload endpoint and implementation. Fix signData to not cancel `timer`. Fix validator_pool should clear attachedValidators table. * Diva protocol enhancement implementation.
This commit is contained in:
parent
d5f454a8f5
commit
0ff86e9538
|
@ -364,6 +364,42 @@ OK: 4/4 Fail: 0/4 Skip: 0/4
|
|||
+ Voluntary exit signatures OK
|
||||
```
|
||||
OK: 8/8 Fail: 0/8 Skip: 0/8
|
||||
## Nimbus remote signer/signing test (web3signer)
|
||||
```diff
|
||||
+ Connection timeout test OK
|
||||
+ Connections pool stress test OK
|
||||
+ Idle connection test OK
|
||||
+ Public keys enumeration (/api/v1/eth2/publicKeys) test OK
|
||||
+ Public keys reload (/reload) test OK
|
||||
+ Signing BeaconBlock (getBlockSignature(altair)) OK
|
||||
+ Signing BeaconBlock (getBlockSignature(bellatrix)) OK
|
||||
+ Signing BeaconBlock (getBlockSignature(capella)) OK
|
||||
+ Signing BeaconBlock (getBlockSignature(deneb)) OK
|
||||
+ Signing BeaconBlock (getBlockSignature(phase0)) OK
|
||||
+ Signing SC contribution and proof (getContributionAndProofSignature()) OK
|
||||
+ Signing SC message (getSyncCommitteeMessage()) OK
|
||||
+ Signing SC selection proof (getSyncCommitteeSelectionProof()) OK
|
||||
+ Signing aggregate and proof (getAggregateAndProofSignature()) OK
|
||||
+ Signing aggregation slot (getSlotSignature()) OK
|
||||
+ Signing attestation (getAttestationSignature()) OK
|
||||
+ Signing deposit message (getDepositMessageSignature()) OK
|
||||
+ Signing phase0 block OK
|
||||
+ Signing randao reveal (getEpochSignature()) OK
|
||||
+ Signing validator registration (getBuilderSignature()) OK
|
||||
+ Signing voluntary exit (getValidatorExitSignature()) OK
|
||||
+ Waiting for signing node (/upcheck) test OK
|
||||
```
|
||||
OK: 22/22 Fail: 0/22 Skip: 0/22
|
||||
## Nimbus remote signer/signing test (web3signer-diva)
|
||||
```diff
|
||||
+ Signing BeaconBlock (getBlockSignature(altair)) OK
|
||||
+ Signing BeaconBlock (getBlockSignature(bellatrix)) OK
|
||||
+ Signing BeaconBlock (getBlockSignature(capella)) OK
|
||||
+ Signing BeaconBlock (getBlockSignature(deneb)) OK
|
||||
+ Signing BeaconBlock (getBlockSignature(phase0)) OK
|
||||
+ Waiting for signing node (/upcheck) test OK
|
||||
```
|
||||
OK: 6/6 Fail: 0/6 Skip: 0/6
|
||||
## Old database versions [Preset: mainnet]
|
||||
```diff
|
||||
+ pre-1.1.0 OK
|
||||
|
@ -640,4 +676,4 @@ OK: 2/2 Fail: 0/2 Skip: 0/2
|
|||
OK: 9/9 Fail: 0/9 Skip: 0/9
|
||||
|
||||
---TOTAL---
|
||||
OK: 357/362 Fail: 0/362 Skip: 5/362
|
||||
OK: 385/390 Fail: 0/390 Skip: 5/390
|
||||
|
|
2
Makefile
2
Makefile
|
@ -340,7 +340,7 @@ FORCE_BUILD_ALONE_ALL_TESTS_DEPS :=
|
|||
endif
|
||||
force_build_alone_all_tests: | $(FORCE_BUILD_ALONE_ALL_TESTS_DEPS)
|
||||
|
||||
all_tests: | build deps force_build_alone_all_tests
|
||||
all_tests: | build deps nimbus_signing_node force_build_alone_all_tests
|
||||
+ echo -e $(BUILD_MSG) "build/$@" && \
|
||||
MAKE="$(MAKE)" V="$(V)" $(ENV_SCRIPT) scripts/compile_nim_program.sh \
|
||||
$@ \
|
||||
|
|
|
@ -986,6 +986,11 @@ type
|
|||
desc: "A directory containing validator keystore passwords"
|
||||
name: "secrets-dir" .}: Option[InputDir]
|
||||
|
||||
expectedFeeRecipient* {.
|
||||
desc: "Signatures for blocks will require proofs of the specified " &
|
||||
"fee recipient"
|
||||
name: "expected-fee-recipient".}: Option[Address]
|
||||
|
||||
serverIdent* {.
|
||||
desc: "Server identifier which will be used in HTTP Host header"
|
||||
name: "server-ident" .}: Option[string]
|
||||
|
|
|
@ -8,7 +8,7 @@ import std/[tables, os, strutils]
|
|||
import serialization, json_serialization,
|
||||
json_serialization/std/[options, net],
|
||||
chronos, presto, presto/secureserver, chronicles, confutils,
|
||||
stew/[base10, results, byteutils, io2]
|
||||
stew/[base10, results, byteutils, io2, bitops2]
|
||||
import "."/spec/datatypes/[base, altair, phase0],
|
||||
"."/spec/[crypto, digest, network, signatures, forks],
|
||||
"."/spec/eth2_apis/[rest_types, eth2_rest_serialization],
|
||||
|
@ -36,36 +36,51 @@ type
|
|||
signingServer: SigningNodeServer
|
||||
keystoreCache: KeystoreCacheRef
|
||||
keysList: string
|
||||
runKeystoreCachePruningLoopFut: Future[void]
|
||||
sigintHandleFut: Future[void]
|
||||
sigtermHandleFut: Future[void]
|
||||
|
||||
proc getRouter*(): RestRouter
|
||||
SigningNodeRef* = ref SigningNode
|
||||
|
||||
proc router(sn: SigningNode): RestRouter =
|
||||
SigningNodeError* = object of CatchableError
|
||||
|
||||
proc validate(key: string, value: string): int =
|
||||
case key
|
||||
of "{validator_key}":
|
||||
0
|
||||
else:
|
||||
1
|
||||
|
||||
proc getRouter*(): RestRouter =
|
||||
RestRouter.init(validate)
|
||||
|
||||
proc router(sn: SigningNodeRef): RestRouter =
|
||||
case sn.signingServer.kind
|
||||
of SigningNodeKind.Secure:
|
||||
sn.signingServer.sserver.router
|
||||
of SigningNodeKind.NonSecure:
|
||||
sn.signingServer.nserver.router
|
||||
|
||||
proc start(sn: SigningNode) =
|
||||
proc start(sn: SigningNodeRef) =
|
||||
case sn.signingServer.kind
|
||||
of SigningNodeKind.Secure:
|
||||
sn.signingServer.sserver.start()
|
||||
of SigningNodeKind.NonSecure:
|
||||
sn.signingServer.nserver.start()
|
||||
|
||||
proc stop(sn: SigningNode) {.async.} =
|
||||
proc stop(sn: SigningNodeRef) {.async.} =
|
||||
case sn.signingServer.kind
|
||||
of SigningNodeKind.Secure:
|
||||
await sn.signingServer.sserver.stop()
|
||||
of SigningNodeKind.NonSecure:
|
||||
await sn.signingServer.nserver.stop()
|
||||
|
||||
proc close(sn: SigningNode) {.async.} =
|
||||
proc close(sn: SigningNodeRef) {.async.} =
|
||||
case sn.signingServer.kind
|
||||
of SigningNodeKind.Secure:
|
||||
await sn.signingServer.sserver.stop()
|
||||
await sn.signingServer.sserver.closeWait()
|
||||
of SigningNodeKind.NonSecure:
|
||||
await sn.signingServer.nserver.stop()
|
||||
await sn.signingServer.nserver.closeWait()
|
||||
|
||||
proc loadTLSCert(pathName: InputFile): Result[TLSCertificate, cstring] =
|
||||
let data =
|
||||
|
@ -95,106 +110,49 @@ proc loadTLSKey(pathName: InputFile): Result[TLSPrivateKey, cstring] =
|
|||
return err("Invalid private key or incorrect file format")
|
||||
ok(key)
|
||||
|
||||
proc initValidators(sn: var SigningNode): bool =
|
||||
info "Initializaing validators", path = sn.config.validatorsDir()
|
||||
var publicKeyIdents: seq[string]
|
||||
for keystore in listLoadableKeystores(sn.config, sn.keystoreCache):
|
||||
# Not relevant in signing node
|
||||
# TODO don't print when loading validators
|
||||
let feeRecipient = default(Eth1Address)
|
||||
case keystore.kind
|
||||
of KeystoreKind.Local:
|
||||
discard sn.attachedValidators.addValidator(keystore,
|
||||
feeRecipient,
|
||||
defaultGasLimit)
|
||||
publicKeyIdents.add("\"0x" & keystore.pubkey.toHex() & "\"")
|
||||
of KeystoreKind.Remote:
|
||||
error "Signing node do not support remote validators",
|
||||
validator_pubkey = keystore.pubkey
|
||||
return false
|
||||
sn.keysList = "[" & publicKeyIdents.join(", ") & "]"
|
||||
true
|
||||
|
||||
proc init(t: typedesc[SigningNode], config: SigningNodeConf): SigningNode =
|
||||
var sn = SigningNode(
|
||||
config: config,
|
||||
keystoreCache: KeystoreCacheRef.init()
|
||||
)
|
||||
|
||||
if not(initValidators(sn)):
|
||||
fatal "Could not find/initialize local validators"
|
||||
quit 1
|
||||
|
||||
asyncSpawn runKeystoreCachePruningLoop(sn.keystoreCache)
|
||||
|
||||
let
|
||||
address = initTAddress(config.bindAddress, config.bindPort)
|
||||
serverFlags = {HttpServerFlags.QueryCommaSeparatedArray,
|
||||
HttpServerFlags.NotifyDisconnect}
|
||||
timeout =
|
||||
if config.requestTimeout < 0:
|
||||
warn "Negative value of request timeout, using default instead"
|
||||
seconds(defaultSigningNodeRequestTimeout)
|
||||
else:
|
||||
seconds(config.requestTimeout)
|
||||
serverIdent =
|
||||
if config.serverIdent.isSome():
|
||||
config.serverIdent.get()
|
||||
else:
|
||||
NimbusSigningNodeIdent
|
||||
|
||||
sn.signingServer =
|
||||
if config.tlsEnabled:
|
||||
if config.tlsCertificate.isNone():
|
||||
fatal "TLS certificate path is missing, please use --tls-cert option"
|
||||
quit 1
|
||||
|
||||
if config.tlsPrivateKey.isNone():
|
||||
fatal "TLS private key path is missing, please use --tls-key option"
|
||||
quit 1
|
||||
|
||||
let cert =
|
||||
block:
|
||||
let res = loadTLSCert(config.tlsCertificate.get())
|
||||
if res.isErr():
|
||||
fatal "Could not initialize SSL certificate", reason = $res.error()
|
||||
quit 1
|
||||
res.get()
|
||||
let key =
|
||||
block:
|
||||
let res = loadTLSKey(config.tlsPrivateKey.get())
|
||||
if res.isErr():
|
||||
fatal "Could not initialize SSL private key", reason = $res.error()
|
||||
quit 1
|
||||
res.get()
|
||||
let res = SecureRestServerRef.new(getRouter(), address, key, cert,
|
||||
serverFlags = serverFlags,
|
||||
httpHeadersTimeout = timeout,
|
||||
serverIdent = serverIdent)
|
||||
if res.isErr():
|
||||
fatal "HTTPS(REST) server could not be started", address = $address,
|
||||
reason = $res.error()
|
||||
quit 1
|
||||
SigningNodeServer(kind: SigningNodeKind.Secure, sserver: res.get())
|
||||
else:
|
||||
let res = RestServerRef.new(getRouter(), address,
|
||||
serverFlags = serverFlags,
|
||||
httpHeadersTimeout = timeout,
|
||||
serverIdent = serverIdent)
|
||||
if res.isErr():
|
||||
fatal "HTTP(REST) server could not be started", address = $address,
|
||||
reason = $res.error()
|
||||
quit 1
|
||||
SigningNodeServer(kind: SigningNodeKind.NonSecure, nserver: res.get())
|
||||
sn
|
||||
proc new(t: typedesc[SigningNodeRef], config: SigningNodeConf): SigningNodeRef =
|
||||
when declared(waitSignal):
|
||||
SigningNodeRef(
|
||||
config: config,
|
||||
sigintHandleFut: waitSignal(SIGINT),
|
||||
sigtermHandleFut: waitSignal(SIGTERM),
|
||||
keystoreCache: KeystoreCacheRef.init()
|
||||
)
|
||||
else:
|
||||
SigningNodeRef(
|
||||
config: config,
|
||||
sigintHandleFut: newFuture[void]("sigint_placeholder"),
|
||||
sigtermHandleFut: newFuture[void]("sigterm_placeholder"),
|
||||
keystoreCache: KeystoreCacheRef.init()
|
||||
)
|
||||
|
||||
template errorResponse(code: HttpCode, message: string): RestApiResponse =
|
||||
RestApiResponse.response("{\"error\": \"" & message & "\"}", code)
|
||||
|
||||
template signatureResponse(code: HttpCode, signature: string): RestApiResponse =
|
||||
RestApiResponse.response("{\"signature\": \"0x" & signature & "\"}", code, "application/json")
|
||||
RestApiResponse.response("{\"signature\": \"0x" & signature & "\"}",
|
||||
code, "application/json")
|
||||
|
||||
proc installApiHandlers*(node: SigningNode) =
|
||||
proc loadKeystores*(node: SigningNodeRef) =
|
||||
var keysList: seq[string]
|
||||
for keystore in listLoadableKeystores(node.config, node.keystoreCache):
|
||||
# Not relevant in signing node
|
||||
# TODO don't print when loading validators
|
||||
let feeRecipient = default(Eth1Address)
|
||||
case keystore.kind
|
||||
of KeystoreKind.Local:
|
||||
discard node.attachedValidators.addValidator(keystore,
|
||||
feeRecipient,
|
||||
defaultGasLimit)
|
||||
keysList.add("\"0x" & keystore.pubkey.toHex() & "\"")
|
||||
of KeystoreKind.Remote:
|
||||
warn "Signing node do not support remote validators",
|
||||
path = node.config.validatorsDir(),
|
||||
validator_pubkey = keystore.pubkey
|
||||
|
||||
node.keysList = "[" & keysList.join(", ") & "]"
|
||||
|
||||
proc installApiHandlers*(node: SigningNodeRef) =
|
||||
var router = node.router()
|
||||
|
||||
router.api(MethodGet, "/api/v1/eth2/publicKeys") do () -> RestApiResponse:
|
||||
|
@ -205,6 +163,11 @@ proc installApiHandlers*(node: SigningNode) =
|
|||
return RestApiResponse.response("{\"status\": \"OK\"}", Http200,
|
||||
"application/json")
|
||||
|
||||
router.api(MethodPost, "/reload") do () -> RestApiResponse:
|
||||
node.attachedValidators.close()
|
||||
node.loadKeystores()
|
||||
return RestApiResponse.response(Http200)
|
||||
|
||||
router.api(MethodPost, "/api/v1/eth2/sign/{validator_key}") do (
|
||||
validator_key: ValidatorPubKey,
|
||||
contentBody: Option[ContentBody]) -> RestApiResponse:
|
||||
|
@ -231,108 +194,137 @@ proc installApiHandlers*(node: SigningNode) =
|
|||
of Web3SignerRequestKind.AggregationSlot:
|
||||
let
|
||||
forkInfo = request.forkInfo.get()
|
||||
cooked = get_slot_signature(forkInfo.fork,
|
||||
signature = get_slot_signature(forkInfo.fork,
|
||||
forkInfo.genesis_validators_root,
|
||||
request.aggregationSlot.slot, validator.data.privateKey)
|
||||
signature = cooked.toValidatorSig().toHex()
|
||||
request.aggregationSlot.slot,
|
||||
validator.data.privateKey).toValidatorSig().toHex()
|
||||
signatureResponse(Http200, signature)
|
||||
of Web3SignerRequestKind.AggregateAndProof:
|
||||
let
|
||||
forkInfo = request.forkInfo.get()
|
||||
cooked = get_aggregate_and_proof_signature(forkInfo.fork,
|
||||
signature = get_aggregate_and_proof_signature(forkInfo.fork,
|
||||
forkInfo.genesis_validators_root, request.aggregateAndProof,
|
||||
validator.data.privateKey)
|
||||
signature = cooked.toValidatorSig().toHex()
|
||||
validator.data.privateKey).toValidatorSig().toHex()
|
||||
signatureResponse(Http200, signature)
|
||||
of Web3SignerRequestKind.Attestation:
|
||||
let
|
||||
forkInfo = request.forkInfo.get()
|
||||
cooked = get_attestation_signature(forkInfo.fork,
|
||||
signature = get_attestation_signature(forkInfo.fork,
|
||||
forkInfo.genesis_validators_root, request.attestation,
|
||||
validator.data.privateKey)
|
||||
signature = cooked.toValidatorSig().toHex()
|
||||
validator.data.privateKey).toValidatorSig().toHex()
|
||||
signatureResponse(Http200, signature)
|
||||
of Web3SignerRequestKind.Block:
|
||||
let
|
||||
forkInfo = request.forkInfo.get()
|
||||
blck = request.blck
|
||||
blockRoot = hash_tree_root(blck)
|
||||
cooked = get_block_signature(forkInfo.fork,
|
||||
forkInfo.genesis_validators_root, blck.slot, blockRoot,
|
||||
validator.data.privateKey)
|
||||
signature = cooked.toValidatorSig().toHex()
|
||||
signature = get_block_signature(forkInfo.fork,
|
||||
forkInfo.genesis_validators_root, blck.slot, hash_tree_root(blck),
|
||||
validator.data.privateKey).toValidatorSig().toHex()
|
||||
signatureResponse(Http200, signature)
|
||||
of Web3SignerRequestKind.BlockV2:
|
||||
let
|
||||
forkInfo = request.forkInfo.get()
|
||||
forked = request.beaconBlock
|
||||
blockRoot = hash_tree_root(forked)
|
||||
cooked =
|
||||
withBlck(forked):
|
||||
if node.config.expectedFeeRecipient.isNone():
|
||||
let
|
||||
forkInfo = request.forkInfo.get()
|
||||
blockRoot = hash_tree_root(request.beaconBlock)
|
||||
signature = withBlck(request.beaconBlock):
|
||||
get_block_signature(forkInfo.fork,
|
||||
forkInfo.genesis_validators_root, blck.slot, blockRoot,
|
||||
validator.data.privateKey)
|
||||
signature = cooked.toValidatorSig().toHex()
|
||||
validator.data.privateKey).toValidatorSig().toHex()
|
||||
return signatureResponse(Http200, signature)
|
||||
|
||||
let (feeRecipientIndex, blockHeader) =
|
||||
case request.beaconBlock.kind
|
||||
of ConsensusFork.Phase0, ConsensusFork.Altair:
|
||||
# `phase0` and `altair` blocks do not have `fee_recipient`, so
|
||||
# we return an error.
|
||||
return errorResponse(Http400, BlockIncorrectFork)
|
||||
of ConsensusFork.Bellatrix:
|
||||
(GeneralizedIndex(401), request.beaconBlock.bellatrixData)
|
||||
of ConsensusFork.Capella:
|
||||
(GeneralizedIndex(401), request.beaconBlock.capellaData)
|
||||
of ConsensusFork.Deneb:
|
||||
(GeneralizedIndex(401), request.beaconBlock.denebData)
|
||||
|
||||
if request.proofs.isNone() or len(request.proofs.get()) == 0:
|
||||
return errorResponse(Http400, MissingMerkleProofError)
|
||||
|
||||
let proof = request.proofs.get()[0]
|
||||
|
||||
if proof.index != feeRecipientIndex:
|
||||
return errorResponse(Http400, InvalidMerkleProofIndexError)
|
||||
|
||||
let feeRecipientRoot = hash_tree_root(distinctBase(
|
||||
node.config.expectedFeeRecipient.get()))
|
||||
|
||||
if not(is_valid_merkle_branch(feeRecipientRoot, proof.merkleProofs,
|
||||
log2trunc(proof.index),
|
||||
get_subtree_index(proof.index),
|
||||
blockHeader.body_root)):
|
||||
return errorResponse(Http400, InvalidMerkleProofError)
|
||||
|
||||
let
|
||||
forkInfo = request.forkInfo.get()
|
||||
blockRoot = hash_tree_root(request.beaconBlock)
|
||||
signature = withBlck(request.beaconBlock):
|
||||
get_block_signature(forkInfo.fork,
|
||||
forkInfo.genesis_validators_root, blck.slot, blockRoot,
|
||||
validator.data.privateKey).toValidatorSig().toHex()
|
||||
signatureResponse(Http200, signature)
|
||||
of Web3SignerRequestKind.Deposit:
|
||||
let
|
||||
data = DepositMessage(pubkey: request.deposit.pubkey,
|
||||
withdrawal_credentials: request.deposit.withdrawalCredentials,
|
||||
amount: request.deposit.amount)
|
||||
cooked = get_deposit_signature(data,
|
||||
request.deposit.genesisForkVersion, validator.data.privateKey)
|
||||
signature = cooked.toValidatorSig().toHex()
|
||||
signature = get_deposit_signature(data,
|
||||
request.deposit.genesisForkVersion,
|
||||
validator.data.privateKey).toValidatorSig().toHex()
|
||||
signatureResponse(Http200, signature)
|
||||
of Web3SignerRequestKind.RandaoReveal:
|
||||
let
|
||||
forkInfo = request.forkInfo.get()
|
||||
cooked = get_epoch_signature(forkInfo.fork,
|
||||
signature = get_epoch_signature(forkInfo.fork,
|
||||
forkInfo.genesis_validators_root, request.randaoReveal.epoch,
|
||||
validator.data.privateKey)
|
||||
signature = cooked.toValidatorSig().toHex()
|
||||
validator.data.privateKey).toValidatorSig().toHex()
|
||||
signatureResponse(Http200, signature)
|
||||
of Web3SignerRequestKind.VoluntaryExit:
|
||||
let
|
||||
forkInfo = request.forkInfo.get()
|
||||
cooked = get_voluntary_exit_signature(forkInfo.fork,
|
||||
signature = get_voluntary_exit_signature(forkInfo.fork,
|
||||
forkInfo.genesis_validators_root, request.voluntaryExit,
|
||||
validator.data.privateKey)
|
||||
signature = cooked.toValidatorSig().toHex()
|
||||
validator.data.privateKey).toValidatorSig().toHex()
|
||||
signatureResponse(Http200, signature)
|
||||
of Web3SignerRequestKind.SyncCommitteeMessage:
|
||||
let
|
||||
forkInfo = request.forkInfo.get()
|
||||
msg = request.syncCommitteeMessage
|
||||
cooked = get_sync_committee_message_signature(forkInfo.fork,
|
||||
signature = get_sync_committee_message_signature(forkInfo.fork,
|
||||
forkInfo.genesis_validators_root, msg.slot, msg.beaconBlockRoot,
|
||||
validator.data.privateKey)
|
||||
signature = cooked.toValidatorSig().toHex()
|
||||
validator.data.privateKey).toValidatorSig().toHex()
|
||||
signatureResponse(Http200, signature)
|
||||
of Web3SignerRequestKind.SyncCommitteeSelectionProof:
|
||||
let
|
||||
forkInfo = request.forkInfo.get()
|
||||
msg = request.syncAggregatorSelectionData
|
||||
subcommittee = SyncSubcommitteeIndex.init(msg.subcommittee_index).valueOr:
|
||||
return errorResponse(Http400, InvalidSubCommitteeIndexValueError)
|
||||
cooked = get_sync_committee_selection_proof(forkInfo.fork,
|
||||
subcommittee =
|
||||
SyncSubcommitteeIndex.init(msg.subcommittee_index).valueOr:
|
||||
return errorResponse(Http400, InvalidSubCommitteeIndexValueError)
|
||||
signature = get_sync_committee_selection_proof(forkInfo.fork,
|
||||
forkInfo.genesis_validators_root, msg.slot, subcommittee,
|
||||
validator.data.privateKey)
|
||||
signature = cooked.toValidatorSig().toHex()
|
||||
validator.data.privateKey).toValidatorSig().toHex()
|
||||
signatureResponse(Http200, signature)
|
||||
of Web3SignerRequestKind.SyncCommitteeContributionAndProof:
|
||||
let
|
||||
forkInfo = request.forkInfo.get()
|
||||
msg = request.syncCommitteeContributionAndProof
|
||||
cooked = get_contribution_and_proof_signature(
|
||||
signature = get_contribution_and_proof_signature(
|
||||
forkInfo.fork, forkInfo.genesis_validators_root, msg,
|
||||
validator.data.privateKey)
|
||||
signature = cooked.toValidatorSig().toHex()
|
||||
validator.data.privateKey).toValidatorSig().toHex()
|
||||
signatureResponse(Http200, signature)
|
||||
of Web3SignerRequestKind.ValidatorRegistration:
|
||||
let
|
||||
forkInfo = request.forkInfo.get()
|
||||
cooked = get_builder_signature(
|
||||
forkInfo.fork, ValidatorRegistrationV1(
|
||||
signature = get_builder_signature(forkInfo.fork,
|
||||
ValidatorRegistrationV1(
|
||||
fee_recipient:
|
||||
ExecutionAddress(data: distinctBase(Eth1Address.fromHex(
|
||||
request.validatorRegistration.feeRecipient))),
|
||||
|
@ -340,34 +332,142 @@ proc installApiHandlers*(node: SigningNode) =
|
|||
timestamp: request.validatorRegistration.timestamp,
|
||||
pubkey: request.validatorRegistration.pubkey,
|
||||
),
|
||||
validator.data.privateKey)
|
||||
signature = cooked.toValidatorSig().toHex()
|
||||
validator.data.privateKey).toValidatorSig().toHex()
|
||||
signatureResponse(Http200, signature)
|
||||
|
||||
proc validate(key: string, value: string): int =
|
||||
case key
|
||||
of "{validator_key}":
|
||||
0
|
||||
else:
|
||||
1
|
||||
|
||||
proc getRouter*(): RestRouter =
|
||||
RestRouter.init(validate)
|
||||
|
||||
programMain:
|
||||
let config = makeBannerAndConfig("Nimbus signing node " & fullVersionStr,
|
||||
SigningNodeConf)
|
||||
setupLogging(config.logLevel, config.logStdout, config.logFile)
|
||||
|
||||
var sn = SigningNode.init(config)
|
||||
proc asyncInit(sn: SigningNodeRef) {.async.} =
|
||||
notice "Launching signing node", version = fullVersionStr,
|
||||
cmdParams = commandLineParams(), config,
|
||||
validators_count = sn.attachedValidators.count()
|
||||
cmdParams = commandLineParams(), config = sn.config
|
||||
|
||||
info "Initializaing validators", path = sn.config.validatorsDir()
|
||||
sn.loadKeystores()
|
||||
|
||||
if sn.attachedValidators.count() == 0:
|
||||
fatal "Could not find/initialize local validators"
|
||||
raise newException(SigningNodeError, "")
|
||||
|
||||
let
|
||||
address = initTAddress(sn.config.bindAddress, sn.config.bindPort)
|
||||
serverFlags = {HttpServerFlags.QueryCommaSeparatedArray,
|
||||
HttpServerFlags.NotifyDisconnect}
|
||||
timeout =
|
||||
if sn.config.requestTimeout < 0:
|
||||
warn "Negative value of request timeout, using default instead"
|
||||
seconds(defaultSigningNodeRequestTimeout)
|
||||
else:
|
||||
seconds(sn.config.requestTimeout)
|
||||
serverIdent =
|
||||
if sn.config.serverIdent.isSome():
|
||||
sn.config.serverIdent.get()
|
||||
else:
|
||||
NimbusSigningNodeIdent
|
||||
|
||||
sn.signingServer =
|
||||
if sn.config.tlsEnabled:
|
||||
if sn.config.tlsCertificate.isNone():
|
||||
fatal "TLS certificate path is missing, please use --tls-cert option"
|
||||
raise newException(SigningNodeError, "")
|
||||
|
||||
if sn.config.tlsPrivateKey.isNone():
|
||||
fatal "TLS private key path is missing, please use --tls-key option"
|
||||
raise newException(SigningNodeError, "")
|
||||
|
||||
let cert =
|
||||
block:
|
||||
let res = loadTLSCert(sn.config.tlsCertificate.get())
|
||||
if res.isErr():
|
||||
fatal "Could not initialize SSL certificate",
|
||||
reason = $res.error()
|
||||
raise newException(SigningNodeError, "")
|
||||
res.get()
|
||||
let key =
|
||||
block:
|
||||
let res = loadTLSKey(sn.config.tlsPrivateKey.get())
|
||||
if res.isErr():
|
||||
fatal "Could not initialize SSL private key",
|
||||
reason = $res.error()
|
||||
raise newException(SigningNodeError, "")
|
||||
res.get()
|
||||
let res = SecureRestServerRef.new(getRouter(), address, key, cert,
|
||||
serverFlags = serverFlags,
|
||||
httpHeadersTimeout = timeout,
|
||||
serverIdent = serverIdent)
|
||||
if res.isErr():
|
||||
fatal "HTTPS(REST) server could not be started", address = $address,
|
||||
reason = $res.error()
|
||||
raise newException(SigningNodeError, "")
|
||||
SigningNodeServer(kind: SigningNodeKind.Secure, sserver: res.get())
|
||||
else:
|
||||
let res = RestServerRef.new(getRouter(), address,
|
||||
serverFlags = serverFlags,
|
||||
httpHeadersTimeout = timeout,
|
||||
serverIdent = serverIdent)
|
||||
if res.isErr():
|
||||
fatal "HTTP(REST) server could not be started", address = $address,
|
||||
reason = $res.error()
|
||||
raise newException(SigningNodeError, "")
|
||||
SigningNodeServer(kind: SigningNodeKind.NonSecure, nserver: res.get())
|
||||
|
||||
proc asyncRun*(sn: SigningNodeRef) {.async.} =
|
||||
sn.runKeystoreCachePruningLoopFut =
|
||||
runKeystorecachePruningLoop(sn.keystoreCache)
|
||||
sn.installApiHandlers()
|
||||
sn.start()
|
||||
|
||||
var future = newFuture[void]("signing-node-mainLoop")
|
||||
try:
|
||||
runForever()
|
||||
finally:
|
||||
waitFor sn.stop()
|
||||
waitFor sn.close()
|
||||
discard sn.stop()
|
||||
await future
|
||||
except CancelledError:
|
||||
debug "Main loop interrupted"
|
||||
except CatchableError as exc:
|
||||
warn "Main loop failed with unexpected error", err_name = $exc.name,
|
||||
reason = $exc.msg
|
||||
|
||||
debug "Stopping main processing loop"
|
||||
var pending: seq[Future[void]]
|
||||
if not(sn.runKeystoreCachePruningLoopFut.finished()):
|
||||
pending.add(cancelAndWait(sn.runKeystoreCachePruningLoopFut))
|
||||
pending.add(sn.stop())
|
||||
pending.add(sn.close())
|
||||
await allFutures(pending)
|
||||
|
||||
template runWithSignals(sn: SigningNodeRef, body: untyped): bool =
|
||||
let future = body
|
||||
discard await race(future, sn.sigintHandleFut, sn.sigtermHandleFut)
|
||||
if future.finished():
|
||||
if future.failed() or future.cancelled():
|
||||
let exc = future.readError()
|
||||
debug "Signing node initialization failed"
|
||||
var pending: seq[Future[void]]
|
||||
if not(sn.sigintHandleFut.finished()):
|
||||
pending.add(cancelAndWait(sn.sigintHandleFut))
|
||||
if not(sn.sigtermHandleFut.finished()):
|
||||
pending.add(cancelAndWait(sn.sigtermHandleFut))
|
||||
await allFutures(pending)
|
||||
false
|
||||
else:
|
||||
true
|
||||
else:
|
||||
let signal = if sn.sigintHandleFut.finished(): "SIGINT" else: "SIGTERM"
|
||||
info "Got interrupt, trying to shutdown gracefully", signal = signal
|
||||
var pending = @[cancelAndWait(future)]
|
||||
if not(sn.sigintHandleFut.finished()):
|
||||
pending.add(cancelAndWait(sn.sigintHandleFut))
|
||||
if not(sn.sigtermHandleFut.finished()):
|
||||
pending.add(cancelAndWait(sn.sigtermHandleFut))
|
||||
await allFutures(pending)
|
||||
false
|
||||
|
||||
proc runSigningNode(config: SigningNodeConf) {.async.} =
|
||||
let sn = SigningNodeRef.new(config)
|
||||
if not sn.runWithSignals(asyncInit sn):
|
||||
return
|
||||
if not sn.runWithSignals(asyncRun sn):
|
||||
return
|
||||
|
||||
programMain:
|
||||
let config =
|
||||
makeBannerAndConfig("Nimbus signing node " & fullVersionStr,
|
||||
SigningNodeConf)
|
||||
setupLogging(config.logLevel, config.logStdout, config.logFile)
|
||||
waitFor runSigningNode(config)
|
||||
|
|
|
@ -228,3 +228,9 @@ const
|
|||
"BLS to execution change was broadcast"
|
||||
AggregationSelectionNotImplemented* =
|
||||
"Attestation and sync committee aggreggation selection are not implemented"
|
||||
MissingMerkleProofError* =
|
||||
"Required merkle proof is missing"
|
||||
InvalidMerkleProofError* =
|
||||
"The given merkle proof is invalid"
|
||||
InvalidMerkleProofIndexError* =
|
||||
"The given merkle proof index is invalid"
|
||||
|
|
|
@ -1901,6 +1901,8 @@ proc writeValue*(writer: var JsonWriter[RestJson],
|
|||
# https://github.com/ConsenSys/web3signer/blob/41c0cbfabcb1fca9587b59e058b7eb29f152c60c/core/src/main/resources/openapi-specs/eth2/signing/schemas.yaml#L418-L497
|
||||
writer.writeField("beacon_block", value.beaconBlock)
|
||||
|
||||
if isSome(value.proofs):
|
||||
writer.writeField("proofs", value.proofs.get())
|
||||
of Web3SignerRequestKind.Deposit:
|
||||
writer.writeField("type", "DEPOSIT")
|
||||
if isSome(value.signingRoot):
|
||||
|
@ -1967,6 +1969,7 @@ proc readValue*(reader: var JsonReader[RestJson],
|
|||
forkInfo: Option[Web3SignerForkInfo]
|
||||
signingRoot: Option[Eth2Digest]
|
||||
data: Option[JsonString]
|
||||
proofs: seq[Web3SignerMerkleProof]
|
||||
dataName: string
|
||||
|
||||
for fieldName in readObjectFields(reader):
|
||||
|
@ -2015,14 +2018,19 @@ proc readValue*(reader: var JsonReader[RestJson],
|
|||
reader.raiseUnexpectedField("Multiple `signingRoot` fields found",
|
||||
"Web3SignerRequest")
|
||||
signingRoot = some(reader.readValue(Eth2Digest))
|
||||
of "proofs":
|
||||
let newProofs = reader.readValue(seq[Web3SignerMerkleProof])
|
||||
proofs.add(newProofs)
|
||||
of "aggregation_slot", "aggregate_and_proof", "block", "beacon_block",
|
||||
"randao_reveal", "voluntary_exit", "sync_committee_message",
|
||||
"sync_aggregator_selection_data", "contribution_and_proof", "attestation":
|
||||
"sync_aggregator_selection_data", "contribution_and_proof",
|
||||
"attestation", "deposit", "validator_registration":
|
||||
if data.isSome():
|
||||
reader.raiseUnexpectedField("Multiple data fields found",
|
||||
"Web3SignerRequest")
|
||||
dataName = fieldName
|
||||
data = some(reader.readValue(JsonString))
|
||||
|
||||
else:
|
||||
unrecognizedFieldWarning()
|
||||
|
||||
|
@ -2108,10 +2116,17 @@ proc readValue*(reader: var JsonReader[RestJson],
|
|||
reader.raiseUnexpectedValue(
|
||||
"Incorrect field `beacon_block` format")
|
||||
res.get()
|
||||
Web3SignerRequest(
|
||||
kind: Web3SignerRequestKind.BlockV2,
|
||||
forkInfo: forkInfo, signingRoot: signingRoot, beaconBlock: data
|
||||
)
|
||||
if len(proofs) > 0:
|
||||
Web3SignerRequest(
|
||||
kind: Web3SignerRequestKind.BlockV2,
|
||||
forkInfo: forkInfo, signingRoot: signingRoot, beaconBlock: data,
|
||||
proofs: Opt.some(proofs)
|
||||
)
|
||||
else:
|
||||
Web3SignerRequest(
|
||||
kind: Web3SignerRequestKind.BlockV2,
|
||||
forkInfo: forkInfo, signingRoot: signingRoot, beaconBlock: data
|
||||
)
|
||||
of Web3SignerRequestKind.Deposit:
|
||||
if dataName != "deposit":
|
||||
reader.raiseUnexpectedValue("Field `deposit` is missing")
|
||||
|
|
|
@ -10,14 +10,23 @@ import
|
|||
chronicles, metrics,
|
||||
chronos, chronos/apps/http/httpclient, presto, presto/client,
|
||||
serialization, json_serialization,
|
||||
json_serialization/std/[options, net, sets],
|
||||
json_serialization/std/[net, sets],
|
||||
stew/[results, base10, byteutils],
|
||||
"."/[rest_types, eth2_rest_serialization]
|
||||
|
||||
export chronos, httpclient, client, rest_types, eth2_rest_serialization, results
|
||||
|
||||
type
|
||||
Web3SignerResult*[T] = Result[T, string]
|
||||
Web3SignerErrorKind* {.pure.} = enum
|
||||
Error400, Error404, Error412, Error500, CommError, UnexpectedError,
|
||||
UknownStatus, InvalidContentType, InvalidPlain, InvalidContent,
|
||||
InvalidSignature, TimeoutError
|
||||
|
||||
Web3SignerError* = object
|
||||
kind*: Web3SignerErrorKind
|
||||
message*: string
|
||||
|
||||
Web3SignerResult*[T] = Result[T, Web3SignerError]
|
||||
Web3SignerDataResponse* = Web3SignerResult[CookedSig]
|
||||
|
||||
declareCounter nbc_remote_signer_requests,
|
||||
|
@ -50,7 +59,7 @@ declareCounter nbc_remote_signer_unknown_responses,
|
|||
declareCounter nbc_remote_signer_communication_errors,
|
||||
"Number of communication errors"
|
||||
|
||||
declareHistogram nbc_remote_signer_time,
|
||||
declareHistogram nbc_remote_signer_duration,
|
||||
"Time(s) used to generate signature usign remote signer",
|
||||
buckets = [0.050, 0.100, 0.500, 1.0, 5.0, 10.0]
|
||||
|
||||
|
@ -59,6 +68,11 @@ proc getUpcheck*(): RestResponse[Web3SignerStatusResponse] {.
|
|||
meth: MethodGet, accept: "application/json" .}
|
||||
## https://consensys.github.io/web3signer/web3signer-eth2.html#tag/Server-Status
|
||||
|
||||
proc reload*(): RestPlainResponse {.
|
||||
rest, endpoint: "/reload",
|
||||
meth: MethodPost, accept: "application/json" .}
|
||||
## https://consensys.github.io/web3signer/web3signer-eth2.html#tag/Reload-Signer-Keys/operation/RELOAD
|
||||
|
||||
proc getKeys*(): RestResponse[Web3SignerKeysResponse] {.
|
||||
rest, endpoint: "/api/v1/eth2/publicKeys",
|
||||
meth: MethodGet, accept: "application/json" .}
|
||||
|
@ -70,132 +84,225 @@ proc signDataPlain*(identifier: ValidatorPubKey,
|
|||
meth: MethodPost, accept: "application/json" .}
|
||||
# https://consensys.github.io/web3signer/web3signer-eth2.html#tag/Signing
|
||||
|
||||
proc init(t: typedesc[Web3SignerError], kind: Web3SignerErrorKind,
|
||||
message: string): Web3SignerError =
|
||||
Web3SignerError(kind: kind, message: message)
|
||||
|
||||
proc signData*(client: RestClientRef, identifier: ValidatorPubKey,
|
||||
body: Web3SignerRequest
|
||||
): Future[Web3SignerDataResponse] {.async.} =
|
||||
let startSignTick = Moment.now()
|
||||
inc(nbc_remote_signer_requests)
|
||||
let response =
|
||||
try:
|
||||
await client.signDataPlain(identifier, body,
|
||||
restAcceptType = "application/json")
|
||||
except RestError as exc:
|
||||
let msg = "[" & $exc.name & "] " & $exc.msg
|
||||
debug "Error occured while generating signature",
|
||||
validator = shortLog(identifier),
|
||||
remote_signer = $client.address.getUri(),
|
||||
error_name = $exc.name, error_msg = $exc.msg,
|
||||
signDur = Moment.now() - startSignTick
|
||||
inc(nbc_remote_signer_communication_errors)
|
||||
return Web3SignerDataResponse.err(msg)
|
||||
except CatchableError as exc:
|
||||
let msg = "[" & $exc.name & "] " & $exc.msg
|
||||
debug "Unexpected error occured while generating signature",
|
||||
validator = shortLog(identifier),
|
||||
remote_signer = $client.address.getUri(),
|
||||
error_name = $exc.name, error_msg = $exc.msg,
|
||||
signDur = Moment.now() - startSignTick
|
||||
inc(nbc_remote_signer_communication_errors)
|
||||
return Web3SignerDataResponse.err(msg)
|
||||
|
||||
let res =
|
||||
let
|
||||
startSignMoment = Moment.now()
|
||||
response =
|
||||
try:
|
||||
let
|
||||
res = await client.signDataPlain(identifier, body,
|
||||
restAcceptType = "application/json")
|
||||
duration = Moment.now() - startSignMoment
|
||||
nbc_remote_signer_duration.observe(
|
||||
float(milliseconds(duration)) / 1000.0)
|
||||
res
|
||||
except RestError as exc:
|
||||
return Web3SignerDataResponse.err(
|
||||
Web3SignerError.init(Web3SignerErrorKind.CommError, $exc.msg))
|
||||
except CancelledError as exc:
|
||||
raise exc
|
||||
except CatchableError as exc:
|
||||
return Web3SignerDataResponse.err(
|
||||
Web3SignerError.init(Web3SignerErrorKind.UnexpectedError, $exc.msg))
|
||||
|
||||
return
|
||||
case response.status
|
||||
of 200:
|
||||
inc(nbc_remote_signer_200_responses)
|
||||
let sig =
|
||||
if response.contentType.isNone() or
|
||||
isWildCard(response.contentType.get().mediaType):
|
||||
inc(nbc_remote_signer_failures)
|
||||
return Web3SignerDataResponse.err(
|
||||
"Unable to decode signature from missing or incorrect content")
|
||||
Web3SignerError.init(
|
||||
Web3SignerErrorKind.InvalidContentType,
|
||||
"Unable to decode signature from missing or incorrect content"
|
||||
)
|
||||
)
|
||||
else:
|
||||
let mediaType = response.contentType.get().mediaType
|
||||
if mediaType == TextPlainMediaType:
|
||||
let asStr = fromBytes(string, response.data)
|
||||
let sigFromText = fromHex(ValidatorSig, asStr)
|
||||
if sigFromText.isErr:
|
||||
return Web3SignerDataResponse.err(
|
||||
"Unable to decode signature from plain text")
|
||||
sigFromText.get.load
|
||||
let
|
||||
asStr = fromBytes(string, response.data)
|
||||
sigFromText = fromHex(ValidatorSig, asStr).valueOr:
|
||||
inc(nbc_remote_signer_failures)
|
||||
return Web3SignerDataResponse.err(
|
||||
Web3SignerError.init(
|
||||
Web3SignerErrorKind.InvalidPlain,
|
||||
"Unable to decode signature from plain text"
|
||||
)
|
||||
)
|
||||
sigFromText.load()
|
||||
else:
|
||||
let res = decodeBytes(Web3SignerSignatureResponse, response.data,
|
||||
response.contentType)
|
||||
if res.isErr:
|
||||
let msg = "Unable to decode remote signer response [" &
|
||||
$res.error() & "]"
|
||||
response.contentType).valueOr:
|
||||
inc(nbc_remote_signer_failures)
|
||||
return Web3SignerDataResponse.err(msg)
|
||||
res.get.signature.load
|
||||
return Web3SignerDataResponse.err(
|
||||
Web3SignerError.init(
|
||||
Web3SignerErrorKind.InvalidContent,
|
||||
"Unable to decode remote signer response [" & $error & "]"
|
||||
)
|
||||
)
|
||||
res.signature.load()
|
||||
|
||||
if sig.isNone:
|
||||
let msg = "Remote signer returns invalid signature"
|
||||
if sig.isNone():
|
||||
inc(nbc_remote_signer_failures)
|
||||
return Web3SignerDataResponse.err(msg)
|
||||
return Web3SignerDataResponse.err(
|
||||
Web3SignerError.init(
|
||||
Web3SignerErrorKind.InvalidSignature,
|
||||
"Remote signer returns invalid signature"
|
||||
)
|
||||
)
|
||||
|
||||
Web3SignerDataResponse.ok(sig.get)
|
||||
inc(nbc_remote_signer_signatures)
|
||||
Web3SignerDataResponse.ok(sig.get())
|
||||
of 400:
|
||||
inc(nbc_remote_signer_400_responses)
|
||||
let res = decodeBytes(Web3SignerErrorResponse, response.data,
|
||||
response.contentType)
|
||||
let msg =
|
||||
if res.isErr():
|
||||
"Remote signer returns 400 Bad Request Format Error"
|
||||
else:
|
||||
"Remote signer returns 400 Bad Request Format Error [" &
|
||||
res.get().error & "]"
|
||||
Web3SignerDataResponse.err(msg)
|
||||
let message =
|
||||
block:
|
||||
let res = decodeBytes(Web3SignerErrorResponse, response.data,
|
||||
response.contentType)
|
||||
if res.isErr():
|
||||
"Remote signer returns 400 Bad Request Format Error"
|
||||
else:
|
||||
res.get().error
|
||||
Web3SignerDataResponse.err(
|
||||
Web3SignerError.init(Web3SignerErrorKind.Error400, message))
|
||||
of 404:
|
||||
let res = decodeBytes(Web3SignerErrorResponse, response.data,
|
||||
response.contentType)
|
||||
let msg =
|
||||
if res.isErr():
|
||||
"Remote signer returns 404 Validator's Key Not Found Error"
|
||||
else:
|
||||
"Remote signer returns 404 Validator's Key Not Found Error [" &
|
||||
res.get().error & "]"
|
||||
inc(nbc_remote_signer_404_responses)
|
||||
Web3SignerDataResponse.err(msg)
|
||||
let message =
|
||||
block:
|
||||
let res = decodeBytes(Web3SignerErrorResponse, response.data,
|
||||
response.contentType)
|
||||
if res.isErr():
|
||||
"Remote signer returns 404 Validator's Key Not Found Error"
|
||||
else:
|
||||
res.get().error
|
||||
Web3SignerDataResponse.err(
|
||||
Web3SignerError.init(Web3SignerErrorKind.Error404, message))
|
||||
of 412:
|
||||
let res = decodeBytes(Web3SignerErrorResponse, response.data,
|
||||
response.contentType)
|
||||
let msg =
|
||||
if res.isErr():
|
||||
"Remote signer returns 412 Slashing Protection Error"
|
||||
else:
|
||||
"Remote signer returns 412 Slashing Protection Error [" &
|
||||
res.get().error & "]"
|
||||
inc(nbc_remote_signer_412_responses)
|
||||
Web3SignerDataResponse.err(msg)
|
||||
let message =
|
||||
block:
|
||||
let res = decodeBytes(Web3SignerErrorResponse, response.data,
|
||||
response.contentType)
|
||||
if res.isErr():
|
||||
"Remote signer returns 412 Slashing Protection Error"
|
||||
else:
|
||||
res.get().error
|
||||
Web3SignerDataResponse.err(
|
||||
Web3SignerError.init(Web3SignerErrorKind.Error412, message))
|
||||
of 500:
|
||||
let res = decodeBytes(Web3SignerErrorResponse, response.data,
|
||||
response.contentType)
|
||||
let msg =
|
||||
if res.isErr():
|
||||
"Remote signer returns 500 Internal Server Error"
|
||||
else:
|
||||
"Remote signer returns 500 Internal Server Error [" &
|
||||
res.get().error & "]"
|
||||
inc(nbc_remote_signer_500_responses)
|
||||
Web3SignerDataResponse.err(msg)
|
||||
let message =
|
||||
block:
|
||||
let res = decodeBytes(Web3SignerErrorResponse, response.data,
|
||||
response.contentType)
|
||||
if res.isErr():
|
||||
"Remote signer returns 500 Internal Server Error"
|
||||
else:
|
||||
res.get().error
|
||||
Web3SignerDataResponse.err(
|
||||
Web3SignerError.init(Web3SignerErrorKind.Error500, message))
|
||||
else:
|
||||
let msg = "Remote signer returns unexpected status code " &
|
||||
Base10.toString(uint64(response.status))
|
||||
inc(nbc_remote_signer_unknown_responses)
|
||||
Web3SignerDataResponse.err(msg)
|
||||
let message =
|
||||
block:
|
||||
let res = decodeBytes(Web3SignerErrorResponse, response.data,
|
||||
response.contentType)
|
||||
if res.isErr():
|
||||
"Remote signer returns unexpected status code " &
|
||||
Base10.toString(uint64(response.status))
|
||||
else:
|
||||
res.get().error
|
||||
Web3SignerDataResponse.err(
|
||||
Web3SignerError.init(Web3SignerErrorKind.UknownStatus, message))
|
||||
|
||||
if res.isOk():
|
||||
let delay = Moment.now() - startSignTick
|
||||
inc(nbc_remote_signer_signatures)
|
||||
nbc_remote_signer_time.observe(float(milliseconds(delay)) / 1000.0)
|
||||
debug "Signature was successfully generated",
|
||||
validator = shortLog(identifier),
|
||||
remote_signer = $client.address.getUri(),
|
||||
signDur = delay
|
||||
else:
|
||||
inc(nbc_remote_signer_failures)
|
||||
debug "Signature generation was failed",
|
||||
validator = shortLog(identifier),
|
||||
remote_signer = $client.address.getUri(),
|
||||
error_msg = res.error(),
|
||||
signDur = Moment.now() - startSignTick
|
||||
proc signData*(
|
||||
client: RestClientRef,
|
||||
identifier: ValidatorPubKey,
|
||||
timerFut: Future[void],
|
||||
attemptsCount: int,
|
||||
body: Web3SignerRequest
|
||||
): Future[Web3SignerDataResponse] {.async.} =
|
||||
doAssert(attemptsCount >= 1)
|
||||
|
||||
return res
|
||||
const BackoffTimeouts = [
|
||||
10.milliseconds, 100.milliseconds, 1.seconds, 2.seconds, 5.seconds
|
||||
]
|
||||
|
||||
var
|
||||
attempt = 0
|
||||
currentTimeout = 0
|
||||
|
||||
while true:
|
||||
var
|
||||
operationFut: Future[Web3SignerDataResponse]
|
||||
lastError: Opt[Web3SignerError]
|
||||
try:
|
||||
operationFut = signData(client, identifier, body)
|
||||
if isNil(timerFut):
|
||||
await allFutures(operationFut)
|
||||
else:
|
||||
discard await race(timerFut, operationFut)
|
||||
except CancelledError as exc:
|
||||
if not(operationFut.finished()):
|
||||
await operationFut.cancelAndWait()
|
||||
raise exc
|
||||
|
||||
if not(operationFut.finished()):
|
||||
await operationFut.cancelAndWait()
|
||||
if lastError.isSome():
|
||||
# We return last know error instead of timeout error.
|
||||
return Web3SignerDataResponse.err(lastError.get())
|
||||
else:
|
||||
return Web3SignerDataResponse.err(
|
||||
Web3SignerError.init(
|
||||
Web3SignerErrorKind.TimeoutError,
|
||||
"Operation timed out"
|
||||
)
|
||||
)
|
||||
else:
|
||||
let resp = operationFut.read()
|
||||
if resp.isOk():
|
||||
return resp
|
||||
|
||||
case resp.error.kind
|
||||
of Web3SignerErrorKind.Error404,
|
||||
Web3SignerErrorKind.Error412,
|
||||
Web3SignerErrorKind.Error500,
|
||||
Web3SignerErrorKind.CommError,
|
||||
Web3SignerErrorKind.UnexpectedError:
|
||||
## Non-critical errors
|
||||
if attempt == attemptsCount:
|
||||
# Number of attempts exceeded, so we return result we have.
|
||||
return resp
|
||||
else:
|
||||
# We have some attempts left, so we show debug log about current
|
||||
# attempt
|
||||
debug "Unable to get signature using remote signer",
|
||||
kind = resp.error.kind, reason = resp.error.message,
|
||||
attempts_count = attemptsCount, attempt = attempt
|
||||
lastError = Opt.some(resp.error)
|
||||
inc(attempt)
|
||||
await sleepAsync(BackoffTimeouts[currentTimeout])
|
||||
if currentTimeout < len(BackoffTimeouts) - 1:
|
||||
inc currentTimeout
|
||||
of Web3SignerErrorKind.Error400,
|
||||
Web3SignerErrorKind.UknownStatus,
|
||||
Web3SignerErrorKind.InvalidContentType,
|
||||
Web3SignerErrorKind.InvalidPlain,
|
||||
Web3SignerErrorKind.InvalidContent,
|
||||
Web3SignerErrorKind.InvalidSignature:
|
||||
# Critical errors
|
||||
return resp
|
||||
of Web3SignerErrorKind.TimeoutError:
|
||||
raiseAssert "Timeout error should not be happened"
|
||||
|
|
|
@ -520,8 +520,7 @@ type
|
|||
signature*: ValidatorSig
|
||||
slot*: Slot
|
||||
|
||||
Web3SignerKeysResponse* = object
|
||||
keys*: seq[ValidatorPubKey]
|
||||
Web3SignerKeysResponse* = seq[ValidatorPubKey]
|
||||
|
||||
Web3SignerStatusResponse* = object
|
||||
status*: string
|
||||
|
@ -564,6 +563,10 @@ type
|
|||
timestamp*: uint64
|
||||
pubkey*: ValidatorPubKey
|
||||
|
||||
Web3SignerMerkleProof* = object
|
||||
index*: GeneralizedIndex
|
||||
merkleProofs* {.serializedFieldName: "merkle_proofs".}: seq[Eth2Digest]
|
||||
|
||||
Web3SignerRequestKind* {.pure.} = enum
|
||||
AggregationSlot, AggregateAndProof, Attestation, Block, BlockV2,
|
||||
Deposit, RandaoReveal, VoluntaryExit, SyncCommitteeMessage,
|
||||
|
@ -588,6 +591,7 @@ type
|
|||
of Web3SignerRequestKind.BlockV2:
|
||||
beaconBlock* {.
|
||||
serializedFieldName: "beacon_block".}: Web3SignerForkedBeaconBlock
|
||||
proofs*: Opt[seq[Web3SignerMerkleProof]]
|
||||
of Web3SignerRequestKind.Deposit:
|
||||
deposit*: Web3SignerDepositData
|
||||
of Web3SignerRequestKind.RandaoReveal:
|
||||
|
@ -759,7 +763,8 @@ func init*(t: typedesc[Web3SignerRequest], fork: Fork,
|
|||
)
|
||||
|
||||
func init*(t: typedesc[Web3SignerRequest], fork: Fork,
|
||||
genesis_validators_root: Eth2Digest, data: Web3SignerForkedBeaconBlock,
|
||||
genesis_validators_root: Eth2Digest,
|
||||
data: Web3SignerForkedBeaconBlock,
|
||||
signingRoot: Option[Eth2Digest] = none[Eth2Digest]()
|
||||
): Web3SignerRequest =
|
||||
Web3SignerRequest(
|
||||
|
@ -771,6 +776,22 @@ func init*(t: typedesc[Web3SignerRequest], fork: Fork,
|
|||
beaconBlock: data
|
||||
)
|
||||
|
||||
func init*(t: typedesc[Web3SignerRequest], fork: Fork,
|
||||
genesis_validators_root: Eth2Digest,
|
||||
data: Web3SignerForkedBeaconBlock,
|
||||
proofs: openArray[Web3SignerMerkleProof],
|
||||
signingRoot: Option[Eth2Digest] = none[Eth2Digest]()
|
||||
): Web3SignerRequest =
|
||||
Web3SignerRequest(
|
||||
kind: Web3SignerRequestKind.BlockV2,
|
||||
forkInfo: some(Web3SignerForkInfo(
|
||||
fork: fork, genesis_validators_root: genesis_validators_root
|
||||
)),
|
||||
signingRoot: signingRoot,
|
||||
proofs: Opt.some(@proofs),
|
||||
beaconBlock: data
|
||||
)
|
||||
|
||||
func init*(t: typedesc[Web3SignerRequest], genesisForkVersion: Version,
|
||||
data: DepositMessage,
|
||||
signingRoot: Option[Eth2Digest] = none[Eth2Digest]()
|
||||
|
|
|
@ -157,6 +157,7 @@ type
|
|||
flags*: set[RemoteKeystoreFlag]
|
||||
remotes*: seq[RemoteSignerInfo]
|
||||
threshold*: uint32
|
||||
remoteType*: RemoteSignerType
|
||||
|
||||
NetKeystore* = object
|
||||
crypto*: Crypto
|
||||
|
@ -166,7 +167,7 @@ type
|
|||
version*: int
|
||||
|
||||
RemoteSignerType* {.pure.} = enum
|
||||
Web3Signer
|
||||
Web3Signer, Web3SignerDiva
|
||||
|
||||
RemoteKeystore* = object
|
||||
version*: uint64
|
||||
|
@ -598,6 +599,8 @@ proc writeValue*(writer: var JsonWriter, value: RemoteKeystore)
|
|||
case value.remoteType
|
||||
of RemoteSignerType.Web3Signer:
|
||||
writer.writeField("type", "web3signer")
|
||||
of RemoteSignerType.Web3SignerDiva:
|
||||
writer.writeField("type", "web3signer-diva")
|
||||
if value.description.isSome():
|
||||
writer.writeField("description", value.description.get())
|
||||
if RemoteKeystoreFlag.IgnoreSSLVerification in value.flags:
|
||||
|
@ -704,6 +707,8 @@ proc readValue*(reader: var JsonReader, value: var RemoteKeystore)
|
|||
case res.toLowerAscii()
|
||||
of "web3signer":
|
||||
RemoteSignerType.Web3Signer
|
||||
of "web3signer-diva":
|
||||
RemoteSignerType.Web3SignerDiva
|
||||
else:
|
||||
reader.raiseUnexpectedValue("Unsupported remote signer `type` value")
|
||||
else:
|
||||
|
|
|
@ -140,7 +140,8 @@ func init*(T: type KeystoreData, keystore: RemoteKeystore,
|
|||
description: keystore.description,
|
||||
version: keystore.version,
|
||||
remotes: keystore.remotes,
|
||||
threshold: keystore.threshold
|
||||
threshold: keystore.threshold,
|
||||
remoteType: keystore.remoteType
|
||||
))
|
||||
|
||||
func init*(T: type KeystoreData, cookedKey: CookedPubKey,
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
import
|
||||
std/[tables, json, streams, sequtils, uri],
|
||||
chronos, chronicles, metrics, eth/async_utils,
|
||||
chronos, chronicles, metrics,
|
||||
json_serialization/std/net,
|
||||
presto, presto/client,
|
||||
|
||||
|
@ -268,6 +268,7 @@ proc close*(pool: var ValidatorPool) =
|
|||
if res.isErr():
|
||||
notice "Could not unlock validator's keystore file",
|
||||
pubkey = validator.pubkey, validator = shortLog(validator)
|
||||
pool.validators.clear()
|
||||
|
||||
iterator publicKeys*(pool: ValidatorPool): ValidatorPubKey =
|
||||
for item in pool.validators.keys():
|
||||
|
@ -388,10 +389,13 @@ proc signWithDistributedKey(v: AttachedValidator,
|
|||
doAssert v.data.threshold <= uint32(v.clients.len)
|
||||
|
||||
let
|
||||
signatureReqs = mapIt(v.clients, it[0].signData(it[1].pubkey, request))
|
||||
deadline = sleepAsync(WEB3_SIGNER_DELAY_TOLERANCE)
|
||||
signatureReqs = mapIt(v.clients, it[0].signData(it[1].pubkey, deadline,
|
||||
2, request))
|
||||
|
||||
await allFutures(signatureReqs) or deadline
|
||||
await allFutures(signatureReqs)
|
||||
|
||||
if not(deadline.finished()): await cancelAndWait(deadline)
|
||||
|
||||
var shares: seq[SignatureShare]
|
||||
var neededShares = v.data.threshold
|
||||
|
@ -404,7 +408,9 @@ proc signWithDistributedKey(v: AttachedValidator,
|
|||
else:
|
||||
warn "Failed to obtain signature from remote signer",
|
||||
pubkey = shareInfo.pubkey,
|
||||
signerUrl = $(v.clients[i][0].address)
|
||||
signerUrl = $(v.clients[i][0].address),
|
||||
reason = req.read.error.message,
|
||||
kind = req.read.error.kind
|
||||
|
||||
if neededShares == 0:
|
||||
let recovered = shares.recoverSignature()
|
||||
|
@ -413,17 +419,19 @@ proc signWithDistributedKey(v: AttachedValidator,
|
|||
return SignatureResult.err "Not enough shares to recover the signature"
|
||||
|
||||
proc signWithSingleKey(v: AttachedValidator,
|
||||
request: Web3SignerRequest): Future[SignatureResult]
|
||||
{.async.} =
|
||||
request: Web3SignerRequest): Future[SignatureResult] {.
|
||||
async.} =
|
||||
doAssert v.clients.len == 1
|
||||
let (client, info) = v.clients[0]
|
||||
let res = awaitWithTimeout(client.signData(info.pubkey, request),
|
||||
WEB3_SIGNER_DELAY_TOLERANCE):
|
||||
return SignatureResult.err "Timeout"
|
||||
if res.isErr:
|
||||
return SignatureResult.err res.error
|
||||
let
|
||||
deadline = sleepAsync(WEB3_SIGNER_DELAY_TOLERANCE)
|
||||
(client, info) = v.clients[0]
|
||||
res = await client.signData(info.pubkey, deadline, 2, request)
|
||||
|
||||
if not(deadline.finished()): await cancelAndWait(deadline)
|
||||
if res.isErr():
|
||||
return SignatureResult.err(res.error.message)
|
||||
else:
|
||||
return SignatureResult.ok res.get.toValidatorSig
|
||||
return SignatureResult.ok(res.get().toValidatorSig())
|
||||
|
||||
proc signData(v: AttachedValidator,
|
||||
request: Web3SignerRequest): Future[SignatureResult] =
|
||||
|
@ -435,6 +443,55 @@ proc signData(v: AttachedValidator,
|
|||
else:
|
||||
v.signWithDistributedKey(request)
|
||||
|
||||
proc getFeeRecipientProof(blck: ForkedBeaconBlock | ForkedBlindedBeaconBlock |
|
||||
bellatrix_mev.BlindedBeaconBlock |
|
||||
capella_mev.BlindedBeaconBlock
|
||||
): Result[Web3SignerMerkleProof, string] =
|
||||
when blck is ForkedBlindedBeaconBlock:
|
||||
case blck.kind
|
||||
of ConsensusFork.Phase0:
|
||||
err("Invalid block fork: phase0")
|
||||
of ConsensusFork.Altair:
|
||||
err("Invalid block fork: altair")
|
||||
of ConsensusFork.Bellatrix:
|
||||
const FeeRecipientIndex = GeneralizedIndex(401)
|
||||
let res = ? build_proof(blck.bellatrixData.body, FeeRecipientIndex)
|
||||
ok(Web3SignerMerkleProof(index: FeeRecipientIndex, merkleProofs: @res))
|
||||
of ConsensusFork.Capella:
|
||||
const FeeRecipientIndex = GeneralizedIndex(401)
|
||||
let res = ? build_proof(blck.capellaData.body, FeeRecipientIndex)
|
||||
ok(Web3SignerMerkleProof(index: FeeRecipientIndex, merkleProofs: @res))
|
||||
of ConsensusFork.Deneb:
|
||||
const FeeRecipientIndex = GeneralizedIndex(401)
|
||||
let res = ? build_proof(blck.denebData.body, FeeRecipientIndex)
|
||||
ok(Web3SignerMerkleProof(index: FeeRecipientIndex, merkleProofs: @res))
|
||||
elif blck is bellatrix_mev.BlindedBeaconBlock:
|
||||
const FeeRecipientIndex = GeneralizedIndex(401)
|
||||
let res = ? build_proof(blck.body, FeeRecipientIndex)
|
||||
ok(Web3SignerMerkleProof(index: FeeRecipientIndex, merkleProofs: @res))
|
||||
elif blck is capella_mev.BlindedBeaconBlock:
|
||||
const FeeRecipientIndex = GeneralizedIndex(401)
|
||||
let res = ? build_proof(blck.body, FeeRecipientIndex)
|
||||
ok(Web3SignerMerkleProof(index: FeeRecipientIndex, merkleProofs: @res))
|
||||
else:
|
||||
case blck.kind
|
||||
of ConsensusFork.Phase0:
|
||||
err("Invalid block fork: phase0")
|
||||
of ConsensusFork.Altair:
|
||||
err("Invalid block fork: altair")
|
||||
of ConsensusFork.Bellatrix:
|
||||
const FeeRecipientIndex = GeneralizedIndex(401)
|
||||
let res = ? build_proof(blck.bellatrixData.body, FeeRecipientIndex)
|
||||
ok(Web3SignerMerkleProof(index: FeeRecipientIndex, merkleProofs: @res))
|
||||
of ConsensusFork.Capella:
|
||||
const FeeRecipientIndex = GeneralizedIndex(401)
|
||||
let res = ? build_proof(blck.capellaData.body, FeeRecipientIndex)
|
||||
ok(Web3SignerMerkleProof(index: FeeRecipientIndex, merkleProofs: @res))
|
||||
of ConsensusFork.Deneb:
|
||||
const FeeRecipientIndex = GeneralizedIndex(401)
|
||||
let res = ? build_proof(blck.denebData.body, FeeRecipientIndex)
|
||||
ok(Web3SignerMerkleProof(index: FeeRecipientIndex, merkleProofs: @res))
|
||||
|
||||
# https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.5/specs/phase0/validator.md#signature
|
||||
proc getBlockSignature*(v: AttachedValidator, fork: Fork,
|
||||
genesis_validators_root: Eth2Digest, slot: Slot,
|
||||
|
@ -451,76 +508,151 @@ proc getBlockSignature*(v: AttachedValidator, fork: Fork,
|
|||
fork, genesis_validators_root, slot, block_root,
|
||||
v.data.privateKey).toValidatorSig())
|
||||
of ValidatorKind.Remote:
|
||||
when blck is ForkedBlindedBeaconBlock:
|
||||
let
|
||||
web3SignerBlock =
|
||||
case blck.kind
|
||||
of ConsensusFork.Phase0:
|
||||
Web3SignerForkedBeaconBlock(
|
||||
kind: ConsensusFork.Phase0,
|
||||
phase0Data: blck.phase0Data)
|
||||
of ConsensusFork.Altair:
|
||||
Web3SignerForkedBeaconBlock(
|
||||
kind: ConsensusFork.Altair,
|
||||
altairData: blck.altairData)
|
||||
of ConsensusFork.Bellatrix:
|
||||
Web3SignerForkedBeaconBlock(
|
||||
kind: ConsensusFork.Bellatrix,
|
||||
bellatrixData: blck.bellatrixData.toBeaconBlockHeader)
|
||||
of ConsensusFork.Capella:
|
||||
Web3SignerForkedBeaconBlock(
|
||||
kind: ConsensusFork.Capella,
|
||||
capellaData: blck.capellaData.toBeaconBlockHeader)
|
||||
of ConsensusFork.Deneb:
|
||||
Web3SignerForkedBeaconBlock(
|
||||
kind: ConsensusFork.Deneb,
|
||||
denebData: blck.denebData.toBeaconBlockHeader)
|
||||
|
||||
request = Web3SignerRequest.init(
|
||||
fork, genesis_validators_root, web3SignerBlock)
|
||||
await v.signData(request)
|
||||
elif blck is bellatrix_mev.BlindedBeaconBlock:
|
||||
let request = Web3SignerRequest.init(
|
||||
fork, genesis_validators_root,
|
||||
Web3SignerForkedBeaconBlock(
|
||||
kind: ConsensusFork.Bellatrix,
|
||||
bellatrixData: blck.toBeaconBlockHeader))
|
||||
await v.signData(request)
|
||||
elif blck is capella_mev.BlindedBeaconBlock:
|
||||
let request = Web3SignerRequest.init(
|
||||
fork, genesis_validators_root,
|
||||
Web3SignerForkedBeaconBlock(
|
||||
kind: ConsensusFork.Capella,
|
||||
capellaData: blck.toBeaconBlockHeader))
|
||||
await v.signData(request)
|
||||
else:
|
||||
let
|
||||
web3SignerBlock =
|
||||
case blck.kind
|
||||
of ConsensusFork.Phase0:
|
||||
Web3SignerForkedBeaconBlock(
|
||||
kind: ConsensusFork.Phase0,
|
||||
phase0Data: blck.phase0Data)
|
||||
of ConsensusFork.Altair:
|
||||
Web3SignerForkedBeaconBlock(
|
||||
kind: ConsensusFork.Altair,
|
||||
altairData: blck.altairData)
|
||||
of ConsensusFork.Bellatrix:
|
||||
Web3SignerForkedBeaconBlock(
|
||||
kind: ConsensusFork.Bellatrix,
|
||||
bellatrixData: blck.bellatrixData.toBeaconBlockHeader)
|
||||
of ConsensusFork.Capella:
|
||||
Web3SignerForkedBeaconBlock(
|
||||
kind: ConsensusFork.Capella,
|
||||
capellaData: blck.capellaData.toBeaconBlockHeader)
|
||||
of ConsensusFork.Deneb:
|
||||
Web3SignerForkedBeaconBlock(
|
||||
kind: ConsensusFork.Deneb,
|
||||
denebData: blck.denebData.toBeaconBlockHeader)
|
||||
|
||||
request = Web3SignerRequest.init(
|
||||
fork, genesis_validators_root, web3SignerBlock)
|
||||
await v.signData(request)
|
||||
let web3SignerRequest =
|
||||
when blck is ForkedBlindedBeaconBlock:
|
||||
case blck.kind
|
||||
of ConsensusFork.Phase0:
|
||||
case v.data.remoteType
|
||||
of RemoteSignerType.Web3Signer:
|
||||
Web3SignerRequest.init(fork, genesis_validators_root,
|
||||
Web3SignerForkedBeaconBlock(kind: ConsensusFork.Phase0,
|
||||
phase0Data: blck.phase0Data))
|
||||
of RemoteSignerType.Web3SignerDiva:
|
||||
return SignatureResult.err("Invalid beacon block fork version")
|
||||
of ConsensusFork.Altair:
|
||||
case v.data.remoteType
|
||||
of RemoteSignerType.Web3Signer:
|
||||
Web3SignerRequest.init(fork, genesis_validators_root,
|
||||
Web3SignerForkedBeaconBlock(kind: ConsensusFork.Altair,
|
||||
altairData: blck.altairData))
|
||||
of RemoteSignerType.Web3SignerDiva:
|
||||
return SignatureResult.err("Invalid beacon block fork version")
|
||||
of ConsensusFork.Bellatrix:
|
||||
case v.data.remoteType
|
||||
of RemoteSignerType.Web3Signer:
|
||||
Web3SignerRequest.init(fork, genesis_validators_root,
|
||||
Web3SignerForkedBeaconBlock(kind: ConsensusFork.Bellatrix,
|
||||
bellatrixData: blck.bellatrixData.toBeaconBlockHeader))
|
||||
of RemoteSignerType.Web3SignerDiva:
|
||||
let res = getFeeRecipientProof(blck)
|
||||
if res.isErr(): return SignatureResult.err(res.error)
|
||||
Web3SignerRequest.init(fork, genesis_validators_root,
|
||||
Web3SignerForkedBeaconBlock(kind: ConsensusFork.Bellatrix,
|
||||
bellatrixData: blck.bellatrixData.toBeaconBlockHeader),
|
||||
[res.get()])
|
||||
of ConsensusFork.Capella:
|
||||
case v.data.remoteType
|
||||
of RemoteSignerType.Web3Signer:
|
||||
Web3SignerRequest.init(fork, genesis_validators_root,
|
||||
Web3SignerForkedBeaconBlock(kind: ConsensusFork.Capella,
|
||||
capellaData: blck.capellaData.toBeaconBlockHeader))
|
||||
of RemoteSignerType.Web3SignerDiva:
|
||||
let res = getFeeRecipientProof(blck)
|
||||
if res.isErr(): return SignatureResult.err(res.error)
|
||||
Web3SignerRequest.init(fork, genesis_validators_root,
|
||||
Web3SignerForkedBeaconBlock(kind: ConsensusFork.Capella,
|
||||
capellaData: blck.capellaData.toBeaconBlockHeader),
|
||||
[res.get()])
|
||||
of ConsensusFork.Deneb:
|
||||
case v.data.remoteType
|
||||
of RemoteSignerType.Web3Signer:
|
||||
Web3SignerRequest.init(fork, genesis_validators_root,
|
||||
Web3SignerForkedBeaconBlock(kind: ConsensusFork.Deneb,
|
||||
denebData: blck.denebData.toBeaconBlockHeader))
|
||||
of RemoteSignerType.Web3SignerDiva:
|
||||
let res = getFeeRecipientProof(blck)
|
||||
if res.isErr(): return SignatureResult.err(res.error)
|
||||
Web3SignerRequest.init(fork, genesis_validators_root,
|
||||
Web3SignerForkedBeaconBlock(kind: ConsensusFork.Deneb,
|
||||
denebData: blck.denebData.toBeaconBlockHeader),
|
||||
[res.get()])
|
||||
elif blck is bellatrix_mev.BlindedBeaconBlock:
|
||||
case v.data.remoteType
|
||||
of RemoteSignerType.Web3Signer:
|
||||
Web3SignerRequest.init(fork, genesis_validators_root,
|
||||
Web3SignerForkedBeaconBlock(kind: ConsensusFork.Bellatrix,
|
||||
bellatrixData: blck.toBeaconBlockHeader)
|
||||
)
|
||||
of RemoteSignerType.Web3SignerDiva:
|
||||
let res = getFeeRecipientProof(blck)
|
||||
if res.isErr(): return SignatureResult.err(res.error)
|
||||
Web3SignerRequest.init(fork, genesis_validators_root,
|
||||
Web3SignerForkedBeaconBlock(kind: ConsensusFork.Bellatrix,
|
||||
bellatrixData: blck.toBeaconBlockHeader),
|
||||
[res.get()])
|
||||
elif blck is capella_mev.BlindedBeaconBlock:
|
||||
case v.data.remoteType
|
||||
of RemoteSignerType.Web3Signer:
|
||||
Web3SignerRequest.init(fork, genesis_validators_root,
|
||||
Web3SignerForkedBeaconBlock(kind: ConsensusFork.Capella,
|
||||
capellaData: blck.toBeaconBlockHeader))
|
||||
of RemoteSignerType.Web3SignerDiva:
|
||||
let res = getFeeRecipientProof(blck)
|
||||
if res.isErr(): return SignatureResult.err(res.error)
|
||||
Web3SignerRequest.init(fork, genesis_validators_root,
|
||||
Web3SignerForkedBeaconBlock(kind: ConsensusFork.Capella,
|
||||
capellaData: blck.toBeaconBlockHeader),
|
||||
[res.get()])
|
||||
else:
|
||||
case blck.kind
|
||||
of ConsensusFork.Phase0:
|
||||
# In case of `phase0` block we did not send merkle proof.
|
||||
case v.data.remoteType
|
||||
of RemoteSignerType.Web3Signer:
|
||||
Web3SignerRequest.init(fork, genesis_validators_root,
|
||||
Web3SignerForkedBeaconBlock(kind: ConsensusFork.Phase0,
|
||||
phase0Data: blck.phase0Data))
|
||||
of RemoteSignerType.Web3SignerDiva:
|
||||
return SignatureResult.err("Invalid beacon block fork version")
|
||||
of ConsensusFork.Altair:
|
||||
# In case of `altair` block we did not send merkle proof.
|
||||
case v.data.remoteType
|
||||
of RemoteSignerType.Web3Signer:
|
||||
Web3SignerRequest.init(fork, genesis_validators_root,
|
||||
Web3SignerForkedBeaconBlock(kind: ConsensusFork.Altair,
|
||||
altairData: blck.altairData))
|
||||
of RemoteSignerType.Web3SignerDiva:
|
||||
return SignatureResult.err("Invalid beacon block fork version")
|
||||
of ConsensusFork.Bellatrix:
|
||||
case v.data.remoteType
|
||||
of RemoteSignerType.Web3Signer:
|
||||
Web3SignerRequest.init(fork, genesis_validators_root,
|
||||
Web3SignerForkedBeaconBlock(kind: ConsensusFork.Bellatrix,
|
||||
bellatrixData: blck.bellatrixData.toBeaconBlockHeader))
|
||||
of RemoteSignerType.Web3SignerDiva:
|
||||
let res = getFeeRecipientProof(blck)
|
||||
if res.isErr(): return SignatureResult.err(res.error)
|
||||
Web3SignerRequest.init(fork, genesis_validators_root,
|
||||
Web3SignerForkedBeaconBlock(kind: ConsensusFork.Bellatrix,
|
||||
bellatrixData: blck.bellatrixData.toBeaconBlockHeader),
|
||||
[res.get()])
|
||||
of ConsensusFork.Capella:
|
||||
case v.data.remoteType
|
||||
of RemoteSignerType.Web3Signer:
|
||||
Web3SignerRequest.init(fork, genesis_validators_root,
|
||||
Web3SignerForkedBeaconBlock(kind: ConsensusFork.Capella,
|
||||
capellaData: blck.capellaData.toBeaconBlockHeader))
|
||||
of RemoteSignerType.Web3SignerDiva:
|
||||
let res = getFeeRecipientProof(blck)
|
||||
if res.isErr(): return SignatureResult.err(res.error)
|
||||
Web3SignerRequest.init(fork, genesis_validators_root,
|
||||
Web3SignerForkedBeaconBlock(kind: ConsensusFork.Capella,
|
||||
capellaData: blck.capellaData.toBeaconBlockHeader),
|
||||
[res.get()])
|
||||
of ConsensusFork.Deneb:
|
||||
case v.data.remoteType
|
||||
of RemoteSignerType.Web3Signer:
|
||||
Web3SignerRequest.init(fork, genesis_validators_root,
|
||||
Web3SignerForkedBeaconBlock(kind: ConsensusFork.Deneb,
|
||||
denebData: blck.denebData.toBeaconBlockHeader))
|
||||
of RemoteSignerType.Web3SignerDiva:
|
||||
let res = getFeeRecipientProof(blck)
|
||||
if res.isErr(): return SignatureResult.err(res.error)
|
||||
Web3SignerRequest.init(fork, genesis_validators_root,
|
||||
Web3SignerForkedBeaconBlock(kind: ConsensusFork.Deneb,
|
||||
denebData: blck.denebData.toBeaconBlockHeader),
|
||||
[res.get()])
|
||||
await v.signData(web3SignerRequest)
|
||||
|
||||
# https://github.com/ethereum/consensus-specs/blob/v1.3.0-rc.5/specs/phase0/validator.md#aggregate-signature
|
||||
proc getAttestationSignature*(v: AttachedValidator, fork: Fork,
|
||||
|
@ -662,6 +794,34 @@ proc getSlotSignature*(v: AttachedValidator, fork: Fork,
|
|||
v.slotSignature = Opt.some((slot, signature.get))
|
||||
return signature
|
||||
|
||||
proc getValidatorExitSignature*(v: AttachedValidator, fork: Fork,
|
||||
genesis_validators_root: Eth2Digest,
|
||||
voluntary_exit: VoluntaryExit
|
||||
): Future[SignatureResult] {.async.} =
|
||||
return
|
||||
case v.kind
|
||||
of ValidatorKind.Local:
|
||||
SignatureResult.ok(get_voluntary_exit_signature(
|
||||
fork, genesis_validators_root, voluntary_exit,
|
||||
v.data.privateKey).toValidatorSig())
|
||||
of ValidatorKind.Remote:
|
||||
let request = Web3SignerRequest.init(fork, genesis_validators_root,
|
||||
voluntary_exit)
|
||||
await v.signData(request)
|
||||
|
||||
proc getDepositMessageSignature*(v: AttachedValidator, version: Version,
|
||||
deposit_message: DepositMessage
|
||||
): Future[SignatureResult] {.async.} =
|
||||
return
|
||||
case v.kind
|
||||
of ValidatorKind.Local:
|
||||
SignatureResult.ok(get_deposit_signature(
|
||||
deposit_message, version,
|
||||
v.data.privateKey).toValidatorSig())
|
||||
of ValidatorKind.Remote:
|
||||
let request = Web3SignerRequest.init(version, deposit_message)
|
||||
await v.signData(request)
|
||||
|
||||
# https://github.com/ethereum/builder-specs/blob/v0.3.0/specs/bellatrix/builder.md#signing
|
||||
proc getBuilderSignature*(v: AttachedValidator, fork: Fork,
|
||||
validatorRegistration: ValidatorRegistrationV1):
|
||||
|
|
|
@ -45,6 +45,7 @@ import # Unit test
|
|||
./test_sync_manager,
|
||||
./test_validator_pool,
|
||||
./test_zero_signature,
|
||||
./test_signing_node,
|
||||
./fork_choice/tests_fork_choice,
|
||||
./consensus_spec/all_tests as consensus_all_tests,
|
||||
./slashing_protection/test_fixtures,
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1 +1 @@
|
|||
Subproject commit ba20cebf0f729b52d90487f34efb5923dfc7b2c7
|
||||
Subproject commit 2b440a443f3fc29197f267879e16bb8057ccc0ed
|
Loading…
Reference in New Issue