From 91c9839d139a30efc50fa9341243a6a33f36f9c8 Mon Sep 17 00:00:00 2001 From: chirag-parmar Date: Fri, 21 Feb 2025 03:39:05 +0530 Subject: [PATCH 01/18] eth call support --- nimbus_verified_proxy/rpc/rpc_eth_api.nim | 255 +++++++++++++++------- 1 file changed, 182 insertions(+), 73 deletions(-) diff --git a/nimbus_verified_proxy/rpc/rpc_eth_api.nim b/nimbus_verified_proxy/rpc/rpc_eth_api.nim index 5d0aac5133..17ed4c61a6 100644 --- a/nimbus_verified_proxy/rpc/rpc_eth_api.nim +++ b/nimbus_verified_proxy/rpc/rpc_eth_api.nim @@ -13,7 +13,12 @@ import chronicles, json_rpc/[rpcserver, rpcclient, rpcproxy], eth/common/accounts, - web3/eth_api, + web3/[primitives, eth_api_types, eth_api], + ../../nimbus/beacon/web3_eth_conv, + ../../nimbus/common/common, + ../../nimbus/db/ledger, + ../../nimbus/transaction/call_evm, + ../../nimbus/[evm/types, evm/state], ../validate_proof, ../block_cache @@ -73,12 +78,132 @@ proc getBlockByTag( of BlockNumber: proxy.blockCache.getByNumber(tag.blockNumber) +proc getBlockByHash( + proxy: VerifiedRpcProxy, blockHash: Hash32 +): results.Opt[BlockObject] {.raises: [ValueError].} = + checkPreconditions(proxy) + proxy.blockCache.getPayloadByHash(blockHash) + proc getBlockByTagOrThrow( proxy: VerifiedRpcProxy, quantityTag: BlockTag ): BlockObject {.raises: [ValueError].} = getBlockByTag(proxy, quantityTag).valueOr: raise newException(ValueError, "No block stored for given tag " & $quantityTag) +proc getBlockByHashOrThrow( + proxy: VerifiedRpcProxy, blockHash: Hash32 +): BlockObject {.raises: [ValueError].} = + getBlockByHash(proxy, blockHash).valueOr: + raise newException(ValueError, "No block stored for given hash " & $blockHash) + +proc getBlockHeaderByTagOrThrow( + proxy: VerifiedRpcProxy, quantityTag: BlockTag +): Header {.raises: [ValueError].} = + let blk = getBlockByTag(proxy, quantityTag).valueOr: + raise newException(ValueError, "No block stored for given tag " & $quantityTag) + + return Header( + parentHash: blk.parentHash, + ommersHash: blk.sha3Uncles, + coinbase: blk.miner, + stateRoot: blk.stateRoot, + transactionsRoot: blk.transactionsRoot, + receiptsRoot: blk.receiptsRoot, + logsBloom: blk.logsBloom, + difficulty: blk.difficulty, + number: distinctBase(blk.number), + gasLimit: distinctBase(blk.gasLimit), + gasUsed: distinctBase(blk.gasUsed), + timestamp: blk.timestamp.ethTime, + extraData: distinctBase(blk.extraData), + mixHash: Bytes32(distinctBase(blk.mixHash)), + nonce: blk.nonce.get, + baseFeePerGas: blk.baseFeePerGas, + withdrawalsRoot: blk.withdrawalsRoot, + blobGasUsed: blk.blobGasUsed.u64, + excessBlobGas: blk.excessBlobGas.u64, + parentBeaconBlockRoot: blk.parentBeaconBlockRoot, + requestsHash: blk.requestsHash + ) + +proc getBlockHeaderByHashOrThrow( + proxy: VerifiedRpcProxy, blockHash: Hash32 +): Header {.raises: [ValueError].} = + let blk = getBlockByHash(proxy, blockHash).valueOr: + raise newException(ValueError, "No block stored for given hash " & $blockHash) + + return Header( + parentHash: blk.parentHash, + ommersHash: blk.sha3Uncles, + coinbase: blk.miner, + stateRoot: blk.stateRoot, + transactionsRoot: blk.transactionsRoot, + receiptsRoot: blk.receiptsRoot, + logsBloom: blk.logsBloom, + difficulty: blk.difficulty, + number: distinctBase(blk.number), + gasLimit: distinctBase(blk.gasLimit), + gasUsed: distinctBase(blk.gasUsed), + timestamp: blk.timestamp.ethTime, + extraData: distinctBase(blk.extraData), + mixHash: Bytes32(distinctBase(blk.mixHash)), + nonce: blk.nonce.get, + baseFeePerGas: blk.baseFeePerGas, + withdrawalsRoot: blk.withdrawalsRoot, + blobGasUsed: blk.blobGasUsed.u64, + excessBlobGas: blk.excessBlobGas.u64, + parentBeaconBlockRoot: blk.parentBeaconBlockRoot, + requestsHash: blk.requestsHash + ) + +proc getAccount(lcProxy: VerifiedRpcProxy, address: Address, quantityTag: BlockTag): Future[Account] {.async: (raises: [ValueError, CatchableError]).} = + let + blk = lcProxy.getBlockByTagOrThrow(quantityTag) + blockNumber = blk.number.uint64 + + let + proof = await lcProxy.rpcClient.eth_getProof(address, @[], blockId(blockNumber)) + account = getAccountFromProof( + blk.stateRoot, proof.address, proof.balance, proof.nonce, proof.codeHash, + proof.storageHash, proof.accountProof, + ).valueOr: + raise newException(ValueError, error) + + return account + +proc getCode(lcProxy: VerifiedRpcProxy, address: Address, quantityTag: BlockTag): Future[seq[byte]] {.async: (raises: [ValueError, CatchableError]).} = + let + blk = lcProxy.getBlockByTagOrThrow(quantityTag) + blockNumber = blk.number.uint64 + account = await lcProxy.getAccount(address, quantityTag) + + info "Forwarding eth_getCode", blockNumber + + if account.codeHash == EMPTY_CODE_HASH: + # account does not have any code, return empty hex data + return @[] + + let code = await lcProxy.rpcClient.eth_getCode(address, blockId(blockNumber)) + + if isValidCode(account, code): + return code + else: + raise newException(ValueError, "received code doesn't match the account code hash") + +proc getStorageAt(lcProxy: VerifiedRpcProxy, address: Address, slot: UInt256, quantityTag: BlockTag): Future[UInt256] {.async: (raises: [ValueError, CatchableError]).} = + let + blk = lcProxy.getBlockByTagOrThrow(quantityTag) + blockNumber = blk.number.uint64 + + info "Forwarding eth_getStorageAt", blockNumber + + let + proof = await lcProxy.rpcClient.eth_getProof(address, @[slot], blockId(blockNumber)) + slotValue = getStorageData(blk.stateRoot, slot, proof).valueOr: + raise newException(ValueError, error) + + slotValue + proc installEthApiHandlers*(lcProxy: VerifiedRpcProxy) = lcProxy.proxy.rpc("eth_chainId") do() -> UInt256: lcProxy.chainId @@ -93,95 +218,79 @@ proc installEthApiHandlers*(lcProxy: VerifiedRpcProxy) = lcProxy.proxy.rpc("eth_getBalance") do( address: Address, quantityTag: BlockTag ) -> UInt256: - # When requesting state for `latest` block number, we need to translate - # `latest` to actual block number as `latest` on proxy and on data provider - # can mean different blocks and ultimatly piece received piece of state - # must by validated against correct state root - let - blk = lcProxy.getBlockByTagOrThrow(quantityTag) - blockNumber = blk.number.uint64 - - info "Forwarding eth_getBalance call", blockNumber - - let - proof = await lcProxy.rpcClient.eth_getProof(address, @[], blockId(blockNumber)) - account = getAccountFromProof( - blk.stateRoot, proof.address, proof.balance, proof.nonce, proof.codeHash, - proof.storageHash, proof.accountProof, - ).valueOr: - raise newException(ValueError, error) - + let account = await lcProxy.getAccount(address, quantityTag) account.balance lcProxy.proxy.rpc("eth_getStorageAt") do( address: Address, slot: UInt256, quantityTag: BlockTag ) -> UInt256: - let - blk = lcProxy.getBlockByTagOrThrow(quantityTag) - blockNumber = blk.number.uint64 - - info "Forwarding eth_getStorageAt", blockNumber - - let proof = - await lcProxy.rpcClient.eth_getProof(address, @[slot], blockId(blockNumber)) - - getStorageData(blk.stateRoot, slot, proof).valueOr: - raise newException(ValueError, error) + await lcProxy.getStorageAt(address, slot, quantityTag) lcProxy.proxy.rpc("eth_getTransactionCount") do( address: Address, quantityTag: BlockTag - ) -> uint64: - let - blk = lcProxy.getBlockByTagOrThrow(quantityTag) - blockNumber = blk.number.uint64 - - info "Forwarding eth_getTransactionCount", blockNumber - - let - proof = await lcProxy.rpcClient.eth_getProof(address, @[], blockId(blockNumber)) - - account = getAccountFromProof( - blk.stateRoot, proof.address, proof.balance, proof.nonce, proof.codeHash, - proof.storageHash, proof.accountProof, - ).valueOr: - raise newException(ValueError, error) - - account.nonce + ) -> Quantity: + let account = await lcProxy.getAccount(address, quantityTag) + Quantity(account.nonce) lcProxy.proxy.rpc("eth_getCode") do( address: Address, quantityTag: BlockTag ) -> seq[byte]: - let - blk = lcProxy.getBlockByTagOrThrow(quantityTag) - blockNumber = blk.number.uint64 - - info "Forwarding eth_getCode", blockNumber - let - proof = await lcProxy.rpcClient.eth_getProof(address, @[], blockId(blockNumber)) - account = getAccountFromProof( - blk.stateRoot, proof.address, proof.balance, proof.nonce, proof.codeHash, - proof.storageHash, proof.accountProof, - ).valueOr: - raise newException(ValueError, error) - - if account.codeHash == EMPTY_CODE_HASH: - # account does not have any code, return empty hex data - return @[] - - let code = await lcProxy.rpcClient.eth_getCode(address, blockId(blockNumber)) - - if isValidCode(account, code): - return code - else: - raise newException( - ValueError, "Received code which does not match the account code hash" - ) + await lcProxy.getCode(address, quantityTag) + + lcProxy.proxy.rpc("eth_call") do( + args: TransactionArgs, quantityTag: BlockTag + ) -> seq[byte]: + + # eth_call + # 1. get the code with proof + let to = if args.to.isSome(): args.to.get() + else: raise newException(ValueError, "contract address missing in transaction args") + + # 2. get all storage locations that are accessed + let + code = await lcProxy.getCode(to, quantityTag) + header = lcProxy.getBlockHeaderByTagOrThrow(quantityTag) + blkNumber = header.number.uint64 + parent = lcProxy.getBlockHeaderByHashOrThrow(header.parentHash) + accessListResult = await lcProxy.rpcClient.eth_createAccessList(args, blockId(blkNumber)) + + let accessList = if not accessListResult.error.isSome(): accessListResult.accessList + else: raise newException(ValueError, "couldn't get an access list for eth call") + + # 3. pull the storage values that are access along with their accounts and initialize db + let + com = CommonRef.new(newCoreDbRef DefaultDbMemory, nil) + fork = com.toEVMFork(header) + vmState = BaseVMState() + + vmState.init(parent, header, com, com.db.baseTxFrame()) + vmState.mutateLedger: + for accessPair in accessList: + let + accountAddr = accessPair.address + acc = await lcProxy.getAccount(accountAddr, quantityTag) + accCode = await lcProxy.getCode(accountAddr, quantityTag) + + db.setNonce(accountAddr, acc.nonce) + db.setBalance(accountAddr, acc.balance) + db.setCode(accountAddr, accCode) + + for slot in accessPair.storageKeys: + let slotInt = UInt256.fromHex(toHex(slot)) + let slotValue = await lcProxy.getStorageAt(accountAddr, slotInt, quantityTag) + db.setStorage(accountAddr, slotInt, slotValue) + db.persist(clearEmptyAccount = false) # settle accounts storage + + # 4. run the evm with the initialized storage + let evmResult = rpcCallEvm(args, header, vmState).valueOr: + raise newException(ValueError, "rpcCallEvm error: " & $error.code) + + evmResult.output # TODO: # Following methods are forwarded directly to the web3 provider and therefore # are not validated in any way. lcProxy.proxy.registerProxyMethod("net_version") - lcProxy.proxy.registerProxyMethod("eth_call") lcProxy.proxy.registerProxyMethod("eth_sendRawTransaction") lcProxy.proxy.registerProxyMethod("eth_getTransactionReceipt") From 55b3c159ec09d57cd49a46921bad8a8f7b096896 Mon Sep 17 00:00:00 2001 From: chirag-parmar Date: Tue, 25 Feb 2025 10:20:51 +0530 Subject: [PATCH 02/18] rebase fix --- nimbus_verified_proxy/rpc/rpc_eth_api.nim | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/nimbus_verified_proxy/rpc/rpc_eth_api.nim b/nimbus_verified_proxy/rpc/rpc_eth_api.nim index 17ed4c61a6..067ddd4a8f 100644 --- a/nimbus_verified_proxy/rpc/rpc_eth_api.nim +++ b/nimbus_verified_proxy/rpc/rpc_eth_api.nim @@ -14,11 +14,11 @@ import json_rpc/[rpcserver, rpcclient, rpcproxy], eth/common/accounts, web3/[primitives, eth_api_types, eth_api], - ../../nimbus/beacon/web3_eth_conv, - ../../nimbus/common/common, - ../../nimbus/db/ledger, - ../../nimbus/transaction/call_evm, - ../../nimbus/[evm/types, evm/state], + ../../execution_chain/beacon/web3_eth_conv, + ../../execution_chain/common/common, + ../../execution_chain/db/ledger, + ../../execution_chain/transaction/call_evm, + ../../execution_chain/[evm/types, evm/state], ../validate_proof, ../block_cache From 9f68995dae9ee9aabd1acd277c2bc21d42aaa9da Mon Sep 17 00:00:00 2001 From: chirag-parmar Date: Fri, 28 Feb 2025 11:06:31 +0530 Subject: [PATCH 03/18] use cache of headers instead of blocks --- nimbus_verified_proxy/block_cache.nim | 48 --- nimbus_verified_proxy/header_store.nim | 120 +++++++ .../nimbus_verified_proxy.nim | 150 ++++----- nimbus_verified_proxy/rpc/rpc_eth_api.nim | 304 ++++++++---------- 4 files changed, 304 insertions(+), 318 deletions(-) delete mode 100644 nimbus_verified_proxy/block_cache.nim create mode 100644 nimbus_verified_proxy/header_store.nim diff --git a/nimbus_verified_proxy/block_cache.nim b/nimbus_verified_proxy/block_cache.nim deleted file mode 100644 index bc3430c6ee..0000000000 --- a/nimbus_verified_proxy/block_cache.nim +++ /dev/null @@ -1,48 +0,0 @@ -# nimbus_verified_proxy -# Copyright (c) 2022-2024 Status Research & Development GmbH -# Licensed and distributed under either of -# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT). -# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0). -# at your option. This file may not be copied, modified, or distributed except according to those terms. - -{.push raises: [].} - -import eth/common/hashes, web3/eth_api_types, minilru, results - -## Cache for payloads received through block gossip and validated by the -## consensus light client. -## The payloads are stored in order of arrival. When the cache is full, the -## oldest payload is deleted first. -type BlockCache* = ref object - blocks: LruCache[Hash32, BlockObject] - -proc new*(T: type BlockCache, max: uint32): T = - let maxAsInt = int(max) - BlockCache(blocks: LruCache[Hash32, BlockObject].init(maxAsInt)) - -func len*(self: BlockCache): int = - len(self.blocks) - -func isEmpty*(self: BlockCache): bool = - len(self.blocks) == 0 - -proc add*(self: BlockCache, payload: BlockObject) = - # Only add if it didn't exist before - the implementation of `latest` relies - # on this.. - if payload.hash notin self.blocks: - self.blocks.put(payload.hash, payload) - -proc latest*(self: BlockCache): Opt[BlockObject] = - for b in self.blocks.values: - return Opt.some(b) - Opt.none(BlockObject) - -proc getByNumber*(self: BlockCache, number: Quantity): Opt[BlockObject] = - for b in self.blocks.values: - if b.number == number: - return Opt.some(b) - - Opt.none(BlockObject) - -proc getPayloadByHash*(self: BlockCache, hash: Hash32): Opt[BlockObject] = - self.blocks.get(hash) diff --git a/nimbus_verified_proxy/header_store.nim b/nimbus_verified_proxy/header_store.nim new file mode 100644 index 0000000000..7cf8d976c0 --- /dev/null +++ b/nimbus_verified_proxy/header_store.nim @@ -0,0 +1,120 @@ +# nimbus_verified_proxy +# Copyright (c) 2022-2024 Status Research & Development GmbH +# Licensed and distributed under either of +# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT). +# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0). +# at your option. This file may not be copied, modified, or distributed except according to those terms. + +{.push raises: [].} + +import + eth/common/hashes, + eth/common/headers, + web3/eth_api_types, + std/tables, + beacon_chain/spec/beaconstate, + beacon_chain/spec/datatypes/[phase0, altair, bellatrix], + beacon_chain/[light_client, nimbus_binary_common, version], + beacon_chain/el/engine_api_conversions, + minilru, + results + +type + HeaderStore* = ref object + headers: LruCache[Hash32, Header] + hashes: Table[base.BlockNumber, Hash32] + +func convHeader(lcHeader: ForkedLightClientHeader): Header = + withForkyHeader(lcHeader): + template p(): auto = forkyHeader.execution + + when lcDataFork >= LightClientDataFork.Capella: + let withdrawalsRoot = Opt.some(p.withdrawals_root.asBlockHash) + else: + let withdrawalsRoot = Opt.none(Hash32) + + when lcDataFork >= LightClientDataFork.Deneb: + let + blobGasUsed = Opt.some(p.blob_gas_used) + excessBlobGas = Opt.some(p.excess_blob_gas) + parentBeaconBlockRoot = Opt.some(forkyHeader.beacon.parent_root.asBlockHash) + else: + let + blobGasUsed = Opt.none(uint64) + excessBlobGas = Opt.none(uint64) + parentBeaconBlockRoot = Opt.none(Hash32) + + when lcDataFork >= LightClientDataFork.Electra: + # TODO: there is no visibility of the execution requests hash in light client header + let requestsHash = Opt.none(Hash32) + else: + let requestsHash = Opt.none(Hash32) + + when lcDataFork > LightClientDataFork.Altair: + let h = Header( + parentHash: p.parent_hash.asBlockHash, + ommersHash: EMPTY_UNCLE_HASH, + coinbase: addresses.Address(p.fee_recipient.data), + stateRoot: p.state_root.asBlockHash, + transactionsRoot: p.transactions_root.asBlockHash, + receiptsRoot: p.receipts_root.asBlockHash, + logsBloom: FixedBytes[BYTES_PER_LOGS_BLOOM](p.logs_bloom.data), + difficulty: DifficultyInt(0.u256), + number: base.BlockNumber(p.block_number), + gasLimit: GasInt(p.gas_limit), + gasUsed: GasInt(p.gas_used), + timestamp: EthTime(p.timestamp), + extraData: seq[byte](p.extra_data), + mixHash: p.prev_randao.data.to(Bytes32), + nonce: default(Bytes8), + baseFeePerGas: Opt.some(p.base_fee_per_gas), + withdrawalsRoot: withdrawalsRoot, + blobGasUsed: blobGasUsed, + excessBlobGas: excessBlobGas, + parentBeaconBlockRoot: parentBeaconBlockRoot, + requestsHash: requestsHash + ) + else: + # INFO: should never reach this point because running verified + # proxy for altair doesn't make sense + let h = Header() + return h + +proc new*(T: type HeaderStore, max: int): T = + HeaderStore(headers: LruCache[Hash32, Header].init(max)) + +func len*(self: HeaderStore): int = + len(self.headers) + +func isEmpty*(self: HeaderStore): bool = + len(self.headers) == 0 + +proc add*(self: HeaderStore, header: ForkedLightClientHeader) = + # Only add if it didn't exist before - the implementation of `latest` relies + # on this.. + let execHeader = convHeader(header) + withForkyHeader(header): + when lcDataFork > LightClientDataFork.Altair: + let execHash = forkyHeader.execution.block_hash.asBlockHash + + if execHash notin self.headers: + self.headers.put(execHash, execHeader) + self.hashes[execHeader.number] = execHash + +proc latest*(self: HeaderStore): Opt[Header] = + for h in self.headers.values: + return Opt.some(h) + + Opt.none(Header) + +proc get*(self: HeaderStore, number: base.BlockNumber): Opt[Header] = + let hash = + try: + self.hashes[number] + except: + return Opt.none(Header) + + return self.headers.peek(hash) + +proc get*(self: HeaderStore, hash: Hash32): Opt[Header] = + self.headers.peek(hash) diff --git a/nimbus_verified_proxy/nimbus_verified_proxy.nim b/nimbus_verified_proxy/nimbus_verified_proxy.nim index 0446328120..e793ea5342 100644 --- a/nimbus_verified_proxy/nimbus_verified_proxy.nim +++ b/nimbus_verified_proxy/nimbus_verified_proxy.nim @@ -14,18 +14,17 @@ import confutils, eth/common/[keys, eth_types_rlp], json_rpc/rpcproxy, - beacon_chain/el/[el_manager, engine_api_conversions], + beacon_chain/el/[el_manager], beacon_chain/gossip_processing/optimistic_processor, beacon_chain/networking/network_metadata, beacon_chain/networking/topic_params, beacon_chain/spec/beaconstate, beacon_chain/spec/datatypes/[phase0, altair, bellatrix], beacon_chain/[light_client, nimbus_binary_common, version], - ../execution_chain/rpc/[cors, rpc_utils], - ../execution_chain/beacon/payload_conv, + ../execution_chain/rpc/cors, ./rpc/rpc_eth_api, ./nimbus_verified_proxy_conf, - ./block_cache + ./header_store from beacon_chain/gossip_processing/eth2_processor import toValidationResult @@ -57,12 +56,7 @@ func getConfiguredChainId(networkMetadata: Eth2NetworkMetadata): UInt256 = proc run*( config: VerifiedProxyConf, ctx: ptr Context ) {.raises: [CatchableError], gcsafe.} = - var headerCallback: OnHeaderCallback - if ctx != nil: - headerCallback = ctx.onHeader - - # Required as both Eth2Node and LightClient requires correct config type - var lcConfig = config.asLightClientConf() + # Required as both Eth2Node and LightClient requires correct config type {.gcsafe.}: setupLogging(config.logLevel, config.logStdout, none(OutFile)) @@ -73,16 +67,30 @@ proc run*( except Exception: notice "commandLineParams() exception" + # load constants and metadata for the selected chain + let metadata = loadEth2Network(config.eth2Network) + + # initialze verified proxy let - metadata = loadEth2Network(config.eth2Network) chainId = getConfiguredChainId(metadata) + authHooks = @[httpCors(@[])] # TODO: for now we serve all cross origin requests + # TODO: write a comment + clientConfig = config.web3url.asClientConfig() - for node in metadata.bootstrapNodes: - lcConfig.bootstrapNodes.add node + rpcProxy = RpcProxy.new( + [initTAddress(config.rpcAddress, config.rpcPort)], clientConfig, authHooks + ) - template cfg(): auto = - metadata.cfg + headerStore = HeaderStore.new(64) # block cache contains blocks downloaded from p2p + verifiedProxy = VerifiedRpcProxy.new(rpcProxy, headerStore, chainId) + # add handlers that verify RPC calls /rpc/rpc_eth_api.nim + verifiedProxy.installEthApiHandlers() + + # just for short hand convenience + template cfg(): auto = metadata.cfg + + # initiialize beacon node genesis data, beacon clock and forkDigests let genesisState = try: @@ -97,11 +105,14 @@ proc run*( except CatchableError as err: raiseAssert "Invalid baked-in state: " & err.msg + # getStateField reads seeks info directly from a byte array + # get genesis time and instantiate the beacon clock genesisTime = getStateField(genesisState[], genesis_time) beaconClock = BeaconClock.init(genesisTime).valueOr: error "Invalid genesis time in state", genesisTime quit QuitFailure + # get the function that itself get the current beacon time getBeaconTime = beaconClock.getBeaconTimeFn() genesis_validators_root = getStateField(genesisState[], genesis_validators_root) @@ -109,6 +120,13 @@ proc run*( genesisBlockRoot = get_initial_beacon_block(genesisState[]).root + # transform the config to fit as a light client config and as a p2p node(Eth2Node) config + var lcConfig = config.asLightClientConf() + for node in metadata.bootstrapNodes: + lcConfig.bootstrapNodes.add node + + # create new network keys, create a p2p node(Eth2Node) and create a light client + let rng = keys.newRng() netKeys = getRandomNetKeys(rng[]) @@ -117,96 +135,29 @@ proc run*( rng, lcConfig, netKeys, cfg, forkDigests, getBeaconTime, genesis_validators_root ) - blockCache = BlockCache.new(uint32(64)) - - # TODO: for now we serve all cross origin requests - authHooks = @[httpCors(@[])] - - clientConfig = config.web3url.asClientConfig() - - rpcProxy = RpcProxy.new( - [initTAddress(config.rpcAddress, config.rpcPort)], clientConfig, authHooks - ) - - verifiedProxy = VerifiedRpcProxy.new(rpcProxy, blockCache, chainId) - - optimisticHandler = proc( - signedBlock: ForkedSignedBeaconBlock - ) {.async: (raises: [CancelledError]).} = - notice "New LC optimistic block", - opt = signedBlock.toBlockId(), wallSlot = getBeaconTime().slotOrZero - withBlck(signedBlock): - when consensusFork >= ConsensusFork.Bellatrix: - if forkyBlck.message.is_execution_block: - template payload(): auto = - forkyBlck.message.body - - try: - # TODO parentBeaconBlockRoot / requestsHash - let blk = ethBlock( - executionPayload(payload.asEngineExecutionPayload()), - parentBeaconBlockRoot = Opt.none(Hash32), - requestsHash = Opt.none(Hash32), - ) - blockCache.add(populateBlockObject(blk.header.rlpHash, blk, 0.u256, true)) - except RlpError as exc: - debug "Invalid block received", err = exc.msg - - optimisticProcessor = initOptimisticProcessor(getBeaconTime, optimisticHandler) - + # light client is set to optimistic finalization mode lightClient = createLightClient( network, rng, lcConfig, cfg, forkDigests, getBeaconTime, genesis_validators_root, LightClientFinalizationMode.Optimistic, ) - verifiedProxy.installEthApiHandlers() - - info "Listening to incoming network requests" - network.registerProtocol( - PeerSync, - PeerSync.NetworkState.init(cfg, forkDigests, genesisBlockRoot, getBeaconTime), - ) - network.addValidator( - getBeaconBlocksTopic(forkDigests.phase0), - proc(signedBlock: phase0.SignedBeaconBlock): ValidationResult = - toValidationResult(optimisticProcessor.processSignedBeaconBlock(signedBlock)), - ) - network.addValidator( - getBeaconBlocksTopic(forkDigests.altair), - proc(signedBlock: altair.SignedBeaconBlock): ValidationResult = - toValidationResult(optimisticProcessor.processSignedBeaconBlock(signedBlock)), - ) - network.addValidator( - getBeaconBlocksTopic(forkDigests.bellatrix), - proc(signedBlock: bellatrix.SignedBeaconBlock): ValidationResult = - toValidationResult(optimisticProcessor.processSignedBeaconBlock(signedBlock)), - ) - network.addValidator( - getBeaconBlocksTopic(forkDigests.capella), - proc(signedBlock: capella.SignedBeaconBlock): ValidationResult = - toValidationResult(optimisticProcessor.processSignedBeaconBlock(signedBlock)), - ) - network.addValidator( - getBeaconBlocksTopic(forkDigests.deneb), - proc(signedBlock: deneb.SignedBeaconBlock): ValidationResult = - toValidationResult(optimisticProcessor.processSignedBeaconBlock(signedBlock)), - ) - lightClient.installMessageValidators() - + # start the p2p network and rpcProxy waitFor network.startListening() waitFor network.start() waitFor rpcProxy.start() + + # verify chain id that the proxy is connected to waitFor verifiedProxy.verifyChaindId() proc onFinalizedHeader( lightClient: LightClient, finalizedHeader: ForkedLightClientHeader ) = withForkyHeader(finalizedHeader): - when lcDataFork > LightClientDataFork.None: + when lcDataFork > LightClientDataFork.Altair: info "New LC finalized header", finalized_header = shortLog(forkyHeader) - if headerCallback != nil: + if ctx != nil: try: - headerCallback(cstring(Json.encode(forkyHeader)), 0) + ctx.onHeader(cstring(Json.encode(forkyHeader)), 0) except SerializationError as e: error "finalizedHeaderCallback exception", error = e.msg @@ -214,11 +165,12 @@ proc run*( lightClient: LightClient, optimisticHeader: ForkedLightClientHeader ) = withForkyHeader(optimisticHeader): - when lcDataFork > LightClientDataFork.None: + when lcDataFork > LightClientDataFork.Altair: info "New LC optimistic header", optimistic_header = shortLog(forkyHeader) - if headerCallback != nil: + headerStore.add(optimisticHeader) + if ctx != nil: try: - headerCallback(cstring(Json.encode(forkyHeader)), 1) + ctx.onHeader(cstring(Json.encode(forkyHeader)), 1) except SerializationError as e: error "optimisticHeaderCallback exception", error = e.msg @@ -279,11 +231,12 @@ proc run*( blocksGossipState = targetGossipState - proc onSecond(time: Moment) = + proc updateGossipStatus(time: Moment) = let wallSlot = getBeaconTime().slotOrZero() updateBlocksGossipStatus(wallSlot + 1) lightClient.updateGossipStatus(wallSlot + 1) + # updates gossip status every second every second proc runOnSecondLoop() {.async.} = let sleepTime = chronos.seconds(1) while true: @@ -291,15 +244,20 @@ proc run*( await chronos.sleepAsync(sleepTime) let afterSleep = chronos.now(chronos.Moment) let sleepTime = afterSleep - start - onSecond(start) + updateGossipStatus(start) let finished = chronos.now(chronos.Moment) let processingTime = finished - afterSleep trace "onSecond task completed", sleepTime, processingTime - onSecond(Moment.now()) + # update gossip status before starting the light client + updateGossipStatus(Moment.now()) + # start the light client lightClient.start() + # launch a async routine asyncSpawn runOnSecondLoop() + + # run an infinite loop and wait for a stop signal while true: poll() if ctx != nil and ctx.stop: @@ -308,7 +266,7 @@ proc run*( waitFor rpcProxy.stop() ctx.cleanup() # Notify client that cleanup is finished - headerCallback(nil, 2) + ctx.onHeader(nil, 2) break when isMainModule: diff --git a/nimbus_verified_proxy/rpc/rpc_eth_api.nim b/nimbus_verified_proxy/rpc/rpc_eth_api.nim index 067ddd4a8f..b0d0504b8b 100644 --- a/nimbus_verified_proxy/rpc/rpc_eth_api.nim +++ b/nimbus_verified_proxy/rpc/rpc_eth_api.nim @@ -13,6 +13,7 @@ import chronicles, json_rpc/[rpcserver, rpcclient, rpcproxy], eth/common/accounts, + eth/common/addresses, web3/[primitives, eth_api_types, eth_api], ../../execution_chain/beacon/web3_eth_conv, ../../execution_chain/common/common, @@ -20,7 +21,7 @@ import ../../execution_chain/transaction/call_evm, ../../execution_chain/[evm/types, evm/state], ../validate_proof, - ../block_cache + ../header_store logScope: topics = "verified_proxy" @@ -28,178 +29,106 @@ logScope: type VerifiedRpcProxy* = ref object proxy: RpcProxy - blockCache: BlockCache - chainId: UInt256 - - QuantityTagKind = enum - LatestBlock - BlockNumber + headerStore: HeaderStore + chainId: Quantity BlockTag = eth_api_types.RtBlockIdentifier - QuantityTag = object - case kind: QuantityTagKind - of LatestBlock: - discard - of BlockNumber: - blockNumber: Quantity +template checkPreconditions(proxy: VerifiedRpcProxy) = + if proxy.headerStore.isEmpty(): + raise newException(ValueError, "Syncing") + +template rpcClient(lcProxy: VerifiedRpcProxy): RpcClient = + lcProxy.proxy.getClient() + +proc resolveTag( + self: VerifiedRpcProxy, blockTag: BlockTag +): base.BlockNumber {.raises: [ValueError].} = + self.checkPreconditions() -func parseQuantityTag(blockTag: BlockTag): Result[QuantityTag, string] = if blockTag.kind == bidAlias: - let tag = blockTag.alias.toLowerAscii + let tag = blockTag.alias.toLowerAscii() case tag of "latest": - return ok(QuantityTag(kind: LatestBlock)) + let hLatest = self.headerStore.latest() + if hLatest.isSome: + return hLatest.get().number + else: + raise newException(ValueError, "No block stored for given tag " & $blockTag) else: - return err("Unsupported blockTag: " & tag) + raise newException(ValueError, "No support for block tag " & $blockTag) else: - let quantity = blockTag.number - return ok(QuantityTag(kind: BlockNumber, blockNumber: quantity)) + return base.BlockNumber(distinctBase(blockTag.number)) -template checkPreconditions(proxy: VerifiedRpcProxy) = - if proxy.blockCache.isEmpty(): - raise newException(ValueError, "Syncing") - -template rpcClient(lcProxy: VerifiedRpcProxy): RpcClient = - lcProxy.proxy.getClient() - -proc getBlockByTag( - proxy: VerifiedRpcProxy, quantityTag: BlockTag -): results.Opt[BlockObject] {.raises: [ValueError].} = - checkPreconditions(proxy) - - let tag = parseQuantityTag(quantityTag).valueOr: - raise newException(ValueError, error) - - case tag.kind - of LatestBlock: - # this will always return some block, as we always checkPreconditions - proxy.blockCache.latest - of BlockNumber: - proxy.blockCache.getByNumber(tag.blockNumber) - -proc getBlockByHash( - proxy: VerifiedRpcProxy, blockHash: Hash32 -): results.Opt[BlockObject] {.raises: [ValueError].} = - checkPreconditions(proxy) - proxy.blockCache.getPayloadByHash(blockHash) - -proc getBlockByTagOrThrow( - proxy: VerifiedRpcProxy, quantityTag: BlockTag -): BlockObject {.raises: [ValueError].} = - getBlockByTag(proxy, quantityTag).valueOr: - raise newException(ValueError, "No block stored for given tag " & $quantityTag) - -proc getBlockByHashOrThrow( - proxy: VerifiedRpcProxy, blockHash: Hash32 -): BlockObject {.raises: [ValueError].} = - getBlockByHash(proxy, blockHash).valueOr: - raise newException(ValueError, "No block stored for given hash " & $blockHash) - -proc getBlockHeaderByTagOrThrow( - proxy: VerifiedRpcProxy, quantityTag: BlockTag -): Header {.raises: [ValueError].} = - let blk = getBlockByTag(proxy, quantityTag).valueOr: - raise newException(ValueError, "No block stored for given tag " & $quantityTag) - - return Header( - parentHash: blk.parentHash, - ommersHash: blk.sha3Uncles, - coinbase: blk.miner, - stateRoot: blk.stateRoot, - transactionsRoot: blk.transactionsRoot, - receiptsRoot: blk.receiptsRoot, - logsBloom: blk.logsBloom, - difficulty: blk.difficulty, - number: distinctBase(blk.number), - gasLimit: distinctBase(blk.gasLimit), - gasUsed: distinctBase(blk.gasUsed), - timestamp: blk.timestamp.ethTime, - extraData: distinctBase(blk.extraData), - mixHash: Bytes32(distinctBase(blk.mixHash)), - nonce: blk.nonce.get, - baseFeePerGas: blk.baseFeePerGas, - withdrawalsRoot: blk.withdrawalsRoot, - blobGasUsed: blk.blobGasUsed.u64, - excessBlobGas: blk.excessBlobGas.u64, - parentBeaconBlockRoot: blk.parentBeaconBlockRoot, - requestsHash: blk.requestsHash - ) - -proc getBlockHeaderByHashOrThrow( - proxy: VerifiedRpcProxy, blockHash: Hash32 +# TODO: pull a header from the RPC if not in cache +proc getHeaderByHash( + self: VerifiedRpcProxy, blockHash: Hash32 ): Header {.raises: [ValueError].} = - let blk = getBlockByHash(proxy, blockHash).valueOr: - raise newException(ValueError, "No block stored for given hash " & $blockHash) - - return Header( - parentHash: blk.parentHash, - ommersHash: blk.sha3Uncles, - coinbase: blk.miner, - stateRoot: blk.stateRoot, - transactionsRoot: blk.transactionsRoot, - receiptsRoot: blk.receiptsRoot, - logsBloom: blk.logsBloom, - difficulty: blk.difficulty, - number: distinctBase(blk.number), - gasLimit: distinctBase(blk.gasLimit), - gasUsed: distinctBase(blk.gasUsed), - timestamp: blk.timestamp.ethTime, - extraData: distinctBase(blk.extraData), - mixHash: Bytes32(distinctBase(blk.mixHash)), - nonce: blk.nonce.get, - baseFeePerGas: blk.baseFeePerGas, - withdrawalsRoot: blk.withdrawalsRoot, - blobGasUsed: blk.blobGasUsed.u64, - excessBlobGas: blk.excessBlobGas.u64, - parentBeaconBlockRoot: blk.parentBeaconBlockRoot, - requestsHash: blk.requestsHash - ) - -proc getAccount(lcProxy: VerifiedRpcProxy, address: Address, quantityTag: BlockTag): Future[Account] {.async: (raises: [ValueError, CatchableError]).} = - let - blk = lcProxy.getBlockByTagOrThrow(quantityTag) - blockNumber = blk.number.uint64 + self.checkPreconditions() + self.headerStore.get(blockHash).valueOr: + raise newException(ValueError, "No block stored for given tag " & $blockHash) +# TODO: pull a header from the RPC if not in cache +proc getHeaderByTag( + self: VerifiedRpcProxy, blockTag: BlockTag +): Header {.raises: [ValueError].} = + let n = self.resolveTag(blockTag) + self.headerStore.get(n).valueOr: + raise newException(ValueError, "No block stored for given tag " & $blockTag) + +proc getAccount( + lcProxy: VerifiedRpcProxy, + address: addresses.Address, + blockNumber: base.BlockNumber, + stateRoot: Root +): Future[Account] {.async: (raises: [ValueError, CatchableError]).} = let proof = await lcProxy.rpcClient.eth_getProof(address, @[], blockId(blockNumber)) account = getAccountFromProof( - blk.stateRoot, proof.address, proof.balance, proof.nonce, proof.codeHash, + stateRoot, proof.address, proof.balance, proof.nonce, proof.codeHash, proof.storageHash, proof.accountProof, ).valueOr: raise newException(ValueError, error) return account -proc getCode(lcProxy: VerifiedRpcProxy, address: Address, quantityTag: BlockTag): Future[seq[byte]] {.async: (raises: [ValueError, CatchableError]).} = - let - blk = lcProxy.getBlockByTagOrThrow(quantityTag) - blockNumber = blk.number.uint64 - account = await lcProxy.getAccount(address, quantityTag) - - info "Forwarding eth_getCode", blockNumber +proc getCode( + lcProxy: VerifiedRpcProxy, + address: addresses.Address, + blockNumber: base.BlockNumber, + stateRoot: Root +): Future[seq[byte]] {.async: (raises: [ValueError, CatchableError]).} = + # get verified account details for the address at blockNumber + let account = await lcProxy.getAccount(address, blockNumber, stateRoot) + # if the account does not have any code, return empty hex data if account.codeHash == EMPTY_CODE_HASH: - # account does not have any code, return empty hex data return @[] + info "Forwarding eth_getCode", blockNumber + let code = await lcProxy.rpcClient.eth_getCode(address, blockId(blockNumber)) + # verify the byte code. since we verified the account against + # the state root we just need to verify the code hash if isValidCode(account, code): return code else: raise newException(ValueError, "received code doesn't match the account code hash") -proc getStorageAt(lcProxy: VerifiedRpcProxy, address: Address, slot: UInt256, quantityTag: BlockTag): Future[UInt256] {.async: (raises: [ValueError, CatchableError]).} = - let - blk = lcProxy.getBlockByTagOrThrow(quantityTag) - blockNumber = blk.number.uint64 +proc getStorageAt( + lcProxy: VerifiedRpcProxy, + address: addresses.Address, + slot: UInt256, + blockNumber: base.BlockNumber, + stateRoot: Root +): Future[UInt256] {.async: (raises: [ValueError, CatchableError]).} = info "Forwarding eth_getStorageAt", blockNumber let proof = await lcProxy.rpcClient.eth_getProof(address, @[slot], blockId(blockNumber)) - slotValue = getStorageData(blk.stateRoot, slot, proof).valueOr: + slotValue = getStorageData(stateRoot, slot, proof).valueOr: raise newException(ValueError, error) slotValue @@ -208,54 +137,80 @@ proc installEthApiHandlers*(lcProxy: VerifiedRpcProxy) = lcProxy.proxy.rpc("eth_chainId") do() -> UInt256: lcProxy.chainId - lcProxy.proxy.rpc("eth_blockNumber") do() -> uint64: - ## Returns the number of the most recent block. - let latest = lcProxy.blockCache.latest.valueOr: + lcProxy.proxy.rpc("eth_blockNumber") do() -> Quantity: + # Returns the number of the most recent block seen by the light client. + lcProxy.checkPreconditions() + + let hLatest = lcProxy.headerStore.latest() + if hLatest.isNone: raise newException(ValueError, "Syncing") - latest.number.uint64 + return Quantity(hLatest.get().number) lcProxy.proxy.rpc("eth_getBalance") do( - address: Address, quantityTag: BlockTag + address: addresses.Address, blockTag: BlockTag ) -> UInt256: - let account = await lcProxy.getAccount(address, quantityTag) + let + blockNumber = lcProxy.resolveTag(blockTag) + header = lcProxy.headerStore.get(blockNumber).valueOr: + raise newException(ValueError, "No block stored for given tag " & $blockTag) + account = await lcProxy.getAccount(address, blockNumber, header.stateRoot) + account.balance lcProxy.proxy.rpc("eth_getStorageAt") do( - address: Address, slot: UInt256, quantityTag: BlockTag + address: addresses.Address, slot: UInt256, blockTag: BlockTag ) -> UInt256: - await lcProxy.getStorageAt(address, slot, quantityTag) + let + blockNumber = lcProxy.resolveTag(blockTag) + header = lcProxy.headerStore.get(blockNumber).valueOr: + raise newException(ValueError, "No block stored for given tag " & $blockTag) + + await lcProxy.getStorageAt(address, slot, blockNumber, header.stateRoot) lcProxy.proxy.rpc("eth_getTransactionCount") do( - address: Address, quantityTag: BlockTag + address: addresses.Address, blockTag: BlockTag ) -> Quantity: - let account = await lcProxy.getAccount(address, quantityTag) + let + blockNumber = lcProxy.resolveTag(blockTag) + header = lcProxy.headerStore.get(blockNumber).valueOr: + raise newException(ValueError, "No block stored for given tag " & $blockTag) + + account = await lcProxy.getAccount(address, blockNumber, header.stateRoot) + Quantity(account.nonce) lcProxy.proxy.rpc("eth_getCode") do( - address: Address, quantityTag: BlockTag + address: addresses.Address, blockTag: BlockTag ) -> seq[byte]: - await lcProxy.getCode(address, quantityTag) + let + blockNumber = lcProxy.resolveTag(blockTag) + header = lcProxy.headerStore.get(blockNumber).valueOr: + raise newException(ValueError, "No block stored for given tag " & $blockTag) + + await lcProxy.getCode(address, blockNumber, header.stateRoot) lcProxy.proxy.rpc("eth_call") do( - args: TransactionArgs, quantityTag: BlockTag + args: TransactionArgs, blockTag: BlockTag ) -> seq[byte]: # eth_call # 1. get the code with proof - let to = if args.to.isSome(): args.to.get() - else: raise newException(ValueError, "contract address missing in transaction args") + let + to = if args.to.isSome(): args.to.get() + else: raise newException(ValueError, "contract address missing in transaction args") + blockNumber = lcProxy.resolveTag(blockTag) + header = lcProxy.headerStore.get(blockNumber).valueOr: + raise newException(ValueError, "No block stored for given tag " & $blockTag) + code = await lcProxy.getCode(to, blockNumber, header.stateRoot) # 2. get all storage locations that are accessed let - code = await lcProxy.getCode(to, quantityTag) - header = lcProxy.getBlockHeaderByTagOrThrow(quantityTag) - blkNumber = header.number.uint64 - parent = lcProxy.getBlockHeaderByHashOrThrow(header.parentHash) - accessListResult = await lcProxy.rpcClient.eth_createAccessList(args, blockId(blkNumber)) - - let accessList = if not accessListResult.error.isSome(): accessListResult.accessList - else: raise newException(ValueError, "couldn't get an access list for eth call") + parent = lcProxy.headerStore.get(header.parentHash).valueOr: + raise newException(ValueError, "No block stored for given tag " & $blockTag) + accessListResult = await lcProxy.rpcClient.eth_createAccessList(args, blockId(blockNumber)) + accessList = if not accessListResult.error.isSome(): accessListResult.accessList + else: raise newException(ValueError, "couldn't get an access list for eth call") # 3. pull the storage values that are access along with their accounts and initialize db let @@ -268,16 +223,17 @@ proc installEthApiHandlers*(lcProxy: VerifiedRpcProxy) = for accessPair in accessList: let accountAddr = accessPair.address - acc = await lcProxy.getAccount(accountAddr, quantityTag) - accCode = await lcProxy.getCode(accountAddr, quantityTag) + acc = await lcProxy.getAccount(accountAddr, blockNumber, header.stateRoot) + accCode = await lcProxy.getCode(accountAddr, blockNumber, header.stateRoot) db.setNonce(accountAddr, acc.nonce) db.setBalance(accountAddr, acc.balance) db.setCode(accountAddr, accCode) for slot in accessPair.storageKeys: - let slotInt = UInt256.fromHex(toHex(slot)) - let slotValue = await lcProxy.getStorageAt(accountAddr, slotInt, quantityTag) + let + slotInt = UInt256.fromHex(toHex(slot)) + slotValue = await lcProxy.getStorageAt(accountAddr, slotInt, blockNumber, header.stateRoot) db.setStorage(accountAddr, slotInt, slotValue) db.persist(clearEmptyAccount = false) # settle accounts storage @@ -296,20 +252,20 @@ proc installEthApiHandlers*(lcProxy: VerifiedRpcProxy) = # TODO currently we do not handle fullTransactions flag. It require updates on # nim-web3 side - lcProxy.proxy.rpc("eth_getBlockByNumber") do( - quantityTag: BlockTag, fullTransactions: bool - ) -> Opt[BlockObject]: - lcProxy.getBlockByTag(quantityTag) - - lcProxy.proxy.rpc("eth_getBlockByHash") do( - blockHash: Hash32, fullTransactions: bool - ) -> Opt[BlockObject]: - lcProxy.blockCache.getPayloadByHash(blockHash) +# lcProxy.proxy.rpc("eth_getBlockByNumber") do( +# blockTag: BlockTag, fullTransactions: bool +# ) -> Opt[BlockObject]: +# lcProxy.getBlockByTag(blockTag) +# +# lcProxy.proxy.rpc("eth_getBlockByHash") do( +# blockHash: Hash32, fullTransactions: bool +# ) -> Opt[BlockObject]: +# lcProxy.blockCache.getPayloadByHash(blockHash) proc new*( - T: type VerifiedRpcProxy, proxy: RpcProxy, blockCache: BlockCache, chainId: UInt256 + T: type VerifiedRpcProxy, proxy: RpcProxy, headerStore: HeaderStore, chainId: Quantity ): T = - VerifiedRpcProxy(proxy: proxy, blockCache: blockCache, chainId: chainId) + VerifiedRpcProxy(proxy: proxy, headerStore: headerStore, chainId: chainId) # Used to be in eth1_monitor.nim; not sure why it was deleted, # so I copied it here. --Adam From cf579febd482930eb744f745b7835237e287547a Mon Sep 17 00:00:00 2001 From: chirag-parmar Date: Tue, 4 Mar 2025 01:45:14 +0530 Subject: [PATCH 04/18] fixes --- nimbus_verified_proxy/nimbus_verified_proxy.nim | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/nimbus_verified_proxy/nimbus_verified_proxy.nim b/nimbus_verified_proxy/nimbus_verified_proxy.nim index e793ea5342..6e4bfb14b4 100644 --- a/nimbus_verified_proxy/nimbus_verified_proxy.nim +++ b/nimbus_verified_proxy/nimbus_verified_proxy.nim @@ -56,7 +56,6 @@ func getConfiguredChainId(networkMetadata: Eth2NetworkMetadata): UInt256 = proc run*( config: VerifiedProxyConf, ctx: ptr Context ) {.raises: [CatchableError], gcsafe.} = - # Required as both Eth2Node and LightClient requires correct config type {.gcsafe.}: setupLogging(config.logLevel, config.logStdout, none(OutFile)) @@ -141,6 +140,11 @@ proc run*( LightClientFinalizationMode.Optimistic, ) + # find out what this does + network.registerProtocol( + PeerSync, PeerSync.NetworkState.init( + cfg, forkDigests, genesisBlockRoot, getBeaconTime)) + # start the p2p network and rpcProxy waitFor network.startListening() waitFor network.start() @@ -177,6 +181,7 @@ proc run*( lightClient.onFinalizedHeader = onFinalizedHeader lightClient.onOptimisticHeader = onOptimisticHeader lightClient.trustedBlockRoot = some config.trustedBlockRoot + lightClient.installMessageValidators() func shouldSyncOptimistically(wallSlot: Slot): bool = let optimisticHeader = lightClient.optimisticHeader From 5f696a0de5c561efdef66612c0ed67d95b1d98fd Mon Sep 17 00:00:00 2001 From: chirag-parmar Date: Fri, 14 Mar 2025 10:43:57 +0530 Subject: [PATCH 05/18] download headers on the fly --- nimbus_verified_proxy/header_store.nim | 10 ++ nimbus_verified_proxy/rpc/rpc_eth_api.nim | 153 ++++++++++++++++------ 2 files changed, 126 insertions(+), 37 deletions(-) diff --git a/nimbus_verified_proxy/header_store.nim b/nimbus_verified_proxy/header_store.nim index 7cf8d976c0..670db20d5e 100644 --- a/nimbus_verified_proxy/header_store.nim +++ b/nimbus_verified_proxy/header_store.nim @@ -107,6 +107,16 @@ proc latest*(self: HeaderStore): Opt[Header] = Opt.none(Header) +proc earliest*(self: HeaderStore): Opt[Header] = + if self.headers.len() == 0: + return Opt.none(Header) + + var hash: Hash32 + for h in self.headers.keys: + hash = h + + self.headers.peek(hash) + proc get*(self: HeaderStore, number: base.BlockNumber): Opt[Header] = let hash = try: diff --git a/nimbus_verified_proxy/rpc/rpc_eth_api.nim b/nimbus_verified_proxy/rpc/rpc_eth_api.nim index b0d0504b8b..5b3f3cd48e 100644 --- a/nimbus_verified_proxy/rpc/rpc_eth_api.nim +++ b/nimbus_verified_proxy/rpc/rpc_eth_api.nim @@ -60,21 +60,112 @@ proc resolveTag( else: return base.BlockNumber(distinctBase(blockTag.number)) -# TODO: pull a header from the RPC if not in cache +proc convHeader(blk: BlockObject): Header = + let + nonce = if blk.nonce.isSome: blk.nonce.get + else: default(Bytes8) + + return Header( + parentHash: blk.parentHash, + ommersHash: blk.sha3Uncles, + coinbase: blk.miner, + stateRoot: blk.stateRoot, + transactionsRoot: blk.transactionsRoot, + receiptsRoot: blk.receiptsRoot, + logsBloom: blk.logsBloom, + difficulty: blk.difficulty, + number: base.BlockNumber(distinctBase(blk.number)), + gasLimit: GasInt(blk.gasLimit.uint64), + gasUsed: GasInt(blk.gasUsed.uint64), + timestamp: ethTime(blk.timestamp), + extraData: seq[byte](blk.extraData), + mixHash: Bytes32(distinctBase(blk.mixHash)), + nonce: nonce, + baseFeePerGas: blk.baseFeePerGas, + withdrawalsRoot: blk.withdrawalsRoot, + blobGasUsed: blk.blobGasUsed.u64, + excessBlobGas: blk.excessBlobGas.u64, + parentBeaconBlockRoot: blk.parentBeaconBlockRoot, + requestsHash: blk.requestsHash + ) + +proc walkBlocks( + self: VerifiedRpcProxy, + sourceNum: base.BlockNumber, + targetNum: base.BlockNumber, + sourceHash: Hash32, + targetHash: Hash32): Future[bool] {.async: (raises: [ValueError, CatchableError]).} = + + var nextHash = sourceHash + info "starting block walk to verify", blockHash=targetHash + + # TODO: use batch calls to get all blocks at once by number + for i in 0 ..< sourceNum - targetNum: + # TODO: use a verified hash cache + let blk = await self.rpcClient.eth_getBlockByHash(nextHash, false) + info "getting next block", hash=nextHash, number=blk.number, remaining=sourceNum-base.BlockNumber(distinctBase(blk.number)) + + if blk.parentHash == targetHash: + return true + + nextHash = blk.parentHash + + return false + proc getHeaderByHash( self: VerifiedRpcProxy, blockHash: Hash32 -): Header {.raises: [ValueError].} = +): Future[Header] {.async: (raises: [ValueError, CatchableError]).} = self.checkPreconditions() - self.headerStore.get(blockHash).valueOr: - raise newException(ValueError, "No block stored for given tag " & $blockHash) + let cachedHeader = self.headerStore.get(blockHash) + + if cachedHeader.isNone(): + debug "did not find the header in the cache", blockHash=blockHash + else: + return cachedHeader.get() + + # get the source block + let earliestHeader = self.headerStore.earliest.valueOr: + raise newException(ValueError, "Syncing") + + # get the target block + let blk = await self.rpcClient.eth_getBlockByHash(blockHash, false) + let header = convHeader(blk) + + # walk blocks backwards(time) from source to target + let isLinked = await self.walkBlocks(earliestHeader.number, header.number, earliestHeader.parentHash, blockHash) + + if not isLinked: + raise newException(ValueError, "the requested block is not part of the canonical chain") + + return header -# TODO: pull a header from the RPC if not in cache proc getHeaderByTag( self: VerifiedRpcProxy, blockTag: BlockTag -): Header {.raises: [ValueError].} = - let n = self.resolveTag(blockTag) - self.headerStore.get(n).valueOr: - raise newException(ValueError, "No block stored for given tag " & $blockTag) +): Future[Header] {.async: (raises: [ValueError, CatchableError]).} = + let + n = self.resolveTag(blockTag) + cachedHeader = self.headerStore.get(n) + + if cachedHeader.isNone(): + debug "did not find the header in the cache", blockTag=blockTag + else: + return cachedHeader.get() + + # get the source block + let earliestHeader = self.headerStore.earliest.valueOr: + raise newException(ValueError, "Syncing") + + # get the target block + let blk = await self.rpcClient.eth_getBlockByNumber(blockTag, false) + let header = convHeader(blk) + + # walk blocks backwards(time) from source to target + let isLinked = await self.walkBlocks(earliestHeader.number, header.number, earliestHeader.parentHash, blk.hash) + + if not isLinked: + raise newException(ValueError, "the requested block is not part of the canonical chain") + + return header proc getAccount( lcProxy: VerifiedRpcProxy, @@ -151,32 +242,25 @@ proc installEthApiHandlers*(lcProxy: VerifiedRpcProxy) = address: addresses.Address, blockTag: BlockTag ) -> UInt256: let - blockNumber = lcProxy.resolveTag(blockTag) - header = lcProxy.headerStore.get(blockNumber).valueOr: - raise newException(ValueError, "No block stored for given tag " & $blockTag) - account = await lcProxy.getAccount(address, blockNumber, header.stateRoot) + header = await lcProxy.getHeaderByTag(blockTag) + account = await lcProxy.getAccount(address, header.number, header.stateRoot) account.balance lcProxy.proxy.rpc("eth_getStorageAt") do( address: addresses.Address, slot: UInt256, blockTag: BlockTag ) -> UInt256: - let - blockNumber = lcProxy.resolveTag(blockTag) - header = lcProxy.headerStore.get(blockNumber).valueOr: - raise newException(ValueError, "No block stored for given tag " & $blockTag) + let header = await lcProxy.getHeaderByTag(blockTag) + - await lcProxy.getStorageAt(address, slot, blockNumber, header.stateRoot) + await lcProxy.getStorageAt(address, slot, header.number, header.stateRoot) lcProxy.proxy.rpc("eth_getTransactionCount") do( address: addresses.Address, blockTag: BlockTag ) -> Quantity: let - blockNumber = lcProxy.resolveTag(blockTag) - header = lcProxy.headerStore.get(blockNumber).valueOr: - raise newException(ValueError, "No block stored for given tag " & $blockTag) - - account = await lcProxy.getAccount(address, blockNumber, header.stateRoot) + header = await lcProxy.getHeaderByTag(blockTag) + account = await lcProxy.getAccount(address, header.number, header.stateRoot) Quantity(account.nonce) @@ -184,11 +268,9 @@ proc installEthApiHandlers*(lcProxy: VerifiedRpcProxy) = address: addresses.Address, blockTag: BlockTag ) -> seq[byte]: let - blockNumber = lcProxy.resolveTag(blockTag) - header = lcProxy.headerStore.get(blockNumber).valueOr: - raise newException(ValueError, "No block stored for given tag " & $blockTag) + header = await lcProxy.getHeaderByTag(blockTag) - await lcProxy.getCode(address, blockNumber, header.stateRoot) + await lcProxy.getCode(address,header.number, header.stateRoot) lcProxy.proxy.rpc("eth_call") do( args: TransactionArgs, blockTag: BlockTag @@ -199,16 +281,13 @@ proc installEthApiHandlers*(lcProxy: VerifiedRpcProxy) = let to = if args.to.isSome(): args.to.get() else: raise newException(ValueError, "contract address missing in transaction args") - blockNumber = lcProxy.resolveTag(blockTag) - header = lcProxy.headerStore.get(blockNumber).valueOr: - raise newException(ValueError, "No block stored for given tag " & $blockTag) - code = await lcProxy.getCode(to, blockNumber, header.stateRoot) + header = await lcProxy.getHeaderByTag(blockTag) + code = await lcProxy.getCode(to, header.number, header.stateRoot) # 2. get all storage locations that are accessed let - parent = lcProxy.headerStore.get(header.parentHash).valueOr: - raise newException(ValueError, "No block stored for given tag " & $blockTag) - accessListResult = await lcProxy.rpcClient.eth_createAccessList(args, blockId(blockNumber)) + parent = await lcProxy.getHeaderByHash(header.parentHash) + accessListResult = await lcProxy.rpcClient.eth_createAccessList(args, blockId(header.number)) accessList = if not accessListResult.error.isSome(): accessListResult.accessList else: raise newException(ValueError, "couldn't get an access list for eth call") @@ -223,8 +302,8 @@ proc installEthApiHandlers*(lcProxy: VerifiedRpcProxy) = for accessPair in accessList: let accountAddr = accessPair.address - acc = await lcProxy.getAccount(accountAddr, blockNumber, header.stateRoot) - accCode = await lcProxy.getCode(accountAddr, blockNumber, header.stateRoot) + acc = await lcProxy.getAccount(accountAddr, header.number, header.stateRoot) + accCode = await lcProxy.getCode(accountAddr, header.number, header.stateRoot) db.setNonce(accountAddr, acc.nonce) db.setBalance(accountAddr, acc.balance) @@ -233,7 +312,7 @@ proc installEthApiHandlers*(lcProxy: VerifiedRpcProxy) = for slot in accessPair.storageKeys: let slotInt = UInt256.fromHex(toHex(slot)) - slotValue = await lcProxy.getStorageAt(accountAddr, slotInt, blockNumber, header.stateRoot) + slotValue = await lcProxy.getStorageAt(accountAddr, slotInt, header.number, header.stateRoot) db.setStorage(accountAddr, slotInt, slotValue) db.persist(clearEmptyAccount = false) # settle accounts storage From d8584d891a9fcd2f83d36a7df65569806863241b Mon Sep 17 00:00:00 2001 From: chirag-parmar Date: Fri, 14 Mar 2025 11:31:59 +0530 Subject: [PATCH 06/18] verify headers --- nimbus_verified_proxy/rpc/rpc_eth_api.nim | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/nimbus_verified_proxy/rpc/rpc_eth_api.nim b/nimbus_verified_proxy/rpc/rpc_eth_api.nim index 5b3f3cd48e..860b7c05ab 100644 --- a/nimbus_verified_proxy/rpc/rpc_eth_api.nim +++ b/nimbus_verified_proxy/rpc/rpc_eth_api.nim @@ -103,7 +103,7 @@ proc walkBlocks( for i in 0 ..< sourceNum - targetNum: # TODO: use a verified hash cache let blk = await self.rpcClient.eth_getBlockByHash(nextHash, false) - info "getting next block", hash=nextHash, number=blk.number, remaining=sourceNum-base.BlockNumber(distinctBase(blk.number)) + info "getting next block", hash=nextHash, number=blk.number, remaining=distinctBase(blk.number) - targetNum if blk.parentHash == targetHash: return true @@ -131,6 +131,13 @@ proc getHeaderByHash( let blk = await self.rpcClient.eth_getBlockByHash(blockHash, false) let header = convHeader(blk) + # verify header hash + if header.rlpHash != blk.hash: + raise newException(ValueError, "hashed block header doesn't match with blk.hash(downloaded)") + + if blockHash != blk.hash: + raise newException(ValueError, "the blk.hash(downloaded) doesn't match with the provided hash") + # walk blocks backwards(time) from source to target let isLinked = await self.walkBlocks(earliestHeader.number, header.number, earliestHeader.parentHash, blockHash) @@ -159,6 +166,13 @@ proc getHeaderByTag( let blk = await self.rpcClient.eth_getBlockByNumber(blockTag, false) let header = convHeader(blk) +# verify header hash + if header.rlpHash != blk.hash: + raise newException(ValueError, "hashed block header doesn't match with blk.hash(downloaded)") + + if blockHash != blk.hash: + raise newException(ValueError, "the blk.hash(downloaded) doesn't match with the provided hash") + # walk blocks backwards(time) from source to target let isLinked = await self.walkBlocks(earliestHeader.number, header.number, earliestHeader.parentHash, blk.hash) From 1d5a061128ddec6bf2ca6a2dedc3fb6b09a871f8 Mon Sep 17 00:00:00 2001 From: chirag-parmar Date: Wed, 19 Mar 2025 14:59:08 +0530 Subject: [PATCH 07/18] reorg --- nimbus_verified_proxy/header_store.nim | 2 +- .../nimbus_verified_proxy.nim | 1 + nimbus_verified_proxy/rpc/accounts.nim | 158 +++++++++ nimbus_verified_proxy/rpc/blocks.nim | 162 ++++++++++ nimbus_verified_proxy/rpc/rpc_eth_api.nim | 301 +++--------------- nimbus_verified_proxy/types.nim | 22 ++ 6 files changed, 388 insertions(+), 258 deletions(-) create mode 100644 nimbus_verified_proxy/rpc/accounts.nim create mode 100644 nimbus_verified_proxy/rpc/blocks.nim create mode 100644 nimbus_verified_proxy/types.nim diff --git a/nimbus_verified_proxy/header_store.nim b/nimbus_verified_proxy/header_store.nim index 670db20d5e..56a8d3ac66 100644 --- a/nimbus_verified_proxy/header_store.nim +++ b/nimbus_verified_proxy/header_store.nim @@ -14,7 +14,7 @@ import std/tables, beacon_chain/spec/beaconstate, beacon_chain/spec/datatypes/[phase0, altair, bellatrix], - beacon_chain/[light_client, nimbus_binary_common, version], + beacon_chain/[light_client, nimbus_binary_common], beacon_chain/el/engine_api_conversions, minilru, results diff --git a/nimbus_verified_proxy/nimbus_verified_proxy.nim b/nimbus_verified_proxy/nimbus_verified_proxy.nim index 6e4bfb14b4..233fe33736 100644 --- a/nimbus_verified_proxy/nimbus_verified_proxy.nim +++ b/nimbus_verified_proxy/nimbus_verified_proxy.nim @@ -23,6 +23,7 @@ import beacon_chain/[light_client, nimbus_binary_common, version], ../execution_chain/rpc/cors, ./rpc/rpc_eth_api, + ./types, ./nimbus_verified_proxy_conf, ./header_store diff --git a/nimbus_verified_proxy/rpc/accounts.nim b/nimbus_verified_proxy/rpc/accounts.nim new file mode 100644 index 0000000000..4f4c89ec7f --- /dev/null +++ b/nimbus_verified_proxy/rpc/accounts.nim @@ -0,0 +1,158 @@ +# nimbus_verified_proxy +# Copyright (c) 2022-2024 Status Research & Development GmbH +# Licensed and distributed under either of +# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT). +# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0). +# at your option. This file may not be copied, modified, or distributed except according to those terms. + +{.push raises: [].} + +import + std/sequtils, + stint, + results, + chronicles, + eth/common/[base_rlp, accounts_rlp, hashes_rlp], + ../../execution_chain/beacon/web3_eth_conv, + eth/common/addresses, + eth/common/eth_types_rlp, + eth/trie/[hexary_proof_verification], + json_rpc/[rpcproxy, rpcserver, rpcclient], + web3/[primitives, eth_api_types, eth_api], + ../types, + ../header_store + +export results, stint, hashes_rlp, accounts_rlp, eth_api_types + +template rpcClient(vp: VerifiedRpcProxy): RpcClient = + vp.proxy.getClient() + +proc getAccountFromProof( + stateRoot: Hash32, + accountAddress: Address, + accountBalance: UInt256, + accountNonce: Quantity, + accountCodeHash: Hash32, + accountStorageRoot: Hash32, + mptNodes: seq[RlpEncodedBytes], +): Result[Account, string] = + let + mptNodesBytes = mptNodes.mapIt(distinctBase(it)) + acc = Account( + nonce: distinctBase(accountNonce), + balance: accountBalance, + storageRoot: accountStorageRoot, + codeHash: accountCodeHash, + ) + accountEncoded = rlp.encode(acc) + accountKey = toSeq(keccak256((accountAddress.data)).data) + + let proofResult = verifyMptProof(mptNodesBytes, stateRoot, accountKey, accountEncoded) + + case proofResult.kind + of MissingKey: + return ok(EMPTY_ACCOUNT) + of ValidProof: + return ok(acc) + of InvalidProof: + return err(proofResult.errorMsg) + +proc getStorageFromProof( + account: Account, storageProof: StorageProof +): Result[UInt256, string] = + let + storageMptNodes = storageProof.proof.mapIt(distinctBase(it)) + key = toSeq(keccak256(toBytesBE(storageProof.key)).data) + encodedValue = rlp.encode(storageProof.value) + proofResult = + verifyMptProof(storageMptNodes, account.storageRoot, key, encodedValue) + + case proofResult.kind + of MissingKey: + return ok(UInt256.zero) + of ValidProof: + return ok(storageProof.value) + of InvalidProof: + return err(proofResult.errorMsg) + +proc getStorageFromProof( + stateRoot: Hash32, requestedSlot: UInt256, proof: ProofResponse +): Result[UInt256, string] = + let account = + ?getAccountFromProof( + stateRoot, proof.address, proof.balance, proof.nonce, proof.codeHash, + proof.storageHash, proof.accountProof, + ) + + if account.storageRoot == EMPTY_ROOT_HASH: + # valid account with empty storage, in that case getStorageAt + # return 0 value + return ok(u256(0)) + + if len(proof.storageProof) != 1: + return err("no storage proof for requested slot") + + let storageProof = proof.storageProof[0] + + if len(storageProof.proof) == 0: + return err("empty mpt proof for account with not empty storage") + + if storageProof.key != requestedSlot: + return err("received proof for invalid slot") + + getStorageFromProof(account, storageProof) + +proc getAccount*( + lcProxy: VerifiedRpcProxy, + address: Address, + blockNumber: base.BlockNumber, + stateRoot: Root): Future[Account] {.async: (raises: [ValueError, CatchableError]).} = + let + proof = await lcProxy.rpcClient.eth_getProof(address, @[], blockId(blockNumber)) + account = getAccountFromProof( + stateRoot, proof.address, proof.balance, proof.nonce, proof.codeHash, + proof.storageHash, proof.accountProof, + ).valueOr: + raise newException(ValueError, error) + + return account + +proc getCode*( + lcProxy: VerifiedRpcProxy, + address: Address, + blockNumber: base.BlockNumber, + stateRoot: Root): Future[seq[byte]] {.async: (raises: [ValueError, CatchableError]).} = + # get verified account details for the address at blockNumber + let account = await lcProxy.getAccount(address, blockNumber, stateRoot) + + # if the account does not have any code, return empty hex data + if account.codeHash == EMPTY_CODE_HASH: + return @[] + + info "Forwarding eth_getCode", blockNumber + + let code = await lcProxy.rpcClient.eth_getCode(address, blockId(blockNumber)) + + # verify the byte code. since we verified the account against + # the state root we just need to verify the code hash + if account.codeHash == keccak256(code): + return code + else: + raise newException(ValueError, "received code doesn't match the account code hash") + +proc getStorageAt*( + lcProxy: VerifiedRpcProxy, + address: Address, + slot: UInt256, + blockNumber: base.BlockNumber, + stateRoot: Root +): Future[UInt256] {.async: (raises: [ValueError, CatchableError]).} = + + info "Forwarding eth_getStorageAt", blockNumber + + let + proof = await lcProxy.rpcClient.eth_getProof(address, @[slot], blockId(blockNumber)) + slotValue = getStorageFromProof(stateRoot, slot, proof).valueOr: + raise newException(ValueError, error) + + slotValue diff --git a/nimbus_verified_proxy/rpc/blocks.nim b/nimbus_verified_proxy/rpc/blocks.nim new file mode 100644 index 0000000000..b35539b853 --- /dev/null +++ b/nimbus_verified_proxy/rpc/blocks.nim @@ -0,0 +1,162 @@ +# nimbus_verified_proxy +# Copyright (c) 2022-2024 Status Research & Development GmbH +# Licensed and distributed under either of +# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT). +# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0). +# at your option. This file may not be copied, modified, or distributed except according to those terms. + +{.push raises: [].} + +import + std/strutils, + results, + chronicles, + web3/[primitives, eth_api_types, eth_api], + json_rpc/[rpcproxy, rpcserver, rpcclient], + eth/common/addresses, + eth/common/eth_types_rlp, + ../../execution_chain/beacon/web3_eth_conv, + ../types, + ../header_store + +type BlockTag* = eth_api_types.RtBlockIdentifier + +template rpcClient(vp: VerifiedRpcProxy): RpcClient = + vp.proxy.getClient() + +proc resolveTag( + self: VerifiedRpcProxy, blockTag: BlockTag +): base.BlockNumber {.raises: [ValueError].} = + if blockTag.kind == bidAlias: + let tag = blockTag.alias.toLowerAscii() + case tag + of "latest": + let hLatest = self.headerStore.latest() + if hLatest.isSome: + return hLatest.get().number + else: + raise newException(ValueError, "Couldn't get the latest block number from header store") + else: + raise newException(ValueError, "No support for block tag " & $blockTag) + else: + return base.BlockNumber(distinctBase(blockTag.number)) + +proc convHeader(blk: BlockObject): Header = + let + nonce = if blk.nonce.isSome: blk.nonce.get + else: default(Bytes8) + + return Header( + parentHash: blk.parentHash, + ommersHash: blk.sha3Uncles, + coinbase: blk.miner, + stateRoot: blk.stateRoot, + transactionsRoot: blk.transactionsRoot, + receiptsRoot: blk.receiptsRoot, + logsBloom: blk.logsBloom, + difficulty: blk.difficulty, + number: base.BlockNumber(distinctBase(blk.number)), + gasLimit: GasInt(blk.gasLimit.uint64), + gasUsed: GasInt(blk.gasUsed.uint64), + timestamp: ethTime(blk.timestamp), + extraData: seq[byte](blk.extraData), + mixHash: Bytes32(distinctBase(blk.mixHash)), + nonce: nonce, + baseFeePerGas: blk.baseFeePerGas, + withdrawalsRoot: blk.withdrawalsRoot, + blobGasUsed: blk.blobGasUsed.u64, + excessBlobGas: blk.excessBlobGas.u64, + parentBeaconBlockRoot: blk.parentBeaconBlockRoot, + requestsHash: blk.requestsHash + ) + +proc walkBlocks( + self: VerifiedRpcProxy, + sourceNum: base.BlockNumber, + targetNum: base.BlockNumber, + sourceHash: Hash32, + targetHash: Hash32): Future[bool] {.async: (raises: [ValueError, CatchableError]).} = + + var nextHash = sourceHash + info "starting block walk to verify", blockHash=targetHash + + # TODO: use batch calls to get all blocks at once by number + for i in 0 ..< sourceNum - targetNum: + # TODO: use a verified hash cache + let blk = await self.rpcClient.eth_getBlockByHash(nextHash, false) + info "getting next block", hash=nextHash, number=blk.number, remaining=distinctBase(blk.number) - targetNum + + if blk.parentHash == targetHash: + return true + + nextHash = blk.parentHash + + return false + +proc getHeaderByHash*( + self: VerifiedRpcProxy, blockHash: Hash32 +): Future[Header] {.async: (raises: [ValueError, CatchableError]).} = + let cachedHeader = self.headerStore.get(blockHash) + + if cachedHeader.isNone(): + debug "did not find the header in the cache", blockHash=blockHash + else: + return cachedHeader.get() + + # get the source block + let earliestHeader = self.headerStore.earliest.valueOr: + raise newException(ValueError, "Syncing") + + # get the target block + let blk = await self.rpcClient.eth_getBlockByHash(blockHash, false) + let header = convHeader(blk) + + # verify header hash + if header.rlpHash != blk.hash: + raise newException(ValueError, "hashed block header doesn't match with blk.hash(downloaded)") + + if blockHash != blk.hash: + raise newException(ValueError, "the blk.hash(downloaded) doesn't match with the provided hash") + + # walk blocks backwards(time) from source to target + let isLinked = await self.walkBlocks(earliestHeader.number, header.number, earliestHeader.parentHash, blockHash) + + if not isLinked: + raise newException(ValueError, "the requested block is not part of the canonical chain") + + return header + +proc getHeaderByTag*( + self: VerifiedRpcProxy, blockTag: BlockTag +): Future[Header] {.async: (raises: [ValueError, CatchableError]).} = + let + n = self.resolveTag(blockTag) + cachedHeader = self.headerStore.get(n) + + if cachedHeader.isNone(): + debug "did not find the header in the cache", blockTag=blockTag + else: + return cachedHeader.get() + + # get the source block + let earliestHeader = self.headerStore.earliest.valueOr: + raise newException(ValueError, "Syncing") + + # get the target block + let blk = await self.rpcClient.eth_getBlockByNumber(blockTag, false) + let header = convHeader(blk) + + # verify header hash + if header.rlpHash != blk.hash: + raise newException(ValueError, "hashed block header doesn't match with blk.hash(downloaded)") + + if n != header.number: + raise newException(ValueError, "the downloaded block number doesn't match with the requested block number") + + # walk blocks backwards(time) from source to target + let isLinked = await self.walkBlocks(earliestHeader.number, header.number, earliestHeader.parentHash, blk.hash) + + if not isLinked: + raise newException(ValueError, "the requested block is not part of the canonical chain") + + return header diff --git a/nimbus_verified_proxy/rpc/rpc_eth_api.nim b/nimbus_verified_proxy/rpc/rpc_eth_api.nim index 860b7c05ab..110eb040dc 100644 --- a/nimbus_verified_proxy/rpc/rpc_eth_api.nim +++ b/nimbus_verified_proxy/rpc/rpc_eth_api.nim @@ -21,272 +21,64 @@ import ../../execution_chain/transaction/call_evm, ../../execution_chain/[evm/types, evm/state], ../validate_proof, - ../header_store + ../header_store, + ../types, + ./blocks, + ./accounts logScope: topics = "verified_proxy" -type - VerifiedRpcProxy* = ref object - proxy: RpcProxy - headerStore: HeaderStore - chainId: Quantity - - BlockTag = eth_api_types.RtBlockIdentifier - -template checkPreconditions(proxy: VerifiedRpcProxy) = - if proxy.headerStore.isEmpty(): - raise newException(ValueError, "Syncing") - -template rpcClient(lcProxy: VerifiedRpcProxy): RpcClient = - lcProxy.proxy.getClient() - -proc resolveTag( - self: VerifiedRpcProxy, blockTag: BlockTag -): base.BlockNumber {.raises: [ValueError].} = - self.checkPreconditions() - - if blockTag.kind == bidAlias: - let tag = blockTag.alias.toLowerAscii() - case tag - of "latest": - let hLatest = self.headerStore.latest() - if hLatest.isSome: - return hLatest.get().number - else: - raise newException(ValueError, "No block stored for given tag " & $blockTag) - else: - raise newException(ValueError, "No support for block tag " & $blockTag) - else: - return base.BlockNumber(distinctBase(blockTag.number)) - -proc convHeader(blk: BlockObject): Header = - let - nonce = if blk.nonce.isSome: blk.nonce.get - else: default(Bytes8) - - return Header( - parentHash: blk.parentHash, - ommersHash: blk.sha3Uncles, - coinbase: blk.miner, - stateRoot: blk.stateRoot, - transactionsRoot: blk.transactionsRoot, - receiptsRoot: blk.receiptsRoot, - logsBloom: blk.logsBloom, - difficulty: blk.difficulty, - number: base.BlockNumber(distinctBase(blk.number)), - gasLimit: GasInt(blk.gasLimit.uint64), - gasUsed: GasInt(blk.gasUsed.uint64), - timestamp: ethTime(blk.timestamp), - extraData: seq[byte](blk.extraData), - mixHash: Bytes32(distinctBase(blk.mixHash)), - nonce: nonce, - baseFeePerGas: blk.baseFeePerGas, - withdrawalsRoot: blk.withdrawalsRoot, - blobGasUsed: blk.blobGasUsed.u64, - excessBlobGas: blk.excessBlobGas.u64, - parentBeaconBlockRoot: blk.parentBeaconBlockRoot, - requestsHash: blk.requestsHash - ) - -proc walkBlocks( - self: VerifiedRpcProxy, - sourceNum: base.BlockNumber, - targetNum: base.BlockNumber, - sourceHash: Hash32, - targetHash: Hash32): Future[bool] {.async: (raises: [ValueError, CatchableError]).} = - - var nextHash = sourceHash - info "starting block walk to verify", blockHash=targetHash - - # TODO: use batch calls to get all blocks at once by number - for i in 0 ..< sourceNum - targetNum: - # TODO: use a verified hash cache - let blk = await self.rpcClient.eth_getBlockByHash(nextHash, false) - info "getting next block", hash=nextHash, number=blk.number, remaining=distinctBase(blk.number) - targetNum - - if blk.parentHash == targetHash: - return true - - nextHash = blk.parentHash - - return false - -proc getHeaderByHash( - self: VerifiedRpcProxy, blockHash: Hash32 -): Future[Header] {.async: (raises: [ValueError, CatchableError]).} = - self.checkPreconditions() - let cachedHeader = self.headerStore.get(blockHash) - - if cachedHeader.isNone(): - debug "did not find the header in the cache", blockHash=blockHash - else: - return cachedHeader.get() - - # get the source block - let earliestHeader = self.headerStore.earliest.valueOr: - raise newException(ValueError, "Syncing") - - # get the target block - let blk = await self.rpcClient.eth_getBlockByHash(blockHash, false) - let header = convHeader(blk) - - # verify header hash - if header.rlpHash != blk.hash: - raise newException(ValueError, "hashed block header doesn't match with blk.hash(downloaded)") - - if blockHash != blk.hash: - raise newException(ValueError, "the blk.hash(downloaded) doesn't match with the provided hash") - - # walk blocks backwards(time) from source to target - let isLinked = await self.walkBlocks(earliestHeader.number, header.number, earliestHeader.parentHash, blockHash) - - if not isLinked: - raise newException(ValueError, "the requested block is not part of the canonical chain") - - return header - -proc getHeaderByTag( - self: VerifiedRpcProxy, blockTag: BlockTag -): Future[Header] {.async: (raises: [ValueError, CatchableError]).} = - let - n = self.resolveTag(blockTag) - cachedHeader = self.headerStore.get(n) - - if cachedHeader.isNone(): - debug "did not find the header in the cache", blockTag=blockTag - else: - return cachedHeader.get() - - # get the source block - let earliestHeader = self.headerStore.earliest.valueOr: - raise newException(ValueError, "Syncing") - - # get the target block - let blk = await self.rpcClient.eth_getBlockByNumber(blockTag, false) - let header = convHeader(blk) - -# verify header hash - if header.rlpHash != blk.hash: - raise newException(ValueError, "hashed block header doesn't match with blk.hash(downloaded)") - - if blockHash != blk.hash: - raise newException(ValueError, "the blk.hash(downloaded) doesn't match with the provided hash") - - # walk blocks backwards(time) from source to target - let isLinked = await self.walkBlocks(earliestHeader.number, header.number, earliestHeader.parentHash, blk.hash) - - if not isLinked: - raise newException(ValueError, "the requested block is not part of the canonical chain") - - return header - -proc getAccount( - lcProxy: VerifiedRpcProxy, - address: addresses.Address, - blockNumber: base.BlockNumber, - stateRoot: Root -): Future[Account] {.async: (raises: [ValueError, CatchableError]).} = - let - proof = await lcProxy.rpcClient.eth_getProof(address, @[], blockId(blockNumber)) - account = getAccountFromProof( - stateRoot, proof.address, proof.balance, proof.nonce, proof.codeHash, - proof.storageHash, proof.accountProof, - ).valueOr: - raise newException(ValueError, error) - - return account - -proc getCode( - lcProxy: VerifiedRpcProxy, - address: addresses.Address, - blockNumber: base.BlockNumber, - stateRoot: Root -): Future[seq[byte]] {.async: (raises: [ValueError, CatchableError]).} = - # get verified account details for the address at blockNumber - let account = await lcProxy.getAccount(address, blockNumber, stateRoot) - - # if the account does not have any code, return empty hex data - if account.codeHash == EMPTY_CODE_HASH: - return @[] - - info "Forwarding eth_getCode", blockNumber - - let code = await lcProxy.rpcClient.eth_getCode(address, blockId(blockNumber)) - - # verify the byte code. since we verified the account against - # the state root we just need to verify the code hash - if isValidCode(account, code): - return code - else: - raise newException(ValueError, "received code doesn't match the account code hash") - -proc getStorageAt( - lcProxy: VerifiedRpcProxy, - address: addresses.Address, - slot: UInt256, - blockNumber: base.BlockNumber, - stateRoot: Root -): Future[UInt256] {.async: (raises: [ValueError, CatchableError]).} = - - info "Forwarding eth_getStorageAt", blockNumber - - let - proof = await lcProxy.rpcClient.eth_getProof(address, @[slot], blockId(blockNumber)) - slotValue = getStorageData(stateRoot, slot, proof).valueOr: - raise newException(ValueError, error) - - slotValue - -proc installEthApiHandlers*(lcProxy: VerifiedRpcProxy) = - lcProxy.proxy.rpc("eth_chainId") do() -> UInt256: - lcProxy.chainId - - lcProxy.proxy.rpc("eth_blockNumber") do() -> Quantity: - # Returns the number of the most recent block seen by the light client. - lcProxy.checkPreconditions() +template rpcClient*(vp: VerifiedRpcProxy): RpcClient = + vp.proxy.getClient() + +proc installEthApiHandlers*(vp: VerifiedRpcProxy) = + vp.proxy.rpc("eth_chainId") do() -> Quantity: + vp.chainId - let hLatest = lcProxy.headerStore.latest() + # eth_blockNumber - get latest tag from header store + vp.proxy.rpc("eth_blockNumber") do() -> Quantity: + # Returns the number of the most recent block seen by the light client. + let hLatest = vp.headerStore.latest() if hLatest.isNone: raise newException(ValueError, "Syncing") return Quantity(hLatest.get().number) - lcProxy.proxy.rpc("eth_getBalance") do( + vp.proxy.rpc("eth_getBalance") do( address: addresses.Address, blockTag: BlockTag ) -> UInt256: let - header = await lcProxy.getHeaderByTag(blockTag) - account = await lcProxy.getAccount(address, header.number, header.stateRoot) + header = await vp.getHeaderByTag(blockTag) + account = await vp.getAccount(address, header.number, header.stateRoot) account.balance - lcProxy.proxy.rpc("eth_getStorageAt") do( + vp.proxy.rpc("eth_getStorageAt") do( address: addresses.Address, slot: UInt256, blockTag: BlockTag ) -> UInt256: - let header = await lcProxy.getHeaderByTag(blockTag) - + let header = await vp.getHeaderByTag(blockTag) - await lcProxy.getStorageAt(address, slot, header.number, header.stateRoot) + await vp.getStorageAt(address, slot, header.number, header.stateRoot) - lcProxy.proxy.rpc("eth_getTransactionCount") do( + vp.proxy.rpc("eth_getTransactionCount") do( address: addresses.Address, blockTag: BlockTag ) -> Quantity: let - header = await lcProxy.getHeaderByTag(blockTag) - account = await lcProxy.getAccount(address, header.number, header.stateRoot) + header = await vp.getHeaderByTag(blockTag) + account = await vp.getAccount(address, header.number, header.stateRoot) Quantity(account.nonce) - lcProxy.proxy.rpc("eth_getCode") do( + vp.proxy.rpc("eth_getCode") do( address: addresses.Address, blockTag: BlockTag ) -> seq[byte]: let - header = await lcProxy.getHeaderByTag(blockTag) + header = await vp.getHeaderByTag(blockTag) - await lcProxy.getCode(address,header.number, header.stateRoot) + await vp.getCode(address,header.number, header.stateRoot) - lcProxy.proxy.rpc("eth_call") do( + vp.proxy.rpc("eth_call") do( args: TransactionArgs, blockTag: BlockTag ) -> seq[byte]: @@ -295,13 +87,13 @@ proc installEthApiHandlers*(lcProxy: VerifiedRpcProxy) = let to = if args.to.isSome(): args.to.get() else: raise newException(ValueError, "contract address missing in transaction args") - header = await lcProxy.getHeaderByTag(blockTag) - code = await lcProxy.getCode(to, header.number, header.stateRoot) + header = await vp.getHeaderByTag(blockTag) + code = await vp.getCode(to, header.number, header.stateRoot) # 2. get all storage locations that are accessed let - parent = await lcProxy.getHeaderByHash(header.parentHash) - accessListResult = await lcProxy.rpcClient.eth_createAccessList(args, blockId(header.number)) + parent = await vp.getHeaderByHash(header.parentHash) + accessListResult = await vp.rpcClient.eth_createAccessList(args, blockId(header.number)) accessList = if not accessListResult.error.isSome(): accessListResult.accessList else: raise newException(ValueError, "couldn't get an access list for eth call") @@ -316,8 +108,8 @@ proc installEthApiHandlers*(lcProxy: VerifiedRpcProxy) = for accessPair in accessList: let accountAddr = accessPair.address - acc = await lcProxy.getAccount(accountAddr, header.number, header.stateRoot) - accCode = await lcProxy.getCode(accountAddr, header.number, header.stateRoot) + acc = await vp.getAccount(accountAddr, header.number, header.stateRoot) + accCode = await vp.getCode(accountAddr, header.number, header.stateRoot) db.setNonce(accountAddr, acc.nonce) db.setBalance(accountAddr, acc.balance) @@ -326,7 +118,7 @@ proc installEthApiHandlers*(lcProxy: VerifiedRpcProxy) = for slot in accessPair.storageKeys: let slotInt = UInt256.fromHex(toHex(slot)) - slotValue = await lcProxy.getStorageAt(accountAddr, slotInt, header.number, header.stateRoot) + slotValue = await vp.getStorageAt(accountAddr, slotInt, header.number, header.stateRoot) db.setStorage(accountAddr, slotInt, slotValue) db.persist(clearEmptyAccount = false) # settle accounts storage @@ -339,26 +131,21 @@ proc installEthApiHandlers*(lcProxy: VerifiedRpcProxy) = # TODO: # Following methods are forwarded directly to the web3 provider and therefore # are not validated in any way. - lcProxy.proxy.registerProxyMethod("net_version") - lcProxy.proxy.registerProxyMethod("eth_sendRawTransaction") - lcProxy.proxy.registerProxyMethod("eth_getTransactionReceipt") + vp.proxy.registerProxyMethod("net_version") + vp.proxy.registerProxyMethod("eth_sendRawTransaction") + vp.proxy.registerProxyMethod("eth_getTransactionReceipt") # TODO currently we do not handle fullTransactions flag. It require updates on # nim-web3 side -# lcProxy.proxy.rpc("eth_getBlockByNumber") do( +# vp.proxy.rpc("eth_getBlockByNumber") do( # blockTag: BlockTag, fullTransactions: bool # ) -> Opt[BlockObject]: -# lcProxy.getBlockByTag(blockTag) +# vp.getBlockByTag(blockTag) # -# lcProxy.proxy.rpc("eth_getBlockByHash") do( +# vp.proxy.rpc("eth_getBlockByHash") do( # blockHash: Hash32, fullTransactions: bool # ) -> Opt[BlockObject]: -# lcProxy.blockCache.getPayloadByHash(blockHash) - -proc new*( - T: type VerifiedRpcProxy, proxy: RpcProxy, headerStore: HeaderStore, chainId: Quantity -): T = - VerifiedRpcProxy(proxy: proxy, headerStore: headerStore, chainId: chainId) +# vp.blockCache.getPayloadByHash(blockHash) # Used to be in eth1_monitor.nim; not sure why it was deleted, # so I copied it here. --Adam @@ -396,13 +183,13 @@ template awaitWithRetries*[T]( read(f) -proc verifyChaindId*(p: VerifiedRpcProxy): Future[void] {.async.} = - let localId = p.chainId +proc verifyChaindId*(vp: VerifiedRpcProxy): Future[void] {.async.} = + let localId = vp.chainId # retry 2 times, if the data provider fails despite the re-tries, propagate # exception to the caller. let providerId = - awaitWithRetries(p.rpcClient.eth_chainId(), retries = 2, timeout = seconds(30)) + awaitWithRetries(vp.rpcClient.eth_chainId(), retries = 2, timeout = seconds(30)) # This is a chain/network mismatch error between the Nimbus verified proxy and # the application using it. Fail fast to avoid misusage. The user must fix diff --git a/nimbus_verified_proxy/types.nim b/nimbus_verified_proxy/types.nim new file mode 100644 index 0000000000..01cfaaa2d0 --- /dev/null +++ b/nimbus_verified_proxy/types.nim @@ -0,0 +1,22 @@ +# nimbus_verified_proxy +# Copyright (c) 2022-2024 Status Research & Development GmbH +# Licensed and distributed under either of +# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT). +# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0). +# at your option. This file may not be copied, modified, or distributed except according to those terms. + +import + json_rpc/[rpcproxy], + web3/[primitives, eth_api_types, eth_api], + ./header_store + +type + VerifiedRpcProxy* = ref object + proxy*: RpcProxy + headerStore*: HeaderStore + chainId*: Quantity + +proc new*( + T: type VerifiedRpcProxy, proxy: RpcProxy, headerStore: HeaderStore, chainId: Quantity +): T = + VerifiedRpcProxy(proxy: proxy, headerStore: headerStore, chainId: chainId) From 06f9abcadfde145d551a7e7702c954ebe0974ce3 Mon Sep 17 00:00:00 2001 From: chirag-parmar Date: Tue, 25 Mar 2025 21:03:07 +0530 Subject: [PATCH 08/18] transactions support --- nimbus_verified_proxy/rpc/blocks.nim | 83 ++++++++++++++- nimbus_verified_proxy/rpc/rpc_eth_api.nim | 93 +++++++++++++--- nimbus_verified_proxy/rpc/transactions.nim | 118 +++++++++++++++++++++ 3 files changed, 279 insertions(+), 15 deletions(-) create mode 100644 nimbus_verified_proxy/rpc/transactions.nim diff --git a/nimbus_verified_proxy/rpc/blocks.nim b/nimbus_verified_proxy/rpc/blocks.nim index b35539b853..1e745afbaa 100644 --- a/nimbus_verified_proxy/rpc/blocks.nim +++ b/nimbus_verified_proxy/rpc/blocks.nim @@ -15,9 +15,11 @@ import json_rpc/[rpcproxy, rpcserver, rpcclient], eth/common/addresses, eth/common/eth_types_rlp, + eth/trie/[hexary, ordered_trie, db, trie_defs], ../../execution_chain/beacon/web3_eth_conv, ../types, - ../header_store + ../header_store, + ./transactions type BlockTag* = eth_api_types.RtBlockIdentifier @@ -93,6 +95,83 @@ proc walkBlocks( return false +proc getBlockByHash*( + self: VerifiedRpcProxy, blockHash: Hash32, fullTransactions: bool +): Future[BlockObject] {.async: (raises: [ValueError, CatchableError]).} = + # get the target block + let blk = await self.rpcClient.eth_getBlockByHash(blockHash, fullTransactions) + let header = convHeader(blk) + + # verify header hash + if header.rlpHash != blockHash: + raise newException(ValueError, "hashed block header doesn't match with blk.hash(downloaded)") + + if blockHash != blk.hash: + raise newException(ValueError, "the downloaded block hash doesn't match with the requested hash") + + let earliestHeader = self.headerStore.earliest.valueOr: + raise newException(ValueError, "Syncing") + + # walk blocks backwards(time) from source to target + let isLinked = await self.walkBlocks(earliestHeader.number, header.number, earliestHeader.parentHash, blockHash) + + if not isLinked: + raise newException(ValueError, "the requested block is not part of the canonical chain") + + # verify transactions + if fullTransactions: + let verified = verifyTransactions(header.transactionsRoot, blk.transactions).valueOr: + raise newException(ValueError, "error while verifying transactions root") + if not verified: + raise newException(ValueError, "transactions within the block do not yield the same transaction root") + + # verify withdrawals + if blk.withdrawals.isSome(): + if blk.withdrawalsRoot.get() != orderedTrieRoot(blk.withdrawals.get()): + raise newException(ValueError, "withdrawals within the block do not yield the same withdrawals root") + + return blk + +proc getBlockByTag*( + self: VerifiedRpcProxy, blockTag: BlockTag, fullTransactions: bool +): Future[BlockObject] {.async: (raises: [ValueError, CatchableError]).} = + let n = self.resolveTag(blockTag) + + # get the target block + let blk = await self.rpcClient.eth_getBlockByNumber(blockTag, false) + let header = convHeader(blk) + + # verify header hash + if header.rlpHash != blk.hash: + raise newException(ValueError, "hashed block header doesn't match with blk.hash(downloaded)") + + if n != header.number: + raise newException(ValueError, "the downloaded block number doesn't match with the requested block number") + + # get the source block + let earliestHeader = self.headerStore.earliest.valueOr: + raise newException(ValueError, "Syncing") + + # walk blocks backwards(time) from source to target + let isLinked = await self.walkBlocks(earliestHeader.number, header.number, earliestHeader.parentHash, blk.hash) + + if not isLinked: + raise newException(ValueError, "the requested block is not part of the canonical chain") + + # verify transactions + if fullTransactions: + let verified = verifyTransactions(header.transactionsRoot, blk.transactions).valueOr: + raise newException(ValueError, "error while verifying transactions root") + if not verified: + raise newException(ValueError, "transactions within the block do not yield the same transaction root") + + # verify withdrawals + if blk.withdrawals.isSome(): + if blk.withdrawalsRoot.get() != orderedTrieRoot(blk.withdrawals.get()): + raise newException(ValueError, "withdrawals within the block do not yield the same withdrawals root") + + return blk + proc getHeaderByHash*( self: VerifiedRpcProxy, blockHash: Hash32 ): Future[Header] {.async: (raises: [ValueError, CatchableError]).} = @@ -129,7 +208,7 @@ proc getHeaderByHash*( proc getHeaderByTag*( self: VerifiedRpcProxy, blockTag: BlockTag ): Future[Header] {.async: (raises: [ValueError, CatchableError]).} = - let + let n = self.resolveTag(blockTag) cachedHeader = self.headerStore.get(n) diff --git a/nimbus_verified_proxy/rpc/rpc_eth_api.nim b/nimbus_verified_proxy/rpc/rpc_eth_api.nim index 110eb040dc..ff94d02fe8 100644 --- a/nimbus_verified_proxy/rpc/rpc_eth_api.nim +++ b/nimbus_verified_proxy/rpc/rpc_eth_api.nim @@ -24,7 +24,8 @@ import ../header_store, ../types, ./blocks, - ./accounts + ./accounts, + ./transactions logScope: topics = "verified_proxy" @@ -36,6 +37,84 @@ proc installEthApiHandlers*(vp: VerifiedRpcProxy) = vp.proxy.rpc("eth_chainId") do() -> Quantity: vp.chainId + vp.proxy.rpc("eth_getBlockByNumber") do( + blockTag: BlockTag, fullTransactions: bool + ) -> Opt[BlockObject]: + try: + let blk = await vp.getBlockByTag(blockTag, fullTransactions) + return Opt.some(blk) + except ValueError as e: + # should return Opt.none but we also want to transmit error related info + # return Opt.none(BlockObject) + raise newException(ValueError, e.msg) # raising an exception will return the error message + + vp.proxy.rpc("eth_getBlockByHash") do( + blockHash: Hash32, fullTransactions: bool + ) -> Opt[BlockObject]: + try: + let blk = await vp.getBlockByHash(blockHash, fullTransactions) + return Opt.some(blk) + except ValueError as e: + # should return Opt.none but we also want to transmit error related info + # return Opt.none(BlockObject) + raise newException(ValueError, e.msg) # raising an exception will return the error message + + vp.proxy.rpc("eth_getUncleCountByBlockNumber") do( + blockTag: BlockTag + ) -> Quantity: + let blk = await vp.getBlockByTag(blockTag, false) + return Quantity(blk.uncles.len()) + + vp.proxy.rpc("eth_getUncleCountByBlockHash") do( + blockHash: Hash32 + ) -> Quantity: + let blk = await vp.getBlockByHash(blockHash, false) + return Quantity(blk.uncles.len()) + + vp.proxy.rpc("eth_getBlockTransactionCountByNumber") do( + blockTag: BlockTag + ) -> Quantity: + let blk = await vp.getBlockByTag(blockTag, true) + return Quantity(blk.transactions.len) + + vp.proxy.rpc("eth_getBlockTransactionCountByHash") do( + blockHash: Hash32 + ) -> Quantity: + let blk = await vp.getBlockByHash(blockHash, true) + return Quantity(blk.transactions.len) + + vp.proxy.rpc("eth_getTransactionByBlockNumberAndIndex") do( + blockTag: BlockTag, index: Quantity + ) -> TransactionObject: + let blk = await vp.getBlockByTag(blockTag, true) + if distinctBase(index) >= uint64(blk.transactions.len): + raise newException(ValueError, "provided transaction index is outside bounds") + let x = blk.transactions[distinctBase(index)] + doAssert x.kind == tohTx + return x.tx + + vp.proxy.rpc("eth_getTransactionByBlockHashAndIndex") do( + blockHash: Hash32, index: Quantity + ) -> TransactionObject: + let blk = await vp.getBlockByHash(blockHash, true) + if distinctBase(index) >= uint64(blk.transactions.len): + raise newException(ValueError, "provided transaction index is outside bounds") + let x = blk.transactions[distinctBase(index)] + doAssert x.kind == tohTx + return x.tx + + vp.proxy.rpc("eth_getTransactionByHash") do( + txHash: Hash32 + ) -> TransactionObject: + let tx = await vp.rpcClient.eth_getTransactionByHash(txHash) + if tx.hash != txHash: + raise newException(ValueError, "the downloaded transaction hash doesn't match the requested transaction hash") + + if not checkTxHash(tx, txHash): + raise newException(ValueError, "the transaction doesn't hash to the provided hash") + + return tx + # eth_blockNumber - get latest tag from header store vp.proxy.rpc("eth_blockNumber") do() -> Quantity: # Returns the number of the most recent block seen by the light client. @@ -135,18 +214,6 @@ proc installEthApiHandlers*(vp: VerifiedRpcProxy) = vp.proxy.registerProxyMethod("eth_sendRawTransaction") vp.proxy.registerProxyMethod("eth_getTransactionReceipt") - # TODO currently we do not handle fullTransactions flag. It require updates on - # nim-web3 side -# vp.proxy.rpc("eth_getBlockByNumber") do( -# blockTag: BlockTag, fullTransactions: bool -# ) -> Opt[BlockObject]: -# vp.getBlockByTag(blockTag) -# -# vp.proxy.rpc("eth_getBlockByHash") do( -# blockHash: Hash32, fullTransactions: bool -# ) -> Opt[BlockObject]: -# vp.blockCache.getPayloadByHash(blockHash) - # Used to be in eth1_monitor.nim; not sure why it was deleted, # so I copied it here. --Adam template awaitWithRetries*[T]( diff --git a/nimbus_verified_proxy/rpc/transactions.nim b/nimbus_verified_proxy/rpc/transactions.nim new file mode 100644 index 0000000000..4e280f537f --- /dev/null +++ b/nimbus_verified_proxy/rpc/transactions.nim @@ -0,0 +1,118 @@ +# nimbus_verified_proxy +# Copyright (c) 2022-2024 Status Research & Development GmbH +# Licensed and distributed under either of +# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT). +# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0). +# at your option. This file may not be copied, modified, or distributed except according to those terms. + +{.push raises: [].} + +import + std/sequtils, + stint, + results, + chronicles, + eth/common/[base_rlp, transactions_rlp, receipts_rlp, hashes_rlp], + ../../execution_chain/beacon/web3_eth_conv, + eth/common/addresses, + eth/common/eth_types_rlp, + eth/trie/[hexary, ordered_trie, db, trie_defs], + json_rpc/[rpcproxy, rpcserver, rpcclient], + web3/[primitives, eth_api_types, eth_api], + ../types, + ../header_store + +export results, stint, hashes_rlp, accounts_rlp, eth_api_types + +template rpcClient(vp: VerifiedRpcProxy): RpcClient = + vp.proxy.getClient() + +template calcWithdrawalsRoot*(withdrawals: openArray[Withdrawal]): Root = + orderedTrieRoot(withdrawals) + +func vHashes(x: Opt[seq[Hash32]]): seq[VersionedHash] = + if x.isNone: return + else: x.get + +func authList(x: Opt[seq[Authorization]]): seq[Authorization] = + if x.isNone: return + else: x.get + +proc toTransaction(tx: TransactionObject): Transaction = + Transaction( + txType : tx.`type`.get(0.Web3Quantity).TxType, + chainId : tx.chainId.get(0.Web3Quantity).ChainId, + nonce : tx.nonce.AccountNonce, + gasPrice : tx.gasPrice.GasInt, + maxPriorityFeePerGas: tx.maxPriorityFeePerGas.get(0.Web3Quantity).GasInt, + maxFeePerGas : tx.maxFeePerGas.get(0.Web3Quantity).GasInt, + gasLimit : tx.gas.GasInt, + to : tx.to, + value : tx.value, + payload : tx.input, + accessList : tx.accessList.get(@[]), + maxFeePerBlobGas: tx.maxFeePerBlobGas.get(0.u256), + versionedHashes : vHashes(tx.blobVersionedHashes), + V : tx.v.uint64, + R : tx.r, + S : tx.s, + authorizationList: authList(tx.authorizationList), + ) + +proc toTransactions(txs: openArray[TxOrHash]): seq[Transaction] {.raises: [ValueError].} = + for x in txs: + if x.kind == tohTx: + result.add toTransaction(x.tx) + else: + raise newException(ValueError, "cannot construct a transaction trie using only txhashes") + +proc checkTxHash*(txObj: TransactionObject, txHash: Hash32): bool = + let tx = toTransaction(txObj) + if tx.rlpHash != txHash: + return false + + return true + +template toLog(lg: LogObject): Log = + Log( + address: lg.address, + topics: lg.topics, + data: lg.data + ) + +proc toLogs(logs: openArray[LogObject]): seq[Log] = + result = map(logs, proc(x: LogObject): Log = toLog(x)) + +proc toReceipt(rec: ReceiptObject): Receipt = + let isHash = if rec.status.isSome: false + else: true + + var status = false + if rec.status.isSome: + if rec.status.get() == 1.Quantity: + status = true + + return Receipt( + hash: rec.transactionHash, + isHash: isHash, + status: status, + cumulativeGasUsed: rec.cumulativeGasUsed.GasInt, + logs: toLogs(rec.logs), + logsBloom: rec.logsBloom, + receiptType: rec.`type`.get(0.Web3Quantity).ReceiptType + ) + +proc verifyTransactions*( + txRoot: Hash32, + transactions: seq[TxOrHash], +): Result[bool, string] = + + try: + let txs = toTransactions(transactions) + let rootHash = orderedTrieRoot(txs) + if rootHash == txRoot: + return ok(true) + except ValueError as e: + return err(e.msg) + + ok(false) From 8f62f3955b4a215afd9616926676963efa6b6ace Mon Sep 17 00:00:00 2001 From: chirag-parmar Date: Mon, 31 Mar 2025 14:51:05 +0530 Subject: [PATCH 09/18] receipt fix --- nimbus_verified_proxy/rpc/receipts.nim | 95 ++++++++++++++++++++++ nimbus_verified_proxy/rpc/rpc_eth_api.nim | 23 +++++- nimbus_verified_proxy/rpc/transactions.nim | 29 ------- 3 files changed, 117 insertions(+), 30 deletions(-) create mode 100644 nimbus_verified_proxy/rpc/receipts.nim diff --git a/nimbus_verified_proxy/rpc/receipts.nim b/nimbus_verified_proxy/rpc/receipts.nim new file mode 100644 index 0000000000..a98b5583ac --- /dev/null +++ b/nimbus_verified_proxy/rpc/receipts.nim @@ -0,0 +1,95 @@ +# nimbus_verified_proxy +# Copyright (c) 2022-2024 Status Research & Development GmbH +# Licensed and distributed under either of +# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT). +# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0). +# at your option. This file may not be copied, modified, or distributed except according to those terms. + +{.push raises: [].} + +import + std/sequtils, + stint, + results, + chronicles, + eth/common/[base_rlp, transactions_rlp, receipts_rlp, hashes_rlp], + ../../execution_chain/beacon/web3_eth_conv, + eth/common/addresses, + eth/common/eth_types_rlp, + eth/trie/[hexary, ordered_trie, db, trie_defs], + json_rpc/[rpcproxy, rpcserver, rpcclient], + web3/[primitives, eth_api_types, eth_api], + ../types, + ./blocks, + ../header_store + +export results, stint, hashes_rlp, accounts_rlp, eth_api_types + +template rpcClient(vp: VerifiedRpcProxy): RpcClient = + vp.proxy.getClient() + +template toLog(lg: LogObject): Log = + Log( + address: lg.address, + topics: lg.topics, + data: lg.data + ) + +proc toLogs(logs: openArray[LogObject]): seq[Log] = + result = map(logs, proc(x: LogObject): Log = toLog(x)) + +proc toReceipt(rec: ReceiptObject): Receipt = + let isHash = if rec.status.isSome: false + else: true + + var status = false + if rec.status.isSome: + if rec.status.get() == 1.Quantity: + status = true + + return Receipt( + hash: rec.transactionHash, + isHash: isHash, + status: status, + cumulativeGasUsed: rec.cumulativeGasUsed.GasInt, + logs: toLogs(rec.logs), + logsBloom: rec.logsBloom, + receiptType: rec.`type`.get(0.Web3Quantity).ReceiptType + ) + +proc toReceipts(recs: openArray[ReceiptObject]): seq[Receipt] = + for r in recs: + result.add(toReceipt(r)) + +proc getReceiptsByBlockTag*( + vp: VerifiedRpcProxy, blockTag: BlockTag +): Future[seq[ReceiptObject]] {.async: (raises: [ValueError, CatchableError]).} = + + let + header = await vp.getHeaderByTag(blockTag) + rxs = await vp.rpcClient.eth_getBlockReceipts(blockTag) + + if rxs.isSome(): + if orderedTrieRoot(toReceipts(rxs.get())) != header.receiptsRoot: + raise newException(ValueError, "downloaded receipts do not evaluate to the receipts root of the block") + else: + raise newException(ValueError, "error downloading the receipts") + + return rxs.get() + +proc getReceiptsByBlockHash*( + vp: VerifiedRpcProxy, blockHash: Hash32 +): Future[seq[ReceiptObject]] {.async: (raises: [ValueError, CatchableError]).} = + + let + header = await vp.getHeaderByHash(blockHash) + blockTag = BlockTag(RtBlockIdentifier(kind: bidNumber, number: Quantity(header.number))) + rxs = await vp.rpcClient.eth_getBlockReceipts(blockTag) + + if rxs.isSome(): + if orderedTrieRoot(toReceipts(rxs.get())) != header.receiptsRoot: + raise newException(ValueError, "downloaded receipts do not evaluate to the receipts root of the block") + else: + raise newException(ValueError, "error downloading the receipts") + + return rxs.get() diff --git a/nimbus_verified_proxy/rpc/rpc_eth_api.nim b/nimbus_verified_proxy/rpc/rpc_eth_api.nim index ff94d02fe8..f3238bc53d 100644 --- a/nimbus_verified_proxy/rpc/rpc_eth_api.nim +++ b/nimbus_verified_proxy/rpc/rpc_eth_api.nim @@ -25,7 +25,8 @@ import ../types, ./blocks, ./accounts, - ./transactions + ./transactions, + ./receipts logScope: topics = "verified_proxy" @@ -115,6 +116,26 @@ proc installEthApiHandlers*(vp: VerifiedRpcProxy) = return tx + # TODO: this method should also support block hashes. For that, the client call should also suupport block hashes + vp.proxy.rpc("eth_getBlockReceipts") do( + blockTag: BlockTag + ) -> Opt[seq[ReceiptObject]]: + let rxs = await vp.getReceiptsByBlockTag(blockTag) + return Opt.some(rxs) + + vp.proxy.rpc("eth_getTransactionReceipt") do( + txHash: Hash32 + ) -> ReceiptObject: + let + rx = await vp.rpcClient.eth_getTransactionReceipt(txHash) + rxs = await vp.getReceiptsByBlockHash(rx.blockHash) + + for r in rxs: + if r.transactionHash == txHash: + return r + + raise newException(ValueError, "receipt couldn't be verified") + # eth_blockNumber - get latest tag from header store vp.proxy.rpc("eth_blockNumber") do() -> Quantity: # Returns the number of the most recent block seen by the light client. diff --git a/nimbus_verified_proxy/rpc/transactions.nim b/nimbus_verified_proxy/rpc/transactions.nim index 4e280f537f..8a54687795 100644 --- a/nimbus_verified_proxy/rpc/transactions.nim +++ b/nimbus_verified_proxy/rpc/transactions.nim @@ -73,35 +73,6 @@ proc checkTxHash*(txObj: TransactionObject, txHash: Hash32): bool = return true -template toLog(lg: LogObject): Log = - Log( - address: lg.address, - topics: lg.topics, - data: lg.data - ) - -proc toLogs(logs: openArray[LogObject]): seq[Log] = - result = map(logs, proc(x: LogObject): Log = toLog(x)) - -proc toReceipt(rec: ReceiptObject): Receipt = - let isHash = if rec.status.isSome: false - else: true - - var status = false - if rec.status.isSome: - if rec.status.get() == 1.Quantity: - status = true - - return Receipt( - hash: rec.transactionHash, - isHash: isHash, - status: status, - cumulativeGasUsed: rec.cumulativeGasUsed.GasInt, - logs: toLogs(rec.logs), - logsBloom: rec.logsBloom, - receiptType: rec.`type`.get(0.Web3Quantity).ReceiptType - ) - proc verifyTransactions*( txRoot: Hash32, transactions: seq[TxOrHash], From e61f2e3e535713a6eeb6573dd73d1f429cbe1c9c Mon Sep 17 00:00:00 2001 From: chirag-parmar Date: Mon, 31 Mar 2025 17:27:57 +0530 Subject: [PATCH 10/18] rebase fix --- nimbus_verified_proxy/rpc/rpc_eth_api.nim | 2 +- nimbus_verified_proxy/rpc/transactions.nim | 2 +- nimbus_verified_proxy/types.nim | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/nimbus_verified_proxy/rpc/rpc_eth_api.nim b/nimbus_verified_proxy/rpc/rpc_eth_api.nim index f3238bc53d..2e998d0a09 100644 --- a/nimbus_verified_proxy/rpc/rpc_eth_api.nim +++ b/nimbus_verified_proxy/rpc/rpc_eth_api.nim @@ -35,7 +35,7 @@ template rpcClient*(vp: VerifiedRpcProxy): RpcClient = vp.proxy.getClient() proc installEthApiHandlers*(vp: VerifiedRpcProxy) = - vp.proxy.rpc("eth_chainId") do() -> Quantity: + vp.proxy.rpc("eth_chainId") do() -> UInt256: vp.chainId vp.proxy.rpc("eth_getBlockByNumber") do( diff --git a/nimbus_verified_proxy/rpc/transactions.nim b/nimbus_verified_proxy/rpc/transactions.nim index 8a54687795..a630d17207 100644 --- a/nimbus_verified_proxy/rpc/transactions.nim +++ b/nimbus_verified_proxy/rpc/transactions.nim @@ -41,7 +41,7 @@ func authList(x: Opt[seq[Authorization]]): seq[Authorization] = proc toTransaction(tx: TransactionObject): Transaction = Transaction( txType : tx.`type`.get(0.Web3Quantity).TxType, - chainId : tx.chainId.get(0.Web3Quantity).ChainId, + chainId : tx.chainId.get(0.u256), nonce : tx.nonce.AccountNonce, gasPrice : tx.gasPrice.GasInt, maxPriorityFeePerGas: tx.maxPriorityFeePerGas.get(0.Web3Quantity).GasInt, diff --git a/nimbus_verified_proxy/types.nim b/nimbus_verified_proxy/types.nim index 01cfaaa2d0..8e9aa19759 100644 --- a/nimbus_verified_proxy/types.nim +++ b/nimbus_verified_proxy/types.nim @@ -7,16 +7,16 @@ import json_rpc/[rpcproxy], - web3/[primitives, eth_api_types, eth_api], + stint, ./header_store type VerifiedRpcProxy* = ref object proxy*: RpcProxy headerStore*: HeaderStore - chainId*: Quantity + chainId*: UInt256 proc new*( - T: type VerifiedRpcProxy, proxy: RpcProxy, headerStore: HeaderStore, chainId: Quantity + T: type VerifiedRpcProxy, proxy: RpcProxy, headerStore: HeaderStore, chainId: UInt256 ): T = VerifiedRpcProxy(proxy: proxy, headerStore: headerStore, chainId: chainId) From 1604592a0beee7a7744245d3fd74d4936f886579 Mon Sep 17 00:00:00 2001 From: chirag-parmar Date: Tue, 1 Apr 2025 15:05:58 +0530 Subject: [PATCH 11/18] fixes --- nimbus_verified_proxy/rpc/rpc_eth_api.nim | 28 ++++++++--------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/nimbus_verified_proxy/rpc/rpc_eth_api.nim b/nimbus_verified_proxy/rpc/rpc_eth_api.nim index 2e998d0a09..c82eb184f8 100644 --- a/nimbus_verified_proxy/rpc/rpc_eth_api.nim +++ b/nimbus_verified_proxy/rpc/rpc_eth_api.nim @@ -40,25 +40,13 @@ proc installEthApiHandlers*(vp: VerifiedRpcProxy) = vp.proxy.rpc("eth_getBlockByNumber") do( blockTag: BlockTag, fullTransactions: bool - ) -> Opt[BlockObject]: - try: - let blk = await vp.getBlockByTag(blockTag, fullTransactions) - return Opt.some(blk) - except ValueError as e: - # should return Opt.none but we also want to transmit error related info - # return Opt.none(BlockObject) - raise newException(ValueError, e.msg) # raising an exception will return the error message + ) -> BlockObject: + await vp.getBlockByTag(blockTag, fullTransactions) vp.proxy.rpc("eth_getBlockByHash") do( blockHash: Hash32, fullTransactions: bool - ) -> Opt[BlockObject]: - try: - let blk = await vp.getBlockByHash(blockHash, fullTransactions) - return Opt.some(blk) - except ValueError as e: - # should return Opt.none but we also want to transmit error related info - # return Opt.none(BlockObject) - raise newException(ValueError, e.msg) # raising an exception will return the error message + ) -> BlockObject: + await vp.getBlockByHash(blockHash, fullTransactions) vp.proxy.rpc("eth_getUncleCountByBlockNumber") do( blockTag: BlockTag @@ -156,10 +144,12 @@ proc installEthApiHandlers*(vp: VerifiedRpcProxy) = vp.proxy.rpc("eth_getStorageAt") do( address: addresses.Address, slot: UInt256, blockTag: BlockTag - ) -> UInt256: - let header = await vp.getHeaderByTag(blockTag) + ) -> FixedBytes[32]: + let + header = await vp.getHeaderByTag(blockTag) + storage = await vp.getStorageAt(address, slot, header.number, header.stateRoot) - await vp.getStorageAt(address, slot, header.number, header.stateRoot) + FixedBytes[32](storage.toBytesBE) vp.proxy.rpc("eth_getTransactionCount") do( address: addresses.Address, blockTag: BlockTag From 1d4adde8415dedb2e8861552a9a7b2d490b3f0d0 Mon Sep 17 00:00:00 2001 From: chirag-parmar Date: Thu, 8 May 2025 14:15:02 +0530 Subject: [PATCH 12/18] solve c compilation error --- nimbus_verified_proxy/rpc/accounts.nim | 46 ++++-- nimbus_verified_proxy/rpc/blocks.nim | 122 ++++++++++------ nimbus_verified_proxy/rpc/receipts.nim | 34 +++-- nimbus_verified_proxy/rpc/rpc_eth_api.nim | 166 ++++++++++++++-------- 4 files changed, 233 insertions(+), 135 deletions(-) diff --git a/nimbus_verified_proxy/rpc/accounts.nim b/nimbus_verified_proxy/rpc/accounts.nim index 4f4c89ec7f..5fead19f2e 100644 --- a/nimbus_verified_proxy/rpc/accounts.nim +++ b/nimbus_verified_proxy/rpc/accounts.nim @@ -106,14 +106,21 @@ proc getAccount*( lcProxy: VerifiedRpcProxy, address: Address, blockNumber: base.BlockNumber, - stateRoot: Root): Future[Account] {.async: (raises: [ValueError, CatchableError]).} = + stateRoot: Root): Future[Result[Account, string]] {.async: (raises: []).} = + + info "Forwarding eth_getAccount", blockNumber + let - proof = await lcProxy.rpcClient.eth_getProof(address, @[], blockId(blockNumber)) + proof = + try: + await lcProxy.rpcClient.eth_getProof(address, @[], blockId(blockNumber)) + except CatchableError as e: + return err(e.msg) + account = getAccountFromProof( stateRoot, proof.address, proof.balance, proof.nonce, proof.codeHash, proof.storageHash, proof.accountProof, - ).valueOr: - raise newException(ValueError, error) + ) return account @@ -121,24 +128,29 @@ proc getCode*( lcProxy: VerifiedRpcProxy, address: Address, blockNumber: base.BlockNumber, - stateRoot: Root): Future[seq[byte]] {.async: (raises: [ValueError, CatchableError]).} = + stateRoot: Root): Future[Result[seq[byte], string]] {.async: (raises: []).} = # get verified account details for the address at blockNumber - let account = await lcProxy.getAccount(address, blockNumber, stateRoot) + let account = (await lcProxy.getAccount(address, blockNumber, stateRoot)).valueOr: + return err(error) # if the account does not have any code, return empty hex data if account.codeHash == EMPTY_CODE_HASH: - return @[] + return ok(newSeq[byte]()) info "Forwarding eth_getCode", blockNumber - let code = await lcProxy.rpcClient.eth_getCode(address, blockId(blockNumber)) + let code = + try: + await lcProxy.rpcClient.eth_getCode(address, blockId(blockNumber)) + except CatchableError as e: + return err(e.msg) # verify the byte code. since we verified the account against # the state root we just need to verify the code hash if account.codeHash == keccak256(code): - return code + return ok(code) else: - raise newException(ValueError, "received code doesn't match the account code hash") + return err("received code doesn't match the account code hash") proc getStorageAt*( lcProxy: VerifiedRpcProxy, @@ -146,13 +158,17 @@ proc getStorageAt*( slot: UInt256, blockNumber: base.BlockNumber, stateRoot: Root -): Future[UInt256] {.async: (raises: [ValueError, CatchableError]).} = +): Future[Result[UInt256, string]] {.async: (raises: []).} = info "Forwarding eth_getStorageAt", blockNumber let - proof = await lcProxy.rpcClient.eth_getProof(address, @[slot], blockId(blockNumber)) - slotValue = getStorageFromProof(stateRoot, slot, proof).valueOr: - raise newException(ValueError, error) + proof = + try: + await lcProxy.rpcClient.eth_getProof(address, @[slot], blockId(blockNumber)) + except CatchableError as e: + return err(e.msg) + + slotValue = getStorageFromProof(stateRoot, slot, proof) - slotValue + return slotValue diff --git a/nimbus_verified_proxy/rpc/blocks.nim b/nimbus_verified_proxy/rpc/blocks.nim index 1e745afbaa..a54a558eae 100644 --- a/nimbus_verified_proxy/rpc/blocks.nim +++ b/nimbus_verified_proxy/rpc/blocks.nim @@ -28,22 +28,22 @@ template rpcClient(vp: VerifiedRpcProxy): RpcClient = proc resolveTag( self: VerifiedRpcProxy, blockTag: BlockTag -): base.BlockNumber {.raises: [ValueError].} = +): Result[base.BlockNumber, string] = if blockTag.kind == bidAlias: let tag = blockTag.alias.toLowerAscii() case tag of "latest": let hLatest = self.headerStore.latest() if hLatest.isSome: - return hLatest.get().number + return ok(hLatest.get().number) else: - raise newException(ValueError, "Couldn't get the latest block number from header store") + return err("Couldn't get the latest block number from header store") else: - raise newException(ValueError, "No support for block tag " & $blockTag) + return err("No support for block tag " & $blockTag) else: - return base.BlockNumber(distinctBase(blockTag.number)) + return ok(base.BlockNumber(distinctBase(blockTag.number))) -proc convHeader(blk: BlockObject): Header = +proc convHeader(blk: eth_api_types.BlockObject): Header = let nonce = if blk.nonce.isSome: blk.nonce.get else: default(Bytes8) @@ -77,7 +77,7 @@ proc walkBlocks( sourceNum: base.BlockNumber, targetNum: base.BlockNumber, sourceHash: Hash32, - targetHash: Hash32): Future[bool] {.async: (raises: [ValueError, CatchableError]).} = + targetHash: Hash32): Future[bool] {.async: (raises: []).} = var nextHash = sourceHash info "starting block walk to verify", blockHash=targetHash @@ -85,8 +85,14 @@ proc walkBlocks( # TODO: use batch calls to get all blocks at once by number for i in 0 ..< sourceNum - targetNum: # TODO: use a verified hash cache - let blk = await self.rpcClient.eth_getBlockByHash(nextHash, false) - info "getting next block", hash=nextHash, number=blk.number, remaining=distinctBase(blk.number) - targetNum + let blk = + try: + await self.rpcClient.eth_getBlockByHash(nextHash, false) + except: + # TODO: retry before failing? + return false + + trace "getting next block", hash=nextHash, number=blk.number, remaining=distinctBase(blk.number) - targetNum if blk.parentHash == targetHash: return true @@ -97,145 +103,167 @@ proc walkBlocks( proc getBlockByHash*( self: VerifiedRpcProxy, blockHash: Hash32, fullTransactions: bool -): Future[BlockObject] {.async: (raises: [ValueError, CatchableError]).} = +): Future[Result[eth_api_types.BlockObject, string]] {.async: (raises: []).} = # get the target block - let blk = await self.rpcClient.eth_getBlockByHash(blockHash, fullTransactions) + let blk = + try: + await self.rpcClient.eth_getBlockByHash(blockHash, fullTransactions) + except CatchableError as e: + return err(e.msg) + let header = convHeader(blk) # verify header hash if header.rlpHash != blockHash: - raise newException(ValueError, "hashed block header doesn't match with blk.hash(downloaded)") + return err("hashed block header doesn't match with blk.hash(downloaded)") if blockHash != blk.hash: - raise newException(ValueError, "the downloaded block hash doesn't match with the requested hash") + return err("the downloaded block hash doesn't match with the requested hash") let earliestHeader = self.headerStore.earliest.valueOr: - raise newException(ValueError, "Syncing") + return err("syncing") # walk blocks backwards(time) from source to target let isLinked = await self.walkBlocks(earliestHeader.number, header.number, earliestHeader.parentHash, blockHash) if not isLinked: - raise newException(ValueError, "the requested block is not part of the canonical chain") + return err("the requested block is not part of the canonical chain") # verify transactions if fullTransactions: let verified = verifyTransactions(header.transactionsRoot, blk.transactions).valueOr: - raise newException(ValueError, "error while verifying transactions root") + return err("error while verifying transactions root") if not verified: - raise newException(ValueError, "transactions within the block do not yield the same transaction root") + return err("transactions within the block do not yield the same transaction root") # verify withdrawals if blk.withdrawals.isSome(): if blk.withdrawalsRoot.get() != orderedTrieRoot(blk.withdrawals.get()): - raise newException(ValueError, "withdrawals within the block do not yield the same withdrawals root") + return err("withdrawals within the block do not yield the same withdrawals root") - return blk + return ok(blk) proc getBlockByTag*( self: VerifiedRpcProxy, blockTag: BlockTag, fullTransactions: bool -): Future[BlockObject] {.async: (raises: [ValueError, CatchableError]).} = - let n = self.resolveTag(blockTag) +): Future[Result[BlockObject, string]] {.async: (raises: []).} = + let n = self.resolveTag(blockTag).valueOr: + return err(error) # get the target block - let blk = await self.rpcClient.eth_getBlockByNumber(blockTag, false) + let blk = + try: + await self.rpcClient.eth_getBlockByNumber(blockTag, false) + except CatchableError as e: + return err(e.msg) + let header = convHeader(blk) # verify header hash if header.rlpHash != blk.hash: - raise newException(ValueError, "hashed block header doesn't match with blk.hash(downloaded)") + return err("hashed block header doesn't match with blk.hash(downloaded)") if n != header.number: - raise newException(ValueError, "the downloaded block number doesn't match with the requested block number") + return err("the downloaded block number doesn't match with the requested block number") # get the source block let earliestHeader = self.headerStore.earliest.valueOr: - raise newException(ValueError, "Syncing") + return err("Syncing") # walk blocks backwards(time) from source to target let isLinked = await self.walkBlocks(earliestHeader.number, header.number, earliestHeader.parentHash, blk.hash) if not isLinked: - raise newException(ValueError, "the requested block is not part of the canonical chain") + return err("the requested block is not part of the canonical chain") # verify transactions if fullTransactions: let verified = verifyTransactions(header.transactionsRoot, blk.transactions).valueOr: - raise newException(ValueError, "error while verifying transactions root") + return err("error while verifying transactions root") if not verified: - raise newException(ValueError, "transactions within the block do not yield the same transaction root") + return err("transactions within the block do not yield the same transaction root") # verify withdrawals if blk.withdrawals.isSome(): if blk.withdrawalsRoot.get() != orderedTrieRoot(blk.withdrawals.get()): - raise newException(ValueError, "withdrawals within the block do not yield the same withdrawals root") + return err("withdrawals within the block do not yield the same withdrawals root") - return blk + return ok(blk) proc getHeaderByHash*( self: VerifiedRpcProxy, blockHash: Hash32 -): Future[Header] {.async: (raises: [ValueError, CatchableError]).} = +): Future[Result[Header, string]] {.async: (raises: []).} = let cachedHeader = self.headerStore.get(blockHash) if cachedHeader.isNone(): debug "did not find the header in the cache", blockHash=blockHash else: - return cachedHeader.get() + return ok(cachedHeader.get()) # get the source block let earliestHeader = self.headerStore.earliest.valueOr: - raise newException(ValueError, "Syncing") + return err("Syncing") # get the target block - let blk = await self.rpcClient.eth_getBlockByHash(blockHash, false) + let blk = + try: + await self.rpcClient.eth_getBlockByHash(blockHash, false) + except CatchableError as e: + return err(e.msg) + let header = convHeader(blk) # verify header hash if header.rlpHash != blk.hash: - raise newException(ValueError, "hashed block header doesn't match with blk.hash(downloaded)") + return err("hashed block header doesn't match with blk.hash(downloaded)") if blockHash != blk.hash: - raise newException(ValueError, "the blk.hash(downloaded) doesn't match with the provided hash") + return err("the blk.hash(downloaded) doesn't match with the provided hash") # walk blocks backwards(time) from source to target let isLinked = await self.walkBlocks(earliestHeader.number, header.number, earliestHeader.parentHash, blockHash) if not isLinked: - raise newException(ValueError, "the requested block is not part of the canonical chain") + return err("the requested block is not part of the canonical chain") - return header + return ok(header) proc getHeaderByTag*( self: VerifiedRpcProxy, blockTag: BlockTag -): Future[Header] {.async: (raises: [ValueError, CatchableError]).} = +): Future[Result[Header, string]] {.async: (raises: []).} = let - n = self.resolveTag(blockTag) + n = self.resolveTag(blockTag).valueOr: + return err(error) cachedHeader = self.headerStore.get(n) if cachedHeader.isNone(): debug "did not find the header in the cache", blockTag=blockTag else: - return cachedHeader.get() + return ok(cachedHeader.get()) # get the source block let earliestHeader = self.headerStore.earliest.valueOr: - raise newException(ValueError, "Syncing") + return err("Syncing") # get the target block - let blk = await self.rpcClient.eth_getBlockByNumber(blockTag, false) + let blk = + try: + await self.rpcClient.eth_getBlockByNumber(blockTag, false) + except CatchableError as e: + return err(e.msg) + let header = convHeader(blk) # verify header hash if header.rlpHash != blk.hash: - raise newException(ValueError, "hashed block header doesn't match with blk.hash(downloaded)") + return err("hashed block header doesn't match with blk.hash(downloaded)") if n != header.number: - raise newException(ValueError, "the downloaded block number doesn't match with the requested block number") + return err("the downloaded block number doesn't match with the requested block number") # walk blocks backwards(time) from source to target let isLinked = await self.walkBlocks(earliestHeader.number, header.number, earliestHeader.parentHash, blk.hash) if not isLinked: - raise newException(ValueError, "the requested block is not part of the canonical chain") + return err("the requested block is not part of the canonical chain") - return header + return ok(header) diff --git a/nimbus_verified_proxy/rpc/receipts.nim b/nimbus_verified_proxy/rpc/receipts.nim index a98b5583ac..b5c23c5244 100644 --- a/nimbus_verified_proxy/rpc/receipts.nim +++ b/nimbus_verified_proxy/rpc/receipts.nim @@ -63,33 +63,43 @@ proc toReceipts(recs: openArray[ReceiptObject]): seq[Receipt] = proc getReceiptsByBlockTag*( vp: VerifiedRpcProxy, blockTag: BlockTag -): Future[seq[ReceiptObject]] {.async: (raises: [ValueError, CatchableError]).} = +): Future[Result[seq[ReceiptObject], string]] {.async: (raises: []).} = let - header = await vp.getHeaderByTag(blockTag) - rxs = await vp.rpcClient.eth_getBlockReceipts(blockTag) + header = (await vp.getHeaderByTag(blockTag)).valueOr: + return err(error) + rxs = + try: + await vp.rpcClient.eth_getBlockReceipts(blockTag) + except CatchableError as e: + return err(e.msg) if rxs.isSome(): if orderedTrieRoot(toReceipts(rxs.get())) != header.receiptsRoot: - raise newException(ValueError, "downloaded receipts do not evaluate to the receipts root of the block") + return err("downloaded receipts do not evaluate to the receipts root of the block") else: - raise newException(ValueError, "error downloading the receipts") + return err("error downloading the receipts") - return rxs.get() + return ok(rxs.get()) proc getReceiptsByBlockHash*( vp: VerifiedRpcProxy, blockHash: Hash32 -): Future[seq[ReceiptObject]] {.async: (raises: [ValueError, CatchableError]).} = +): Future[Result[seq[ReceiptObject], string]] {.async: (raises: []).} = let - header = await vp.getHeaderByHash(blockHash) + header = (await vp.getHeaderByHash(blockHash)).valueOr: + return err(error) blockTag = BlockTag(RtBlockIdentifier(kind: bidNumber, number: Quantity(header.number))) - rxs = await vp.rpcClient.eth_getBlockReceipts(blockTag) + rxs = + try: + await vp.rpcClient.eth_getBlockReceipts(blockTag) + except CatchableError as e: + return err(e.msg) if rxs.isSome(): if orderedTrieRoot(toReceipts(rxs.get())) != header.receiptsRoot: - raise newException(ValueError, "downloaded receipts do not evaluate to the receipts root of the block") + return err("downloaded receipts do not evaluate to the receipts root of the block") else: - raise newException(ValueError, "error downloading the receipts") + return err("error downloading the receipts") - return rxs.get() + return ok(rxs.get()) diff --git a/nimbus_verified_proxy/rpc/rpc_eth_api.nim b/nimbus_verified_proxy/rpc/rpc_eth_api.nim index c82eb184f8..03c1217d4b 100644 --- a/nimbus_verified_proxy/rpc/rpc_eth_api.nim +++ b/nimbus_verified_proxy/rpc/rpc_eth_api.nim @@ -41,41 +41,48 @@ proc installEthApiHandlers*(vp: VerifiedRpcProxy) = vp.proxy.rpc("eth_getBlockByNumber") do( blockTag: BlockTag, fullTransactions: bool ) -> BlockObject: - await vp.getBlockByTag(blockTag, fullTransactions) + (await vp.getBlockByTag(blockTag, fullTransactions)).valueOr: + raise newException(ValueError, error) vp.proxy.rpc("eth_getBlockByHash") do( blockHash: Hash32, fullTransactions: bool ) -> BlockObject: - await vp.getBlockByHash(blockHash, fullTransactions) - - vp.proxy.rpc("eth_getUncleCountByBlockNumber") do( - blockTag: BlockTag - ) -> Quantity: - let blk = await vp.getBlockByTag(blockTag, false) - return Quantity(blk.uncles.len()) - - vp.proxy.rpc("eth_getUncleCountByBlockHash") do( - blockHash: Hash32 - ) -> Quantity: - let blk = await vp.getBlockByHash(blockHash, false) - return Quantity(blk.uncles.len()) - - vp.proxy.rpc("eth_getBlockTransactionCountByNumber") do( - blockTag: BlockTag - ) -> Quantity: - let blk = await vp.getBlockByTag(blockTag, true) - return Quantity(blk.transactions.len) - - vp.proxy.rpc("eth_getBlockTransactionCountByHash") do( - blockHash: Hash32 - ) -> Quantity: - let blk = await vp.getBlockByHash(blockHash, true) - return Quantity(blk.transactions.len) + (await vp.getBlockByHash(blockHash, fullTransactions)).valueOr: + raise newException(ValueError, error) + +# vp.proxy.rpc("eth_getUncleCountByBlockNumber") do( +# blockTag: BlockTag +# ) -> Quantity: +# let blk = (await vp.getBlockByTag(blockTag, false)).valueOr: +# raise newException(ValueError, error) +# return Quantity(blk.uncles.len()) +# +# vp.proxy.rpc("eth_getUncleCountByBlockHash") do( +# blockHash: Hash32 +# ) -> Quantity: +# let blk = (await vp.getBlockByHash(blockHash, false)).valueOr: +# raise newException(ValueError, error) +# return Quantity(blk.uncles.len()) +# +# vp.proxy.rpc("eth_getBlockTransactionCountByNumber") do( +# blockTag: BlockTag +# ) -> Quantity: +# let blk = (await vp.getBlockByTag(blockTag, true)).valueOr: +# raise newException(ValueError, error) +# return Quantity(blk.transactions.len) +# +# vp.proxy.rpc("eth_getBlockTransactionCountByHash") do( +# blockHash: Hash32 +# ) -> Quantity: +# let blk = (await vp.getBlockByHash(blockHash, true)).valueOr: +# raise newException(ValueError, error) +# return Quantity(blk.transactions.len) vp.proxy.rpc("eth_getTransactionByBlockNumberAndIndex") do( blockTag: BlockTag, index: Quantity ) -> TransactionObject: - let blk = await vp.getBlockByTag(blockTag, true) + let blk = (await vp.getBlockByTag(blockTag, true)).valueOr: + raise newException(ValueError, error) if distinctBase(index) >= uint64(blk.transactions.len): raise newException(ValueError, "provided transaction index is outside bounds") let x = blk.transactions[distinctBase(index)] @@ -85,7 +92,9 @@ proc installEthApiHandlers*(vp: VerifiedRpcProxy) = vp.proxy.rpc("eth_getTransactionByBlockHashAndIndex") do( blockHash: Hash32, index: Quantity ) -> TransactionObject: - let blk = await vp.getBlockByHash(blockHash, true) + let blk = (await vp.getBlockByHash(blockHash, true)).valueOr: + raise newException(ValueError, error) + if distinctBase(index) >= uint64(blk.transactions.len): raise newException(ValueError, "provided transaction index is outside bounds") let x = blk.transactions[distinctBase(index)] @@ -95,7 +104,11 @@ proc installEthApiHandlers*(vp: VerifiedRpcProxy) = vp.proxy.rpc("eth_getTransactionByHash") do( txHash: Hash32 ) -> TransactionObject: - let tx = await vp.rpcClient.eth_getTransactionByHash(txHash) + let tx = + try: + await vp.rpcClient.eth_getTransactionByHash(txHash) + except CatchableError as e: + raise newException(ValueError, e.msg) if tx.hash != txHash: raise newException(ValueError, "the downloaded transaction hash doesn't match the requested transaction hash") @@ -108,15 +121,21 @@ proc installEthApiHandlers*(vp: VerifiedRpcProxy) = vp.proxy.rpc("eth_getBlockReceipts") do( blockTag: BlockTag ) -> Opt[seq[ReceiptObject]]: - let rxs = await vp.getReceiptsByBlockTag(blockTag) + let rxs = (await vp.getReceiptsByBlockTag(blockTag)).valueOr: + raise newException(ValueError, error) return Opt.some(rxs) vp.proxy.rpc("eth_getTransactionReceipt") do( txHash: Hash32 ) -> ReceiptObject: let - rx = await vp.rpcClient.eth_getTransactionReceipt(txHash) - rxs = await vp.getReceiptsByBlockHash(rx.blockHash) + rx = + try: + await vp.rpcClient.eth_getTransactionReceipt(txHash) + except CatchableError as e: + raise newException(ValueError, e.msg) + rxs = (await vp.getReceiptsByBlockHash(rx.blockHash)).valueOr: + raise newException(ValueError, error) for r in rxs: if r.transactionHash == txHash: @@ -124,21 +143,24 @@ proc installEthApiHandlers*(vp: VerifiedRpcProxy) = raise newException(ValueError, "receipt couldn't be verified") - # eth_blockNumber - get latest tag from header store - vp.proxy.rpc("eth_blockNumber") do() -> Quantity: - # Returns the number of the most recent block seen by the light client. - let hLatest = vp.headerStore.latest() - if hLatest.isNone: - raise newException(ValueError, "Syncing") - - return Quantity(hLatest.get().number) +# # eth_blockNumber - get latest tag from header store +# vp.proxy.rpc("eth_blockNumber") do() -> Quantity: +# # Returns the number of the most recent block seen by the light client. +# let hLatest = vp.headerStore.latest() +# if hLatest.isNone: +# raise newException(ValueError, "Syncing") +# +# return Quantity(hLatest.get().number) vp.proxy.rpc("eth_getBalance") do( address: addresses.Address, blockTag: BlockTag ) -> UInt256: let - header = await vp.getHeaderByTag(blockTag) - account = await vp.getAccount(address, header.number, header.stateRoot) + header = (await vp.getHeaderByTag(blockTag)).valueOr: + raise newException(ValueError, error) + account = (await vp.getAccount(address, header.number, header.stateRoot)).valueOr: + raise newException(ValueError, error) + account.balance @@ -146,27 +168,35 @@ proc installEthApiHandlers*(vp: VerifiedRpcProxy) = address: addresses.Address, slot: UInt256, blockTag: BlockTag ) -> FixedBytes[32]: let - header = await vp.getHeaderByTag(blockTag) - storage = await vp.getStorageAt(address, slot, header.number, header.stateRoot) + header = (await vp.getHeaderByTag(blockTag)).valueOr: + raise newException(ValueError, error) - FixedBytes[32](storage.toBytesBE) + storage = (await vp.getStorageAt(address, slot, header.number, header.stateRoot)).valueOr: + raise newException(ValueError, error) - vp.proxy.rpc("eth_getTransactionCount") do( - address: addresses.Address, blockTag: BlockTag - ) -> Quantity: - let - header = await vp.getHeaderByTag(blockTag) - account = await vp.getAccount(address, header.number, header.stateRoot) + FixedBytes[32](storage.toBytesBE) - Quantity(account.nonce) +# vp.proxy.rpc("eth_getTransactionCount") do( +# address: addresses.Address, blockTag: BlockTag +# ) -> Quantity: +# let +# header = (await vp.getHeaderByTag(blockTag)).valueOr: +# raise newException(ValueError, error) +# +# account = (await vp.getAccount(address, header.number, header.stateRoot)).valueOr: +# raise newException(ValueError, error) +# +# Quantity(account.nonce) vp.proxy.rpc("eth_getCode") do( address: addresses.Address, blockTag: BlockTag ) -> seq[byte]: let - header = await vp.getHeaderByTag(blockTag) + header = (await vp.getHeaderByTag(blockTag)).valueOr: + raise newException(ValueError, error) - await vp.getCode(address,header.number, header.stateRoot) + (await vp.getCode(address,header.number, header.stateRoot)).valueOr: + raise newException(ValueError, error) vp.proxy.rpc("eth_call") do( args: TransactionArgs, blockTag: BlockTag @@ -177,13 +207,22 @@ proc installEthApiHandlers*(vp: VerifiedRpcProxy) = let to = if args.to.isSome(): args.to.get() else: raise newException(ValueError, "contract address missing in transaction args") - header = await vp.getHeaderByTag(blockTag) - code = await vp.getCode(to, header.number, header.stateRoot) + header = (await vp.getHeaderByTag(blockTag)).valueOr: + raise newException(ValueError, error) + + code = (await vp.getCode(to, header.number, header.stateRoot)).valueOr: + raise newException(ValueError, error) # 2. get all storage locations that are accessed let - parent = await vp.getHeaderByHash(header.parentHash) - accessListResult = await vp.rpcClient.eth_createAccessList(args, blockId(header.number)) + parent = (await vp.getHeaderByHash(header.parentHash)).valueOr: + raise newException(ValueError, error) + + accessListResult = + try: + await vp.rpcClient.eth_createAccessList(args, blockId(header.number)) + except CatchableError as e: + raise newException(ValueError, e.msg) accessList = if not accessListResult.error.isSome(): accessListResult.accessList else: raise newException(ValueError, "couldn't get an access list for eth call") @@ -198,8 +237,12 @@ proc installEthApiHandlers*(vp: VerifiedRpcProxy) = for accessPair in accessList: let accountAddr = accessPair.address - acc = await vp.getAccount(accountAddr, header.number, header.stateRoot) - accCode = await vp.getCode(accountAddr, header.number, header.stateRoot) + acc = (await vp.getAccount(accountAddr, header.number, header.stateRoot)).valueOr: + raise newException(ValueError, error) + + accCode = (await vp.getCode(accountAddr, header.number, header.stateRoot)).valueOr: + raise newException(ValueError, error) + db.setNonce(accountAddr, acc.nonce) db.setBalance(accountAddr, acc.balance) @@ -208,7 +251,8 @@ proc installEthApiHandlers*(vp: VerifiedRpcProxy) = for slot in accessPair.storageKeys: let slotInt = UInt256.fromHex(toHex(slot)) - slotValue = await vp.getStorageAt(accountAddr, slotInt, header.number, header.stateRoot) + slotValue = (await vp.getStorageAt(accountAddr, slotInt, header.number, header.stateRoot)).valueOr: + raise newException(ValueError, error) db.setStorage(accountAddr, slotInt, slotValue) db.persist(clearEmptyAccount = false) # settle accounts storage From 1502a10a155bf648cd69a49f217aaebb52ea570d Mon Sep 17 00:00:00 2001 From: chirag-parmar Date: Sat, 10 May 2025 11:52:24 +0530 Subject: [PATCH 13/18] async evm --- nimbus_verified_proxy/nim.cfg | 2 +- .../nimbus_verified_proxy.nim | 7 +- nimbus_verified_proxy/rpc/evm.nim | 35 ++++++ nimbus_verified_proxy/rpc/rpc_eth_api.nim | 105 +++++++++--------- nimbus_verified_proxy/types.nim | 4 +- 5 files changed, 98 insertions(+), 55 deletions(-) create mode 100644 nimbus_verified_proxy/rpc/evm.nim diff --git a/nimbus_verified_proxy/nim.cfg b/nimbus_verified_proxy/nim.cfg index 2e06d07ecd..b4db75f916 100644 --- a/nimbus_verified_proxy/nim.cfg +++ b/nimbus_verified_proxy/nim.cfg @@ -14,5 +14,5 @@ # Use only `secp256k1` public key cryptography as an identity in LibP2P. -d:"libp2p_pki_schemes=secp256k1" - +-d:stateless --hint[Processing]:off diff --git a/nimbus_verified_proxy/nimbus_verified_proxy.nim b/nimbus_verified_proxy/nimbus_verified_proxy.nim index 233fe33736..d235553566 100644 --- a/nimbus_verified_proxy/nimbus_verified_proxy.nim +++ b/nimbus_verified_proxy/nimbus_verified_proxy.nim @@ -25,7 +25,8 @@ import ./rpc/rpc_eth_api, ./types, ./nimbus_verified_proxy_conf, - ./header_store + ./header_store, + ./rpc/evm from beacon_chain/gossip_processing/eth2_processor import toValidationResult @@ -82,8 +83,10 @@ proc run*( ) headerStore = HeaderStore.new(64) # block cache contains blocks downloaded from p2p - verifiedProxy = VerifiedRpcProxy.new(rpcProxy, headerStore, chainId) + var verifiedProxy = VerifiedRpcProxy.new(rpcProxy, headerStore, chainId) + + verifiedProxy.initEvm() # add handlers that verify RPC calls /rpc/rpc_eth_api.nim verifiedProxy.installEthApiHandlers() diff --git a/nimbus_verified_proxy/rpc/evm.nim b/nimbus_verified_proxy/rpc/evm.nim new file mode 100644 index 0000000000..4582ca02cb --- /dev/null +++ b/nimbus_verified_proxy/rpc/evm.nim @@ -0,0 +1,35 @@ +import + ../../fluffy/evm/async_evm, + ../../fluffy/evm/async_evm_backend, + ./accounts, + ../types + +export async_evm, async_evm_backend + +proc toAsyncEvmStateBackend(vp: VerifiedRpcProxy): AsyncEvmStateBackend = + let + accProc = proc( + header: Header, address: Address + ): Future[Opt[Account]] {.async: (raises: [CancelledError]).} = + let account = (await vp.getAccount(address, header.number, header.stateRoot)).valueOr: + return Opt.none(Account) + return Opt.some(account) + + storageProc = proc( + header: Header, address: Address, slotKey: UInt256 + ): Future[Opt[UInt256]] {.async: (raises: [CancelledError]).} = + let storageSlot = (await vp.getStorageAt(address, slotKey, header.number, header.stateRoot)).valueOr: + return Opt.none(UInt256) + return Opt.some(storageSlot) + + codeProc = proc( + header: Header, address: Address + ): Future[Opt[seq[byte]]] {.async: (raises: [CancelledError]).} = + let code = (await vp.getCode(address, header.number, header.stateRoot)).valueOr: + return Opt.none(seq[byte]) + return Opt.some(code) + + AsyncEvmStateBackend.init(accProc, storageProc, codeProc) + +proc initEvm*(vp: var VerifiedRpcProxy) = + vp.evm = AsyncEvm.init(vp.toAsyncEvmStateBackend()) diff --git a/nimbus_verified_proxy/rpc/rpc_eth_api.nim b/nimbus_verified_proxy/rpc/rpc_eth_api.nim index 03c1217d4b..8ab518cfed 100644 --- a/nimbus_verified_proxy/rpc/rpc_eth_api.nim +++ b/nimbus_verified_proxy/rpc/rpc_eth_api.nim @@ -26,7 +26,8 @@ import ./blocks, ./accounts, ./transactions, - ./receipts + ./receipts, + ./evm logScope: topics = "verified_proxy" @@ -197,71 +198,73 @@ proc installEthApiHandlers*(vp: VerifiedRpcProxy) = (await vp.getCode(address,header.number, header.stateRoot)).valueOr: raise newException(ValueError, error) - + vp.proxy.rpc("eth_call") do( - args: TransactionArgs, blockTag: BlockTag + tx: TransactionArgs, blockTag: BlockTag, optimisticStateFetch: Opt[bool] ) -> seq[byte]: + if tx.to.isNone(): + raise newException(ValueError, "to address is required") - # eth_call - # 1. get the code with proof - let - to = if args.to.isSome(): args.to.get() - else: raise newException(ValueError, "contract address missing in transaction args") - header = (await vp.getHeaderByTag(blockTag)).valueOr: - raise newException(ValueError, error) - - code = (await vp.getCode(to, header.number, header.stateRoot)).valueOr: - raise newException(ValueError, error) + if blockTag.kind == bidAlias: + raise newException(ValueError, "tag not yet implemented") - # 2. get all storage locations that are accessed - let - parent = (await vp.getHeaderByHash(header.parentHash)).valueOr: + let + header = (await vp.getHeaderByTag(blockTag)).valueOr: raise newException(ValueError, error) - accessListResult = - try: - await vp.rpcClient.eth_createAccessList(args, blockId(header.number)) - except CatchableError as e: - raise newException(ValueError, e.msg) - accessList = if not accessListResult.error.isSome(): accessListResult.accessList - else: raise newException(ValueError, "couldn't get an access list for eth call") + optimisticStateFetch = optimisticStateFetch.valueOr: + true - # 3. pull the storage values that are access along with their accounts and initialize db - let - com = CommonRef.new(newCoreDbRef DefaultDbMemory, nil) - fork = com.toEVMFork(header) - vmState = BaseVMState() + let callResult = (await vp.evm.call(header, tx, optimisticStateFetch)).valueOr: + raise newException(ValueError, error) - vmState.init(parent, header, com, com.db.baseTxFrame()) - vmState.mutateLedger: - for accessPair in accessList: - let - accountAddr = accessPair.address - acc = (await vp.getAccount(accountAddr, header.number, header.stateRoot)).valueOr: - raise newException(ValueError, error) + return callResult.output - accCode = (await vp.getCode(accountAddr, header.number, header.stateRoot)).valueOr: - raise newException(ValueError, error) + vp.proxy.rpc("eth_createAccessList") do( + tx: TransactionArgs, blockTag: BlockTag, optimisticStateFetch: Opt[bool] + ) -> AccessListResult: + if tx.to.isNone(): + raise newException(ValueError, "to address is required") + if blockTag.kind == bidAlias: + raise newException(ValueError, "tag not yet implemented") - db.setNonce(accountAddr, acc.nonce) - db.setBalance(accountAddr, acc.balance) - db.setCode(accountAddr, accCode) + let + header = (await vp.getHeaderByTag(blockTag)).valueOr: + raise newException(ValueError, error) - for slot in accessPair.storageKeys: - let - slotInt = UInt256.fromHex(toHex(slot)) - slotValue = (await vp.getStorageAt(accountAddr, slotInt, header.number, header.stateRoot)).valueOr: - raise newException(ValueError, error) - db.setStorage(accountAddr, slotInt, slotValue) - db.persist(clearEmptyAccount = false) # settle accounts storage + optimisticStateFetch = optimisticStateFetch.valueOr: + true - # 4. run the evm with the initialized storage - let evmResult = rpcCallEvm(args, header, vmState).valueOr: - raise newException(ValueError, "rpcCallEvm error: " & $error.code) + let (accessList, error, gasUsed) = ( + await vp.evm.createAccessList(header, tx, optimisticStateFetch) + ).valueOr: + raise newException(ValueError, error) - evmResult.output + return + AccessListResult(accessList: accessList, error: error, gasUsed: gasUsed.Quantity) +# vp.proxy.rpc("eth_estimateGas") do( +# tx: TransactionArgs, blockTag: BlockTag, optimisticStateFetch: Opt[bool] +# ) -> Quantity: +# if tx.to.isNone(): +# raise newException(ValueError, "to address is required") +# +# if blockTag.kind == bidAlias: +# raise newException(ValueError, "tag not yet implemented") +# +# let +# header = (await vp.getHeaderByTag(blockTag)).valueOr: +# raise newException(ValueError, error) +# +# optimisticStateFetch = optimisticStateFetch.valueOr: +# true +# +# let gasEstimate = (await vp.evm.estimateGas(header, tx, optimisticStateFetch)).valueOr: +# raise newException(ValueError, error) +# +# return gasEstimate.Quantity +# # TODO: # Following methods are forwarded directly to the web3 provider and therefore # are not validated in any way. diff --git a/nimbus_verified_proxy/types.nim b/nimbus_verified_proxy/types.nim index 8e9aa19759..97e9baf256 100644 --- a/nimbus_verified_proxy/types.nim +++ b/nimbus_verified_proxy/types.nim @@ -8,10 +8,12 @@ import json_rpc/[rpcproxy], stint, - ./header_store + ./header_store, + ../fluffy/evm/async_evm type VerifiedRpcProxy* = ref object + evm*: AsyncEvm proxy*: RpcProxy headerStore*: HeaderStore chainId*: UInt256 From d873e21c547ded84d781213d04204c4519e26344 Mon Sep 17 00:00:00 2001 From: chirag-parmar Date: Sat, 10 May 2025 11:53:25 +0530 Subject: [PATCH 14/18] nph --- nimbus_verified_proxy/header_store.nim | 24 +-- .../nimbus_verified_proxy.nim | 9 +- nimbus_verified_proxy/rpc/accounts.nim | 16 +- nimbus_verified_proxy/rpc/blocks.nim | 65 ++++--- nimbus_verified_proxy/rpc/evm.nim | 15 +- nimbus_verified_proxy/rpc/receipts.nim | 42 ++--- nimbus_verified_proxy/rpc/rpc_eth_api.nim | 177 +++++++++--------- nimbus_verified_proxy/rpc/transactions.nim | 54 +++--- nimbus_verified_proxy/types.nim | 22 +-- 9 files changed, 218 insertions(+), 206 deletions(-) diff --git a/nimbus_verified_proxy/header_store.nim b/nimbus_verified_proxy/header_store.nim index 56a8d3ac66..90704ba1b3 100644 --- a/nimbus_verified_proxy/header_store.nim +++ b/nimbus_verified_proxy/header_store.nim @@ -7,7 +7,7 @@ {.push raises: [].} -import +import eth/common/hashes, eth/common/headers, web3/eth_api_types, @@ -16,17 +16,17 @@ import beacon_chain/spec/datatypes/[phase0, altair, bellatrix], beacon_chain/[light_client, nimbus_binary_common], beacon_chain/el/engine_api_conversions, - minilru, + minilru, results -type - HeaderStore* = ref object - headers: LruCache[Hash32, Header] - hashes: Table[base.BlockNumber, Hash32] +type HeaderStore* = ref object + headers: LruCache[Hash32, Header] + hashes: Table[base.BlockNumber, Hash32] -func convHeader(lcHeader: ForkedLightClientHeader): Header = +func convHeader(lcHeader: ForkedLightClientHeader): Header = withForkyHeader(lcHeader): - template p(): auto = forkyHeader.execution + template p(): auto = + forkyHeader.execution when lcDataFork >= LightClientDataFork.Capella: let withdrawalsRoot = Opt.some(p.withdrawals_root.asBlockHash) @@ -34,12 +34,12 @@ func convHeader(lcHeader: ForkedLightClientHeader): Header = let withdrawalsRoot = Opt.none(Hash32) when lcDataFork >= LightClientDataFork.Deneb: - let + let blobGasUsed = Opt.some(p.blob_gas_used) excessBlobGas = Opt.some(p.excess_blob_gas) parentBeaconBlockRoot = Opt.some(forkyHeader.beacon.parent_root.asBlockHash) else: - let + let blobGasUsed = Opt.none(uint64) excessBlobGas = Opt.none(uint64) parentBeaconBlockRoot = Opt.none(Hash32) @@ -72,7 +72,7 @@ func convHeader(lcHeader: ForkedLightClientHeader): Header = blobGasUsed: blobGasUsed, excessBlobGas: excessBlobGas, parentBeaconBlockRoot: parentBeaconBlockRoot, - requestsHash: requestsHash + requestsHash: requestsHash, ) else: # INFO: should never reach this point because running verified @@ -118,7 +118,7 @@ proc earliest*(self: HeaderStore): Opt[Header] = self.headers.peek(hash) proc get*(self: HeaderStore, number: base.BlockNumber): Opt[Header] = - let hash = + let hash = try: self.hashes[number] except: diff --git a/nimbus_verified_proxy/nimbus_verified_proxy.nim b/nimbus_verified_proxy/nimbus_verified_proxy.nim index d235553566..9fed75eccc 100644 --- a/nimbus_verified_proxy/nimbus_verified_proxy.nim +++ b/nimbus_verified_proxy/nimbus_verified_proxy.nim @@ -58,7 +58,6 @@ func getConfiguredChainId(networkMetadata: Eth2NetworkMetadata): UInt256 = proc run*( config: VerifiedProxyConf, ctx: ptr Context ) {.raises: [CatchableError], gcsafe.} = - {.gcsafe.}: setupLogging(config.logLevel, config.logStdout, none(OutFile)) @@ -91,7 +90,8 @@ proc run*( verifiedProxy.installEthApiHandlers() # just for short hand convenience - template cfg(): auto = metadata.cfg + template cfg(): auto = + metadata.cfg # initiialize beacon node genesis data, beacon clock and forkDigests let @@ -146,8 +146,9 @@ proc run*( # find out what this does network.registerProtocol( - PeerSync, PeerSync.NetworkState.init( - cfg, forkDigests, genesisBlockRoot, getBeaconTime)) + PeerSync, + PeerSync.NetworkState.init(cfg, forkDigests, genesisBlockRoot, getBeaconTime), + ) # start the p2p network and rpcProxy waitFor network.startListening() diff --git a/nimbus_verified_proxy/rpc/accounts.nim b/nimbus_verified_proxy/rpc/accounts.nim index 5fead19f2e..2a53d36782 100644 --- a/nimbus_verified_proxy/rpc/accounts.nim +++ b/nimbus_verified_proxy/rpc/accounts.nim @@ -106,12 +106,12 @@ proc getAccount*( lcProxy: VerifiedRpcProxy, address: Address, blockNumber: base.BlockNumber, - stateRoot: Root): Future[Result[Account, string]] {.async: (raises: []).} = - + stateRoot: Root, +): Future[Result[Account, string]] {.async: (raises: []).} = info "Forwarding eth_getAccount", blockNumber let - proof = + proof = try: await lcProxy.rpcClient.eth_getProof(address, @[], blockId(blockNumber)) except CatchableError as e: @@ -128,7 +128,8 @@ proc getCode*( lcProxy: VerifiedRpcProxy, address: Address, blockNumber: base.BlockNumber, - stateRoot: Root): Future[Result[seq[byte], string]] {.async: (raises: []).} = + stateRoot: Root, +): Future[Result[seq[byte], string]] {.async: (raises: []).} = # get verified account details for the address at blockNumber let account = (await lcProxy.getAccount(address, blockNumber, stateRoot)).valueOr: return err(error) @@ -139,7 +140,7 @@ proc getCode*( info "Forwarding eth_getCode", blockNumber - let code = + let code = try: await lcProxy.rpcClient.eth_getCode(address, blockId(blockNumber)) except CatchableError as e: @@ -157,13 +158,12 @@ proc getStorageAt*( address: Address, slot: UInt256, blockNumber: base.BlockNumber, - stateRoot: Root + stateRoot: Root, ): Future[Result[UInt256, string]] {.async: (raises: []).} = - info "Forwarding eth_getStorageAt", blockNumber let - proof = + proof = try: await lcProxy.rpcClient.eth_getProof(address, @[slot], blockId(blockNumber)) except CatchableError as e: diff --git a/nimbus_verified_proxy/rpc/blocks.nim b/nimbus_verified_proxy/rpc/blocks.nim index a54a558eae..ad42b56f6d 100644 --- a/nimbus_verified_proxy/rpc/blocks.nim +++ b/nimbus_verified_proxy/rpc/blocks.nim @@ -44,9 +44,11 @@ proc resolveTag( return ok(base.BlockNumber(distinctBase(blockTag.number))) proc convHeader(blk: eth_api_types.BlockObject): Header = - let - nonce = if blk.nonce.isSome: blk.nonce.get - else: default(Bytes8) + let nonce = + if blk.nonce.isSome: + blk.nonce.get + else: + default(Bytes8) return Header( parentHash: blk.parentHash, @@ -69,30 +71,33 @@ proc convHeader(blk: eth_api_types.BlockObject): Header = blobGasUsed: blk.blobGasUsed.u64, excessBlobGas: blk.excessBlobGas.u64, parentBeaconBlockRoot: blk.parentBeaconBlockRoot, - requestsHash: blk.requestsHash + requestsHash: blk.requestsHash, ) proc walkBlocks( - self: VerifiedRpcProxy, - sourceNum: base.BlockNumber, - targetNum: base.BlockNumber, - sourceHash: Hash32, - targetHash: Hash32): Future[bool] {.async: (raises: []).} = - + self: VerifiedRpcProxy, + sourceNum: base.BlockNumber, + targetNum: base.BlockNumber, + sourceHash: Hash32, + targetHash: Hash32, +): Future[bool] {.async: (raises: []).} = var nextHash = sourceHash - info "starting block walk to verify", blockHash=targetHash + info "starting block walk to verify", blockHash = targetHash # TODO: use batch calls to get all blocks at once by number for i in 0 ..< sourceNum - targetNum: # TODO: use a verified hash cache - let blk = + let blk = try: await self.rpcClient.eth_getBlockByHash(nextHash, false) except: # TODO: retry before failing? return false - trace "getting next block", hash=nextHash, number=blk.number, remaining=distinctBase(blk.number) - targetNum + trace "getting next block", + hash = nextHash, + number = blk.number, + remaining = distinctBase(blk.number) - targetNum if blk.parentHash == targetHash: return true @@ -105,7 +110,7 @@ proc getBlockByHash*( self: VerifiedRpcProxy, blockHash: Hash32, fullTransactions: bool ): Future[Result[eth_api_types.BlockObject, string]] {.async: (raises: []).} = # get the target block - let blk = + let blk = try: await self.rpcClient.eth_getBlockByHash(blockHash, fullTransactions) except CatchableError as e: @@ -124,7 +129,9 @@ proc getBlockByHash*( return err("syncing") # walk blocks backwards(time) from source to target - let isLinked = await self.walkBlocks(earliestHeader.number, header.number, earliestHeader.parentHash, blockHash) + let isLinked = await self.walkBlocks( + earliestHeader.number, header.number, earliestHeader.parentHash, blockHash + ) if not isLinked: return err("the requested block is not part of the canonical chain") @@ -150,7 +157,7 @@ proc getBlockByTag*( return err(error) # get the target block - let blk = + let blk = try: await self.rpcClient.eth_getBlockByNumber(blockTag, false) except CatchableError as e: @@ -163,14 +170,17 @@ proc getBlockByTag*( return err("hashed block header doesn't match with blk.hash(downloaded)") if n != header.number: - return err("the downloaded block number doesn't match with the requested block number") + return + err("the downloaded block number doesn't match with the requested block number") # get the source block let earliestHeader = self.headerStore.earliest.valueOr: return err("Syncing") # walk blocks backwards(time) from source to target - let isLinked = await self.walkBlocks(earliestHeader.number, header.number, earliestHeader.parentHash, blk.hash) + let isLinked = await self.walkBlocks( + earliestHeader.number, header.number, earliestHeader.parentHash, blk.hash + ) if not isLinked: return err("the requested block is not part of the canonical chain") @@ -195,7 +205,7 @@ proc getHeaderByHash*( let cachedHeader = self.headerStore.get(blockHash) if cachedHeader.isNone(): - debug "did not find the header in the cache", blockHash=blockHash + debug "did not find the header in the cache", blockHash = blockHash else: return ok(cachedHeader.get()) @@ -204,7 +214,7 @@ proc getHeaderByHash*( return err("Syncing") # get the target block - let blk = + let blk = try: await self.rpcClient.eth_getBlockByHash(blockHash, false) except CatchableError as e: @@ -220,7 +230,9 @@ proc getHeaderByHash*( return err("the blk.hash(downloaded) doesn't match with the provided hash") # walk blocks backwards(time) from source to target - let isLinked = await self.walkBlocks(earliestHeader.number, header.number, earliestHeader.parentHash, blockHash) + let isLinked = await self.walkBlocks( + earliestHeader.number, header.number, earliestHeader.parentHash, blockHash + ) if not isLinked: return err("the requested block is not part of the canonical chain") @@ -236,7 +248,7 @@ proc getHeaderByTag*( cachedHeader = self.headerStore.get(n) if cachedHeader.isNone(): - debug "did not find the header in the cache", blockTag=blockTag + debug "did not find the header in the cache", blockTag = blockTag else: return ok(cachedHeader.get()) @@ -245,7 +257,7 @@ proc getHeaderByTag*( return err("Syncing") # get the target block - let blk = + let blk = try: await self.rpcClient.eth_getBlockByNumber(blockTag, false) except CatchableError as e: @@ -258,10 +270,13 @@ proc getHeaderByTag*( return err("hashed block header doesn't match with blk.hash(downloaded)") if n != header.number: - return err("the downloaded block number doesn't match with the requested block number") + return + err("the downloaded block number doesn't match with the requested block number") # walk blocks backwards(time) from source to target - let isLinked = await self.walkBlocks(earliestHeader.number, header.number, earliestHeader.parentHash, blk.hash) + let isLinked = await self.walkBlocks( + earliestHeader.number, header.number, earliestHeader.parentHash, blk.hash + ) if not isLinked: return err("the requested block is not part of the canonical chain") diff --git a/nimbus_verified_proxy/rpc/evm.nim b/nimbus_verified_proxy/rpc/evm.nim index 4582ca02cb..36c94d3ff0 100644 --- a/nimbus_verified_proxy/rpc/evm.nim +++ b/nimbus_verified_proxy/rpc/evm.nim @@ -1,10 +1,7 @@ -import - ../../fluffy/evm/async_evm, - ../../fluffy/evm/async_evm_backend, - ./accounts, - ../types +import + ../../fluffy/evm/async_evm, ../../fluffy/evm/async_evm_backend, ./accounts, ../types -export async_evm, async_evm_backend +export async_evm, async_evm_backend proc toAsyncEvmStateBackend(vp: VerifiedRpcProxy): AsyncEvmStateBackend = let @@ -18,9 +15,11 @@ proc toAsyncEvmStateBackend(vp: VerifiedRpcProxy): AsyncEvmStateBackend = storageProc = proc( header: Header, address: Address, slotKey: UInt256 ): Future[Opt[UInt256]] {.async: (raises: [CancelledError]).} = - let storageSlot = (await vp.getStorageAt(address, slotKey, header.number, header.stateRoot)).valueOr: + let storageSlot = ( + await vp.getStorageAt(address, slotKey, header.number, header.stateRoot) + ).valueOr: return Opt.none(UInt256) - return Opt.some(storageSlot) + return Opt.some(storageSlot) codeProc = proc( header: Header, address: Address diff --git a/nimbus_verified_proxy/rpc/receipts.nim b/nimbus_verified_proxy/rpc/receipts.nim index b5c23c5244..8478f09231 100644 --- a/nimbus_verified_proxy/rpc/receipts.nim +++ b/nimbus_verified_proxy/rpc/receipts.nim @@ -28,21 +28,20 @@ export results, stint, hashes_rlp, accounts_rlp, eth_api_types template rpcClient(vp: VerifiedRpcProxy): RpcClient = vp.proxy.getClient() -template toLog(lg: LogObject): Log = - Log( - address: lg.address, - topics: lg.topics, - data: lg.data - ) +template toLog(lg: LogObject): Log = + Log(address: lg.address, topics: lg.topics, data: lg.data) proc toLogs(logs: openArray[LogObject]): seq[Log] = - result = map(logs, proc(x: LogObject): Log = toLog(x)) + result = map( + logs, + proc(x: LogObject): Log = + toLog(x), + ) proc toReceipt(rec: ReceiptObject): Receipt = - let isHash = if rec.status.isSome: false - else: true + let isHash = if rec.status.isSome: false else: true - var status = false + var status = false if rec.status.isSome: if rec.status.get() == 1.Quantity: status = true @@ -50,11 +49,11 @@ proc toReceipt(rec: ReceiptObject): Receipt = return Receipt( hash: rec.transactionHash, isHash: isHash, - status: status, + status: status, cumulativeGasUsed: rec.cumulativeGasUsed.GasInt, logs: toLogs(rec.logs), logsBloom: rec.logsBloom, - receiptType: rec.`type`.get(0.Web3Quantity).ReceiptType + receiptType: rec.`type`.get(0.Web3Quantity).ReceiptType, ) proc toReceipts(recs: openArray[ReceiptObject]): seq[Receipt] = @@ -64,11 +63,10 @@ proc toReceipts(recs: openArray[ReceiptObject]): seq[Receipt] = proc getReceiptsByBlockTag*( vp: VerifiedRpcProxy, blockTag: BlockTag ): Future[Result[seq[ReceiptObject], string]] {.async: (raises: []).} = - - let + let header = (await vp.getHeaderByTag(blockTag)).valueOr: return err(error) - rxs = + rxs = try: await vp.rpcClient.eth_getBlockReceipts(blockTag) except CatchableError as e: @@ -76,7 +74,8 @@ proc getReceiptsByBlockTag*( if rxs.isSome(): if orderedTrieRoot(toReceipts(rxs.get())) != header.receiptsRoot: - return err("downloaded receipts do not evaluate to the receipts root of the block") + return + err("downloaded receipts do not evaluate to the receipts root of the block") else: return err("error downloading the receipts") @@ -85,12 +84,12 @@ proc getReceiptsByBlockTag*( proc getReceiptsByBlockHash*( vp: VerifiedRpcProxy, blockHash: Hash32 ): Future[Result[seq[ReceiptObject], string]] {.async: (raises: []).} = - - let + let header = (await vp.getHeaderByHash(blockHash)).valueOr: return err(error) - blockTag = BlockTag(RtBlockIdentifier(kind: bidNumber, number: Quantity(header.number))) - rxs = + blockTag = + BlockTag(RtBlockIdentifier(kind: bidNumber, number: Quantity(header.number))) + rxs = try: await vp.rpcClient.eth_getBlockReceipts(blockTag) except CatchableError as e: @@ -98,7 +97,8 @@ proc getReceiptsByBlockHash*( if rxs.isSome(): if orderedTrieRoot(toReceipts(rxs.get())) != header.receiptsRoot: - return err("downloaded receipts do not evaluate to the receipts root of the block") + return + err("downloaded receipts do not evaluate to the receipts root of the block") else: return err("error downloading the receipts") diff --git a/nimbus_verified_proxy/rpc/rpc_eth_api.nim b/nimbus_verified_proxy/rpc/rpc_eth_api.nim index 8ab518cfed..9450371233 100644 --- a/nimbus_verified_proxy/rpc/rpc_eth_api.nim +++ b/nimbus_verified_proxy/rpc/rpc_eth_api.nim @@ -51,34 +51,33 @@ proc installEthApiHandlers*(vp: VerifiedRpcProxy) = (await vp.getBlockByHash(blockHash, fullTransactions)).valueOr: raise newException(ValueError, error) -# vp.proxy.rpc("eth_getUncleCountByBlockNumber") do( -# blockTag: BlockTag -# ) -> Quantity: -# let blk = (await vp.getBlockByTag(blockTag, false)).valueOr: -# raise newException(ValueError, error) -# return Quantity(blk.uncles.len()) -# -# vp.proxy.rpc("eth_getUncleCountByBlockHash") do( -# blockHash: Hash32 -# ) -> Quantity: -# let blk = (await vp.getBlockByHash(blockHash, false)).valueOr: -# raise newException(ValueError, error) -# return Quantity(blk.uncles.len()) -# -# vp.proxy.rpc("eth_getBlockTransactionCountByNumber") do( -# blockTag: BlockTag -# ) -> Quantity: -# let blk = (await vp.getBlockByTag(blockTag, true)).valueOr: -# raise newException(ValueError, error) -# return Quantity(blk.transactions.len) -# -# vp.proxy.rpc("eth_getBlockTransactionCountByHash") do( -# blockHash: Hash32 -# ) -> Quantity: -# let blk = (await vp.getBlockByHash(blockHash, true)).valueOr: -# raise newException(ValueError, error) -# return Quantity(blk.transactions.len) - + # vp.proxy.rpc("eth_getUncleCountByBlockNumber") do( + # blockTag: BlockTag + # ) -> Quantity: + # let blk = (await vp.getBlockByTag(blockTag, false)).valueOr: + # raise newException(ValueError, error) + # return Quantity(blk.uncles.len()) + # + # vp.proxy.rpc("eth_getUncleCountByBlockHash") do( + # blockHash: Hash32 + # ) -> Quantity: + # let blk = (await vp.getBlockByHash(blockHash, false)).valueOr: + # raise newException(ValueError, error) + # return Quantity(blk.uncles.len()) + # + # vp.proxy.rpc("eth_getBlockTransactionCountByNumber") do( + # blockTag: BlockTag + # ) -> Quantity: + # let blk = (await vp.getBlockByTag(blockTag, true)).valueOr: + # raise newException(ValueError, error) + # return Quantity(blk.transactions.len) + # + # vp.proxy.rpc("eth_getBlockTransactionCountByHash") do( + # blockHash: Hash32 + # ) -> Quantity: + # let blk = (await vp.getBlockByHash(blockHash, true)).valueOr: + # raise newException(ValueError, error) + # return Quantity(blk.transactions.len) vp.proxy.rpc("eth_getTransactionByBlockNumberAndIndex") do( blockTag: BlockTag, index: Quantity ) -> TransactionObject: @@ -102,35 +101,33 @@ proc installEthApiHandlers*(vp: VerifiedRpcProxy) = doAssert x.kind == tohTx return x.tx - vp.proxy.rpc("eth_getTransactionByHash") do( - txHash: Hash32 - ) -> TransactionObject: - let tx = + vp.proxy.rpc("eth_getTransactionByHash") do(txHash: Hash32) -> TransactionObject: + let tx = try: await vp.rpcClient.eth_getTransactionByHash(txHash) except CatchableError as e: raise newException(ValueError, e.msg) if tx.hash != txHash: - raise newException(ValueError, "the downloaded transaction hash doesn't match the requested transaction hash") + raise newException( + ValueError, + "the downloaded transaction hash doesn't match the requested transaction hash", + ) if not checkTxHash(tx, txHash): - raise newException(ValueError, "the transaction doesn't hash to the provided hash") + raise + newException(ValueError, "the transaction doesn't hash to the provided hash") return tx # TODO: this method should also support block hashes. For that, the client call should also suupport block hashes - vp.proxy.rpc("eth_getBlockReceipts") do( - blockTag: BlockTag - ) -> Opt[seq[ReceiptObject]]: + vp.proxy.rpc("eth_getBlockReceipts") do(blockTag: BlockTag) -> Opt[seq[ReceiptObject]]: let rxs = (await vp.getReceiptsByBlockTag(blockTag)).valueOr: raise newException(ValueError, error) return Opt.some(rxs) - vp.proxy.rpc("eth_getTransactionReceipt") do( - txHash: Hash32 - ) -> ReceiptObject: - let - rx = + vp.proxy.rpc("eth_getTransactionReceipt") do(txHash: Hash32) -> ReceiptObject: + let + rx = try: await vp.rpcClient.eth_getTransactionReceipt(txHash) except CatchableError as e: @@ -144,15 +141,14 @@ proc installEthApiHandlers*(vp: VerifiedRpcProxy) = raise newException(ValueError, "receipt couldn't be verified") -# # eth_blockNumber - get latest tag from header store -# vp.proxy.rpc("eth_blockNumber") do() -> Quantity: -# # Returns the number of the most recent block seen by the light client. -# let hLatest = vp.headerStore.latest() -# if hLatest.isNone: -# raise newException(ValueError, "Syncing") -# -# return Quantity(hLatest.get().number) - + # # eth_blockNumber - get latest tag from header store + # vp.proxy.rpc("eth_blockNumber") do() -> Quantity: + # # Returns the number of the most recent block seen by the light client. + # let hLatest = vp.headerStore.latest() + # if hLatest.isNone: + # raise newException(ValueError, "Syncing") + # + # return Quantity(hLatest.get().number) vp.proxy.rpc("eth_getBalance") do( address: addresses.Address, blockTag: BlockTag ) -> UInt256: @@ -162,13 +158,12 @@ proc installEthApiHandlers*(vp: VerifiedRpcProxy) = account = (await vp.getAccount(address, header.number, header.stateRoot)).valueOr: raise newException(ValueError, error) - account.balance vp.proxy.rpc("eth_getStorageAt") do( address: addresses.Address, slot: UInt256, blockTag: BlockTag ) -> FixedBytes[32]: - let + let header = (await vp.getHeaderByTag(blockTag)).valueOr: raise newException(ValueError, error) @@ -177,28 +172,26 @@ proc installEthApiHandlers*(vp: VerifiedRpcProxy) = FixedBytes[32](storage.toBytesBE) -# vp.proxy.rpc("eth_getTransactionCount") do( -# address: addresses.Address, blockTag: BlockTag -# ) -> Quantity: -# let -# header = (await vp.getHeaderByTag(blockTag)).valueOr: -# raise newException(ValueError, error) -# -# account = (await vp.getAccount(address, header.number, header.stateRoot)).valueOr: -# raise newException(ValueError, error) -# -# Quantity(account.nonce) - + # vp.proxy.rpc("eth_getTransactionCount") do( + # address: addresses.Address, blockTag: BlockTag + # ) -> Quantity: + # let + # header = (await vp.getHeaderByTag(blockTag)).valueOr: + # raise newException(ValueError, error) + # + # account = (await vp.getAccount(address, header.number, header.stateRoot)).valueOr: + # raise newException(ValueError, error) + # + # Quantity(account.nonce) vp.proxy.rpc("eth_getCode") do( address: addresses.Address, blockTag: BlockTag ) -> seq[byte]: - let - header = (await vp.getHeaderByTag(blockTag)).valueOr: - raise newException(ValueError, error) + let header = (await vp.getHeaderByTag(blockTag)).valueOr: + raise newException(ValueError, error) + + (await vp.getCode(address, header.number, header.stateRoot)).valueOr: + raise newException(ValueError, error) - (await vp.getCode(address,header.number, header.stateRoot)).valueOr: - raise newException(ValueError, error) - vp.proxy.rpc("eth_call") do( tx: TransactionArgs, blockTag: BlockTag, optimisticStateFetch: Opt[bool] ) -> seq[byte]: @@ -244,27 +237,27 @@ proc installEthApiHandlers*(vp: VerifiedRpcProxy) = return AccessListResult(accessList: accessList, error: error, gasUsed: gasUsed.Quantity) -# vp.proxy.rpc("eth_estimateGas") do( -# tx: TransactionArgs, blockTag: BlockTag, optimisticStateFetch: Opt[bool] -# ) -> Quantity: -# if tx.to.isNone(): -# raise newException(ValueError, "to address is required") -# -# if blockTag.kind == bidAlias: -# raise newException(ValueError, "tag not yet implemented") -# -# let -# header = (await vp.getHeaderByTag(blockTag)).valueOr: -# raise newException(ValueError, error) -# -# optimisticStateFetch = optimisticStateFetch.valueOr: -# true -# -# let gasEstimate = (await vp.evm.estimateGas(header, tx, optimisticStateFetch)).valueOr: -# raise newException(ValueError, error) -# -# return gasEstimate.Quantity -# + # vp.proxy.rpc("eth_estimateGas") do( + # tx: TransactionArgs, blockTag: BlockTag, optimisticStateFetch: Opt[bool] + # ) -> Quantity: + # if tx.to.isNone(): + # raise newException(ValueError, "to address is required") + # + # if blockTag.kind == bidAlias: + # raise newException(ValueError, "tag not yet implemented") + # + # let + # header = (await vp.getHeaderByTag(blockTag)).valueOr: + # raise newException(ValueError, error) + # + # optimisticStateFetch = optimisticStateFetch.valueOr: + # true + # + # let gasEstimate = (await vp.evm.estimateGas(header, tx, optimisticStateFetch)).valueOr: + # raise newException(ValueError, error) + # + # return gasEstimate.Quantity + # # TODO: # Following methods are forwarded directly to the web3 provider and therefore # are not validated in any way. diff --git a/nimbus_verified_proxy/rpc/transactions.nim b/nimbus_verified_proxy/rpc/transactions.nim index a630d17207..050c43f296 100644 --- a/nimbus_verified_proxy/rpc/transactions.nim +++ b/nimbus_verified_proxy/rpc/transactions.nim @@ -31,40 +31,48 @@ template calcWithdrawalsRoot*(withdrawals: openArray[Withdrawal]): Root = orderedTrieRoot(withdrawals) func vHashes(x: Opt[seq[Hash32]]): seq[VersionedHash] = - if x.isNone: return - else: x.get + if x.isNone: + return + else: + x.get func authList(x: Opt[seq[Authorization]]): seq[Authorization] = - if x.isNone: return - else: x.get + if x.isNone: + return + else: + x.get proc toTransaction(tx: TransactionObject): Transaction = Transaction( - txType : tx.`type`.get(0.Web3Quantity).TxType, - chainId : tx.chainId.get(0.u256), - nonce : tx.nonce.AccountNonce, - gasPrice : tx.gasPrice.GasInt, + txType: tx.`type`.get(0.Web3Quantity).TxType, + chainId: tx.chainId.get(0.u256), + nonce: tx.nonce.AccountNonce, + gasPrice: tx.gasPrice.GasInt, maxPriorityFeePerGas: tx.maxPriorityFeePerGas.get(0.Web3Quantity).GasInt, - maxFeePerGas : tx.maxFeePerGas.get(0.Web3Quantity).GasInt, - gasLimit : tx.gas.GasInt, - to : tx.to, - value : tx.value, - payload : tx.input, - accessList : tx.accessList.get(@[]), + maxFeePerGas: tx.maxFeePerGas.get(0.Web3Quantity).GasInt, + gasLimit: tx.gas.GasInt, + to: tx.to, + value: tx.value, + payload: tx.input, + accessList: tx.accessList.get(@[]), maxFeePerBlobGas: tx.maxFeePerBlobGas.get(0.u256), - versionedHashes : vHashes(tx.blobVersionedHashes), - V : tx.v.uint64, - R : tx.r, - S : tx.s, + versionedHashes: vHashes(tx.blobVersionedHashes), + V: tx.v.uint64, + R: tx.r, + S: tx.s, authorizationList: authList(tx.authorizationList), ) -proc toTransactions(txs: openArray[TxOrHash]): seq[Transaction] {.raises: [ValueError].} = +proc toTransactions( + txs: openArray[TxOrHash] +): seq[Transaction] {.raises: [ValueError].} = for x in txs: if x.kind == tohTx: result.add toTransaction(x.tx) else: - raise newException(ValueError, "cannot construct a transaction trie using only txhashes") + raise newException( + ValueError, "cannot construct a transaction trie using only txhashes" + ) proc checkTxHash*(txObj: TransactionObject, txHash: Hash32): bool = let tx = toTransaction(txObj) @@ -74,12 +82,10 @@ proc checkTxHash*(txObj: TransactionObject, txHash: Hash32): bool = return true proc verifyTransactions*( - txRoot: Hash32, - transactions: seq[TxOrHash], + txRoot: Hash32, transactions: seq[TxOrHash] ): Result[bool, string] = - try: - let txs = toTransactions(transactions) + let txs = toTransactions(transactions) let rootHash = orderedTrieRoot(txs) if rootHash == txRoot: return ok(true) diff --git a/nimbus_verified_proxy/types.nim b/nimbus_verified_proxy/types.nim index 97e9baf256..93f1d5e28f 100644 --- a/nimbus_verified_proxy/types.nim +++ b/nimbus_verified_proxy/types.nim @@ -5,20 +5,18 @@ # * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0). # at your option. This file may not be copied, modified, or distributed except according to those terms. -import - json_rpc/[rpcproxy], - stint, - ./header_store, - ../fluffy/evm/async_evm +import json_rpc/[rpcproxy], stint, ./header_store, ../fluffy/evm/async_evm -type - VerifiedRpcProxy* = ref object - evm*: AsyncEvm - proxy*: RpcProxy - headerStore*: HeaderStore - chainId*: UInt256 +type VerifiedRpcProxy* = ref object + evm*: AsyncEvm + proxy*: RpcProxy + headerStore*: HeaderStore + chainId*: UInt256 proc new*( - T: type VerifiedRpcProxy, proxy: RpcProxy, headerStore: HeaderStore, chainId: UInt256 + T: type VerifiedRpcProxy, + proxy: RpcProxy, + headerStore: HeaderStore, + chainId: UInt256, ): T = VerifiedRpcProxy(proxy: proxy, headerStore: headerStore, chainId: chainId) From 03f5c34bcd5450e49a180e894f22b43ed4a1e1ad Mon Sep 17 00:00:00 2001 From: chirag-parmar Date: Sat, 10 May 2025 12:00:33 +0530 Subject: [PATCH 15/18] copyright year --- nimbus_verified_proxy/header_store.nim | 2 +- nimbus_verified_proxy/rpc/accounts.nim | 2 +- nimbus_verified_proxy/rpc/blocks.nim | 2 +- nimbus_verified_proxy/rpc/evm.nim | 7 +++++++ nimbus_verified_proxy/rpc/receipts.nim | 2 +- nimbus_verified_proxy/rpc/transactions.nim | 2 +- nimbus_verified_proxy/types.nim | 2 +- 7 files changed, 13 insertions(+), 6 deletions(-) diff --git a/nimbus_verified_proxy/header_store.nim b/nimbus_verified_proxy/header_store.nim index 90704ba1b3..dcd6a10973 100644 --- a/nimbus_verified_proxy/header_store.nim +++ b/nimbus_verified_proxy/header_store.nim @@ -1,5 +1,5 @@ # nimbus_verified_proxy -# Copyright (c) 2022-2024 Status Research & Development GmbH +# Copyright (c) 2022-2025 Status Research & Development GmbH # Licensed and distributed under either of # * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT). # * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0). diff --git a/nimbus_verified_proxy/rpc/accounts.nim b/nimbus_verified_proxy/rpc/accounts.nim index 2a53d36782..7f1be7e402 100644 --- a/nimbus_verified_proxy/rpc/accounts.nim +++ b/nimbus_verified_proxy/rpc/accounts.nim @@ -1,5 +1,5 @@ # nimbus_verified_proxy -# Copyright (c) 2022-2024 Status Research & Development GmbH +# Copyright (c) 2022-2025 Status Research & Development GmbH # Licensed and distributed under either of # * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT). # * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0). diff --git a/nimbus_verified_proxy/rpc/blocks.nim b/nimbus_verified_proxy/rpc/blocks.nim index ad42b56f6d..1eece950a6 100644 --- a/nimbus_verified_proxy/rpc/blocks.nim +++ b/nimbus_verified_proxy/rpc/blocks.nim @@ -1,5 +1,5 @@ # nimbus_verified_proxy -# Copyright (c) 2022-2024 Status Research & Development GmbH +# Copyright (c) 2022-2025 Status Research & Development GmbH # Licensed and distributed under either of # * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT). # * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0). diff --git a/nimbus_verified_proxy/rpc/evm.nim b/nimbus_verified_proxy/rpc/evm.nim index 36c94d3ff0..b8dc2e703e 100644 --- a/nimbus_verified_proxy/rpc/evm.nim +++ b/nimbus_verified_proxy/rpc/evm.nim @@ -1,3 +1,10 @@ +# nimbus_verified_proxy +# Copyright (c) 2022-2025 Status Research & Development GmbH +# Licensed and distributed under either of +# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT). +# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0). +# at your option. This file may not be copied, modified, or distributed except according to those terms. + import ../../fluffy/evm/async_evm, ../../fluffy/evm/async_evm_backend, ./accounts, ../types diff --git a/nimbus_verified_proxy/rpc/receipts.nim b/nimbus_verified_proxy/rpc/receipts.nim index 8478f09231..873313e59b 100644 --- a/nimbus_verified_proxy/rpc/receipts.nim +++ b/nimbus_verified_proxy/rpc/receipts.nim @@ -1,5 +1,5 @@ # nimbus_verified_proxy -# Copyright (c) 2022-2024 Status Research & Development GmbH +# Copyright (c) 2022-2025 Status Research & Development GmbH # Licensed and distributed under either of # * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT). # * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0). diff --git a/nimbus_verified_proxy/rpc/transactions.nim b/nimbus_verified_proxy/rpc/transactions.nim index 050c43f296..3c688206a4 100644 --- a/nimbus_verified_proxy/rpc/transactions.nim +++ b/nimbus_verified_proxy/rpc/transactions.nim @@ -1,5 +1,5 @@ # nimbus_verified_proxy -# Copyright (c) 2022-2024 Status Research & Development GmbH +# Copyright (c) 2022-2025 Status Research & Development GmbH # Licensed and distributed under either of # * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT). # * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0). diff --git a/nimbus_verified_proxy/types.nim b/nimbus_verified_proxy/types.nim index 93f1d5e28f..cad2e4e28d 100644 --- a/nimbus_verified_proxy/types.nim +++ b/nimbus_verified_proxy/types.nim @@ -1,5 +1,5 @@ # nimbus_verified_proxy -# Copyright (c) 2022-2024 Status Research & Development GmbH +# Copyright (c) 2022-2025 Status Research & Development GmbH # Licensed and distributed under either of # * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT). # * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0). From e0fab2219e7c823fb374968c38746279b3c6f5e2 Mon Sep 17 00:00:00 2001 From: chirag-parmar Date: Tue, 13 May 2025 13:54:44 +0530 Subject: [PATCH 16/18] evm execution --- fluffy/evm/async_evm.nim | 6 +++ nimbus_verified_proxy/header_store.nim | 11 +++++ .../nimbus_verified_proxy.nim | 10 ++++- nimbus_verified_proxy/rpc/evm.nim | 2 +- nimbus_verified_proxy/rpc/rpc_eth_api.nim | 45 ++++++++++++++++++- nimbus_verified_proxy/types.nim | 6 ++- 6 files changed, 75 insertions(+), 5 deletions(-) diff --git a/fluffy/evm/async_evm.nim b/fluffy/evm/async_evm.nim index d63d3221d1..c2ee45a3f9 100644 --- a/fluffy/evm/async_evm.nim +++ b/fluffy/evm/async_evm.nim @@ -108,6 +108,12 @@ proc init*( AsyncEvm(com: com, backend: backend) +proc init*( + T: type AsyncEvm, backend: AsyncEvmStateBackend, com: CommonRef +): T = + AsyncEvm(com: com, backend: backend) + + template toCallResult(evmResult: EvmResult[CallResult]): Result[CallResult, string] = let callResult = ?evmResult.mapErr( diff --git a/nimbus_verified_proxy/header_store.nim b/nimbus_verified_proxy/header_store.nim index dcd6a10973..6205361b1f 100644 --- a/nimbus_verified_proxy/header_store.nim +++ b/nimbus_verified_proxy/header_store.nim @@ -107,6 +107,17 @@ proc latest*(self: HeaderStore): Opt[Header] = Opt.none(Header) +proc latestHash*(self: HeaderStore): Opt[Hash32] = + for h in self.headers.values: + let hash = + try: + self.hashes[h.number] + except: + return Opt.none(Hash32) + return Opt.some(hash) + + Opt.none(Hash32) + proc earliest*(self: HeaderStore): Opt[Header] = if self.headers.len() == 0: return Opt.none(Header) diff --git a/nimbus_verified_proxy/nimbus_verified_proxy.nim b/nimbus_verified_proxy/nimbus_verified_proxy.nim index 9fed75eccc..eeddf9da0c 100644 --- a/nimbus_verified_proxy/nimbus_verified_proxy.nim +++ b/nimbus_verified_proxy/nimbus_verified_proxy.nim @@ -22,6 +22,7 @@ import beacon_chain/spec/datatypes/[phase0, altair, bellatrix], beacon_chain/[light_client, nimbus_binary_common, version], ../execution_chain/rpc/cors, + ../execution_chain/common/common, ./rpc/rpc_eth_api, ./types, ./nimbus_verified_proxy_conf, @@ -83,7 +84,14 @@ proc run*( headerStore = HeaderStore.new(64) # block cache contains blocks downloaded from p2p - var verifiedProxy = VerifiedRpcProxy.new(rpcProxy, headerStore, chainId) + let com = CommonRef.new( + DefaultDbMemory.newCoreDbRef(), + taskpool = nil, + config = chainConfigForNetwork(chainId), + initializeDb = false, + ) + + var verifiedProxy = VerifiedRpcProxy.new(com, rpcProxy, headerStore, chainId) verifiedProxy.initEvm() # add handlers that verify RPC calls /rpc/rpc_eth_api.nim diff --git a/nimbus_verified_proxy/rpc/evm.nim b/nimbus_verified_proxy/rpc/evm.nim index b8dc2e703e..247c3f1a5f 100644 --- a/nimbus_verified_proxy/rpc/evm.nim +++ b/nimbus_verified_proxy/rpc/evm.nim @@ -38,4 +38,4 @@ proc toAsyncEvmStateBackend(vp: VerifiedRpcProxy): AsyncEvmStateBackend = AsyncEvmStateBackend.init(accProc, storageProc, codeProc) proc initEvm*(vp: var VerifiedRpcProxy) = - vp.evm = AsyncEvm.init(vp.toAsyncEvmStateBackend()) + vp.evm = AsyncEvm.init(vp.toAsyncEvmStateBackend(), vp.com) diff --git a/nimbus_verified_proxy/rpc/rpc_eth_api.nim b/nimbus_verified_proxy/rpc/rpc_eth_api.nim index 9450371233..e3631573e0 100644 --- a/nimbus_verified_proxy/rpc/rpc_eth_api.nim +++ b/nimbus_verified_proxy/rpc/rpc_eth_api.nim @@ -8,7 +8,7 @@ {.push raises: [].} import - std/strutils, + std/[strutils, algorithm], results, chronicles, json_rpc/[rpcserver, rpcclient, rpcproxy], @@ -18,6 +18,7 @@ import ../../execution_chain/beacon/web3_eth_conv, ../../execution_chain/common/common, ../../execution_chain/db/ledger, + ../../execution_chain/core/eip4844, ../../execution_chain/transaction/call_evm, ../../execution_chain/[evm/types, evm/state], ../validate_proof, @@ -258,6 +259,48 @@ proc installEthApiHandlers*(vp: VerifiedRpcProxy) = # # return gasEstimate.Quantity # + +# vp.proxy.rpc("eth_blobBaseFee") do() -> Quantity: +# let header = vp.headerStore.latest.valueOr: +# raise newException(ValueError, "Syncing") +# +# if header.blobGasUsed.isNone(): +# raise newException(ValueError, "blobGasUsed missing from latest header") +# if header.excessBlobGas.isNone(): +# raise newException(ValueError, "excessBlobGas missing from latest header") +# let blobBaseFee = +# getBlobBaseFee(header.excessBlobGas.get, vp.com, vp.com.toEVMFork(header)) * header.blobGasUsed.get.u256 +# if blobBaseFee > high(uint64).u256: +# raise newException(ValueError, "blobBaseFee is bigger than uint64.max") +# return w3Qty blobBaseFee.truncate(uint64) +# +# vp.proxy.rpc("eth_blobBaseFee") do() -> Quantity: +# # TODO: find a constant for this +# const minGasPrice = 30_000_000_000.GasInt +# var prices = newSeqOfCap[GasInt](64) +# let hash = vp.headerStore.latestHash.valueOr: +# raise newException(ValueError, "Syncing") +# +# let blk = (await vp.getBlockByHash(hash, true)).valueOr: +# raise newException(ValueError, "couldn't get the latest block") +# +# for tx in blk.transactions: +# if tx.kind == tohTx: +# prices.add(distinctBase(tx.tx.gasPrice)) +# +# var medianPrice: GasInt +# if prices.len > 0: +# sort(prices) +# let middle = prices.len div 2 +# if prices.len mod 2 == 0: +# # prevent overflow +# let price = prices[middle].uint64 + prices[middle - 1].uint64 +# medianPrice = (price div 2).GasInt +# else: +# medianPrice = prices[middle] +# +# return w3Qty max(medianPrice, minGasPrice) + # TODO: # Following methods are forwarded directly to the web3 provider and therefore # are not validated in any way. diff --git a/nimbus_verified_proxy/types.nim b/nimbus_verified_proxy/types.nim index cad2e4e28d..b81439968f 100644 --- a/nimbus_verified_proxy/types.nim +++ b/nimbus_verified_proxy/types.nim @@ -5,9 +5,10 @@ # * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0). # at your option. This file may not be copied, modified, or distributed except according to those terms. -import json_rpc/[rpcproxy], stint, ./header_store, ../fluffy/evm/async_evm +import json_rpc/[rpcproxy], stint, ./header_store, ../fluffy/evm/async_evm, ../execution_chain/common/common type VerifiedRpcProxy* = ref object + com*: CommonRef evm*: AsyncEvm proxy*: RpcProxy headerStore*: HeaderStore @@ -15,8 +16,9 @@ type VerifiedRpcProxy* = ref object proc new*( T: type VerifiedRpcProxy, + com: CommonRef, proxy: RpcProxy, headerStore: HeaderStore, chainId: UInt256, ): T = - VerifiedRpcProxy(proxy: proxy, headerStore: headerStore, chainId: chainId) + VerifiedRpcProxy(com: com, proxy: proxy, headerStore: headerStore, chainId: chainId) From c139f21f5b1ccb7739e61c650a8d7e6455128169 Mon Sep 17 00:00:00 2001 From: chirag-parmar Date: Tue, 13 May 2025 14:16:10 +0530 Subject: [PATCH 17/18] nph format --- fluffy/evm/async_evm.nim | 5 +- nimbus_verified_proxy/rpc/rpc_eth_api.nim | 80 +++++++++++------------ nimbus_verified_proxy/types.nim | 7 +- 3 files changed, 47 insertions(+), 45 deletions(-) diff --git a/fluffy/evm/async_evm.nim b/fluffy/evm/async_evm.nim index c2ee45a3f9..80aefab241 100644 --- a/fluffy/evm/async_evm.nim +++ b/fluffy/evm/async_evm.nim @@ -108,12 +108,9 @@ proc init*( AsyncEvm(com: com, backend: backend) -proc init*( - T: type AsyncEvm, backend: AsyncEvmStateBackend, com: CommonRef -): T = +proc init*(T: type AsyncEvm, backend: AsyncEvmStateBackend, com: CommonRef): T = AsyncEvm(com: com, backend: backend) - template toCallResult(evmResult: EvmResult[CallResult]): Result[CallResult, string] = let callResult = ?evmResult.mapErr( diff --git a/nimbus_verified_proxy/rpc/rpc_eth_api.nim b/nimbus_verified_proxy/rpc/rpc_eth_api.nim index e3631573e0..f8eeec78da 100644 --- a/nimbus_verified_proxy/rpc/rpc_eth_api.nim +++ b/nimbus_verified_proxy/rpc/rpc_eth_api.nim @@ -260,46 +260,46 @@ proc installEthApiHandlers*(vp: VerifiedRpcProxy) = # return gasEstimate.Quantity # -# vp.proxy.rpc("eth_blobBaseFee") do() -> Quantity: -# let header = vp.headerStore.latest.valueOr: -# raise newException(ValueError, "Syncing") -# -# if header.blobGasUsed.isNone(): -# raise newException(ValueError, "blobGasUsed missing from latest header") -# if header.excessBlobGas.isNone(): -# raise newException(ValueError, "excessBlobGas missing from latest header") -# let blobBaseFee = -# getBlobBaseFee(header.excessBlobGas.get, vp.com, vp.com.toEVMFork(header)) * header.blobGasUsed.get.u256 -# if blobBaseFee > high(uint64).u256: -# raise newException(ValueError, "blobBaseFee is bigger than uint64.max") -# return w3Qty blobBaseFee.truncate(uint64) -# -# vp.proxy.rpc("eth_blobBaseFee") do() -> Quantity: -# # TODO: find a constant for this -# const minGasPrice = 30_000_000_000.GasInt -# var prices = newSeqOfCap[GasInt](64) -# let hash = vp.headerStore.latestHash.valueOr: -# raise newException(ValueError, "Syncing") -# -# let blk = (await vp.getBlockByHash(hash, true)).valueOr: -# raise newException(ValueError, "couldn't get the latest block") -# -# for tx in blk.transactions: -# if tx.kind == tohTx: -# prices.add(distinctBase(tx.tx.gasPrice)) -# -# var medianPrice: GasInt -# if prices.len > 0: -# sort(prices) -# let middle = prices.len div 2 -# if prices.len mod 2 == 0: -# # prevent overflow -# let price = prices[middle].uint64 + prices[middle - 1].uint64 -# medianPrice = (price div 2).GasInt -# else: -# medianPrice = prices[middle] -# -# return w3Qty max(medianPrice, minGasPrice) + # vp.proxy.rpc("eth_blobBaseFee") do() -> Quantity: + # let header = vp.headerStore.latest.valueOr: + # raise newException(ValueError, "Syncing") + # + # if header.blobGasUsed.isNone(): + # raise newException(ValueError, "blobGasUsed missing from latest header") + # if header.excessBlobGas.isNone(): + # raise newException(ValueError, "excessBlobGas missing from latest header") + # let blobBaseFee = + # getBlobBaseFee(header.excessBlobGas.get, vp.com, vp.com.toEVMFork(header)) * header.blobGasUsed.get.u256 + # if blobBaseFee > high(uint64).u256: + # raise newException(ValueError, "blobBaseFee is bigger than uint64.max") + # return w3Qty blobBaseFee.truncate(uint64) + # + # vp.proxy.rpc("eth_blobBaseFee") do() -> Quantity: + # # TODO: find a constant for this + # const minGasPrice = 30_000_000_000.GasInt + # var prices = newSeqOfCap[GasInt](64) + # let hash = vp.headerStore.latestHash.valueOr: + # raise newException(ValueError, "Syncing") + # + # let blk = (await vp.getBlockByHash(hash, true)).valueOr: + # raise newException(ValueError, "couldn't get the latest block") + # + # for tx in blk.transactions: + # if tx.kind == tohTx: + # prices.add(distinctBase(tx.tx.gasPrice)) + # + # var medianPrice: GasInt + # if prices.len > 0: + # sort(prices) + # let middle = prices.len div 2 + # if prices.len mod 2 == 0: + # # prevent overflow + # let price = prices[middle].uint64 + prices[middle - 1].uint64 + # medianPrice = (price div 2).GasInt + # else: + # medianPrice = prices[middle] + # + # return w3Qty max(medianPrice, minGasPrice) # TODO: # Following methods are forwarded directly to the web3 provider and therefore diff --git a/nimbus_verified_proxy/types.nim b/nimbus_verified_proxy/types.nim index b81439968f..ec6968dd9a 100644 --- a/nimbus_verified_proxy/types.nim +++ b/nimbus_verified_proxy/types.nim @@ -5,7 +5,12 @@ # * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0). # at your option. This file may not be copied, modified, or distributed except according to those terms. -import json_rpc/[rpcproxy], stint, ./header_store, ../fluffy/evm/async_evm, ../execution_chain/common/common +import + json_rpc/[rpcproxy], + stint, + ./header_store, + ../fluffy/evm/async_evm, + ../execution_chain/common/common type VerifiedRpcProxy* = ref object com*: CommonRef From 2c62fc86cb306b5f3ad68a1a87bdb752d5aff0bb Mon Sep 17 00:00:00 2001 From: chirag-parmar Date: Tue, 13 May 2025 14:56:26 +0530 Subject: [PATCH 18/18] remove unused headers --- nimbus_verified_proxy/rpc/accounts.nim | 3 +-- nimbus_verified_proxy/rpc/blocks.nim | 2 +- nimbus_verified_proxy/rpc/receipts.nim | 5 ++--- nimbus_verified_proxy/rpc/transactions.nim | 7 ++----- 4 files changed, 6 insertions(+), 11 deletions(-) diff --git a/nimbus_verified_proxy/rpc/accounts.nim b/nimbus_verified_proxy/rpc/accounts.nim index 7f1be7e402..d5ea5d559d 100644 --- a/nimbus_verified_proxy/rpc/accounts.nim +++ b/nimbus_verified_proxy/rpc/accounts.nim @@ -19,8 +19,7 @@ import eth/trie/[hexary_proof_verification], json_rpc/[rpcproxy, rpcserver, rpcclient], web3/[primitives, eth_api_types, eth_api], - ../types, - ../header_store + ../types export results, stint, hashes_rlp, accounts_rlp, eth_api_types diff --git a/nimbus_verified_proxy/rpc/blocks.nim b/nimbus_verified_proxy/rpc/blocks.nim index 1eece950a6..51234af5dd 100644 --- a/nimbus_verified_proxy/rpc/blocks.nim +++ b/nimbus_verified_proxy/rpc/blocks.nim @@ -15,7 +15,7 @@ import json_rpc/[rpcproxy, rpcserver, rpcclient], eth/common/addresses, eth/common/eth_types_rlp, - eth/trie/[hexary, ordered_trie, db, trie_defs], + eth/trie/[ordered_trie, trie_defs], ../../execution_chain/beacon/web3_eth_conv, ../types, ../header_store, diff --git a/nimbus_verified_proxy/rpc/receipts.nim b/nimbus_verified_proxy/rpc/receipts.nim index 873313e59b..efe76a6816 100644 --- a/nimbus_verified_proxy/rpc/receipts.nim +++ b/nimbus_verified_proxy/rpc/receipts.nim @@ -16,12 +16,11 @@ import ../../execution_chain/beacon/web3_eth_conv, eth/common/addresses, eth/common/eth_types_rlp, - eth/trie/[hexary, ordered_trie, db, trie_defs], + eth/trie/[ordered_trie, trie_defs], json_rpc/[rpcproxy, rpcserver, rpcclient], web3/[primitives, eth_api_types, eth_api], ../types, - ./blocks, - ../header_store + ./blocks export results, stint, hashes_rlp, accounts_rlp, eth_api_types diff --git a/nimbus_verified_proxy/rpc/transactions.nim b/nimbus_verified_proxy/rpc/transactions.nim index 3c688206a4..b5df635f14 100644 --- a/nimbus_verified_proxy/rpc/transactions.nim +++ b/nimbus_verified_proxy/rpc/transactions.nim @@ -8,19 +8,16 @@ {.push raises: [].} import - std/sequtils, stint, results, - chronicles, eth/common/[base_rlp, transactions_rlp, receipts_rlp, hashes_rlp], ../../execution_chain/beacon/web3_eth_conv, eth/common/addresses, eth/common/eth_types_rlp, - eth/trie/[hexary, ordered_trie, db, trie_defs], + eth/trie/[ordered_trie, trie_defs], json_rpc/[rpcproxy, rpcserver, rpcclient], web3/[primitives, eth_api_types, eth_api], - ../types, - ../header_store + ../types export results, stint, hashes_rlp, accounts_rlp, eth_api_types