fix(gnogenesis): decode auth/accounts as gnoland.GnoAccount#5647
Merged
Conversation
The auth/accounts handler marshals whatever acck.GetAccount returns, which on gno.land is a *gnoland.GnoAccount (std.BaseAccount embedded + Attributes BitSet). The previous parser only knew bare std.BaseAccount; amino's strict-field policy rejected the "attributes" field and queryAccountAtHeight silently returned nil, defaulting accNum to 0 for every signer. Generated hardfork genesis files therefore claimed accNum=0 for every SignerInfo, which validateSignerInfo rejects when any balance-init address occupies index 0. Decode directly into gnoland.GnoAccount, matching gnoclient.QueryAccount. Follow-up to gnolang#5511 ("queryAccountAtHeight silent nil" follow-up item). assisted-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Collaborator
🛠 PR Checks SummaryAll Automated Checks passed. ✅ Manual Checks (for Reviewers):
Read More🤖 This bot helps streamline PR reviews by verifying automated checks and providing guidance for contributors and reviewers. ✅ Automated Checks (for Contributors):🟢 Maintainers must be able to edit this pull request (more info) ☑️ Contributor Actions:
☑️ Reviewer Actions:
📚 Resources:Debug
|
moul
approved these changes
May 18, 2026
Member
moul
left a comment
There was a problem hiding this comment.
looks good, but ideally should be unit tested.
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
davd-gzl
reviewed
May 19, 2026
aeddi
approved these changes
May 22, 2026
…de-gnoaccount # Conflicts: # contribs/gnogenesis/internal/fork/source_rpc.go
The original fix in source_rpc.go (gnolang#5647) was orphaned by gnolang#5696, which split that file into source_txs_rpc.go + source_genesis_rpc.go + the data-dir / jsonl-file txs sources. The buggy decode logic was carried into source_txs_rpc.go unchanged, so the fix has to land there. Re-applies the same change in queryAccountAtHeight on rpcTxsSource: decode the auth/accounts response as gnoland.GnoAccount instead of the two-path wrapper + bare BaseAccount fallback that amino's strict-field policy rejects. Also addresses davd-gzl's review comment by adding WARNING prints when the ABCI response carries an Error or empty Data, mirroring the existing warning on the transport-error path. Adds TestQueryAccountAtHeight_WireFormatContract pinning the contract: amino-encoded GnoAccount wire bytes round-trip through gnoland.GnoAccount and break the two previous decoders (regression guard for moul's "ideally should be unit tested" comment). Generated by Claude
jaekwon
pushed a commit
that referenced
this pull request
May 26, 2026
## Summary Follow-up to #5511. Addresses one specific case of the "Still open" item: > `queryAccountAtHeight` silent nil — all error paths return nil with no indication; flaky RPC → wrong sequence metadata. This isn't flaky RPC. The parse fails deterministically on any real gno.land chain. ## The bug `auth/accounts/<addr>` is handled by `authHandler.queryAccount`, which marshals whatever `acck.GetAccount` returns. On gno.land that's a `*gnoland.GnoAccount`: ```go type GnoAccount struct { std.BaseAccount Attributes BitSet `json:"attributes" yaml:"attributes"` } ``` The wire format is therefore `{"BaseAccount":{...},"attributes":...}`. The previous parser tried two paths: 1. A wrapper struct containing only `BaseAccount` — amino's strict-field policy rejects the unknown `"attributes"`. 2. A bare `std.BaseAccount` — same problem, plus `"BaseAccount"` is also unknown to it. Both error. The function silently returns nil, and the caller in `FetchTxs` defaults `accNum` to `0` for the rest of the run. Every `SignerInfo` entry in the generated hardfork genesis claims the same accNum. ## Impact Affects every hardfork generated with `gnogenesis fork generate` against a real gno.land chain: - If the chain has more than one balance entry and `balances[0].Address` is not a tx signer, target boot fails loudly via the `validateSignerInfo` preflight added in #5511: *"account number 0 already assigned to X, cannot reassign to Y"*. - If `balances[0].Address` happens to be the only signer (single-account testbeds), the wrong `accNum=0` still propagates into target account state via the `SignerInfo` force-set in `loadAppState`, just not loudly. That's silent corruption. The hf-glue end-to-end run in #5511 didn't surface this because that testbed only validates against `rpc.gno.land` data; the dominant signer there happened to land at balance index 0. ## Fix Decode the response as `gnoland.GnoAccount` directly. Same pattern as `gnoclient.QueryAccount` (`gno.land/pkg/gnoclient/client_queries.go:59-65`). The package is already imported in this file, so there is no architectural cost. The `std.BaseAccount` fallback is dead — `contribs/gnogenesis` is a gno.land tool — and is removed. 7 lines replacing 11. ## Discovery Caught by an end-to-end transition harness driving source → halt → `fork generate` → target boot → replay-report assertion, against a multi-balance source chain. The handler-side query reproduction is mechanical: any `gnogenesis fork generate -source http://...` against a gno.land chain with signed historical txs hits this. ## AI disclosure Diagnosed and patched with Claude Code. --------- Co-authored-by: moul <94029+moul@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Follow-up to #5511. Addresses one specific case of the "Still open" item:
This isn't flaky RPC. The parse fails deterministically on any real gno.land chain.
The bug
auth/accounts/<addr>is handled byauthHandler.queryAccount, which marshals whateveracck.GetAccountreturns. On gno.land that's a*gnoland.GnoAccount:The wire format is therefore
{"BaseAccount":{...},"attributes":...}. The previous parser tried two paths:BaseAccount— amino's strict-field policy rejects the unknown"attributes".std.BaseAccount— same problem, plus"BaseAccount"is also unknown to it.Both error. The function silently returns nil, and the caller in
FetchTxsdefaultsaccNumto0for the rest of the run. EverySignerInfoentry in the generated hardfork genesis claims the same accNum.Impact
Affects every hardfork generated with
gnogenesis fork generateagainst a real gno.land chain:balances[0].Addressis not a tx signer, target boot fails loudly via thevalidateSignerInfopreflight added in feat(gnoland): chain hardfork mechanism v3 #5511: "account number 0 already assigned to X, cannot reassign to Y".balances[0].Addresshappens to be the only signer (single-account testbeds), the wrongaccNum=0still propagates into target account state via theSignerInfoforce-set inloadAppState, just not loudly. That's silent corruption.The hf-glue end-to-end run in #5511 didn't surface this because that testbed only validates against
rpc.gno.landdata; the dominant signer there happened to land at balance index 0.Fix
Decode the response as
gnoland.GnoAccountdirectly. Same pattern asgnoclient.QueryAccount(gno.land/pkg/gnoclient/client_queries.go:59-65). The package is already imported in this file, so there is no architectural cost. Thestd.BaseAccountfallback is dead —contribs/gnogenesisis a gno.land tool — and is removed.7 lines replacing 11.
Discovery
Caught by an end-to-end transition harness driving source → halt →
fork generate→ target boot → replay-report assertion, against a multi-balance source chain. The handler-side query reproduction is mechanical: anygnogenesis fork generate -source http://...against a gno.land chain with signed historical txs hits this.AI disclosure
Diagnosed and patched with Claude Code.