From 7af5ce10fce4d781faee83ef1e24919b61a20cc2 Mon Sep 17 00:00:00 2001 From: chirag-parmar Date: Mon, 7 Jul 2025 18:47:43 +0530 Subject: [PATCH 01/11] txs, receipts and logs --- nimbus_verified_proxy/rpc/receipts.nim | 2 -- 1 file changed, 2 deletions(-) diff --git a/nimbus_verified_proxy/rpc/receipts.nim b/nimbus_verified_proxy/rpc/receipts.nim index 954b7fa35a..dc35dfe17c 100644 --- a/nimbus_verified_proxy/rpc/receipts.nim +++ b/nimbus_verified_proxy/rpc/receipts.nim @@ -29,7 +29,6 @@ func toReceipt(rec: ReceiptObject): Receipt = let isHash = not rec.status.isSome() let status = rec.status.isSome() and rec.status.get() == 1.Quantity - return Receipt( hash: rec.transactionHash, isHash: isHash, @@ -51,7 +50,6 @@ proc getReceipts( await vp.rpcClient.eth_getBlockReceipts(blockTag) except CatchableError as e: return err(e.msg) - if rxs.isSome(): if orderedTrieRoot(toReceipts(rxs.get())) != header.receiptsRoot: return From 7c988319204a038eb24e823f6ea19793ee037916 Mon Sep 17 00:00:00 2001 From: chirag-parmar Date: Mon, 7 Jul 2025 19:44:07 +0530 Subject: [PATCH 02/11] format --- nimbus_verified_proxy/rpc/receipts.nim | 1 - 1 file changed, 1 deletion(-) diff --git a/nimbus_verified_proxy/rpc/receipts.nim b/nimbus_verified_proxy/rpc/receipts.nim index dc35dfe17c..854ec4c6bc 100644 --- a/nimbus_verified_proxy/rpc/receipts.nim +++ b/nimbus_verified_proxy/rpc/receipts.nim @@ -107,7 +107,6 @@ proc getLogs*( rxs = (await vp.getReceipts(lg.blockHash.get())).valueOr: return err(error) prevBlockHash = lg.blockHash.get() - let txIdx = distinctBase(lg.transactionIndex.get()) logIdx = From dc4ae4980e57e5970611df5003e9620cb499bedf Mon Sep 17 00:00:00 2001 From: chirag-parmar Date: Tue, 8 Jul 2025 18:27:46 +0530 Subject: [PATCH 03/11] add log filters --- nimbus_verified_proxy/rpc/receipts.nim | 21 +++--- nimbus_verified_proxy/rpc/rpc_eth_api.nim | 88 ++++++++++++++++++++++- nimbus_verified_proxy/types.nim | 5 +- 3 files changed, 102 insertions(+), 12 deletions(-) diff --git a/nimbus_verified_proxy/rpc/receipts.nim b/nimbus_verified_proxy/rpc/receipts.nim index 854ec4c6bc..05db2c29eb 100644 --- a/nimbus_verified_proxy/rpc/receipts.nim +++ b/nimbus_verified_proxy/rpc/receipts.nim @@ -84,14 +84,17 @@ proc getReceipts*( await vp.getReceipts(header, numberTag) -proc getLogs*( - vp: VerifiedRpcProxy, filterOptions: FilterOptions +proc verifyLogs*( + vp: VerifiedRpcProxy, logObjs: seq[LogObject] ): Future[Result[seq[LogObject], string]] {.async.} = - let logObjs = - try: - await vp.rpcClient.eth_getLogs(filterOptions) - except CatchableError as e: - return err(e.msg) + let + fromBlock = filter.fromBlock.get(BlockTag(kind:BlockIdentifierKind.bidAlias, alias: "latest")) + toBlock = filter.toBlock.get(BlockTag(kind:BlockIdentifierKind.bidAlias, alias: "latest")) + + bottom = if fromBlock.kind == BlockIdentifierKind.bidNumber: fromBlock.number + else: return err("Cannot verify boundaries for block tags in 'fromBlock' field") + top = if toBlock.kind == BlockIdentifierKind.bidNumber: toBlock.number + else: return err("Cannot verify boundaries for block tags in 'toBlock' field") # store block hashes contains the logs so that we can batch receipt requests var @@ -100,7 +103,7 @@ proc getLogs*( for lg in logObjs: # none only for pending logs before block is built - if lg.blockHash.isSome() and lg.transactionIndex.isSome() and lg.logIndex.isSome(): + if lg.blockNumber.isSome() and lg.blockHash.isSome() and lg.transactionIndex.isSome() and lg.logIndex.isSome(): # exploit sequentiality of logs if prevBlockHash != lg.blockHash.get(): # TODO: a cache will solve downloading the same block receipts for multiple logs @@ -116,7 +119,7 @@ proc getLogs*( if rxLog.address != lg.address or rxLog.data != lg.data or rxLog.topics != lg.topics or - (not match(toLog(lg), filterOptions.address, filterOptions.topics)): + (not match(toLog(lg), filterOptions.address, filterOptions.topics)) or lg.blockNumber.get < bottom or lg.blockNumber.get > top: return err("one of the returned logs is invalid") return ok(logObjs) diff --git a/nimbus_verified_proxy/rpc/rpc_eth_api.nim b/nimbus_verified_proxy/rpc/rpc_eth_api.nim index b5f07e6c27..90884ff89b 100644 --- a/nimbus_verified_proxy/rpc/rpc_eth_api.nim +++ b/nimbus_verified_proxy/rpc/rpc_eth_api.nim @@ -237,6 +237,7 @@ proc installEthApiHandlers*(vp: VerifiedRpcProxy) = await vp.rpcClient.eth_getTransactionByHash(txHash) except CatchableError as e: raise newException(ValueError, e.msg) + if tx.hash != txHash: raise newException( ValueError, @@ -271,10 +272,93 @@ proc installEthApiHandlers*(vp: VerifiedRpcProxy) = raise newException(ValueError, "receipt couldn't be verified") vp.proxy.rpc("eth_getLogs") do(filterOptions: FilterOptions) -> seq[LogObject]: - (await vp.getLogs(filterOptions)).valueOr: + let + logObjs = + try: + await vp.rpcClient.eth_getLogs(filterOptions) + except CatchableError as e: + raise newException(ValueError, e.msg) + + boundsCheck = verifyFilterBoundaries(filterOptions, logObjs).valueOr: + raise newException(ValueError, error) + + if not boundsCheck: + raise newException(ValueError, "Logs out of filter block range") + + let verifiedLogs = (await vp.verifyLogs(logObjs)).valueOr: + raise newException(ValueError, error) + + return verifiedLogs + + vp.proxy.rpc("eth_newFilter") do(filterOptions: FilterOptions) -> int: + let + hexId = + try: + await vp.rpcClient.eth_newFilter(filterOptions) + except CatchableError as e: + raise newException(ValueError, e.msg) + id = fromHex[int](hexId) + + vp.filterStore[id] = filterOptions + return id + + vp.proxy.rpc("eth_uninstallFilter") do(filterId: int) -> bool: + let status = + try: + await vp.rpcClient.eth_uninstallFilter("0x" & toHex(filterId)) + except CatchableError as e: + raise newException(ValueError, e.msg) + + if status and filterId in vp.filterStore: + vp.filterStore.del(filterId) + + return status + + vp.proxy.rpc("eth_getFilterLogs") do(filterId: int) -> seq[LogObject]: + if filterId notin vp.filterStore: + raise newException(ValueError, "Filter doesn't exist") + + let + logObjs = + try: + # use locally stored filter and get logs + await vp.rpcClient.eth_getLogs(vp.filterStore[filterId]) + except CatchableError as e: + raise newException(ValueError, e.msg) + + boundsCheck = verifyFilterBoundaries(vp.filterStore[filterId], logObjs).valueOr: + raise newException(ValueError, error) + + if not boundsCheck: + raise newException(ValueError, "Logs out of filter block range") + + let verifiedLogs = (await vp.verifyLogs(logObjs)).valueOr: raise newException(ValueError, error) - # TODO: + return verifiedLogs + + vp.proxy.rpc("eth_getFilterChanges") do(filterId: int) -> seq[LogObject]: + if filterId notin vp.filterStore: + raise newException(ValueError, "Filter doesn't exist") + + let + logObjs = + try: + await vp.rpcClient.eth_getFilterChanges("0x" & toHex(filterId)) + except CatchableError as e: + raise newException(ValueError, e.msg) + + boundsCheck = verifyFilterBoundaries(vp.filterStore[filterId], logObjs).valueOr: + raise newException(ValueError, error) + + if not boundsCheck: + raise newException(ValueError, "Logs out of filter block range") + + let verifiedLogs = (await vp.verifyLogs(logObjs)).valueOr: + raise newException(ValueError, error) + + return verifiedLogs + # Following methods are forwarded directly to the web3 provider and therefore # are not validated in any way. vp.proxy.registerProxyMethod("net_version") diff --git a/nimbus_verified_proxy/types.nim b/nimbus_verified_proxy/types.nim index b64a91e452..1f260de51d 100644 --- a/nimbus_verified_proxy/types.nim +++ b/nimbus_verified_proxy/types.nim @@ -8,6 +8,7 @@ {.push raises: [], gcsafe.} import + std/tables, json_rpc/[rpcproxy, rpcclient], web3/[eth_api, eth_api_types], stint, @@ -74,6 +75,7 @@ type # TODO: when the list grows big add a config object instead # config parameters + filterStore*: Table[int, FilterOptions] chainId*: UInt256 maxBlockWalk*: uint64 @@ -90,6 +92,7 @@ proc init*( accountsCache: AccountsCache.init(ACCOUNTS_CACHE_SIZE), codeCache: CodeCache.init(CODE_CACHE_SIZE), storageCache: StorageCache.init(STORAGE_CACHE_SIZE), + filterStore: initTable[int, FilterOptions](), chainId: chainId, - maxBlockWalk: maxBlockWalk, + maxBlockWalk: maxBlockWalk ) From 11ef29cdcec5a2a296f8f2b0f2f12c0ba11d19ec Mon Sep 17 00:00:00 2001 From: chirag-parmar Date: Thu, 7 Aug 2025 11:54:58 +0530 Subject: [PATCH 04/11] lint and rebase fixes --- nimbus_verified_proxy/rpc/receipts.nim | 41 +++++++++++++++++------ nimbus_verified_proxy/rpc/rpc_eth_api.nim | 18 +++++----- nimbus_verified_proxy/types.nim | 2 +- 3 files changed, 41 insertions(+), 20 deletions(-) diff --git a/nimbus_verified_proxy/rpc/receipts.nim b/nimbus_verified_proxy/rpc/receipts.nim index 05db2c29eb..4f2af7409f 100644 --- a/nimbus_verified_proxy/rpc/receipts.nim +++ b/nimbus_verified_proxy/rpc/receipts.nim @@ -84,17 +84,38 @@ proc getReceipts*( await vp.getReceipts(header, numberTag) +proc verifyFilterBoundaries*( + filter: FilterOptions, logObjs: seq[LogObject] +): Result[bool, string] = + let + fromBlock = filter.fromBlock.get( + BlockTag(kind: BlockIdentifierKind.bidAlias, alias: "latest") + ) + toBlock = + filter.toBlock.get(BlockTag(kind: BlockIdentifierKind.bidAlias, alias: "latest")) + + bottom = + if fromBlock.kind == BlockIdentifierKind.bidNumber: + fromBlock.number + else: + return err("Cannot verify boundaries for block tags in 'fromBlock' field") + top = + if toBlock.kind == BlockIdentifierKind.bidNumber: + toBlock.number + else: + return err("Cannot verify boundaries for block tags in 'toBlock' field") + + for lg in logObjs: + if lg.blockNumber.isSome: + if lg.blockNumber.get < bottom or lg.blockNumber.get > top: + return ok(false) + + return ok(true) + proc verifyLogs*( vp: VerifiedRpcProxy, logObjs: seq[LogObject] ): Future[Result[seq[LogObject], string]] {.async.} = - let - fromBlock = filter.fromBlock.get(BlockTag(kind:BlockIdentifierKind.bidAlias, alias: "latest")) - toBlock = filter.toBlock.get(BlockTag(kind:BlockIdentifierKind.bidAlias, alias: "latest")) - - bottom = if fromBlock.kind == BlockIdentifierKind.bidNumber: fromBlock.number - else: return err("Cannot verify boundaries for block tags in 'fromBlock' field") - top = if toBlock.kind == BlockIdentifierKind.bidNumber: toBlock.number - else: return err("Cannot verify boundaries for block tags in 'toBlock' field") + var res = newSeq[LogObject]() # store block hashes contains the logs so that we can batch receipt requests var @@ -103,7 +124,7 @@ proc verifyLogs*( for lg in logObjs: # none only for pending logs before block is built - if lg.blockNumber.isSome() and lg.blockHash.isSome() and lg.transactionIndex.isSome() and lg.logIndex.isSome(): + if lg.blockHash.isSome() and lg.transactionIndex.isSome() and lg.logIndex.isSome(): # exploit sequentiality of logs if prevBlockHash != lg.blockHash.get(): # TODO: a cache will solve downloading the same block receipts for multiple logs @@ -119,7 +140,7 @@ proc verifyLogs*( if rxLog.address != lg.address or rxLog.data != lg.data or rxLog.topics != lg.topics or - (not match(toLog(lg), filterOptions.address, filterOptions.topics)) or lg.blockNumber.get < bottom or lg.blockNumber.get > top: + (not match(toLog(lg), filterOptions.address, filterOptions.topics)): return err("one of the returned logs is invalid") return ok(logObjs) diff --git a/nimbus_verified_proxy/rpc/rpc_eth_api.nim b/nimbus_verified_proxy/rpc/rpc_eth_api.nim index 90884ff89b..6242ed76c1 100644 --- a/nimbus_verified_proxy/rpc/rpc_eth_api.nim +++ b/nimbus_verified_proxy/rpc/rpc_eth_api.nim @@ -272,8 +272,8 @@ proc installEthApiHandlers*(vp: VerifiedRpcProxy) = raise newException(ValueError, "receipt couldn't be verified") vp.proxy.rpc("eth_getLogs") do(filterOptions: FilterOptions) -> seq[LogObject]: - let - logObjs = + let + logObjs = try: await vp.rpcClient.eth_getLogs(filterOptions) except CatchableError as e: @@ -291,8 +291,8 @@ proc installEthApiHandlers*(vp: VerifiedRpcProxy) = return verifiedLogs vp.proxy.rpc("eth_newFilter") do(filterOptions: FilterOptions) -> int: - let - hexId = + let + hexId = try: await vp.rpcClient.eth_newFilter(filterOptions) except CatchableError as e: @@ -303,7 +303,7 @@ proc installEthApiHandlers*(vp: VerifiedRpcProxy) = return id vp.proxy.rpc("eth_uninstallFilter") do(filterId: int) -> bool: - let status = + let status = try: await vp.rpcClient.eth_uninstallFilter("0x" & toHex(filterId)) except CatchableError as e: @@ -318,8 +318,8 @@ proc installEthApiHandlers*(vp: VerifiedRpcProxy) = if filterId notin vp.filterStore: raise newException(ValueError, "Filter doesn't exist") - let - logObjs = + let + logObjs = try: # use locally stored filter and get logs await vp.rpcClient.eth_getLogs(vp.filterStore[filterId]) @@ -341,8 +341,8 @@ proc installEthApiHandlers*(vp: VerifiedRpcProxy) = if filterId notin vp.filterStore: raise newException(ValueError, "Filter doesn't exist") - let - logObjs = + let + logObjs = try: await vp.rpcClient.eth_getFilterChanges("0x" & toHex(filterId)) except CatchableError as e: diff --git a/nimbus_verified_proxy/types.nim b/nimbus_verified_proxy/types.nim index 1f260de51d..6e9c729054 100644 --- a/nimbus_verified_proxy/types.nim +++ b/nimbus_verified_proxy/types.nim @@ -94,5 +94,5 @@ proc init*( storageCache: StorageCache.init(STORAGE_CACHE_SIZE), filterStore: initTable[int, FilterOptions](), chainId: chainId, - maxBlockWalk: maxBlockWalk + maxBlockWalk: maxBlockWalk, ) From 2934dfd78d37c4f4d6dc69a1c0dab79a88c96c7f Mon Sep 17 00:00:00 2001 From: chirag-parmar Date: Tue, 12 Aug 2025 18:17:57 +0530 Subject: [PATCH 05/11] resolve filters --- nimbus_verified_proxy/rpc/blocks.nim | 22 +++++----- nimbus_verified_proxy/rpc/receipts.nim | 49 ++++++++++------------- nimbus_verified_proxy/rpc/rpc_eth_api.nim | 44 +++++++------------- 3 files changed, 46 insertions(+), 69 deletions(-) diff --git a/nimbus_verified_proxy/rpc/blocks.nim b/nimbus_verified_proxy/rpc/blocks.nim index 494bf2f1aa..7f3183a8a1 100644 --- a/nimbus_verified_proxy/rpc/blocks.nim +++ b/nimbus_verified_proxy/rpc/blocks.nim @@ -23,26 +23,26 @@ import proc resolveBlockTag*( vp: VerifiedRpcProxy, blockTag: BlockTag -): Result[base.BlockNumber, string] = +): Result[BlockTag, string] = if blockTag.kind == bidAlias: let tag = blockTag.alias.toLowerAscii() case tag of "latest": let hLatest = vp.headerStore.latest.valueOr: return err("Couldn't get the latest block number from header store") - ok(hLatest.number) + ok(BlockTag(kind: bidNumber, number: hLatest.number)) of "finalized": let hFinalized = vp.headerStore.finalized.valueOr: return err("Couldn't get the latest block number from header store") - ok(hFinalized.number) + ok(BlockTag(kind: bidNumber, number: hFinalized.number)) of "earliest": let hEarliest = vp.headerStore.earliest.valueOr: return err("Couldn't get the latest block number from header store") - ok(hEarliest.number) + ok(BlockTag(kind: bidNumber, number: hEarliest.number)) else: err("No support for block tag " & $blockTag) else: - ok(base.BlockNumber(distinctBase(blockTag.number))) + ok(blockTag) func convHeader*(blk: eth_api_types.BlockObject): Header = let nonce = blk.nonce.valueOr: @@ -184,10 +184,8 @@ proc getBlock*( proc getBlock*( vp: VerifiedRpcProxy, blockTag: BlockTag, fullTransactions: bool ): Future[Result[BlockObject, string]] {.async.} = - let - n = vp.resolveBlockTag(blockTag).valueOr: - return err(error) - numberTag = BlockTag(kind: BlockIdentifierKind.bidNumber, number: Quantity(n)) + let numberTag = vp.resolveBlockTag(blockTag).valueOr: + return err(error) # get the target block let blk = @@ -196,7 +194,7 @@ proc getBlock*( except CatchableError as e: return err(e.msg) - if n != distinctBase(blk.number): + if numberTag.number != blk.number: return err("the downloaded block number doesn't match with the requested block number") @@ -235,9 +233,9 @@ proc getHeader*( vp: VerifiedRpcProxy, blockTag: BlockTag ): Future[Result[Header, string]] {.async.} = let - n = vp.resolveBlockTag(blockTag).valueOr: + numberTag = vp.resolveBlockTag(blockTag).valueOr: return err(error) - numberTag = BlockTag(kind: BlockIdentifierKind.bidNumber, number: Quantity(n)) + n = distinctBase(numberTag.number) cachedHeader = vp.headerStore.get(n) if cachedHeader.isNone(): diff --git a/nimbus_verified_proxy/rpc/receipts.nim b/nimbus_verified_proxy/rpc/receipts.nim index 4f2af7409f..6a959ad620 100644 --- a/nimbus_verified_proxy/rpc/receipts.nim +++ b/nimbus_verified_proxy/rpc/receipts.nim @@ -84,37 +84,28 @@ proc getReceipts*( await vp.getReceipts(header, numberTag) -proc verifyFilterBoundaries*( - filter: FilterOptions, logObjs: seq[LogObject] -): Result[bool, string] = +proc resolveFilterTags*(filter: FilterOptions): Result[FilterOptions, string] = let - fromBlock = filter.fromBlock.get( - BlockTag(kind: BlockIdentifierKind.bidAlias, alias: "latest") - ) - toBlock = - filter.toBlock.get(BlockTag(kind: BlockIdentifierKind.bidAlias, alias: "latest")) - - bottom = - if fromBlock.kind == BlockIdentifierKind.bidNumber: - fromBlock.number - else: - return err("Cannot verify boundaries for block tags in 'fromBlock' field") - top = - if toBlock.kind == BlockIdentifierKind.bidNumber: - toBlock.number - else: - return err("Cannot verify boundaries for block tags in 'toBlock' field") - - for lg in logObjs: - if lg.blockNumber.isSome: - if lg.blockNumber.get < bottom or lg.blockNumber.get > top: - return ok(false) + fromBlock = filter.fromBlock.get(types.BlockTag(kind: bidAlias, alias: "latest")) + toBlock = filter.toBlock.get(types.BlockTag(kind: bidAlias, alias: "latest")) + fromBlockNumberTag = resolveBlockTag(fromBlock).valueOr: + return err(error) + toBlockNumberTag = resolveBlockTag(toBlock).valueOr: + return err(error) - return ok(true) + return ok( + FilterOptions( + fromBlock: Opt.some(fromBlockNumberTag), + toBlock: Opt.some(toBlockNumberTag), + address: filter.address, + topics: filter.topics, + blockHash: filter.blockHash, + ) + ) proc verifyLogs*( - vp: VerifiedRpcProxy, logObjs: seq[LogObject] -): Future[Result[seq[LogObject], string]] {.async.} = + vp: VerifiedRpcProxy, filterOptions: FilterOptions, logObjs: seq[LogObject] +): Future[Result[bool, string]] {.async.} = var res = newSeq[LogObject]() # store block hashes contains the logs so that we can batch receipt requests @@ -140,7 +131,9 @@ proc verifyLogs*( if rxLog.address != lg.address or rxLog.data != lg.data or rxLog.topics != lg.topics or + lg.blockNumber.get() < filter.fromBlock.get().number or + lg.blockNumber.get() > filter.toBlock.get().number or (not match(toLog(lg), filterOptions.address, filterOptions.topics)): return err("one of the returned logs is invalid") - return ok(logObjs) + return ok() diff --git a/nimbus_verified_proxy/rpc/rpc_eth_api.nim b/nimbus_verified_proxy/rpc/rpc_eth_api.nim index 6242ed76c1..96a4db0ab8 100644 --- a/nimbus_verified_proxy/rpc/rpc_eth_api.nim +++ b/nimbus_verified_proxy/rpc/rpc_eth_api.nim @@ -273,27 +273,23 @@ proc installEthApiHandlers*(vp: VerifiedRpcProxy) = vp.proxy.rpc("eth_getLogs") do(filterOptions: FilterOptions) -> seq[LogObject]: let + filter = resolveFilterTags(filterOptions).valueOr: + raise newException(ValueError, error) logObjs = try: - await vp.rpcClient.eth_getLogs(filterOptions) + await vp.rpcClient.eth_getLogs(filter) except CatchableError as e: raise newException(ValueError, e.msg) - boundsCheck = verifyFilterBoundaries(filterOptions, logObjs).valueOr: - raise newException(ValueError, error) - - if not boundsCheck: - raise newException(ValueError, "Logs out of filter block range") - - let verifiedLogs = (await vp.verifyLogs(logObjs)).valueOr: - raise newException(ValueError, error) + ?(await vp.verifyLogs(filter, logObjs)) - return verifiedLogs + return logObjs vp.proxy.rpc("eth_newFilter") do(filterOptions: FilterOptions) -> int: let hexId = try: + # filter is not resolved when storing only while fetching await vp.rpcClient.eth_newFilter(filterOptions) except CatchableError as e: raise newException(ValueError, e.msg) @@ -319,45 +315,35 @@ proc installEthApiHandlers*(vp: VerifiedRpcProxy) = raise newException(ValueError, "Filter doesn't exist") let + filter = resolveFilterTags(vp.filterStore[filterId]).valueOr: + raise newException(ValueError, error) logObjs = try: # use locally stored filter and get logs - await vp.rpcClient.eth_getLogs(vp.filterStore[filterId]) + await vp.rpcClient.eth_getLogs(filter) except CatchableError as e: raise newException(ValueError, e.msg) - boundsCheck = verifyFilterBoundaries(vp.filterStore[filterId], logObjs).valueOr: - raise newException(ValueError, error) + ?(await vp.verifyLogs(filter, logObjs)) - if not boundsCheck: - raise newException(ValueError, "Logs out of filter block range") - - let verifiedLogs = (await vp.verifyLogs(logObjs)).valueOr: - raise newException(ValueError, error) - - return verifiedLogs + return logObjs vp.proxy.rpc("eth_getFilterChanges") do(filterId: int) -> seq[LogObject]: if filterId notin vp.filterStore: raise newException(ValueError, "Filter doesn't exist") let + filter = resolveFilterTags(vp.filterStore[filterId]).valueOr: + raise newException(ValueError, error) logObjs = try: await vp.rpcClient.eth_getFilterChanges("0x" & toHex(filterId)) except CatchableError as e: raise newException(ValueError, e.msg) - boundsCheck = verifyFilterBoundaries(vp.filterStore[filterId], logObjs).valueOr: - raise newException(ValueError, error) - - if not boundsCheck: - raise newException(ValueError, "Logs out of filter block range") - - let verifiedLogs = (await vp.verifyLogs(logObjs)).valueOr: - raise newException(ValueError, error) + ?(await vp.verifyLogs(filter, logObjs)) - return verifiedLogs + return logObjs # Following methods are forwarded directly to the web3 provider and therefore # are not validated in any way. From ecb3ebc533d9266346185139837a598efd2aabae Mon Sep 17 00:00:00 2001 From: chirag-parmar Date: Tue, 19 Aug 2025 07:37:52 +0530 Subject: [PATCH 06/11] remove bugs --- nimbus_verified_proxy/rpc/blocks.nim | 6 +-- nimbus_verified_proxy/rpc/receipts.nim | 33 ++++++++++---- nimbus_verified_proxy/rpc/rpc_eth_api.nim | 54 ++++++++--------------- nimbus_verified_proxy/rpc_api_backend.nim | 20 ++++++++- nimbus_verified_proxy/types.nim | 11 ++++- 5 files changed, 73 insertions(+), 51 deletions(-) diff --git a/nimbus_verified_proxy/rpc/blocks.nim b/nimbus_verified_proxy/rpc/blocks.nim index 7f3183a8a1..a03dc5f661 100644 --- a/nimbus_verified_proxy/rpc/blocks.nim +++ b/nimbus_verified_proxy/rpc/blocks.nim @@ -30,15 +30,15 @@ proc resolveBlockTag*( of "latest": let hLatest = vp.headerStore.latest.valueOr: return err("Couldn't get the latest block number from header store") - ok(BlockTag(kind: bidNumber, number: hLatest.number)) + ok(BlockTag(kind: bidNumber, number: Quantity(hLatest.number))) of "finalized": let hFinalized = vp.headerStore.finalized.valueOr: return err("Couldn't get the latest block number from header store") - ok(BlockTag(kind: bidNumber, number: hFinalized.number)) + ok(BlockTag(kind: bidNumber, number: Quantity(hFinalized.number))) of "earliest": let hEarliest = vp.headerStore.earliest.valueOr: return err("Couldn't get the latest block number from header store") - ok(BlockTag(kind: bidNumber, number: hEarliest.number)) + ok(BlockTag(kind: bidNumber, number: Quantity(hEarliest.number))) else: err("No support for block tag " & $blockTag) else: diff --git a/nimbus_verified_proxy/rpc/receipts.nim b/nimbus_verified_proxy/rpc/receipts.nim index 6a959ad620..257bc14409 100644 --- a/nimbus_verified_proxy/rpc/receipts.nim +++ b/nimbus_verified_proxy/rpc/receipts.nim @@ -84,13 +84,15 @@ proc getReceipts*( await vp.getReceipts(header, numberTag) -proc resolveFilterTags*(filter: FilterOptions): Result[FilterOptions, string] = +proc resolveFilterTags*(vp: VerifiedRpcProxy, filter: FilterOptions): Result[FilterOptions, string] = + if filter.blockHash.isSome(): + return ok(filter) let fromBlock = filter.fromBlock.get(types.BlockTag(kind: bidAlias, alias: "latest")) toBlock = filter.toBlock.get(types.BlockTag(kind: bidAlias, alias: "latest")) - fromBlockNumberTag = resolveBlockTag(fromBlock).valueOr: + fromBlockNumberTag = vp.resolveBlockTag(fromBlock).valueOr: return err(error) - toBlockNumberTag = resolveBlockTag(toBlock).valueOr: + toBlockNumberTag = vp.resolveBlockTag(toBlock).valueOr: return err(error) return ok( @@ -104,9 +106,8 @@ proc resolveFilterTags*(filter: FilterOptions): Result[FilterOptions, string] = ) proc verifyLogs*( - vp: VerifiedRpcProxy, filterOptions: FilterOptions, logObjs: seq[LogObject] -): Future[Result[bool, string]] {.async.} = - var res = newSeq[LogObject]() + vp: VerifiedRpcProxy, filter: FilterOptions, logObjs: seq[LogObject] +): Future[Result[void, string]] {.async.} = # store block hashes contains the logs so that we can batch receipt requests var @@ -120,7 +121,7 @@ proc verifyLogs*( if prevBlockHash != lg.blockHash.get(): # TODO: a cache will solve downloading the same block receipts for multiple logs rxs = (await vp.getReceipts(lg.blockHash.get())).valueOr: - return err(error) + return err("Couldn't get block receipt to verify logs") prevBlockHash = lg.blockHash.get() let txIdx = distinctBase(lg.transactionIndex.get()) @@ -133,7 +134,21 @@ proc verifyLogs*( rxLog.topics != lg.topics or lg.blockNumber.get() < filter.fromBlock.get().number or lg.blockNumber.get() > filter.toBlock.get().number or - (not match(toLog(lg), filterOptions.address, filterOptions.topics)): + (not match(toLog(lg), filter.address, filter.topics)): return err("one of the returned logs is invalid") - return ok() + ok() + +proc getLogs*(vp: VerifiedRpcProxy, filter: FilterOptions): Future[Result[seq[LogObject], string]] {.async.} = + let + resolvedFilter = vp.resolveFilterTags(filter).valueOr: + return err(error) + logObjs = + try: + await vp.rpcClient.eth_getLogs(resolvedFilter) + except CatchableError as e: + return err(e.msg) + + ?(await vp.verifyLogs(resolvedFilter, logObjs)) + + return ok(logObjs) diff --git a/nimbus_verified_proxy/rpc/rpc_eth_api.nim b/nimbus_verified_proxy/rpc/rpc_eth_api.nim index 96a4db0ab8..6f5fba3c21 100644 --- a/nimbus_verified_proxy/rpc/rpc_eth_api.nim +++ b/nimbus_verified_proxy/rpc/rpc_eth_api.nim @@ -272,36 +272,25 @@ proc installEthApiHandlers*(vp: VerifiedRpcProxy) = raise newException(ValueError, "receipt couldn't be verified") vp.proxy.rpc("eth_getLogs") do(filterOptions: FilterOptions) -> seq[LogObject]: - let - filter = resolveFilterTags(filterOptions).valueOr: - raise newException(ValueError, error) - logObjs = - try: - await vp.rpcClient.eth_getLogs(filter) - except CatchableError as e: - raise newException(ValueError, e.msg) - - ?(await vp.verifyLogs(filter, logObjs)) - - return logObjs + (await vp.getLogs(filterOptions)).valueOr: + raise newException(ValueError, error) - vp.proxy.rpc("eth_newFilter") do(filterOptions: FilterOptions) -> int: + vp.proxy.rpc("eth_newFilter") do(filterOptions: FilterOptions) -> string: let - hexId = + id = try: # filter is not resolved when storing only while fetching await vp.rpcClient.eth_newFilter(filterOptions) except CatchableError as e: raise newException(ValueError, e.msg) - id = fromHex[int](hexId) vp.filterStore[id] = filterOptions return id - vp.proxy.rpc("eth_uninstallFilter") do(filterId: int) -> bool: + vp.proxy.rpc("eth_uninstallFilter") do(filterId: string) -> bool: let status = try: - await vp.rpcClient.eth_uninstallFilter("0x" & toHex(filterId)) + await vp.rpcClient.eth_uninstallFilter(filterId) except CatchableError as e: raise newException(ValueError, e.msg) @@ -310,40 +299,33 @@ proc installEthApiHandlers*(vp: VerifiedRpcProxy) = return status - vp.proxy.rpc("eth_getFilterLogs") do(filterId: int) -> seq[LogObject]: + vp.proxy.rpc("eth_getFilterLogs") do(filterId: string) -> seq[LogObject]: if filterId notin vp.filterStore: raise newException(ValueError, "Filter doesn't exist") - let - filter = resolveFilterTags(vp.filterStore[filterId]).valueOr: - raise newException(ValueError, error) - logObjs = - try: - # use locally stored filter and get logs - await vp.rpcClient.eth_getLogs(filter) - except CatchableError as e: - raise newException(ValueError, e.msg) - - ?(await vp.verifyLogs(filter, logObjs)) - - return logObjs + (await vp.getLogs(vp.filterStore[filterId])).valueOr: + raise newException(ValueError, error) - vp.proxy.rpc("eth_getFilterChanges") do(filterId: int) -> seq[LogObject]: + vp.proxy.rpc("eth_getFilterChanges") do(filterId: string) -> seq[LogObject]: if filterId notin vp.filterStore: raise newException(ValueError, "Filter doesn't exist") let - filter = resolveFilterTags(vp.filterStore[filterId]).valueOr: + filter = vp.resolveFilterTags(vp.filterStore[filterId]).valueOr: raise newException(ValueError, error) logObjs = try: - await vp.rpcClient.eth_getFilterChanges("0x" & toHex(filterId)) + await vp.rpcClient.eth_getFilterChanges(filterId) except CatchableError as e: raise newException(ValueError, e.msg) - ?(await vp.verifyLogs(filter, logObjs)) + unmarshalledLogs = logObjs.to(seq[LogObject]) + verified = (await vp.verifyLogs(filter, unmarshalledLogs)) + + if verified.isErr(): + raise newException(ValueError, verified.error) - return logObjs + return unmarshalledLogs # Following methods are forwarded directly to the web3 provider and therefore # are not validated in any way. diff --git a/nimbus_verified_proxy/rpc_api_backend.nim b/nimbus_verified_proxy/rpc_api_backend.nim index 75b68f8872..57f8c14913 100644 --- a/nimbus_verified_proxy/rpc_api_backend.nim +++ b/nimbus_verified_proxy/rpc_api_backend.nim @@ -7,7 +7,7 @@ {.push raises: [], gcsafe.} -import json_rpc/[rpcproxy, rpcclient], web3/[eth_api, eth_api_types], stint, ./types +import json_rpc/[rpcproxy, rpcclient], web3/[eth_api, eth_api_types], stint, std/json, ./types proc initNetworkApiBackend*(vp: VerifiedRpcProxy): EthApiBackend = let @@ -59,6 +59,21 @@ proc initNetworkApiBackend*(vp: VerifiedRpcProxy): EthApiBackend = ): Future[seq[LogObject]] {.async: (raw: true).} = vp.proxy.getClient.eth_getLogs(filterOptions) + newFilterProc = proc( + filterOptions: FilterOptions + ): Future[string] {.async: (raw: true).} = + vp.proxy.getClient.eth_newFilter(filterOptions) + + uninstallFilterProc = proc( + filterId: string + ): Future[bool] {.async: (raw: true).} = + vp.proxy.getClient.eth_uninstallFilter(filterId) + + getFilterChangesProc = proc( + filterId: string + ): Future[JsonNode] {.async: (raw: true).} = + vp.proxy.getClient.eth_getFilterChanges(filterId) + EthApiBackend( eth_chainId: ethChainIdProc, eth_getBlockByHash: getBlockByHashProc, @@ -70,4 +85,7 @@ proc initNetworkApiBackend*(vp: VerifiedRpcProxy): EthApiBackend = eth_getLogs: getLogsProc, eth_getTransactionByHash: getTransactionByHashProc, eth_getTransactionReceipt: getTransactionReceiptProc, + eth_newFilter: newFilterProc, + eth_uninstallFilter: uninstallFilterProc, + eth_getFilterChanges: getFilterChangesProc ) diff --git a/nimbus_verified_proxy/types.nim b/nimbus_verified_proxy/types.nim index 6e9c729054..44dfd4917d 100644 --- a/nimbus_verified_proxy/types.nim +++ b/nimbus_verified_proxy/types.nim @@ -12,6 +12,7 @@ import json_rpc/[rpcproxy, rpcclient], web3/[eth_api, eth_api_types], stint, + std/json, minilru, ./header_store, ../execution_chain/evm/async_evm @@ -51,6 +52,10 @@ type GetTransactionReceiptProc = proc(txHash: Hash32): Future[ReceiptObject] {.async.} GetTransactionByHashProc = proc(txHash: Hash32): Future[TransactionObject] {.async.} GetLogsProc = proc(filterOptions: FilterOptions): Future[seq[LogObject]] {.async.} + NewFilterProc = proc(filterOptions: FilterOptions): Future[string] {.async.} + UninstallFilterProc = proc(filterId: string): Future[bool] {.async.} + GetFilterChangesProc = proc(filterid: string): Future[JsonNode] {.async.} + EthApiBackend* = object eth_chainId*: ChainIdProc @@ -63,6 +68,9 @@ type eth_getTransactionReceipt*: GetTransactionReceiptProc eth_getTransactionByHash*: GetTransactionByHashProc eth_getLogs*: GetLogsProc + eth_newFilter*: NewFilterProc + eth_uninstallFilter*: UninstallFilterProc + eth_getFilterChanges*: GetFilterChangesProc VerifiedRpcProxy* = ref object evm*: AsyncEvm @@ -75,7 +83,7 @@ type # TODO: when the list grows big add a config object instead # config parameters - filterStore*: Table[int, FilterOptions] + filterStore*: Table[string, FilterOptions] chainId*: UInt256 maxBlockWalk*: uint64 @@ -92,7 +100,6 @@ proc init*( accountsCache: AccountsCache.init(ACCOUNTS_CACHE_SIZE), codeCache: CodeCache.init(CODE_CACHE_SIZE), storageCache: StorageCache.init(STORAGE_CACHE_SIZE), - filterStore: initTable[int, FilterOptions](), chainId: chainId, maxBlockWalk: maxBlockWalk, ) From be3f132fdc0d74de255c1c697b50661f7e16ce21 Mon Sep 17 00:00:00 2001 From: chirag-parmar Date: Tue, 19 Aug 2025 07:38:25 +0530 Subject: [PATCH 07/11] format --- nimbus_verified_proxy/rpc/receipts.nim | 11 +++++++---- nimbus_verified_proxy/rpc/rpc_eth_api.nim | 15 +++++++-------- nimbus_verified_proxy/rpc_api_backend.nim | 13 ++++++++----- nimbus_verified_proxy/types.nim | 1 - 4 files changed, 22 insertions(+), 18 deletions(-) diff --git a/nimbus_verified_proxy/rpc/receipts.nim b/nimbus_verified_proxy/rpc/receipts.nim index 257bc14409..5473fb6954 100644 --- a/nimbus_verified_proxy/rpc/receipts.nim +++ b/nimbus_verified_proxy/rpc/receipts.nim @@ -84,7 +84,9 @@ proc getReceipts*( await vp.getReceipts(header, numberTag) -proc resolveFilterTags*(vp: VerifiedRpcProxy, filter: FilterOptions): Result[FilterOptions, string] = +proc resolveFilterTags*( + vp: VerifiedRpcProxy, filter: FilterOptions +): Result[FilterOptions, string] = if filter.blockHash.isSome(): return ok(filter) let @@ -106,9 +108,8 @@ proc resolveFilterTags*(vp: VerifiedRpcProxy, filter: FilterOptions): Result[Fil ) proc verifyLogs*( - vp: VerifiedRpcProxy, filter: FilterOptions, logObjs: seq[LogObject] + vp: VerifiedRpcProxy, filter: FilterOptions, logObjs: seq[LogObject] ): Future[Result[void, string]] {.async.} = - # store block hashes contains the logs so that we can batch receipt requests var prevBlockHash: Hash32 @@ -139,7 +140,9 @@ proc verifyLogs*( ok() -proc getLogs*(vp: VerifiedRpcProxy, filter: FilterOptions): Future[Result[seq[LogObject], string]] {.async.} = +proc getLogs*( + vp: VerifiedRpcProxy, filter: FilterOptions +): Future[Result[seq[LogObject], string]] {.async.} = let resolvedFilter = vp.resolveFilterTags(filter).valueOr: return err(error) diff --git a/nimbus_verified_proxy/rpc/rpc_eth_api.nim b/nimbus_verified_proxy/rpc/rpc_eth_api.nim index 6f5fba3c21..578575fbbd 100644 --- a/nimbus_verified_proxy/rpc/rpc_eth_api.nim +++ b/nimbus_verified_proxy/rpc/rpc_eth_api.nim @@ -272,17 +272,16 @@ proc installEthApiHandlers*(vp: VerifiedRpcProxy) = raise newException(ValueError, "receipt couldn't be verified") vp.proxy.rpc("eth_getLogs") do(filterOptions: FilterOptions) -> seq[LogObject]: - (await vp.getLogs(filterOptions)).valueOr: + (await vp.getLogs(filterOptions)).valueOr: raise newException(ValueError, error) vp.proxy.rpc("eth_newFilter") do(filterOptions: FilterOptions) -> string: - let - id = - try: - # filter is not resolved when storing only while fetching - await vp.rpcClient.eth_newFilter(filterOptions) - except CatchableError as e: - raise newException(ValueError, e.msg) + let id = + try: + # filter is not resolved when storing only while fetching + await vp.rpcClient.eth_newFilter(filterOptions) + except CatchableError as e: + raise newException(ValueError, e.msg) vp.filterStore[id] = filterOptions return id diff --git a/nimbus_verified_proxy/rpc_api_backend.nim b/nimbus_verified_proxy/rpc_api_backend.nim index 57f8c14913..608283b2b5 100644 --- a/nimbus_verified_proxy/rpc_api_backend.nim +++ b/nimbus_verified_proxy/rpc_api_backend.nim @@ -7,7 +7,12 @@ {.push raises: [], gcsafe.} -import json_rpc/[rpcproxy, rpcclient], web3/[eth_api, eth_api_types], stint, std/json, ./types +import + json_rpc/[rpcproxy, rpcclient], + web3/[eth_api, eth_api_types], + stint, + std/json, + ./types proc initNetworkApiBackend*(vp: VerifiedRpcProxy): EthApiBackend = let @@ -64,9 +69,7 @@ proc initNetworkApiBackend*(vp: VerifiedRpcProxy): EthApiBackend = ): Future[string] {.async: (raw: true).} = vp.proxy.getClient.eth_newFilter(filterOptions) - uninstallFilterProc = proc( - filterId: string - ): Future[bool] {.async: (raw: true).} = + uninstallFilterProc = proc(filterId: string): Future[bool] {.async: (raw: true).} = vp.proxy.getClient.eth_uninstallFilter(filterId) getFilterChangesProc = proc( @@ -87,5 +90,5 @@ proc initNetworkApiBackend*(vp: VerifiedRpcProxy): EthApiBackend = eth_getTransactionReceipt: getTransactionReceiptProc, eth_newFilter: newFilterProc, eth_uninstallFilter: uninstallFilterProc, - eth_getFilterChanges: getFilterChangesProc + eth_getFilterChanges: getFilterChangesProc, ) diff --git a/nimbus_verified_proxy/types.nim b/nimbus_verified_proxy/types.nim index 44dfd4917d..98e88f213b 100644 --- a/nimbus_verified_proxy/types.nim +++ b/nimbus_verified_proxy/types.nim @@ -56,7 +56,6 @@ type UninstallFilterProc = proc(filterId: string): Future[bool] {.async.} GetFilterChangesProc = proc(filterid: string): Future[JsonNode] {.async.} - EthApiBackend* = object eth_chainId*: ChainIdProc eth_getBlockByHash*: GetBlockByHashProc From 98f32dbbf2d71849349e97e9107eb3906b342ad8 Mon Sep 17 00:00:00 2001 From: chirag-parmar Date: Thu, 21 Aug 2025 23:21:47 +0530 Subject: [PATCH 08/11] fixes --- nimbus_verified_proxy/rpc/rpc_eth_api.nim | 71 +++++++++++-------- nimbus_verified_proxy/rpc_api_backend.nim | 16 ----- nimbus_verified_proxy/tests/test_receipts.nim | 70 ++++++++++++++++++ nimbus_verified_proxy/types.nim | 13 ++-- 4 files changed, 118 insertions(+), 52 deletions(-) diff --git a/nimbus_verified_proxy/rpc/rpc_eth_api.nim b/nimbus_verified_proxy/rpc/rpc_eth_api.nim index 578575fbbd..00aad1d578 100644 --- a/nimbus_verified_proxy/rpc/rpc_eth_api.nim +++ b/nimbus_verified_proxy/rpc/rpc_eth_api.nim @@ -11,6 +11,7 @@ import results, chronicles, stew/byteutils, + nimcrypto/sysrand, json_rpc/[rpcserver, rpcclient, rpcproxy], eth/common/accounts, web3/eth_api, @@ -276,33 +277,30 @@ proc installEthApiHandlers*(vp: VerifiedRpcProxy) = raise newException(ValueError, error) vp.proxy.rpc("eth_newFilter") do(filterOptions: FilterOptions) -> string: - let id = - try: - # filter is not resolved when storing only while fetching - await vp.rpcClient.eth_newFilter(filterOptions) - except CatchableError as e: - raise newException(ValueError, e.msg) + var id: array[8, byte] # 64bits - vp.filterStore[id] = filterOptions - return id + if randomBytes(id) != len(id): + raise newException(ValueError, "Couldn't assign a identifier for the filter") - vp.proxy.rpc("eth_uninstallFilter") do(filterId: string) -> bool: - let status = - try: - await vp.rpcClient.eth_uninstallFilter(filterId) - except CatchableError as e: - raise newException(ValueError, e.msg) + let strId = toHex(id) + + vp.filterStore[strId] = + FilterStoreItem(filter: filterOptions, blockMarker: Opt.none(Quantity)) + + return strId - if status and filterId in vp.filterStore: + vp.proxy.rpc("eth_uninstallFilter") do(filterId: string) -> bool: + if filterId in vp.filterStore: vp.filterStore.del(filterId) + return true - return status + return false vp.proxy.rpc("eth_getFilterLogs") do(filterId: string) -> seq[LogObject]: if filterId notin vp.filterStore: raise newException(ValueError, "Filter doesn't exist") - (await vp.getLogs(vp.filterStore[filterId])).valueOr: + (await vp.getLogs(vp.filterStore[filterId].filter)).valueOr: raise newException(ValueError, error) vp.proxy.rpc("eth_getFilterChanges") do(filterId: string) -> seq[LogObject]: @@ -310,21 +308,38 @@ proc installEthApiHandlers*(vp: VerifiedRpcProxy) = raise newException(ValueError, "Filter doesn't exist") let - filter = vp.resolveFilterTags(vp.filterStore[filterId]).valueOr: + filterItem = vp.filterStore[filterId] + filter = vp.resolveFilterTags(filterItem.filter).valueOr: raise newException(ValueError, error) - logObjs = - try: - await vp.rpcClient.eth_getFilterChanges(filterId) - except CatchableError as e: - raise newException(ValueError, e.msg) + # after resolving toBlock is always some and a number tag + toBlock = filter.toBlock.get().number - unmarshalledLogs = logObjs.to(seq[LogObject]) - verified = (await vp.verifyLogs(filter, unmarshalledLogs)) + if filterItem.blockMarker.isSome() and toBlock <= filterItem.blockMarker.get(): + raise newException(ValueError, "No changes for the filter since the last query") + + let + fromBlock = + if filterItem.blockMarker.isSome(): + Opt.some( + types.BlockTag(kind: bidNumber, number: filterItem.blockMarker.get()) + ) + else: + filter.fromBlock + + changesFilter = FilterOptions( + fromBlock: fromBlock, + toBlock: filter.toBlock, + address: filter.address, + topics: filter.topics, + blockHash: filter.blockHash, + ) + logObjs = (await vp.getLogs(changesFilter)).valueOr: + raise newException(ValueError, error) - if verified.isErr(): - raise newException(ValueError, verified.error) + # all logs verified so we can update blockMarker + vp.filterStore[filterId].blockMarker = Opt.some(toBlock) - return unmarshalledLogs + return logObjs # Following methods are forwarded directly to the web3 provider and therefore # are not validated in any way. diff --git a/nimbus_verified_proxy/rpc_api_backend.nim b/nimbus_verified_proxy/rpc_api_backend.nim index 608283b2b5..637cbf74c6 100644 --- a/nimbus_verified_proxy/rpc_api_backend.nim +++ b/nimbus_verified_proxy/rpc_api_backend.nim @@ -64,19 +64,6 @@ proc initNetworkApiBackend*(vp: VerifiedRpcProxy): EthApiBackend = ): Future[seq[LogObject]] {.async: (raw: true).} = vp.proxy.getClient.eth_getLogs(filterOptions) - newFilterProc = proc( - filterOptions: FilterOptions - ): Future[string] {.async: (raw: true).} = - vp.proxy.getClient.eth_newFilter(filterOptions) - - uninstallFilterProc = proc(filterId: string): Future[bool] {.async: (raw: true).} = - vp.proxy.getClient.eth_uninstallFilter(filterId) - - getFilterChangesProc = proc( - filterId: string - ): Future[JsonNode] {.async: (raw: true).} = - vp.proxy.getClient.eth_getFilterChanges(filterId) - EthApiBackend( eth_chainId: ethChainIdProc, eth_getBlockByHash: getBlockByHashProc, @@ -88,7 +75,4 @@ proc initNetworkApiBackend*(vp: VerifiedRpcProxy): EthApiBackend = eth_getLogs: getLogsProc, eth_getTransactionByHash: getTransactionByHashProc, eth_getTransactionReceipt: getTransactionReceiptProc, - eth_newFilter: newFilterProc, - eth_uninstallFilter: uninstallFilterProc, - eth_getFilterChanges: getFilterChangesProc, ) diff --git a/nimbus_verified_proxy/tests/test_receipts.nim b/nimbus_verified_proxy/tests/test_receipts.nim index dec6a49a6e..ce5927192c 100644 --- a/nimbus_verified_proxy/tests/test_receipts.nim +++ b/nimbus_verified_proxy/tests/test_receipts.nim @@ -92,3 +92,73 @@ suite "test receipts verification": ts.loadLogs(filterOptions, logs) let verifiedLogs = waitFor vp.proxy.getClient().eth_getLogs(filterOptions) check verifiedLogs.len == logs.len + + test "create filters and uninstall filters": + # filter options without any tags would test resolving default "latest" + let filterOptions = FilterOptions( + topics: + @[ + TopicOrList( + kind: SingleOrListKind.slkSingle, + single: + bytes32"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + ) + ], + blockHash: Opt.none(Hash32), + ) + + let + # create a filter + newFilter = waitFor vp.proxy.getClient().eth_newFilter(filterOptions) + # deleting will prove if the filter was created + delStatus = waitFor vp.proxy.getClient().eth_uninstallFilter(newFilter) + + check delStatus + + let + unknownFilterId = "thisisacorrectfilterid" + delStatus2 = waitFor vp.proxy.getClient().eth_uninstallFilter(newFilter) + + check not delStatus2 + + test "get logs using filter changes": + let + blk = getBlockFromJson("nimbus_verified_proxy/tests/data/Paris.json") + rxs = getReceiptsFromJson("nimbus_verified_proxy/tests/data/receipts.json") + logs = getLogsFromJson("nimbus_verified_proxy/tests/data/logs.json") + + # update block tags because getLogs (uses)-> getReceipts (uses)-> getHeader + ts.loadBlockReceipts(blk, rxs) + discard vp.headerStore.add(convHeader(blk), blk.hash) + discard vp.headerStore.updateFinalized(convHeader(blk), blk.hash) + + # filter options without any tags would test resolving default "latest" + let filterOptions = FilterOptions( + topics: + @[ + TopicOrList( + kind: SingleOrListKind.slkSingle, + single: + bytes32"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + ) + ], + blockHash: Opt.none(Hash32), + ) + + ts.loadLogs(filterOptions, logs) + + let + # create a filter + newFilter = waitFor vp.proxy.getClient().eth_newFilter(filterOptions) + filterLogs = waitFor vp.proxy.getClient().eth_getFilterLogs(newFilter) + filterChanges = waitFor vp.proxy.getClient().eth_getFilterChanges(newFilter) + + check filterLogs.len == logs.len + check filterChanges.len == logs.len + + try: + let againFilterChanges = + waitFor vp.proxy.getClient().eth_getFilterChanges(newFilter) + check false + except CatchableError as e: + check true diff --git a/nimbus_verified_proxy/types.nim b/nimbus_verified_proxy/types.nim index 98e88f213b..413da4be7f 100644 --- a/nimbus_verified_proxy/types.nim +++ b/nimbus_verified_proxy/types.nim @@ -12,7 +12,6 @@ import json_rpc/[rpcproxy, rpcclient], web3/[eth_api, eth_api_types], stint, - std/json, minilru, ./header_store, ../execution_chain/evm/async_evm @@ -52,9 +51,6 @@ type GetTransactionReceiptProc = proc(txHash: Hash32): Future[ReceiptObject] {.async.} GetTransactionByHashProc = proc(txHash: Hash32): Future[TransactionObject] {.async.} GetLogsProc = proc(filterOptions: FilterOptions): Future[seq[LogObject]] {.async.} - NewFilterProc = proc(filterOptions: FilterOptions): Future[string] {.async.} - UninstallFilterProc = proc(filterId: string): Future[bool] {.async.} - GetFilterChangesProc = proc(filterid: string): Future[JsonNode] {.async.} EthApiBackend* = object eth_chainId*: ChainIdProc @@ -67,9 +63,10 @@ type eth_getTransactionReceipt*: GetTransactionReceiptProc eth_getTransactionByHash*: GetTransactionByHashProc eth_getLogs*: GetLogsProc - eth_newFilter*: NewFilterProc - eth_uninstallFilter*: UninstallFilterProc - eth_getFilterChanges*: GetFilterChangesProc + + FilterStoreItem* = object + filter*: FilterOptions + blockMarker*: Opt[Quantity] VerifiedRpcProxy* = ref object evm*: AsyncEvm @@ -82,7 +79,7 @@ type # TODO: when the list grows big add a config object instead # config parameters - filterStore*: Table[string, FilterOptions] + filterStore*: Table[string, FilterStoreItem] chainId*: UInt256 maxBlockWalk*: uint64 From 7116edf82f56f5c8edcba66fef811bd26064e166 Mon Sep 17 00:00:00 2001 From: chirag-parmar Date: Fri, 22 Aug 2025 12:02:03 +0530 Subject: [PATCH 09/11] trigger CI --- execution_chain/version.nim | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/execution_chain/version.nim b/execution_chain/version.nim index 6875a4bc5d..8850679341 100644 --- a/execution_chain/version.nim +++ b/execution_chain/version.nim @@ -18,7 +18,10 @@ const static: doAssert(nimbusRevision.len == 8, "nimbusRevision must consist of 8 characters") - doAssert(nimbusRevision.allIt(it in HexDigits), "nimbusRevision should contains only hex chars") + doAssert( + nimbusRevision.allIt(it in HexDigits), + "nimbusRevision should contains only hex chars", + ) proc gitFolderExists(path: string): bool {.compileTime.} = # walk up parent folder to find `.git` folder From bc24ddbf2ba872bdb72f38bf9ecce88f4103edfd Mon Sep 17 00:00:00 2001 From: chirag-parmar Date: Tue, 26 Aug 2025 15:18:58 +0530 Subject: [PATCH 10/11] review --- nimbus_verified_proxy/rpc/rpc_eth_api.nim | 23 +++++++++++++++++++---- nimbus_verified_proxy/types.nim | 2 ++ 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/nimbus_verified_proxy/rpc/rpc_eth_api.nim b/nimbus_verified_proxy/rpc/rpc_eth_api.nim index 00aad1d578..ea94b77061 100644 --- a/nimbus_verified_proxy/rpc/rpc_eth_api.nim +++ b/nimbus_verified_proxy/rpc/rpc_eth_api.nim @@ -277,12 +277,27 @@ proc installEthApiHandlers*(vp: VerifiedRpcProxy) = raise newException(ValueError, error) vp.proxy.rpc("eth_newFilter") do(filterOptions: FilterOptions) -> string: - var id: array[8, byte] # 64bits + if vp.filterStore.len >= MAX_FILTERS: + raise newException(ValueError, "FilterStore already full") - if randomBytes(id) != len(id): - raise newException(ValueError, "Couldn't assign a identifier for the filter") + var + id: array[8, byte] # 64bits + strId: string - let strId = toHex(id) + for i in 0 .. (MAX_ID_TRIES + 1): + if randomBytes(id) != len(id): + raise newException( + ValueError, "Couldn't generate a random identifier for the filter" + ) + + strId = toHex(id) + + if not vp.filterStore.contains(strId): + break + + if i >= MAX_ID_TRIES: + raise + newException(ValueError, "Couldn't create a unique identifier for the filter") vp.filterStore[strId] = FilterStoreItem(filter: filterOptions, blockMarker: Opt.none(Quantity)) diff --git a/nimbus_verified_proxy/types.nim b/nimbus_verified_proxy/types.nim index 413da4be7f..e45bd9dc42 100644 --- a/nimbus_verified_proxy/types.nim +++ b/nimbus_verified_proxy/types.nim @@ -22,6 +22,8 @@ const ACCOUNTS_CACHE_SIZE = 128 CODE_CACHE_SIZE = 64 STORAGE_CACHE_SIZE = 256 + MAX_ID_TRIES* = 10 + MAX_FILTERS* = 256 type AccountsCacheKey* = (Root, Address) From 6e47f4a822b0e3c55ef2343ab00dfabb129daa87 Mon Sep 17 00:00:00 2001 From: chirag-parmar Date: Tue, 26 Aug 2025 15:52:28 +0530 Subject: [PATCH 11/11] revert line --- nimbus_verified_proxy/rpc/receipts.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nimbus_verified_proxy/rpc/receipts.nim b/nimbus_verified_proxy/rpc/receipts.nim index 5473fb6954..a2f915df98 100644 --- a/nimbus_verified_proxy/rpc/receipts.nim +++ b/nimbus_verified_proxy/rpc/receipts.nim @@ -122,7 +122,7 @@ proc verifyLogs*( if prevBlockHash != lg.blockHash.get(): # TODO: a cache will solve downloading the same block receipts for multiple logs rxs = (await vp.getReceipts(lg.blockHash.get())).valueOr: - return err("Couldn't get block receipt to verify logs") + return err(error) prevBlockHash = lg.blockHash.get() let txIdx = distinctBase(lg.transactionIndex.get())