Get rid of HexQuantityStr and HexDataStr usage

Both types are not safe and require validation/conversion
from rpc implementer.
This PR change it to safer types and delegate the conversion
and validation to the rpc library.
This commit is contained in:
jangko 2023-12-27 07:48:53 +07:00
parent 5e95df6bde
commit c0d52ba179
No known key found for this signature in database
GPG Key ID: 31702AE10541E6B9
8 changed files with 133 additions and 153 deletions

View File

@ -10,7 +10,7 @@
import import
std/[times, sequtils, strutils, typetraits], std/[times, sequtils, strutils, typetraits],
json_rpc/[rpcproxy, rpcserver], stew/byteutils, json_rpc/[rpcproxy, rpcserver], stew/byteutils,
web3/[conversions, ethhexstrings], # sigh, for FixedBytes marshalling web3/[conversions], # sigh, for FixedBytes marshalling
eth/[common/eth_types, rlp], eth/[common/eth_types, rlp],
beacon_chain/spec/forks, beacon_chain/spec/forks,
../../nimbus/rpc/[rpc_types, filters], ../../nimbus/rpc/[rpc_types, filters],
@ -218,55 +218,56 @@ proc installEthApiHandlers*(
return some(BlockObject.init(header, body, fullTransactions)) return some(BlockObject.init(header, body, fullTransactions))
rpcServerWithProxy.rpc("eth_getBlockByNumber") do( rpcServerWithProxy.rpc("eth_getBlockByNumber") do(
quantityTag: string, fullTransactions: bool) -> Option[BlockObject]: quantityTag: BlockTag, fullTransactions: bool) -> Option[BlockObject]:
let tag = quantityTag.toLowerAscii
case tag
of "latest":
# TODO:
# I assume this would refer to the content in the latest optimistic update
# in case the majority treshold is not met. And if it is met it is the
# same as the safe version?
raise newException(ValueError, "Latest tag not yet implemented")
of "earliest":
raise newException(ValueError, "Earliest tag not yet implemented")
of "safe":
if beaconLightClient.isNone():
raise newException(ValueError, "Safe tag not yet implemented")
withForkyStore(beaconLightClient.value().store[]): if quantityTag.kind == bidAlias:
when lcDataFork > LightClientDataFork.Altair: let tag = quantityTag.alias.toLowerAscii
let case tag
blockHash = forkyStore.optimistic_header.execution.block_hash of "latest":
(header, body) = (await historyNetwork.getBlock(blockHash)).valueOr: # TODO:
return none(BlockObject) # I assume this would refer to the content in the latest optimistic update
# in case the majority treshold is not met. And if it is met it is the
# same as the safe version?
raise newException(ValueError, "Latest tag not yet implemented")
of "earliest":
raise newException(ValueError, "Earliest tag not yet implemented")
of "safe":
if beaconLightClient.isNone():
raise newException(ValueError, "Safe tag not yet implemented")
return some(BlockObject.init(header, body, fullTransactions)) withForkyStore(beaconLightClient.value().store[]):
else: when lcDataFork > LightClientDataFork.Altair:
raise newException( let
ValueError, "Not available before Capella - not synced?") blockHash = forkyStore.optimistic_header.execution.block_hash
of "finalized": (header, body) = (await historyNetwork.getBlock(blockHash)).valueOr:
if beaconLightClient.isNone(): return none(BlockObject)
raise newException(ValueError, "Finalized tag not yet implemented")
withForkyStore(beaconLightClient.value().store[]): return some(BlockObject.init(header, body, fullTransactions))
when lcDataFork > LightClientDataFork.Altair: else:
let raise newException(
blockHash = forkyStore.finalized_header.execution.block_hash ValueError, "Not available before Capella - not synced?")
(header, body) = (await historyNetwork.getBlock(blockHash)).valueOr: of "finalized":
return none(BlockObject) if beaconLightClient.isNone():
raise newException(ValueError, "Finalized tag not yet implemented")
return some(BlockObject.init(header, body, fullTransactions)) withForkyStore(beaconLightClient.value().store[]):
else: when lcDataFork > LightClientDataFork.Altair:
raise newException( let
ValueError, "Not available before Capella - not synced?") blockHash = forkyStore.finalized_header.execution.block_hash
of "pending": (header, body) = (await historyNetwork.getBlock(blockHash)).valueOr:
raise newException(ValueError, "Pending tag not yet implemented") return none(BlockObject)
return some(BlockObject.init(header, body, fullTransactions))
else:
raise newException(
ValueError, "Not available before Capella - not synced?")
of "pending":
raise newException(ValueError, "Pending tag not yet implemented")
else:
raise newException(ValueError, "Unsupported block tag " & tag)
else: else:
if not validate(quantityTag.HexQuantityStr):
raise newException(ValueError, "Provided block number is not a hex number")
let let
blockNumber = fromHex(UInt256, quantityTag) blockNumber = quantityTag.number.toBlockNumber
maybeBlock = (await historyNetwork.getBlock(blockNumber)).valueOr: maybeBlock = (await historyNetwork.getBlock(blockNumber)).valueOr:
raise newException(ValueError, error) raise newException(ValueError, error)

View File

@ -136,7 +136,6 @@ type
proc fetchAccountAndSlots*(rpcClient: RpcClient, address: EthAddress, slots: seq[UInt256], blockNumber: common.BlockNumber): Future[(Account, AccountProof, seq[StorageProof])] {.async.} = proc fetchAccountAndSlots*(rpcClient: RpcClient, address: EthAddress, slots: seq[UInt256], blockNumber: common.BlockNumber): Future[(Account, AccountProof, seq[StorageProof])] {.async.} =
let t0 = now() let t0 = now()
debug "Got to fetchAccountAndSlots", address=address, slots=slots, blockNumber=blockNumber debug "Got to fetchAccountAndSlots", address=address, slots=slots, blockNumber=blockNumber
#let blockNumberHexStr: HexQuantityStr = encodeQuantity(blockNumber)
let blockNumberUint64 = blockNumber.truncate(uint64) let blockNumberUint64 = blockNumber.truncate(uint64)
let a = web3.Address(address) let a = web3.Address(address)
let bid = blockId(blockNumber.truncate(uint64)) let bid = blockId(blockNumber.truncate(uint64))

View File

@ -9,7 +9,9 @@
import import
std/json, std/json,
json_rpc/rpcserver, rpc_utils, json_rpc/rpcserver,
./rpc_utils,
./rpc_types,
../tracer, ../vm_types, ../tracer, ../vm_types,
../common/common, ../common/common,
../beacon/web3_eth_conv, ../beacon/web3_eth_conv,
@ -63,7 +65,7 @@ proc setupDebugRpc*(com: CommonRef, rpcsrv: RpcServer) =
result = traceTransaction(com, blockHeader, blockBody, txDetails.index, flags) result = traceTransaction(com, blockHeader, blockBody, txDetails.index, flags)
rpcsrv.rpc("debug_dumpBlockStateByNumber") do(quantityTag: string) -> JsonNode: rpcsrv.rpc("debug_dumpBlockStateByNumber") do(quantityTag: BlockTag) -> JsonNode:
## Retrieves the state that corresponds to the block number and returns ## Retrieves the state that corresponds to the block number and returns
## a list of accounts (including storage and code). ## a list of accounts (including storage and code).
## ##
@ -89,7 +91,7 @@ proc setupDebugRpc*(com: CommonRef, rpcsrv: RpcServer) =
result = dumpBlockState(com, header, body) result = dumpBlockState(com, header, body)
rpcsrv.rpc("debug_traceBlockByNumber") do(quantityTag: string, options: Option[TraceOptions]) -> JsonNode: rpcsrv.rpc("debug_traceBlockByNumber") do(quantityTag: BlockTag, options: Option[TraceOptions]) -> JsonNode:
## The traceBlock method will return a full stack trace of all invoked opcodes of all transaction ## The traceBlock method will return a full stack trace of all invoked opcodes of all transaction
## that were included included in this block. ## that were included included in this block.
## ##
@ -119,7 +121,7 @@ proc setupDebugRpc*(com: CommonRef, rpcsrv: RpcServer) =
result = traceBlock(com, header, body, flags) result = traceBlock(com, header, body, flags)
rpcsrv.rpc("debug_setHead") do(quantityTag: string) -> bool: rpcsrv.rpc("debug_setHead") do(quantityTag: BlockTag) -> bool:
## Sets the current head of the local chain by block number. ## Sets the current head of the local chain by block number.
## Note, this is a destructive action and may severely damage your chain. ## Note, this is a destructive action and may severely damage your chain.
## Use with extreme caution. ## Use with extreme caution.

View File

@ -25,15 +25,6 @@ import
../beacon/web3_eth_conv, ../beacon/web3_eth_conv,
./filters ./filters
#[
Note:
* Hexstring types (HexQuantitySt, HexDataStr, Web3Address, Web3Hash)
are parsed to check format before the RPC blocks are executed and will
raise an exception if invalid.
* Many of the RPC calls do not validate hex string types when output, only
type cast to avoid extra processing.
]#
type type
BlockHeader = eth_types.BlockHeader BlockHeader = eth_types.BlockHeader
Hash256 = eth_types.Hash256 Hash256 = eth_types.Hash256
@ -49,9 +40,9 @@ proc setupEthRpc*(
let ac = newAccountStateDB(chainDB, header.stateRoot, com.pruneTrie) let ac = newAccountStateDB(chainDB, header.stateRoot, com.pruneTrie)
result = ReadOnlyStateDB(ac) result = ReadOnlyStateDB(ac)
proc stateDBFromTag(tag: string, readOnly = true): ReadOnlyStateDB proc stateDBFromTag(quantityTag: BlockTag, readOnly = true): ReadOnlyStateDB
{.gcsafe, raises: [CatchableError].} = {.gcsafe, raises: [CatchableError].} =
result = getStateDB(chainDB.headerFromTag(tag)) result = getStateDB(chainDB.headerFromTag(quantityTag))
server.rpc("eth_protocolVersion") do() -> Option[string]: server.rpc("eth_protocolVersion") do() -> Option[string]:
# Old Ethereum wiki documents this as returning a decimal string. # Old Ethereum wiki documents this as returning a decimal string.
@ -112,7 +103,7 @@ proc setupEthRpc*(
## Returns integer of the current block number the client is on. ## Returns integer of the current block number the client is on.
result = w3Qty(chainDB.getCanonicalHead().blockNumber) result = w3Qty(chainDB.getCanonicalHead().blockNumber)
server.rpc("eth_getBalance") do(data: Web3Address, quantityTag: string) -> UInt256: server.rpc("eth_getBalance") do(data: Web3Address, quantityTag: BlockTag) -> UInt256:
## Returns the balance of the account of given address. ## Returns the balance of the account of given address.
## ##
## data: address to check for balance. ## data: address to check for balance.
@ -123,7 +114,7 @@ proc setupEthRpc*(
address = data.ethAddr address = data.ethAddr
result = accDB.getBalance(address) result = accDB.getBalance(address)
server.rpc("eth_getStorageAt") do(data: Web3Address, slot: UInt256, quantityTag: string) -> UInt256: server.rpc("eth_getStorageAt") do(data: Web3Address, slot: UInt256, quantityTag: BlockTag) -> UInt256:
## Returns the value from a storage position at a given address. ## Returns the value from a storage position at a given address.
## ##
## data: address of the storage. ## data: address of the storage.
@ -135,7 +126,7 @@ proc setupEthRpc*(
address = data.ethAddr address = data.ethAddr
result = accDB.getStorage(address, slot)[0] result = accDB.getStorage(address, slot)[0]
server.rpc("eth_getTransactionCount") do(data: Web3Address, quantityTag: string) -> Web3Quantity: server.rpc("eth_getTransactionCount") do(data: Web3Address, quantityTag: BlockTag) -> Web3Quantity:
## Returns the number of transactions sent from an address. ## Returns the number of transactions sent from an address.
## ##
## data: address. ## data: address.
@ -157,7 +148,7 @@ proc setupEthRpc*(
txCount = chainDB.getTransactionCount(header.txRoot) txCount = chainDB.getTransactionCount(header.txRoot)
result = w3Qty(txCount) result = w3Qty(txCount)
server.rpc("eth_getBlockTransactionCountByNumber") do(quantityTag: string) -> Web3Quantity: server.rpc("eth_getBlockTransactionCountByNumber") do(quantityTag: BlockTag) -> Web3Quantity:
## Returns the number of transactions in a block matching the given block number. ## Returns the number of transactions in a block matching the given block number.
## ##
## data: integer of a block number, or the string "earliest", "latest" or "pending", as in the default block parameter. ## data: integer of a block number, or the string "earliest", "latest" or "pending", as in the default block parameter.
@ -178,7 +169,7 @@ proc setupEthRpc*(
unclesCount = chainDB.getUnclesCount(header.ommersHash) unclesCount = chainDB.getUnclesCount(header.ommersHash)
result = w3Qty(unclesCount) result = w3Qty(unclesCount)
server.rpc("eth_getUncleCountByBlockNumber") do(quantityTag: string) -> Web3Quantity: server.rpc("eth_getUncleCountByBlockNumber") do(quantityTag: BlockTag) -> Web3Quantity:
## Returns the number of uncles in a block from a block matching the given block number. ## Returns the number of uncles in a block from a block matching the given block number.
## ##
## quantityTag: integer of a block number, or the string "latest", "earliest" or "pending", see the default block parameter. ## quantityTag: integer of a block number, or the string "latest", "earliest" or "pending", see the default block parameter.
@ -188,7 +179,7 @@ proc setupEthRpc*(
unclesCount = chainDB.getUnclesCount(header.ommersHash) unclesCount = chainDB.getUnclesCount(header.ommersHash)
result = w3Qty(unclesCount.uint) result = w3Qty(unclesCount.uint)
server.rpc("eth_getCode") do(data: Web3Address, quantityTag: string) -> seq[byte]: server.rpc("eth_getCode") do(data: Web3Address, quantityTag: BlockTag) -> seq[byte]:
## Returns code at a given address. ## Returns code at a given address.
## ##
## data: address ## data: address
@ -232,7 +223,7 @@ proc setupEthRpc*(
raise newException(ValueError, "Account locked, please unlock it first") raise newException(ValueError, "Account locked, please unlock it first")
let let
accDB = stateDBFromTag("latest") accDB = stateDBFromTag(blockId("latest"))
tx = unsignedTx(data, chainDB, accDB.getNonce(address) + 1) tx = unsignedTx(data, chainDB, accDB.getNonce(address) + 1)
eip155 = com.isEIP155(com.syncCurrent) eip155 = com.isEIP155(com.syncCurrent)
signedTx = signTransaction(tx, acc.privateKey, com.chainId, eip155) signedTx = signTransaction(tx, acc.privateKey, com.chainId, eip155)
@ -252,7 +243,7 @@ proc setupEthRpc*(
raise newException(ValueError, "Account locked, please unlock it first") raise newException(ValueError, "Account locked, please unlock it first")
let let
accDB = stateDBFromTag("latest") accDB = stateDBFromTag(blockId("latest"))
tx = unsignedTx(data, chainDB, accDB.getNonce(address) + 1) tx = unsignedTx(data, chainDB, accDB.getNonce(address) + 1)
eip155 = com.isEIP155(com.syncCurrent) eip155 = com.isEIP155(com.syncCurrent)
signedTx = signTransaction(tx, acc.privateKey, com.chainId, eip155) signedTx = signTransaction(tx, acc.privateKey, com.chainId, eip155)
@ -276,7 +267,7 @@ proc setupEthRpc*(
raise newException(ValueError, res.error) raise newException(ValueError, res.error)
result = txHash.w3Hash result = txHash.w3Hash
server.rpc("eth_call") do(call: EthCall, quantityTag: string) -> seq[byte]: server.rpc("eth_call") do(call: EthCall, quantityTag: BlockTag) -> seq[byte]:
## Executes a new message call immediately without creating a transaction on the block chain. ## Executes a new message call immediately without creating a transaction on the block chain.
## ##
## call: the transaction call object. ## call: the transaction call object.
@ -288,7 +279,7 @@ proc setupEthRpc*(
res = rpcCallEvm(callData, header, com) res = rpcCallEvm(callData, header, com)
result = res.output result = res.output
server.rpc("eth_estimateGas") do(call: EthCall, quantityTag: string) -> Web3Quantity: server.rpc("eth_estimateGas") do(call: EthCall, quantityTag: BlockTag) -> Web3Quantity:
## Generates and returns an estimate of how much gas is necessary to allow the transaction to complete. ## Generates and returns an estimate of how much gas is necessary to allow the transaction to complete.
## The transaction will not be added to the blockchain. Note that the estimate may be significantly more than ## The transaction will not be added to the blockchain. Note that the estimate may be significantly more than
## the amount of gas actually used by the transaction, for a variety of reasons including EVM mechanics and node performance. ## the amount of gas actually used by the transaction, for a variety of reasons including EVM mechanics and node performance.
@ -318,7 +309,7 @@ proc setupEthRpc*(
else: else:
result = nil result = nil
server.rpc("eth_getBlockByNumber") do(quantityTag: string, fullTransactions: bool) -> BlockObject: server.rpc("eth_getBlockByNumber") do(quantityTag: BlockTag, fullTransactions: bool) -> BlockObject:
## Returns information about a block by block number. ## Returns information about a block by block number.
## ##
## quantityTag: integer of a block number, or the string "earliest", "latest" or "pending", as in the default block parameter. ## quantityTag: integer of a block number, or the string "earliest", "latest" or "pending", as in the default block parameter.
@ -366,7 +357,7 @@ proc setupEthRpc*(
else: else:
result = nil result = nil
server.rpc("eth_getTransactionByBlockNumberAndIndex") do(quantityTag: string, quantity: Web3Quantity) -> TransactionObject: server.rpc("eth_getTransactionByBlockNumberAndIndex") do(quantityTag: BlockTag, quantity: Web3Quantity) -> TransactionObject:
## Returns information about a transaction by block number and transaction index position. ## Returns information about a transaction by block number and transaction index position.
## ##
## quantityTag: a block number, or the string "earliest", "latest" or "pending", as in the default block parameter. ## quantityTag: a block number, or the string "earliest", "latest" or "pending", as in the default block parameter.
@ -425,7 +416,7 @@ proc setupEthRpc*(
result = populateBlockObject(uncles[index], chainDB, false, true) result = populateBlockObject(uncles[index], chainDB, false, true)
result.totalDifficulty = chainDB.getScore(header.hash) result.totalDifficulty = chainDB.getScore(header.hash)
server.rpc("eth_getUncleByBlockNumberAndIndex") do(quantityTag: string, quantity: Web3Quantity) -> BlockObject: server.rpc("eth_getUncleByBlockNumberAndIndex") do(quantityTag: BlockTag, quantity: Web3Quantity) -> BlockObject:
# Returns information about a uncle of a block by number and uncle index position. # Returns information about a uncle of a block by number and uncle index position.
## ##
## quantityTag: a block number, or the string "earliest", "latest" or "pending", as in the default block parameter. ## quantityTag: a block number, or the string "earliest", "latest" or "pending", as in the default block parameter.

View File

@ -18,3 +18,7 @@ export
type type
FilterLog* = eth_api_types.LogObject FilterLog* = eth_api_types.LogObject
# BlockTag instead of BlockId:
# prevent type clash with eth2 BlockId in fluffy/verified_proxy
BlockTag* = eth_api_types.RtBlockIdentifier

View File

@ -13,7 +13,6 @@ import
std/[strutils, algorithm, options], std/[strutils, algorithm, options],
./rpc_types, ./rpc_types,
eth/[common, keys], eth/[common, keys],
web3/ethhexstrings,
../db/core_db, ../db/core_db,
../constants, stint, ../constants, stint,
../utils/utils, ../utils/utils,
@ -23,38 +22,34 @@ import
../beacon/web3_eth_conv ../beacon/web3_eth_conv
const const
defaultTag = "latest" defaultTag = blockId("latest")
type type
BlockHeader = common.BlockHeader BlockHeader = common.BlockHeader
proc headerFromTag*(chain: CoreDbRef, blockTag: string): BlockHeader proc headerFromTag*(chain: CoreDbRef, blockId: BlockTag): BlockHeader
{.gcsafe, raises: [CatchableError].} = {.gcsafe, raises: [CatchableError].} =
let tag = blockTag.toLowerAscii
case tag
of "latest": result = chain.getCanonicalHead()
of "earliest": result = chain.getBlockHeader(GENESIS_BLOCK_NUMBER)
of "safe": result = chain.safeHeader()
of "finalized": result = chain.finalizedHeader()
of "pending":
#TODO: Implement get pending block
raise newException(ValueError, "Pending tag not yet implemented")
else:
if not validate(tag.HexQuantityStr):
raise newException(ValueError, "Invalid hex of blockTag")
let blockNum = stint.fromHex(UInt256, tag)
result = chain.getBlockHeader(blockNum.toBlockNumber)
proc headerFromTag*(chain: CoreDbRef, blockTag: Option[RtBlockIdentifier]): BlockHeader if blockId.kind == bidAlias:
{.gcsafe, raises: [CatchableError].} = let tag = blockId.alias.toLowerAscii
if blockTag.isSome(): case tag
let blockId = blockTag.get of "latest": result = chain.getCanonicalHead()
if blockId.kind == bidAlias: of "earliest": result = chain.getBlockHeader(GENESIS_BLOCK_NUMBER)
return chain.headerFromTag(blockId.alias) of "safe": result = chain.safeHeader()
of "finalized": result = chain.finalizedHeader()
of "pending":
#TODO: Implement get pending block
raise newException(ValueError, "Pending tag not yet implemented")
else: else:
return chain.getBlockHeader(blockId.number.toBlockNumber) raise newException(ValueError, "Unsupported block tag " & tag)
else: else:
return chain.headerFromTag(defaultTag) let blockNum = blockId.number.toBlockNumber
result = chain.getBlockHeader(blockNum)
proc headerFromTag*(chain: CoreDbRef, blockTag: Option[BlockTag]): BlockHeader
{.gcsafe, raises: [CatchableError].} =
let blockId = blockTag.get(defaultTag)
chain.headerFromTag(blockId)
proc calculateMedianGasPrice*(chain: CoreDbRef): GasInt proc calculateMedianGasPrice*(chain: CoreDbRef): GasInt
{.gcsafe, raises: [CatchableError].} = {.gcsafe, raises: [CatchableError].} =

View File

@ -8,14 +8,14 @@
{.push raises: [].} {.push raises: [].}
import import
std/strutils, std/[strutils, typetraits],
stint, stint,
stew/[byteutils, results], stew/[byteutils, results],
chronicles, chronicles,
json_rpc/[rpcproxy, rpcserver, rpcclient], json_rpc/[rpcproxy, rpcserver, rpcclient],
eth/common/eth_types as etypes, eth/common/eth_types as etypes,
web3, web3,
web3/[ethhexstrings, primitives], web3/[primitives, eth_api_types],
beacon_chain/el/el_manager, beacon_chain/el/el_manager,
beacon_chain/networking/network_metadata, beacon_chain/networking/network_metadata,
beacon_chain/spec/forks, beacon_chain/spec/forks,
@ -30,18 +30,6 @@ logScope:
proc `==`(x, y: Quantity): bool {.borrow, noSideEffect.} proc `==`(x, y: Quantity): bool {.borrow, noSideEffect.}
template encodeQuantity(value: UInt256): HexQuantityStr =
hexQuantityStr("0x" & value.toHex())
template encodeHexData(value: UInt256): HexDataStr =
hexDataStr("0x" & toBytesBE(value).toHex)
template bytesToHex(bytes: seq[byte]): HexDataStr =
hexDataStr("0x" & toHex(bytes))
template encodeQuantity(value: Quantity): HexQuantityStr =
hexQuantityStr(encodeQuantity(value.uint64))
type type
VerifiedRpcProxy* = ref object VerifiedRpcProxy* = ref object
proxy: RpcProxy proxy: RpcProxy
@ -51,6 +39,8 @@ type
QuantityTagKind = enum QuantityTagKind = enum
LatestBlock, BlockNumber LatestBlock, BlockNumber
BlockTag = eth_api_types.RtBlockIdentifier
QuantityTag = object QuantityTag = object
case kind: QuantityTagKind case kind: QuantityTagKind
of LatestBlock: of LatestBlock:
@ -58,27 +48,16 @@ type
of BlockNumber: of BlockNumber:
blockNumber: Quantity blockNumber: Quantity
func parseHexIntResult(tag: string): Result[uint64, string] = func parseQuantityTag(blockTag: BlockTag): Result[QuantityTag, string] =
try: if blockTag.kind == bidAlias:
ok(parseHexInt(tag).uint64) let tag = blockTag.alias.toLowerAscii
except ValueError as e: case tag
err(e.msg) of "latest":
return ok(QuantityTag(kind: LatestBlock))
func parseHexQuantity(tag: string): Result[Quantity, string] = else:
let hexQuantity = hexQuantityStr(tag) return err("Unsupported blockTag: " & tag)
if validate(hexQuantity):
let parsed = ? parseHexIntResult(tag)
return ok(Quantity(parsed))
else: else:
return err("Invalid hex quantity.") let quantity = blockTag.number.Quantity
func parseQuantityTag(blockTag: string): Result[QuantityTag, string] =
let tag = blockTag.toLowerAscii
case tag
of "latest":
return ok(QuantityTag(kind: LatestBlock))
else:
let quantity = ? parseHexQuantity(tag)
return ok(QuantityTag(kind: BlockNumber, blockNumber: quantity)) return ok(QuantityTag(kind: BlockNumber, blockNumber: quantity))
template checkPreconditions(proxy: VerifiedRpcProxy) = template checkPreconditions(proxy: VerifiedRpcProxy) =
@ -90,7 +69,7 @@ template rpcClient(lcProxy: VerifiedRpcProxy): RpcClient =
proc getPayloadByTag( proc getPayloadByTag(
proxy: VerifiedRpcProxy, proxy: VerifiedRpcProxy,
quantityTag: string): quantityTag: BlockTag):
results.Opt[ExecutionData] {.raises: [ValueError].} = results.Opt[ExecutionData] {.raises: [ValueError].} =
checkPreconditions(proxy) checkPreconditions(proxy)
@ -110,27 +89,27 @@ proc getPayloadByTag(
proc getPayloadByTagOrThrow( proc getPayloadByTagOrThrow(
proxy: VerifiedRpcProxy, proxy: VerifiedRpcProxy,
quantityTag: string): ExecutionData {.raises: [ValueError].} = quantityTag: BlockTag): ExecutionData {.raises: [ValueError].} =
let tagResult = getPayloadByTag(proxy, quantityTag) let tagResult = getPayloadByTag(proxy, quantityTag)
if tagResult.isErr: if tagResult.isErr:
raise newException(ValueError, "No block stored for given tag " & quantityTag) raise newException(ValueError, "No block stored for given tag " & $quantityTag)
return tagResult.get() return tagResult.get()
proc installEthApiHandlers*(lcProxy: VerifiedRpcProxy) = proc installEthApiHandlers*(lcProxy: VerifiedRpcProxy) =
lcProxy.proxy.rpc("eth_chainId") do() -> HexQuantityStr: lcProxy.proxy.rpc("eth_chainId") do() -> Quantity:
return encodeQuantity(lcProxy.chainId) return lcProxy.chainId
lcProxy.proxy.rpc("eth_blockNumber") do() -> HexQuantityStr: lcProxy.proxy.rpc("eth_blockNumber") do() -> Quantity:
## Returns the number of the most recent block. ## Returns the number of the most recent block.
checkPreconditions(lcProxy) checkPreconditions(lcProxy)
return encodeQuantity(lcProxy.blockCache.latest.get.blockNumber) return lcProxy.blockCache.latest.get.blockNumber
lcProxy.proxy.rpc("eth_getBalance") do( lcProxy.proxy.rpc("eth_getBalance") do(
address: Address, quantityTag: string) -> HexQuantityStr: address: Address, quantityTag: BlockTag) -> UInt256:
# When requesting state for `latest` block number, we need to translate # When requesting state for `latest` block number, we need to translate
# `latest` to actual block number as `latest` on proxy and on data provider # `latest` to actual block number as `latest` on proxy and on data provider
# can mean different blocks and ultimatly piece received piece of state # can mean different blocks and ultimatly piece received piece of state
@ -155,32 +134,31 @@ proc installEthApiHandlers*(lcProxy: VerifiedRpcProxy) =
) )
if accountResult.isOk(): if accountResult.isOk():
return encodeQuantity(accountResult.get.balance) return accountResult.get.balance
else: else:
raise newException(ValueError, accountResult.error) raise newException(ValueError, accountResult.error)
lcProxy.proxy.rpc("eth_getStorageAt") do( lcProxy.proxy.rpc("eth_getStorageAt") do(
address: Address, slot: HexDataStr, quantityTag: string) -> HexDataStr: address: Address, slot: UInt256, quantityTag: BlockTag) -> UInt256:
let let
executionPayload = lcProxy.getPayloadByTagOrThrow(quantityTag) executionPayload = lcProxy.getPayloadByTagOrThrow(quantityTag)
uslot = UInt256.fromHex(slot.string)
blockNumber = executionPayload.blockNumber.uint64 blockNumber = executionPayload.blockNumber.uint64
info "Forwarding eth_getStorageAt", blockNumber info "Forwarding eth_getStorageAt", blockNumber
let proof = await lcProxy.rpcClient.eth_getProof( let proof = await lcProxy.rpcClient.eth_getProof(
address, @[uslot], blockId(blockNumber)) address, @[slot], blockId(blockNumber))
let dataResult = getStorageData(executionPayload.stateRoot, uslot, proof) let dataResult = getStorageData(executionPayload.stateRoot, slot, proof)
if dataResult.isOk(): if dataResult.isOk():
let slotValue = dataResult.get() let slotValue = dataResult.get()
return encodeHexData(slotValue) return slotValue
else: else:
raise newException(ValueError, dataResult.error) raise newException(ValueError, dataResult.error)
lcProxy.proxy.rpc("eth_getTransactionCount") do( lcProxy.proxy.rpc("eth_getTransactionCount") do(
address: Address, quantityTag: string) -> HexQuantityStr: address: Address, quantityTag: BlockTag) -> Quantity:
let let
executionPayload = lcProxy.getPayloadByTagOrThrow(quantityTag) executionPayload = lcProxy.getPayloadByTagOrThrow(quantityTag)
blockNumber = executionPayload.blockNumber.uint64 blockNumber = executionPayload.blockNumber.uint64
@ -201,12 +179,12 @@ proc installEthApiHandlers*(lcProxy: VerifiedRpcProxy) =
) )
if accountResult.isOk(): if accountResult.isOk():
return hexQuantityStr(encodeQuantity(accountResult.get.nonce)) return Quantity(accountResult.get.nonce)
else: else:
raise newException(ValueError, accountResult.error) raise newException(ValueError, accountResult.error)
lcProxy.proxy.rpc("eth_getCode") do( lcProxy.proxy.rpc("eth_getCode") do(
address: Address, quantityTag: string) -> HexDataStr: address: Address, quantityTag: BlockTag) -> seq[byte]:
let let
executionPayload = lcProxy.getPayloadByTagOrThrow(quantityTag) executionPayload = lcProxy.getPayloadByTagOrThrow(quantityTag)
blockNumber = executionPayload.blockNumber.uint64 blockNumber = executionPayload.blockNumber.uint64
@ -231,7 +209,7 @@ proc installEthApiHandlers*(lcProxy: VerifiedRpcProxy) =
if account.codeHash == etypes.EMPTY_CODE_HASH: if account.codeHash == etypes.EMPTY_CODE_HASH:
# account does not have any code, return empty hex data # account does not have any code, return empty hex data
return hexDataStr("0x") return @[]
info "Forwarding eth_getCode", blockNumber info "Forwarding eth_getCode", blockNumber
@ -241,7 +219,7 @@ proc installEthApiHandlers*(lcProxy: VerifiedRpcProxy) =
) )
if isValidCode(account, code): if isValidCode(account, code):
return bytesToHex(code) return code
else: else:
raise newException(ValueError, raise newException(ValueError,
"Received code which does not match the account code hash") "Received code which does not match the account code hash")
@ -257,7 +235,7 @@ proc installEthApiHandlers*(lcProxy: VerifiedRpcProxy) =
# TODO currently we do not handle fullTransactions flag. It require updates on # TODO currently we do not handle fullTransactions flag. It require updates on
# nim-web3 side # nim-web3 side
lcProxy.proxy.rpc("eth_getBlockByNumber") do( lcProxy.proxy.rpc("eth_getBlockByNumber") do(
quantityTag: string, fullTransactions: bool) -> Option[BlockObject]: quantityTag: BlockTag, fullTransactions: bool) -> Option[BlockObject]:
let executionPayload = lcProxy.getPayloadByTag(quantityTag) let executionPayload = lcProxy.getPayloadByTag(quantityTag)
if executionPayload.isErr: if executionPayload.isErr:

View File

@ -14,7 +14,17 @@ import
stint, stew/byteutils stint, stew/byteutils
import ../nimbus/transaction, ../nimbus/utils/ec_recover import ../nimbus/transaction, ../nimbus/utils/ec_recover
from web3/ethhexstrings import encodeQuantity
template stripLeadingZeros(value: string): string =
var cidx = 0
# ignore the last character so we retain '0' on zero value
while cidx < value.len - 1 and value[cidx] == '0':
cidx.inc
value[cidx .. ^1]
func encodeQuantity(value: SomeUnsignedInt): string =
var hValue = value.toHex.stripLeadingZeros
result = "0x" & hValue.toLowerAscii
func hexToInt*(s: string, T: typedesc[SomeInteger]): T = func hexToInt*(s: string, T: typedesc[SomeInteger]): T =
var i = 0 var i = 0