From 1377f93d503c2f9f8d4e513b271758b15318bcd8 Mon Sep 17 00:00:00 2001 From: web3-developer <51288821+web3-developer@users.noreply.github.com> Date: Sat, 15 Jun 2024 01:03:18 +0800 Subject: [PATCH] Implement Fluffy JSON-RPC endpoints. (#2364) * Implement eth_getBalance, eth_getTransactionCount, eth_getStorageAt and eth_getCode JSON-RPCs. * Fixes. --- fluffy/fluffy.nim | 2 +- fluffy/network/state/state_network.nim | 3 +- fluffy/rpc/rpc_eth_api.nim | 179 +++++++++++++++++-------- 3 files changed, 124 insertions(+), 60 deletions(-) diff --git a/fluffy/fluffy.nim b/fluffy/fluffy.nim index 0f5d0a8a4..2d0d90e79 100644 --- a/fluffy/fluffy.nim +++ b/fluffy/fluffy.nim @@ -349,7 +349,7 @@ proc run(config: PortalConf) {.raises: [CatchableError].} = ) if historyNetwork.isSome(): rpcHttpServerWithProxy.installEthApiHandlers( - historyNetwork.get(), beaconLightClient + historyNetwork.get(), beaconLightClient, stateNetwork ) rpcHttpServerWithProxy.installPortalApiHandlers( historyNetwork.get().portalProtocol, "history" diff --git a/fluffy/network/state/state_network.nim b/fluffy/network/state/state_network.nim index da1c77cc5..b512455b1 100644 --- a/fluffy/network/state/state_network.nim +++ b/fluffy/network/state/state_network.nim @@ -223,7 +223,8 @@ proc processContentLoop(n: StateNetwork) {.async: (raises: []).} = trace "processContentLoop canceled" proc start*(n: StateNetwork) = - info "Starting Portal State Network", protocolId = n.portalProtocol.protocolId + info "Starting Portal execution state network", + protocolId = n.portalProtocol.protocolId n.portalProtocol.start() n.processContentLoop = processContentLoop(n) diff --git a/fluffy/rpc/rpc_eth_api.nim b/fluffy/rpc/rpc_eth_api.nim index f9d85dd4a..99ce4df79 100644 --- a/fluffy/rpc/rpc_eth_api.nim +++ b/fluffy/rpc/rpc_eth_api.nim @@ -12,9 +12,11 @@ import json_rpc/[rpcproxy, rpcserver], web3/conversions, # sigh, for FixedBytes marshalling web3/eth_api_types, - eth/[common/eth_types, rlp], + web3/primitives as web3types, + eth/common/eth_types, beacon_chain/spec/forks, ../network/history/[history_network, history_content], + ../network/state/[state_network, state_content, state_endpoints], ../network/beacon/beacon_light_client from ../../nimbus/transaction import getSender, ValidationError @@ -127,6 +129,7 @@ proc installEthApiHandlers*( rpcServerWithProxy: var RpcProxy, historyNetwork: HistoryNetwork, beaconLightClient: Opt[LightClient], + stateNetwork: Opt[StateNetwork], ) = # Supported API rpcServerWithProxy.registerProxyMethod("eth_blockNumber") @@ -343,65 +346,125 @@ proc installEthApiHandlers*( return filteredLogs else: # bloomfilter returned false, there are no logs matching the criteria - return - @[] + return @[] - # rpcServerWithProxy.rpc("eth_getBalance") do( - # data: Address, quantityTag: RtBlockIdentifier - # ) -> UInt256: - # ## Returns the balance of the account of given address. - # ## - # ## data: address to check for balance. - # ## quantityTag: integer block number, or the string "latest", "earliest" or "pending", see the default block parameter. - # ## Returns integer of the current balance in wei. - # # TODO - # raiseAssert("Not implemented") + rpcServerWithProxy.rpc("eth_getBalance") do( + data: web3Types.Address, quantityTag: RtBlockIdentifier + ) -> UInt256: + ## Returns the balance of the account of given address. + ## + ## data: address to check for balance. + ## quantityTag: integer block number, or the string "latest", "earliest" or "pending", see the default block parameter. + ## Returns integer of the current balance in wei. + if stateNetwork.isNone(): + raise newException(ValueError, "State sub-network not enabled") - # rpcServerWithProxy.rpc("eth_getTransactionCount") do( - # data: Address, quantityTag: RtBlockIdentifier - # ) -> Quantity: - # ## Returns the number of transactions sent from an address. - # ## - # ## data: address. - # ## quantityTag: integer block number, or the string "latest", "earliest" or "pending", see the default block parameter. - # ## Returns integer of the number of transactions send from this address. - # # TODO - # raiseAssert("Not implemented") + if quantityTag.kind == bidAlias: + # TODO: Implement + raise newException(ValueError, "tag not yet implemented") + else: + let + blockNumber = quantityTag.number.uint64.u256 + blockHash = (await historyNetwork.getBlockHashByNumber(blockNumber)).valueOr: + raise newException(ValueError, error) - # rpcServerWithProxy.rpc("eth_getStorageAt") do( - # data: Address, slot: UInt256, quantityTag: RtBlockIdentifier - # ) -> FixedBytes[32]: - # ## Returns the value from a storage position at a given address. - # ## - # ## data: address of the storage. - # ## slot: integer of the position in the storage. - # ## quantityTag: integer block number, or the string "latest", "earliest" or "pending", see the default block parameter. - # ## Returns: the value at this storage position. - # # TODO - # raiseAssert("Not implemented") + balance = (await stateNetwork.get().getBalance(blockHash, data.EthAddress)).valueOr: + # Should we return 0 here or throw a more detailed error? + raise newException(ValueError, "Unable to get balance") - # rpcServerWithProxy.rpc("eth_getCode") do( - # data: Address, quantityTag: RtBlockIdentifier - # ) -> seq[byte]: - # ## Returns code at a given address. - # ## - # ## data: address - # ## quantityTag: integer block number, or the string "latest", "earliest" or "pending", see the default block parameter. - # ## Returns the code from the given address. - # # TODO - # raiseAssert("Not implemented") + return balance - # rpcServerWithProxy.rpc("eth_getProof") do( - # address: Address, slots: seq[UInt256], quantityTag: RtBlockIdentifier - # ) -> ProofResponse: - # ## Returns information about an account and storage slots (if the account is a contract - # ## and the slots are requested) along with account and storage proofs which prove the - # ## existence of the values in the state. - # ## See spec here: https://eips.ethereum.org/EIPS/eip-1186 - # ## - # ## data: address of the account. - # ## slots: integers of the positions in the storage to return with storage proofs. - # ## quantityTag: integer block number, or the string "latest", "earliest" or "pending", see the default block parameter. - # ## Returns: the proof response containing the account, account proof and storage proof - # # TODO - # raiseAssert("Not implemented") + rpcServerWithProxy.rpc("eth_getTransactionCount") do( + data: web3Types.Address, quantityTag: RtBlockIdentifier + ) -> Quantity: + ## Returns the number of transactions sent from an address. + ## + ## data: address. + ## quantityTag: integer block number, or the string "latest", "earliest" or "pending", see the default block parameter. + ## Returns integer of the number of transactions send from this address. + if stateNetwork.isNone(): + raise newException(ValueError, "State sub-network not enabled") + + if quantityTag.kind == bidAlias: + # TODO: Implement + raise newException(ValueError, "tag not yet implemented") + else: + let + blockNumber = quantityTag.number.uint64.u256 + blockHash = (await historyNetwork.getBlockHashByNumber(blockNumber)).valueOr: + raise newException(ValueError, error) + + nonce = ( + await stateNetwork.get().getTransactionCount(blockHash, data.EthAddress) + ).valueOr: + # Should we return 0 here or throw a more detailed error? + raise newException(ValueError, "Unable to get transaction count") + return nonce.Quantity + + rpcServerWithProxy.rpc("eth_getStorageAt") do( + data: web3Types.Address, slot: UInt256, quantityTag: RtBlockIdentifier + ) -> FixedBytes[32]: + ## Returns the value from a storage position at a given address. + ## + ## data: address of the storage. + ## slot: integer of the position in the storage. + ## quantityTag: integer block number, or the string "latest", "earliest" or "pending", see the default block parameter. + ## Returns: the value at this storage position. + if stateNetwork.isNone(): + raise newException(ValueError, "State sub-network not enabled") + + if quantityTag.kind == bidAlias: + # TODO: Implement + raise newException(ValueError, "tag not yet implemented") + else: + let + blockNumber = quantityTag.number.uint64.u256 + blockHash = (await historyNetwork.getBlockHashByNumber(blockNumber)).valueOr: + raise newException(ValueError, error) + + slotValue = ( + await stateNetwork.get().getStorageAt(blockHash, data.EthAddress, slot) + ).valueOr: + # Should we return 0 here or throw a more detailed error? + raise newException(ValueError, "Unable to get storage slot") + return FixedBytes[32](slotValue.toBytesBE()) + + rpcServerWithProxy.rpc("eth_getCode") do( + data: web3Types.Address, quantityTag: RtBlockIdentifier + ) -> seq[byte]: + ## Returns code at a given address. + ## + ## data: address + ## quantityTag: integer block number, or the string "latest", "earliest" or "pending", see the default block parameter. + ## Returns the code from the given address. + if stateNetwork.isNone(): + raise newException(ValueError, "State sub-network not enabled") + + if quantityTag.kind == bidAlias: + # TODO: Implement + raise newException(ValueError, "tag not yet implemented") + else: + let + blockNumber = quantityTag.number.uint64.u256 + blockHash = (await historyNetwork.getBlockHashByNumber(blockNumber)).valueOr: + raise newException(ValueError, error) + + bytecode = (await stateNetwork.get().getCode(blockHash, data.EthAddress)).valueOr: + # Should we return empty sequence here or throw a more detailed error? + raise newException(ValueError, "Unable to get code") + return bytecode.asSeq() + + # rpcServerWithProxy.rpc("eth_getProof") do( + # address: Address, slots: seq[UInt256], quantityTag: RtBlockIdentifier + # ) -> ProofResponse: + # ## Returns information about an account and storage slots (if the account is a contract + # ## and the slots are requested) along with account and storage proofs which prove the + # ## existence of the values in the state. + # ## See spec here: https://eips.ethereum.org/EIPS/eip-1186 + # ## + # ## data: address of the account. + # ## slots: integers of the positions in the storage to return with storage proofs. + # ## quantityTag: integer block number, or the string "latest", "earliest" or "pending", see the default block parameter. + # ## Returns: the proof response containing the account, account proof and storage proof + # # TODO + # raiseAssert("Not implemented")