Skip to content

Restore LCD pagination while preserving v6.6 precompile semantics - #3867

Merged
masih merged 7 commits into
mainfrom
masih/relax-deleg-query-pagination
Aug 7, 2026
Merged

Restore LCD pagination while preserving v6.6 precompile semantics#3867
masih merged 7 commits into
mainfrom
masih/relax-deleg-query-pagination

Conversation

@masih

@masih masih commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator

The query hardening introduced in #3494 broke previously valid LCD and gRPC pagination requests. Large limits and offsets were rejected, sparse filtered queries failed after scanning 10,000 entries, and count_total could not calculate totals for large stores.

Restore the pre-v6.6 client-facing pagination behavior:

  • allow store-backed queries to use limits and offsets across the uint64
    range
  • permit sparse filtered LCD/gRPC queries to scan the complete store
  • calculate count_total across stores larger than 10,000 entries
  • saturate offset+limit calculations instead of allowing uint64 overflow
  • preserve the first next_key while count_total scans the remaining store
  • cap GetBlockWithTxs allocation by the block's actual transaction count
  • keep CollectAllTotalSupply internally paged at 1,000 entries

Do not restore the upstream behavior that implicitly enables count_total when limit is omitted.

EVM precompiles invoke several Cosmos query handlers during transaction execution. Changing their filtered scan limits after v6.6 can alter gas consumption and success/revert control flow, producing AppHash and
LastResultsHash divergence.

Add explicit v6.6-compatible filtered paginator variants and select them for EVM execution in the transaction-reachable staking, governance, feegrant, distribution, and authz query handlers. LCD and gRPC requests continue using the repaired unrestricted implementations, while precompiles retain the released v6.6 consensus behavior.

The query hardening introduced in #3494 (PLT-361) broke previously
valid LCD/gRPC pagination requests:

- limits above 1,000 and offsets above 10,000 are rejected with
  InvalidArgument instead of being served
- count_total=true fails with a "scanned more than 10,000 entries"
  error whenever the underlying store is larger than the scan cap,
  so totals cannot be retrieved from large stores at all
- filtered queries (e.g. validators by status) fail mid-pagination
  when the filter has to scan more than 10,000 entries to fill a
  page, leaving no way to enumerate sparse result sets by offset

Clients that paginate with large limits, follow deep offsets, or rely
on pagination.total regressed against every release prior to v6.6.

Restore the pre-v6.6 pagination contract for all valid uint64 inputs:

- accept any uint64 limit and offset; remove MaxScanLimit/MaxOffset,
  the 1,000-per-page MaxLimit cap, and the scan-cutoff errors in
  Paginate, FilteredPaginate and GenericFilteredPaginate
- compute the page end with uint64 saturation (paginationEnd) so
  offset+limit can no longer wrap around on adversarial inputs
- preserve the first next_key found while a count_total scan walks
  the remainder of the store, instead of overwriting it on every
  subsequent iteration (long-standing upstream bug)
- GetBlockWithTxs caps its slice pre-allocation at the number of txs
  actually in the block rather than trusting the requested limit
- CollectAllTotalSupply pages the supply store at a fixed page size
  of 1,000 so genesis export keeps bounded per-page memory

Deliberately NOT restored: upstream's implicit count_total=true when
limit is omitted. EVM precompiles (precompiles/staking) invoke these
query handlers during transaction execution with Limit: 0, and their
store reads are metered against the transaction's gas. An implicit
full-store count would change GasUsed (LastResultsHash) and gas
refunds (AppHash) across versions. For the same reason this change is
consensus-safe: on every code path reachable from block execution the
paginators iterate exactly as before (page requests with limit 0->100,
offset 0, count_total false break at the same iteration), and all
remaining behavioural changes are confined to ABCI Query handlers,
which are committed to neither AppHash nor LastResultsHash.

Regression coverage: uint64-max limits, offsets past 10,000, accurate
count_total on stores larger than 10,000 entries, sparse filters over
large stores, next_key preservation, and multi-page supply collection.
@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown

The latest Buf updates on your PR. Results from workflow Buf / buf (pull_request).

BuildFormatLintBreakingUpdated (UTC)
✅ passed✅ passed✅ passed✅ passedAug 7, 2026, 9:06 AM

@codecov

codecov Bot commented Aug 6, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 66.21622% with 25 lines in your changes missing coverage. Please review.
✅ Project coverage is 60.87%. Comparing base (a8f5cd1) to head (9540a22).

Files with missing lines Patch % Lines
sei-cosmos/types/query/filtered_pagination.go 60.00% 3 Missing and 5 partials ⚠️
sei-cosmos/x/authz/keeper/grpc_query.go 50.00% 3 Missing and 3 partials ⚠️
sei-cosmos/x/staking/keeper/grpc_query.go 50.00% 2 Missing and 2 partials ⚠️
sei-cosmos/x/distribution/keeper/grpc_query.go 50.00% 1 Missing and 1 partial ⚠️
sei-cosmos/x/feegrant/keeper/grpc_query.go 50.00% 1 Missing and 1 partial ⚠️
sei-cosmos/x/gov/keeper/grpc_query.go 50.00% 1 Missing and 1 partial ⚠️
sei-cosmos/x/auth/tx/service.go 0.00% 1 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #3867      +/-   ##
==========================================
- Coverage   61.74%   60.87%   -0.87%     
==========================================
  Files        2381     2287      -94     
  Lines      201667   191186   -10481     
==========================================
- Hits       124513   116392    -8121     
+ Misses      66074    64547    -1527     
+ Partials    11080    10247     -833     
Flag Coverage Δ
sei-chain-pr 76.33% <66.21%> (?)
sei-db 70.41% <ø> (ø)
sei-db-state-db ?

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
sei-cosmos/baseapp/abci.go 64.26% <100.00%> (ø)
sei-cosmos/baseapp/grpcserver.go 73.33% <100.00%> (+0.60%) ⬆️
sei-cosmos/types/context.go 90.87% <100.00%> (+0.16%) ⬆️
sei-cosmos/types/query/pagination.go 90.24% <100.00%> (+3.64%) ⬆️
sei-cosmos/x/bank/keeper/keeper.go 80.70% <100.00%> (ø)
x/evm/keeper/evm.go 75.00% <100.00%> (+0.18%) ⬆️
sei-cosmos/x/auth/tx/service.go 7.63% <0.00%> (+0.11%) ⬆️
sei-cosmos/x/distribution/keeper/grpc_query.go 68.42% <50.00%> (-0.81%) ⬇️
sei-cosmos/x/feegrant/keeper/grpc_query.go 77.46% <50.00%> (-1.95%) ⬇️
sei-cosmos/x/gov/keeper/grpc_query.go 75.51% <50.00%> (-0.88%) ⬇️
... and 3 more

... and 94 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@masih masih changed the title Restore pre-v6.6 pagination semantics for LCD/gRPC queries Restore LCD pagination while preserving v6.6 precompile semantics Aug 6, 2026
@masih
masih marked this pull request as ready for review August 6, 2026 15:37
@cursor

cursor Bot commented Aug 6, 2026

Copy link
Copy Markdown

PR Summary

High Risk
Splits pagination behavior on consensus-critical query paths (precompiles); incorrect IsABCIQuery wiring or handler selection could cause AppHash divergence or change client query semantics.

Overview
Reverts client-facing store pagination to pre-hardening behavior for LCD/gRPC while keeping consensus-safe pagination for EVM precompiles and tx execution.

Pagination (ABCI / LCD / gRPC): Removes tight MaxLimit, MaxOffset, and MaxScanLimit enforcement on Paginate / FilteredPaginate; raises MaxLimit to uint64 max; adds overflow-safe paginationEnd; fixes next_key so the first key past the page is not overwritten during count_total scans. CollectAllTotalSupply pages internally at 1,000 keys; GetBlockWithTxs caps slice allocation by actual block tx count.

Dual code paths: Adds Context.IsABCIQuery() (set only in BaseApp ABCI/gRPC query paths, cleared for StaticCallEVM). Handlers for staking, gov, authz, feegrant, and distribution choose FilteredPaginate / GenericFilteredPaginate when IsABCIQuery(), else *V66 variants that still enforce the 10k scan cap so precompile gas and revert behavior match release v6.6.

Reviewed by Cursor Bugbot for commit 9540a22. Bugbot is set up for automated code reviews on this repo. Configure here.

@masih masih added the backport release/v6.6 Backport to release v6.6 label Aug 6, 2026

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want higher recall? High effort reviews run extra passes and find more bugs. A team admin can switch effort levels in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit f19d52f. Configure here.

Comment thread sei-cosmos/baseapp/abci.go Outdated
Comment thread sei-cosmos/types/context.go
seidroid[bot]
seidroid Bot previously requested changes Aug 6, 2026

@seidroid seidroid Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The pagination repair and the V66 gating are well-targeted (every FilteredPaginate/GenericFilteredPaginate call site reachable from a precompile is converted, and plain Paginate's key-branch never had a scan cap so it correctly needs no variant), but the IsABCIQuery discriminator also flips EVM simulation/tracing contexts onto the repaired paginator, so eth_call/eth_estimateGas/debug_trace* at a historical height will diverge from real execution — and the V66 variants silently dropped the v6.6 limit/offset validation. There is also zero test coverage for the V66 paginators or the branch selection.

Findings: 3 blocking | 7 non-blocking | 4 posted inline

Blockers

  • No test coverage for the consensus-critical half of this change. Every scan-limit test was deleted and replaced with unrestricted-behavior tests; there is not a single test exercising FilteredPaginateV66, GenericFilteredPaginateV66, or the ctx.IsABCIQuery() branch selection in the six converted keepers (grep -rn "V66\|IsABCIQuery" --include=*_test.go returns nothing). Given the PR's own framing — that getting this wrong produces AppHash/LastResultsHash divergence — a refactor could delete the V66 branch and the suite would stay green. Please add: (a) direct tests that the V66 variants still return the scanned more than 10000 entries error on both the key-based and offset-based paths, and (b) a keeper-level test asserting an ABCI-query context takes the unrestricted path while a plain execution context takes the V66 path.
  • 2 blocking issue(s) flagged inline on specific lines.

Non-blocking

  • Both second-opinion passes produced no output: codex-review.md and cursor-review.md are empty files. All findings here are from this pass alone.
  • Security/DoS regression on public endpoints is intentional but unmitigated. CreateQueryContext builds the context with an infinite gas meter, so the repaired Paginate/FilteredPaginate now allow an unauthenticated LCD/gRPC caller to force a full-store walk (count_total: true, offset in the billions, or a sparse filter that never fills the page and scans to the end of the store looking for next_key). That is precisely what #3494 was hardening against. Consider a node-operator-configurable scan cap or a query-context gas meter/timeout rather than removing the bound outright, and note the operational exposure in the release notes.
  • Paginate has no V66 variant. That is correct today only because its key-based branch never had a scan cap and every precompile passes a key-only PageRequest with Limit/Offset/CountTotal unset (verified across precompiles/, including the legacy/v5xxv66 copies, which all dispatch into these same keepers). This is load-bearing but undocumented — a future precompile that sets Limit or CountTotal on a Paginate-backed handler (staking ValidatorUnbondingDelegations/DelegatorDelegations/Redelegations, gov Votes/Deposits, feegrant Allowances, bank SpendableBalances/TotalSupply) would silently change consensus. Worth a comment in pagination.go plus a test asserting precompile page requests stay key-only.
  • Inconsistent next-key condition: the filtered paginators were changed to numHits > end with a nextKey == nil guard, but Paginate still uses count == end+1 (pagination.go:128). Both are correct since the counters increment by one, but the asymmetry invites a future reader to "fix" one of them. Consider aligning them.
  • CollectAllTotalSupply's page size moved from query.MaxLimit to the new supplyPageSize — both 1,000, so no behavior change. Good, and the keeper_test comment update is accurate.
  • 2 suggestion(s)/nit(s) flagged inline on specific lines.

Comment thread sei-cosmos/baseapp/abci.go Outdated
).WithMinGasPrices(app.minGasPrices).WithBlockHeight(height)
).WithMinGasPrices(app.minGasPrices).
WithBlockHeight(height).
WithIsABCIQuery(true)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[blocker] CreateQueryContext is also the context source for EVM simulation and tracing, so this flag makes precompiles behave differently under eth_call than under real execution.

app.RPCContextProvider(i) (app/app.go) returns GetCheckCtx() for LatestCtxHeightabciQuery == false → V66 paginator — but calls CreateQueryContext(i, false) for any explicit height → abciQuery == true → the unrestricted paginator. evmrpc/simulate.go uses that provider for eth_call/eth_estimateGas at a block number (sdkCtx = b.ctxProvider(tmBlock.Block.Height)) and for debug_trace* replay. x/evm's Querier.StaticCall is on the same path.

Concrete divergence: staking ValidatorDelegations filters by validator over the whole delegator-keyed DelegationKey store, so on mainnet it can easily scan >10,000 entries. In a real tx the precompile call errors out (V66 MaxScanLimit); replayed under debug_traceTransaction or queried via eth_call at that same height it now returns results. Same call also behaves differently at latest vs. an explicit height. That breaks trace fidelity and makes eth_estimateGas optimistic relative to execution.

Suggested fix: keep the flag narrow to non-EVM queries — either select on ctx.IsABCIQuery() && !ctx.IsEVM() at the six keeper call sites, or append .WithIsABCIQuery(false) in RPCContextProvider after the WithIsEVM(true).WithTraceMode(true) chain.

return nil, err
}

// Note: unlike upstream cosmos-sdk, limit == 0 must NOT implicitly enable

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[blocker] The VerifyPaginationOffset / VerifyPaginationLimit calls were removed unconditionally rather than gated on enforceV66ScanLimit, so FilteredPaginateV66 is not actually v6.6-equivalent.

Under released v6.6 a request with Limit > 1_000 or Offset > 10_000 returned InvalidArgument here, before any iteration. The V66 variants now accept it. Related: the phase-1 guard below became paginationEnd(offset, MaxScanLimit), which saturates at MaxUint64 for a large offset and therefore never fires — in v6.6 such an offset was rejected outright, so this is a second silent difference on the same path.

Not reachable today (every precompile passes a key-only PageRequest with Limit/Offset unset), but the function's own doc comment promises exact v6.6 preservation for consensus execution, and this is a cheap invariant to hold by construction: move both Verify* calls under if enforceV66ScanLimit, and use offset + MaxScanLimit (unsaturated) in the V66 branch.

const MaxLimit = uint64(1_000)
// MaxLimit is the maximum limit the paginate function can handle
// which equals the maximum value that can be stored in uint64
const MaxLimit = uint64(math.MaxUint64)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[suggestion] MaxLimit = math.MaxUint64 makes the constant a no-op, which quietly weakens the tests that still reference it: TestGetPaginatedTotalSupplyMaxLimit, keeper/genesis_test.go:123, and filtered_pagination_test.go:46 now assert "unbounded works" rather than a boundary. Since VerifyPaginationLimit is gone and nothing enforces it any more, consider deleting MaxLimit outright (forcing call sites to state their own intent, as supplyPageSize now does) instead of leaving a public constant that reads like a cap but isn't one.

}
// Cap the allocation by the number of txs actually in the block so a huge
// user-supplied limit cannot trigger a huge allocation.
txs := make([]*txtypes.Tx, 0, min(limit, blockTxsLn))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nit] The cap is the right fix. Minor: it doesn't account for offset, so a request with offset = blockTxsLn - 1, limit = 10_000 still preallocates blockTxsLn entries. min(limit, blockTxsLn-offset) is exact and offset < blockTxsLn is already guaranteed by the check above.

@masih masih added the ai: skip-review Skips Seidroid AI review triggered via Unified CI. label Aug 6, 2026
@masih
masih dismissed seidroid[bot]’s stale review August 6, 2026 17:42

accepting the risk of missing tests

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not approving — this PR restructures ABCI query context routing and EVM precompile pagination semantics for AppHash safety, which warrants human review regardless of outstanding findings.

Since my last review, commit ae04df3 ("Fix simulation path by explicit flag") addresses the blocker I and another reviewer raised: CreateQueryContext no longer sets abciQuery=true unconditionally, so EVM simulation/tracing paths (RPCContextProvider, eth_call, debug_trace*) now correctly retain the v6.6-capped paginators instead of diverging from consensus behavior. The flag is now set only at the two genuine ABCI-query entry points (handleQueryGRPC, the gRPC server interceptor). I verified this against the current tree and confirmed sei-cosmos/app build cleanly.

One smaller, previously-flagged item remains open: FilteredPaginateV66/GenericFilteredPaginateV66 still don't gate VerifyPaginationOffset/VerifyPaginationLimit behind enforceV66ScanLimit, so they aren't byte-for-byte v6.6-equivalent on offset/limit bounds — flagged as not currently reachable (precompiles only pass key-only PageRequests) but worth tightening for the doc comment's own invariant.

Extended reasoning...

This PR reworks ABCI query-context tainting and adds dual pagination implementations (unrestricted for LCD/gRPC, v6.6-capped for EVM-precompile-reachable queries) across six modules, specifically to avoid AppHash/LastResultsHash divergence. That is exactly the class of change (consensus-critical, cross-cutting context flag) that should get a human look independent of whether every finding is resolved.

The main correctness concern from this thread — CreateQueryContext unconditionally tainting EVM simulation/trace-replay contexts with abciQuery=true, causing eth_call/debug_trace* to diverge from consensus pagination behavior — is fixed by the latest commit, which narrows WithIsABCIQuery(true) to only the two ABCI-query entry points. I confirmed this by reading the current abci.go/grpcserver.go and building the affected packages.

A secondary, previously flagged nit (VerifyPagination* not gated by enforceV66ScanLimit in the V66 paginators) remains, but both the original reporter and I agree it is not reachable today since precompiles only pass key-only PageRequests. No new issues surfaced in this pass.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR touches AppHash/LastResultsHash-sensitive pagination across staking, gov, distribution, feegrant, and authz query handlers plus the EVM precompile call paths, so I'm leaving it for a human rather than shadow-approving.

The abciQuery-leaking-into-EVM-RPC/simulation concern raised earlier (cursor[bot], seidroid[bot], and my own prior review) is resolved by the follow-up commits ae04df3 and 79850d0CreateQueryContext no longer sets abciQuery itself (only the genuine ABCI-query/gRPC call sites do), and StaticCallEVM now forces it off so precompile static calls always mirror consensus behavior regardless of entry path.

No new bugs found this run. Beyond the reported findings, I also checked:

  • The plain Paginate (no V66 variant) call sites (ValidatorUnbondingDelegations, DelegatorDelegations, Votes, Deposits, Allowances, Redelegations) — none are reachable from precompiles, so the missing V66 cap there isn't a live gap.
  • StaticCallEVM's unconditional WithIsABCIQuery(false) — intentional per the "match consensus semantics" commit, not a regression for genuine LCD/gRPC StaticCall queries.

seidroid[bot]'s three remaining nit-level comments (gating Verify* under enforceV66ScanLimit, the now-unenforced MaxLimit constant, and the GetBlockWithTxs allocation off-by-one) are still open and unaddressed on the thread.

Extended reasoning...

No bugs were found by the bug hunting system this run. The two candidates it raised (missing PaginateV66 variant; StaticCallEVM forcing v6.6 semantics for genuine LCD/gRPC StaticCall) were both verified as non-issues: the former because no precompile reaches the plain-Paginate call sites, the latter because forcing v6.6 semantics in StaticCallEVM is the deliberate intent of commit 79850d0 (static precompile calls must reproduce consensus behavior regardless of entry path).

This PR is a fix for a real, previously-shipped regression (#3494 broke LCD/gRPC pagination) and re-introduces v6.6-compatible scan-limit paginator variants specifically to keep AppHash/LastResultsHash stable for precompile-reachable query handlers. That is exactly the kind of consensus-critical, cross-cutting change (new sdk.Context field, dual paginator variants selected via ctx.IsABCIQuery() in five keeper packages, EVM keeper changes) that warrants human sign-off even when no new bugs are found.

Multiple independent reviewers (cursor[bot], seidroid[bot], and my own earlier review) flagged the same blocker across this PR's history: contexts built via CreateQueryContext for historical EVM RPC/trace replay (app.RPCContextProvider) were inheriting abciQuery=true, which would have caused precompile query handlers to diverge from consensus behavior during debug_trace*/eth_call replay. I verified this is now fixed: CreateQueryContext itself no longer sets abciQuery (commit ae04df3), and StaticCallEVM independently forces it false (commit 79850d0), so RPCContextProvider-derived contexts default to v6.6-compatible behavior.

Three lower-severity seidroid[bot] comments remain open and unaddressed: (1) VerifyPaginationOffset/VerifyPaginationLimit were deleted rather than gated under enforceV66ScanLimit — the reporter themselves noted this is unreachable today since precompiles pass key-only PageRequests, but it's a latent divergence from documented v6.6-exact-preservation intent; (2) MaxLimit is now math.MaxUint64, making it a no-op constant that misleadingly still reads as a cap; (3) GetBlockWithTxs's allocation cap doesn't subtract offset, allowing a slightly larger-than-necessary allocation. None of these are functional bugs, but they are unresolved reviewer feedback on a sensitive PR, which per review guidance should not be waved through with an approval.

@masih
masih enabled auto-merge August 6, 2026 19:39

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR touches consensus-critical pagination behavior across many query handlers (staking, gov, feegrant, distribution, authz) with dual code paths meant to keep precompile execution byte-for-byte compatible with released v6.6 semantics while relaxing LCD/gRPC pagination — that split is exactly the kind of change where a subtle mistake causes AppHash or LastResultsHash divergence, so I think this warrants a human look regardless of whether an active bug is found.

Note: the IsABCIQuery leak into RPCContextProvider/historical EVM RPC/debug_trace* replay that I (and other reviewers) flagged on an earlier revision looks fixed by the follow-up commits ae04df3 and 79850d0CreateQueryContext no longer sets the flag itself, and StaticCallEVM now explicitly forces WithIsABCIQuery(false). Still open from a prior reviewer comment: in filtered_pagination.go, the VerifyPaginationOffset/VerifyPaginationLimit calls were dropped unconditionally rather than gated on enforceV66ScanLimit, so FilteredPaginateV66 isn't byte-for-byte v6.6-equivalent for offset/limit validation (currently unreachable since precompiles only pass key-based PageRequests, but worth a human decision on whether to close that gap by construction).

Extended reasoning...

Overview

The PR reworks Cosmos SDK pagination (sei-cosmos/types/query) to restore permissive LCD/gRPC behavior while adding v6.6-locked *V66" paginator variants for EVM-precompile-reachable query handlers in staking, gov, feegrant, distribution, and authz, routed via a new sdk.Context.IsABCIQuery()flag set only on ABCI/gRPC query contexts. It also capsGetBlockWithTxsallocation, re-pagesCollectAllTotalSupply` at 1,000, and saturates offset+limit math to avoid uint64 overflow.

Security risks

No injection/auth-bypass surface. The real risk class here is consensus safety: if the IsABCIQuery/V66 routing is wrong on any reachable path, precompile gas consumption or revert behavior can diverge from what was actually committed, corrupting AppHash or LastResultsHash. A significant instance of exactly this (RPCContextProvider/historical EVM RPC inheriting the unrestricted paginator) was flagged on an earlier revision and appears fixed by the follow-up commits ae04df3/79850d0, which moved WithIsABCIQuery(true) out of CreateQueryContext and into the two ABCI/gRPC query call sites, and made StaticCallEVM force the flag off.

Level of scrutiny

High. This is a targeted fix to a previous consensus-hardening regression, spanning 17 files including baseapp query plumbing, five module query handlers, and the EVM keeper's precompile entrypoints. The correctness argument depends on every precompile-reachable call site consistently choosing the V66 variant, which is not enforceable by the type system — a missed call site is a silent AppHash risk. That argues for human sign-off even with no active bug found this run.

Other factors

A previous reviewer (seidroid) flagged a second, lower-severity gap that remains open: FilteredPaginateV66/GenericFilteredPaginateV66 no longer perform the pre-v6.6 VerifyPaginationOffset/VerifyPaginationLimit checks at all (they were removed unconditionally rather than gated on enforceV66ScanLimit), so the V66 paginators aren't a complete behavioral match for released v6.6 on that axis. It's currently unreachable since every precompile call site passes a key-only PageRequest, but it undermines the exact-preservation claim in the function's own doc comment. Test coverage for the new saturation/next-key/count-total semantics looks reasonably thorough (large-store and sparse-filter cases for both Paginate and FilteredPaginate).

@masih
masih added this pull request to the merge queue Aug 7, 2026
Merged via the queue into main with commit 8e06e53 Aug 7, 2026
71 of 72 checks passed
@masih
masih deleted the masih/relax-deleg-query-pagination branch August 7, 2026 11:44
@seidroid

seidroid Bot commented Aug 7, 2026

Copy link
Copy Markdown

Created backport PR for release/v6.6:

Please cherry-pick the changes locally and resolve any conflicts.

git fetch origin backport-3867-to-release/v6.6
git worktree add --checkout .worktree/backport-3867-to-release/v6.6 backport-3867-to-release/v6.6
cd .worktree/backport-3867-to-release/v6.6
git reset --hard HEAD^
git cherry-pick -x 8e06e535e91a293b2d4624862deda76096925af0
git push --force-with-lease

masih added a commit that referenced this pull request Aug 7, 2026
… precompile semantics (#3873)

Backport of #3867 to `release/v6.6`.

---------

Co-authored-by: Masih H. Derkani <m@derkani.org>
yzang2019 added a commit that referenced this pull request Aug 7, 2026
* main:
  test(config): complete the GetConfig read-site coverage (PLT-893) (#3870)
  Remove interchain swagger API and protos (#3881)
  fix(flatkv): preserve empty misc values and reject malformed empty node imports (#3869)
  fix(evm): count post-admission apply failures in dynamic base-fee gas (CON-359) (#3871)
  scripts: load generator for arctic-1 and atlantic-2 (#3850)
  Update go-releaser heading with experimental notice (#3879)
  fix(evmrpc): stream request-body budget charging to close slowloris gap (PLT-780) (#3836)
  Remove unused interchain accounts implementation (#3875)
  test(config): extend golden value test coverage (PLT-893) (#3861)
  Update v6.6 changelog in prep to cut patch release (#3876)
  Close temporary rootmulti store in connection types setup (#3872)
  Restore LCD pagination while preserving v6.6 precompile semantics (#3867)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai: skip-review Skips Seidroid AI review triggered via Unified CI. backport release/v6.6 Backport to release v6.6 non-app-hash-breaking

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants