From dcaa8c70141233196adf097f3559fb58f27107a8 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Mon, 11 Jul 2022 20:55:51 +0300 Subject: [PATCH] ncli verifyDeposit: a simple helper for inspecting deposit events --- beacon_chain/conf.nim | 16 +-- .../conf/eth2_types_confutils_defs.nim | 27 +++++ beacon_chain/eth1/deposit_contract_abi.nim | 26 +++++ beacon_chain/eth1/eth1_monitor.nim | 22 +--- ncli/ncli.nim | 104 +++++++++++++++--- vendor/nim-web3 | 2 +- 6 files changed, 148 insertions(+), 49 deletions(-) create mode 100644 beacon_chain/conf/eth2_types_confutils_defs.nim create mode 100644 beacon_chain/eth1/deposit_contract_abi.nim diff --git a/beacon_chain/conf.nim b/beacon_chain/conf.nim index 705e7a233..dcbe801d2 100644 --- a/beacon_chain/conf.nim +++ b/beacon_chain/conf.nim @@ -25,13 +25,14 @@ import ./spec/datatypes/base, ./networking/network_metadata, ./validators/slashing_protection_common, + ./conf/eth2_types_confutils_defs, ./filepath from consensus_object_pools/block_pools_types_light_client import LightClientDataImportMode export - uri, nat, enr, + uri, nat, enr, eth2_types_confutils_defs, defaultEth2TcpPort, enabledLogLevel, ValidIpAddress, defs, parseCmdArg, completeCmdArg, network_metadata, network, BlockHashOrNumber, @@ -918,13 +919,6 @@ proc createDumpDirs*(config: BeaconNodeConf) = warn "Could not create dump directory", path = config.dumpDirOutgoing, err = ioErrorMsg(res.error) -func parseCmdArg*(T: type Eth2Digest, input: string): T - {.raises: [ValueError, Defect].} = - Eth2Digest.fromHex(input) - -func completeCmdArg*(T: type Eth2Digest, input: string): seq[string] = - return @[] - func parseCmdArg*(T: type GraffitiBytes, input: string): T {.raises: [ValueError, Defect].} = GraffitiBytes.init(input) @@ -950,12 +944,6 @@ func parseCmdArg*(T: type PubKey0x, input: string): T {.raises: [ValueError, Defect].} = PubKey0x(hexToPaddedByteArray[RawPubKeySize](input)) -func parseCmdArg*(T: type ValidatorPubKey, input: string): T - {.raises: [ValueError, Defect].} = - let res = ValidatorPubKey.fromHex(input) - if res.isErr(): raise (ref ValueError)(msg: $res.error()) - res.get() - func completeCmdArg*(T: type PubKey0x, input: string): seq[string] = return @[] diff --git a/beacon_chain/conf/eth2_types_confutils_defs.nim b/beacon_chain/conf/eth2_types_confutils_defs.nim new file mode 100644 index 000000000..080c7a2d6 --- /dev/null +++ b/beacon_chain/conf/eth2_types_confutils_defs.nim @@ -0,0 +1,27 @@ +import + ../spec/[crypto, digest] + +func parseCmdArg*(T: type Eth2Digest, input: string): T + {.raises: [ValueError, Defect].} = + Eth2Digest.fromHex(input) + +func completeCmdArg*(T: type Eth2Digest, input: string): seq[string] = + return @[] + +func parseCmdArg*(T: type ValidatorPubKey, input: string): T + {.raises: [ValueError, Defect].} = + let res = ValidatorPubKey.fromHex(input) + if res.isErr(): raise (ref ValueError)(msg: $res.error()) + res.get() + +func completeCmdArg*(T: type ValidatorPubKey, input: string): seq[string] = + return @[] + +func parseCmdArg*(T: type ValidatorSig, input: string): T + {.raises: [ValueError, Defect].} = + let res = ValidatorSig.fromHex(input) + if res.isErr(): raise (ref ValueError)(msg: $res.error()) + res.get() + +func completeCmdArg*(T: type ValidatorSig, input: string): seq[string] = + return @[] diff --git a/beacon_chain/eth1/deposit_contract_abi.nim b/beacon_chain/eth1/deposit_contract_abi.nim new file mode 100644 index 000000000..a8aa6a48c --- /dev/null +++ b/beacon_chain/eth1/deposit_contract_abi.nim @@ -0,0 +1,26 @@ +import + web3, web3/ethtypes + +export + web3, ethtypes + +type + PubKeyBytes* = DynamicBytes[48, 48] + WithdrawalCredentialsBytes* = DynamicBytes[32, 32] + SignatureBytes* = DynamicBytes[96, 96] + Int64LeBytes* = DynamicBytes[8, 8] + +contract(DepositContract): + proc deposit(pubkey: PubKeyBytes, + withdrawalCredentials: WithdrawalCredentialsBytes, + signature: SignatureBytes, + deposit_data_root: FixedBytes[32]) + + proc get_deposit_root(): FixedBytes[32] + proc get_deposit_count(): Int64LeBytes + + proc DepositEvent(pubkey: PubKeyBytes, + withdrawalCredentials: WithdrawalCredentialsBytes, + amount: Int64LeBytes, + signature: SignatureBytes, + index: Int64LeBytes) {.event.} diff --git a/beacon_chain/eth1/eth1_monitor.nim b/beacon_chain/eth1/eth1_monitor.nim index 29dd9481c..f8d43b2c3 100644 --- a/beacon_chain/eth1/eth1_monitor.nim +++ b/beacon_chain/eth1/eth1_monitor.nim @@ -21,6 +21,7 @@ import ../networking/network_metadata, ../consensus_object_pools/block_pools_types, ".."/[beacon_chain_db, beacon_node_status, beacon_clock], + ./deposit_contract_abi, ./merkle_minimal from std/times import getTime, inSeconds, initTime, `-` @@ -32,27 +33,6 @@ export logScope: topics = "eth1" -type - PubKeyBytes = DynamicBytes[48, 48] - WithdrawalCredentialsBytes = DynamicBytes[32, 32] - SignatureBytes = DynamicBytes[96, 96] - Int64LeBytes = DynamicBytes[8, 8] - -contract(DepositContract): - proc deposit(pubkey: PubKeyBytes, - withdrawalCredentials: WithdrawalCredentialsBytes, - signature: SignatureBytes, - deposit_data_root: FixedBytes[32]) - - proc get_deposit_root(): FixedBytes[32] - proc get_deposit_count(): Int64LeBytes - - proc DepositEvent(pubkey: PubKeyBytes, - withdrawalCredentials: WithdrawalCredentialsBytes, - amount: Int64LeBytes, - signature: SignatureBytes, - index: Int64LeBytes) {.event.} - const web3Timeouts = 60.seconds hasDepositRootChecks = defined(has_deposit_root_checks) diff --git a/ncli/ncli.nim b/ncli/ncli.nim index c5ac23588..c6f8a5781 100644 --- a/ncli/ncli.nim +++ b/ncli/ncli.nim @@ -7,8 +7,10 @@ import ../beacon_chain/spec/eth2_apis/eth2_rest_serialization, ../beacon_chain/spec/datatypes/[phase0, altair, bellatrix], ../beacon_chain/spec/[ - eth2_ssz_serialization, forks, helpers, state_transition], - ../beacon_chain/networking/network_metadata + eth2_ssz_serialization, forks, helpers, state_transition, signatures], + ../beacon_chain/networking/network_metadata, + ../beacon_chain/conf/eth2_types_confutils_defs, + ../beacon_chain/eth1/deposit_contract_abi type Cmd* = enum @@ -16,6 +18,7 @@ type pretty = "Pretty-print SSZ object" transition = "Run state transition function" slots = "Apply empty slots" + verifyDeposit = "Verify deposit" NcliConf* = object eth2Network* {. @@ -28,29 +31,29 @@ type of hashTreeRoot: htrKind* {. argument - desc: "kind of SSZ object: attester_slashing, attestation, signed_block, block, block_body, block_header, deposit, deposit_data, eth1_data, state, proposer_slashing, or voluntary_exit"}: string + desc: "kind of SSZ object: attester_slashing, attestation, signed_block, block, block_body, block_header, deposit, deposit_data, eth1_data, state, proposer_slashing, or voluntary_exit" .}: string htrFile* {. argument - desc: "filename of SSZ or JSON-encoded object of which to compute hash tree root"}: string + desc: "filename of SSZ or JSON-encoded object of which to compute hash tree root" .}: string of pretty: prettyKind* {. argument - desc: "kind of SSZ object: attester_slashing, attestation, signed_block, block, block_body, block_header, deposit, deposit_data, eth1_data, state, proposer_slashing, or voluntary_exit"}: string + desc: "kind of SSZ object: attester_slashing, attestation, signed_block, block, block_body, block_header, deposit, deposit_data, eth1_data, state, proposer_slashing, or voluntary_exit" .}: string prettyFile* {. argument - desc: "filename of SSZ or JSON-encoded object to pretty-print"}: string + desc: "filename of SSZ or JSON-encoded object to pretty-print" .}: string of transition: preState* {. argument - desc: "State to which to apply specified block"}: string + desc: "State to which to apply specified block" .}: string blck* {. argument - desc: "Block to apply to preState"}: string + desc: "Block to apply to preState" .}: string postState* {. argument @@ -59,20 +62,39 @@ type verifyStateRoot* {. argument desc: "Verify state root (default true)" - defaultValue: true}: bool + defaultValue: true .}: bool of slots: preState2* {. argument - desc: "State to which to apply specified block"}: string + desc: "State to which to apply specified block" .}: string slot* {. argument - desc: "Block to apply to preState"}: uint64 + desc: "Block to apply to preState" .}: uint64 postState2* {. argument - desc: "Filename of state resulting from applying blck to preState"}: string + desc: "Filename of state resulting from applying blck to preState" .}: string + + of verifyDeposit: + pubkey* {. + desc: "The validator's public BLS signing key" .}: Option[ValidatorPubKey] + + withdrawalCredentials* {. + desc: "The validator's withdrawal credentials (The prefixed 32 bytes form mandated by the spec)" + name: "withdrawal-credentials" .}: Option[Eth2Digest] + + amount* {. + defaultValue: 32000000000 + desc: "The deposit amount in gwei" .}: Gwei + + signature* {. + desc: "The deposit signature" .}: Option[ValidatorSig] + + depositData* {. + argument + desc: "Deposit event data from the official deposit contract in ABI form (hex-encoded)" .}: Option[string] template saveSSZFile(filename: string, value: ForkedHashedBeaconState) = case value.kind: @@ -168,7 +190,6 @@ proc doSSZ(conf: NcliConf) = else: raiseAssert "doSSZ() only implements hashTreeRoot and pretty commands" - case kind of "attester_slashing": printit(AttesterSlashing) of "attestation": printit(Attestation) @@ -191,6 +212,62 @@ proc doSSZ(conf: NcliConf) = of "proposer_slashing": printit(ProposerSlashing) of "voluntary_exit": printit(VoluntaryExit) +template init[N: static int](T: type DynamicBytes[N, N]): T = + T newSeq[byte](N) + +proc doVerifyDeposit(conf: NcliConf) = + let cfg = getRuntimeConfig(conf.eth2Network) + + let deposit = if conf.depositData.isSome: + let eventBytes = conf.depositData.get.substr(10) + var + pubkey = init deposit_contract_abi.PubKeyBytes + withdrawalCredentials = init WithdrawalCredentialsBytes + amount = init Int64LeBytes + signature = init SignatureBytes + index = init Int64LeBytes + + echo eventBytes + + try: + var offset = 0 + offset += decode(eventBytes, offset, pubkey) + offset += decode(eventBytes, offset, withdrawalCredentials) + offset += decode(eventBytes, offset, amount) + offset += decode(eventBytes, offset, signature) + except CatchableError as err: + echo "Invalid deposit event: ", err.msg + quit 1 + + DepositData( + pubkey: ValidatorPubKey.init(pubkey.toArray), + withdrawal_credentials: Eth2Digest(data: withdrawalCredentials.toArray), + amount: bytes_to_uint64(amount.toArray), + signature: ValidatorSig.init(signature.toArray)) + else: + var missingParams: seq[string] + + if conf.pubkey.isNone: missingParams.add "pubkey" + if conf.withdrawalCredentials.isNone: missingParams.add "withdrawal-credentials" + if conf.signature.isNone: missingParams.add "singature" + + if missingParams.len > 0: + echo "Please supply the hex-encoded deposit data as an argument or provide all of the following parameters:" + for param in missingParams: + echo " --", param + quit 1 + + DepositData( + pubkey: conf.pubkey.get, + withdrawal_credentials: conf.withdrawalCredentials.get, + amount: conf.amount, + signature: conf.signature.get) + + let status = if verify_deposit_signature(cfg, deposit): "valid" + else: "invalid" + + echo "The deposit is ", status + when isMainModule: let conf = NcliConf.load() @@ -200,3 +277,4 @@ when isMainModule: of pretty: doSSZ(conf) of transition: doTransition(conf) of slots: doSlots(conf) + of verifyDeposit: doVerifyDeposit(conf) diff --git a/vendor/nim-web3 b/vendor/nim-web3 index acfda8192..dfa91e350 160000 --- a/vendor/nim-web3 +++ b/vendor/nim-web3 @@ -1 +1 @@ -Subproject commit acfda8192759c28ae120af1daa691551d993a54e +Subproject commit dfa91e350cf72e80eb44ed1bdbf6429c4de9b858