Skip to content

ENGN-9226 Diego/query tx separation decoding path - #986

Open
xmariachi wants to merge 9 commits into
devfrom
diego/query-tx-separation-decoding-path
Open

ENGN-9226 Diego/query tx separation decoding path#986
xmariachi wants to merge 9 commits into
devfrom
diego/query-tx-separation-decoding-path

Conversation

@xmariachi

@xmariachi xmariachi commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Problem

After the v10 proto bump, transaction query endpoints (e.g. GET /cosmos/tx/v1beta1/txs) can no longer decode pre-upgrade payloads. The strict unknown-field check resolves nested (non-Any) message types by name through the global gogo proto registry, and several historical names are now registered as their v10 equivalents. As a result the whole transaction fails to decode and the query returns a parse error instead of the stored tx.

Constraints (must not affect consensus)

The transaction decoder is shared between the query path and the consensus path (CheckTx, ProcessProposal, FinalizeBlock). Any change that makes a node accept a transaction it previously rejected is therefore consensus-relevant: it changes the set of transactions a node will admit and partially process, which can diverge application state between nodes running different binaries during a rolling upgrade.

Hard requirements for the fix:

  • Do not modify the consensus decoder or its behavior.
  • Do not mutate the app's interface registry or the process-global proto registry.
  • The set of transactions accepted by consensus must be byte-for-byte identical before and after this change.

Solution

Introduce a read-only decoding path used only by the historical-read methods of the tx service (GetTx, GetTxsEvent, GetBlockWithTxs):

  • A separate interface registry, mirroring the app's registry plus the historical mint.v2 types, built without touching the app's own registry.
  • A tolerant decoder that tries the standard strict decoder first (so current transactions keep the identical code path and type) and only falls back to a plain unmarshal — which skips the strict unknown-field walk — for payloads the strict decoder rejects.
  • A composite tx service that routes only the three read methods to the tolerant backend; every other method (including TxDecode) keeps the app's standard strict behavior.

The consensus decoder and the app's registry are left completely unchanged.

Details

  • app/querytx.go (new): the separate registry builder, the tolerant decoder plus a minimal sdk.Tx shim for decoded historical txs, the composite tx service, and an override of RegisterTxService that wires it in.
  • app/app.go: small, behavior-preserving wiring — hoist the custom module basics into a reusable helper, and build/store the read-only registry and tx config at startup.
  • Tests:
    • Historical emissions worker payloads (v2–v9) decode on the query path, both standalone and when nested inside another message.
    • Guard tests asserting the consensus decoder still rejects the same payloads, and that the added mint.v2 types resolve only on the query registry — never on the consensus registry or the global proto registry.
    • An end-to-end test that delivers such a payload through a real block and asserts consensus state (balances, account sequence, gas) is unchanged.
  • Known limitation (separate follow-up): query-only historical types (mint.v2 and the v7/v8 whitelist txs) decode over gRPC but don't render over the REST gateway, which resolves messages via the app registry; a page containing one fails to render. All other historical versions are unaffected.

Summary by cubic

Restores decoding of pre‑v10 transactions on tx query endpoints without changing what consensus accepts. Previously these queries failed to parse stored pre‑v10 payloads; now GetTx, GetTxsEvent, and GetBlockWithTxs decode via a read‑only path while consensus decoding stays strict.

  • Builds a read‑only InterfaceRegistry for queries that mirrors the app registry and adds historical mint.v2 plus missing v7/v8 whitelist messages.
  • Uses a tolerant query‑only decoder that tries strict first, then falls back; rejects message Any with empty or unresolvable type URLs.
  • Routes only the three gRPC read methods to the tolerant backend; all other tx service methods (including TxDecode) remain strict.
  • Keeps consensus behavior unchanged and does not mutate the app registry or the global registry; no migrations or rollout steps.
  • Adds tests for v2–v9 decoding (bare and authz‑wrapped), consensus rejection, registry superset checks, REST gateway behavior (only consensus‑registered types render), and a FinalizeBlock harness that proves no state changes for wrapped v9.
  • Known limitation: REST JSON cannot render mint.v2; gRPC queries are unaffected.

Dependencies

  • Pins golangci-lint workflows to Go 1.23.5 and updates the go-install-hardened workflow reference.

Written for commit c0a6015. Summary will update on new commits.

Review in cubic

@xmariachi
xmariachi force-pushed the diego/query-tx-separation-decoding-path branch from f03e8c6 to dac5510 Compare August 24, 2026 16:20

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review completed against the latest diff

Reply with feedback, questions, or to request a fix.

Fix all with cubic | Re-trigger cubic

Comment thread test/api/historical_tx_decode_test.go
Comment thread test/api/harness_test.go Outdated
Comment thread app/querytx_test.go
Comment thread test/api/historical_tx_decode_test.go Outdated

@helder-moreira helder-moreira left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Checked the consensus claim rather than taking it on reading, since #985 looked fine on inspection too. Diffed both registries at runtime (query = app + exactly the two mint.v2 msgs, nothing missing), and ran a mint.v2 wrapped in MsgExec through FinalizeBlock on dev vs this branch with the Any hand-encoded so dev never links mintv2 — identical code, gas and state, so the new import doesn't move consensus. Also confirmed a v9 tx renders through the real gateway marshaler, which nothing here covers (the tests all call the decoder directly; the bug report is a REST endpoint).

Consensus safety holds. Comments are mostly about the tolerant decoder accepting more than historical payloads.

On rollout: agree on snapshot testing and the single-RPC canary. Worth hitting GetTx and GetBlockWithTxs too, not just txs?query= — different paths, and the shim's interfaces are satisfied structurally, so a mismatch only shows at runtime.

Comment thread app/querytx.go
Comment thread app/querytx.go Outdated
Comment thread app/querytx.go Outdated
Comment thread app/querytx.go
Comment thread app/querytx.go
Comment thread test/api/harness_test.go Outdated
Comment thread app/querytx_test.go Outdated
Comment thread app/querytx_test.go
Comment thread app/querytx_test.go Outdated
Comment thread app/querytx_test.go

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

All reported issues were addressed across 2 files (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Fix all with cubic | Re-trigger cubic

Comment thread app/querytx.go

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

All reported issues were addressed across 1 file (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Fix all with cubic | Re-trigger cubic

Comment thread .github/workflows/go-hardened.yml
@xmariachi xmariachi changed the title Diego/query tx separation decoding path ENGN-9226 Diego/query tx separation decoding path Aug 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants