From 19ecb26cf0e902a51b30a4563d1afdc01b465b6e Mon Sep 17 00:00:00 2001 From: awskii Date: Tue, 11 Aug 2026 01:05:35 +0700 Subject: [PATCH 1/2] rpc: fix parity_listStorageKeys state version and gate eth_getProof on execution parity_listStorageKeys read the account with a latest-state reader (state after the head block) but scanned its storage at Min(bn), the first txNum of the head block, i.e. the state after bn-1. A slot written in the head block was missing from the listing and a slot deleted in it was still listed. state.Dumper, the equivalent path, uses Min(blockNumber+1). eth_getProof resolved a block by canonical hash only. Canonical hashes exist for blocks the header stage has downloaded but execution has not reached, so a request for one walked the history path and surfaced a PrunedError or a root-hash mismatch instead of saying the block is not executed yet. --- rpc/jsonrpc/eth_call.go | 7 +++++++ rpc/jsonrpc/parity_api.go | 4 +++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/rpc/jsonrpc/eth_call.go b/rpc/jsonrpc/eth_call.go index f1cd0e2fb43..95747941efa 100644 --- a/rpc/jsonrpc/eth_call.go +++ b/rpc/jsonrpc/eth_call.go @@ -437,6 +437,13 @@ func (api *APIImpl) GetProof(ctx context.Context, address common.Address, storag return nil, err } + // A canonical hash exists for blocks the header stage has downloaded but + // execution has not reached; the commitment history getProof needs is only + // written by execution. + if err := rpchelper.CheckBlockExecuted(roTx, uint64(requestedBlockNr)); err != nil { + return nil, err + } + err = api.BaseAPI.checkPruneHistory(ctx, roTx, uint64(requestedBlockNr)) if err != nil { return nil, err diff --git a/rpc/jsonrpc/parity_api.go b/rpc/jsonrpc/parity_api.go index 2a4276248d7..41ca1246e5c 100644 --- a/rpc/jsonrpc/parity_api.go +++ b/rpc/jsonrpc/parity_api.go @@ -75,7 +75,9 @@ func (api *ParityAPIImpl) ListStorageKeys(ctx context.Context, account common.Ad } bn := rawdb.ReadCurrentBlockNumber(tx) - minTxNum, err := api._txNumReader.Min(ctx, tx, *bn) + // Min(bn+1) is the first txNum past bn — the state the latest-state account + // read above sees. Min(bn) would scan storage as of the end of bn-1. + minTxNum, err := api._txNumReader.Min(ctx, tx, *bn+1) if err != nil { return nil, err } From 110670cf2071c7581e19e818f8891425803a1a94 Mon Sep 17 00:00:00 2001 From: awskii Date: Tue, 11 Aug 2026 13:21:23 +0700 Subject: [PATCH 2/2] rpc: address review on the getProof gate and the listStorageKeys head read The execution gate read the plain roTx while the block tag was still resolved through the overlay, so during a commit an overlay-resolved head could be reported as not executed. Resolve on the committed view instead, which is also where the commitment-history reads happen. This overlaps the same change in #22533; whichever lands first, the other is a trivial conflict. rawdb.ReadCurrentBlockNumber returns nil when no head header is set, so listStorageKeys dereferenced a nil pointer instead of returning an error. --- rpc/jsonrpc/eth_call.go | 4 +++- rpc/jsonrpc/parity_api.go | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/rpc/jsonrpc/eth_call.go b/rpc/jsonrpc/eth_call.go index 95747941efa..dbb12e9739c 100644 --- a/rpc/jsonrpc/eth_call.go +++ b/rpc/jsonrpc/eth_call.go @@ -432,7 +432,9 @@ func (api *APIImpl) GetProof(ctx context.Context, address common.Address, storag } defer roTx.Rollback() - requestedBlockNr, _, _, err := rpchelper.GetCanonicalBlockNumber(ctx, blockNrOrHash, roTx, api._blockReader, api.filters) + // nil filters: the gate below and the commitment-history reads both go through + // this plain roTx, so the tag has to resolve on that same committed view. + requestedBlockNr, _, _, err := rpchelper.GetCanonicalBlockNumber(ctx, blockNrOrHash, roTx, api._blockReader, nil) if err != nil { return nil, err } diff --git a/rpc/jsonrpc/parity_api.go b/rpc/jsonrpc/parity_api.go index 41ca1246e5c..582e34c717d 100644 --- a/rpc/jsonrpc/parity_api.go +++ b/rpc/jsonrpc/parity_api.go @@ -75,6 +75,9 @@ func (api *ParityAPIImpl) ListStorageKeys(ctx context.Context, account common.Ad } bn := rawdb.ReadCurrentBlockNumber(tx) + if bn == nil { + return nil, errors.New("current block number not found") + } // Min(bn+1) is the first txNum past bn — the state the latest-state account // read above sees. Min(bn) would scan storage as of the end of bn-1. minTxNum, err := api._txNumReader.Min(ctx, tx, *bn+1)