Skip to content

docs(test13): hardfork RC series (review-only against chain/gnoland1)#5589

Closed
aeddi wants to merge 92 commits into
gnolang:chain/gnoland1from
aeddi:chain/test13-rc6
Closed

docs(test13): hardfork RC series (review-only against chain/gnoland1)#5589
aeddi wants to merge 92 commits into
gnolang:chain/gnoland1from
aeddi:chain/test13-rc6

Conversation

@aeddi
Copy link
Copy Markdown
Contributor

@aeddi aeddi commented Apr 24, 2026

Not meant to be merged. This PR is opened against chain/gnoland1 purely so reviewers can use GitHub's compare view — it gives a single click-through diff for every rc branch in the series and an easy "Files changed" tab. The actual work lands by cherry-picking from the rc branches into their respective upstream PRs (or via a coordinated hardfork from chain/gnoland1 directly). Don't press the green button.

A stacked branch series that carries the gnoland1 → test-13 hardfork work. Each rc is a strict superset of the previous one — branch it off, add a focused delta, keep the older rcs intact so bisecting and reviewing never lose history. Open this PR against the highest rc (chain/test13-rc6) unless you're reviewing one specific layer in isolation.

A design/rationale writeup for the launch itself lives in the ADR: gno.land/adr/pr5589_test13_hardfork_launch.md.

Branch graph

Each row links to the branch tree and shows the cumulative diff against the immediate predecessor (what this rc adds).

chain/gnoland1

chain/test13-base (diff vs chain/gnoland1) — hf-glue testbed imported + compat fixes

chain/test13-rc1 (diff vs chain/test13-base) — first hardfork attempt; single-validator test-13 boots locally

chain/test13-rc2 (diff vs chain/test13-rc1) — cherry-picks PR #5485 (valset-via-params v3) + deploy wrap

chain/test13-rc3 (diff vs chain/test13-rc2) — wires EndBlocker at v3 via migration, fixes v3's eager-eval bug

chain/test13-rc4 (diff vs chain/test13-rc3) — tier-1 audits (migrations, tx export, state, balances)

chain/test13-rc5 (diff vs chain/test13-rc4) — tier-2 resilience (reproducibility, nil-mpkg, validator ops, halt)

chain/test13-rc6 (diff vs chain/test13-rc5) — tier-3 audits (realm imports, gas modes, repro-caveat)

chain/test13-base

Delta vs chain/gnoland1: branches from chain/gnoland1 (the gnoland1 betanet genesis + its supporting core fixes, already merged upstream). Imports PR #5486 — the hf-glue testbed that builds a hardfork genesis.json from a source chain + its historical txs — plus the minimum number of small fixes needed to make #5486 run cleanly against the gnoland1 base. Nothing test13-specific yet: no r/sys/validators/v3, no test-13 chain id, no post-fork migrations beyond what #5486 ships.

chain/test13-rc1

Delta vs chain/test13-base: first end-to-end hardfork attempt. Imports the list of gno-core fixes curated on the launch-prep hackmd — these are the gnovm / tm2 / gnoland bugs we deemed worth carrying into the test-13 genesis (each referenced by its upstream PR number). Adds keyless genesis (so a genesis can be built without holding a priv_validator_key.json), the T1-rotation migration, and a chunked tx-archive fetch for flaky RPCs. Boots a single-validator test-13 locally; add-validator via v2 lands on-chain but the valset never moves — the problem rc2 + rc3 chase down.

Delta vs chain/gnoland1 (cumulative)

hf-glue specific

  • b1c288186 feat(hf-glue): add chunked tx-archive fetch script for flaky RPCs
  • d07ff4a78 feat(hf-glue): support keyless genesis via VALIDATOR_ADDR/VALIDATOR_PUBKEY
  • 3e38a0e42 fix(hf-glue): auto-restage genesis on make up via init dependency
  • d42bdddc8 fix(hf-glue): make init-node.sh idempotent
  • e6508790e feat(deployments/gnoland-1): govDAO T1 rotation migration + repair valset-reset

gno-core fixes imported from the launch hackmd

chain/test13-rc2

Delta vs chain/test13-rc1: cherry-picks PR #5485 (valset-via-params v3), scaffolds a test13-specific deployment dir with the new v3-targeted add-validator.sh, and plumbs the v3 realm into the post-fork migration. v3 doesn't deploy cleanly because r/sys/names' namespace check rejects an addpkg into r/sys/*; this rc wraps the addpkg with a disable/restore pair. Also adds multi-validator genesis support in fixvalidator and migrate.sh so gno-cluster-style multi-node launches can be built.

Delta vs chain/gnoland1 (cumulative)

  • 095cee651 fix(deployments/gnoland-1): wrap v3 addpkg with sysnames-check disable/restore
  • ec3e2837f feat(hf-glue): support multi-validator genesis via --valset-list
  • e6953c9b6 feat(deployments/gnoland-1): addpkg r/sys/validators/v3 in post-fork migration
  • 8135d8ddc feat(deployments/test13.gno.land): add add-validator.sh targeting r/sys/validators/v3
  • 256454169 feat: valset updates via VM params keeper (v3) — cherry-pick of upstream PR feat: valset updates via VM params keeper (v3) #5485

chain/test13-rc3

Delta vs chain/test13-rc2: fixes the two reasons add-validator.sh still didn't move the valset on rc2. First, NewValsetChangeExecutor evaluates its changesFn eagerly so the executor no longer closes over the caller's /e/<caller>/run realm (otherwise dao.MustCreateProposal panics on "cannot persist function from the private realm"). Second, bakes a new migration step 08 that sets vm:p:valset_realm_path = gno.land/r/sys/validators/v3 so EndBlocker reads from v3's paths. After rc3 the full loop works end-to-end.

Delta vs chain/gnoland1 (cumulative)

  • f6a7cdd79 feat(deployments/gnoland-1): set vm:p:valset_realm_path to v3 in migration
  • 281d51ed9 fix(r/sys/validators/v3): evaluate changesFn eagerly in NewValsetChangeExecutor

chain/test13-rc4

Delta vs chain/test13-rc3: four tier-1 audit/assertion tools. None of rc3's replay failures panic the chain (absorbed by --skip-failing-genesis-txs), but silent divergence could ruin a launch. These close that hole:

Delta vs chain/gnoland1 (cumulative)

  • 91ca6caea feat(hf-glue): add audit-balances diffing per-signer ugnot source vs replay — surfaces the 13 accounts drained by post-mainnet storage-deposit semantics
  • 3e107b024 feat(hf-glue): add state-diff tool comparing replay vs source-chain realms — renders each realm on both sides + diffs
  • 6765715af feat(hf-glue): add verify-txs-jsonl integrity check vs source-chain RPC — asserts total_txs + spot-checks random heights
  • 5c2911949 feat(hf-glue): add assert-migrations script verifying post-replay state — one positive check per migration step's intended effect

chain/test13-rc5

Delta vs chain/test13-rc4: tier-2 resilience work. Proves the genesis rebuilds deterministically, teaches gno to survive mid-write process kills without crash-looping, adds every remaining validator-ops primitive beyond add, and patches one missing corner in the rc4 assertion script.

Delta vs chain/gnoland1 (cumulative)

  • 4246860cc fix(hf-glue): accept empty string as "no pending update" in assert-migrations — v3 init doesn't seed the key, so fresh-boot state legitimately presents as empty
  • ef5db46d5 feat(deployments/test13.gno.land): add rm/change-power/batch govDAO scripts — full valset-ops surface against v3; change-power uses an atomic remove+add to work around v3's lack of in-place update
  • b15ffde6e fix(gnovm): survive partial mempackage writes on restart — root-caused the mempackage.go:889 nil-pointer crash on cluster nodes killed mid-replay; defensive nil-skip so a half-persisted store boots instead of crash-looping
  • c4e0ffb32 feat(hf-glue): add verify-reproducibility building genesis twice, asserting SHA match — local proxy for the cross-machine attestation validators will run at launch

rc5 also proved, live against a 2-node cluster: add-validator → change-power → rm-validator → batch-add → mixed remove+update → halt-height set via govDAO → BaseApp panics at block_height + 1 exactly as designed.

chain/test13-rc6

Delta vs chain/test13-rc5: tier-3 audits and a documentation fix for a debugging pitfall.

Delta vs chain/gnoland1 (cumulative)

  • 821e23753 docs(hf-glue): clarify verify-reproducibility assumes a clean OUT dir — after chasing phantom nondeterminism, the root cause was always stale out/ between runs; comment-only update so the next debugger doesn't waste the same cycle
  • 754f75d66 feat(hf-glue): add compare-gas-modes A/B-testing strict vs source replay — gnogenesis fork test runs twice, once per mode, diffs failure counts. Surfaces that on rc6 genesis source mode eliminates zero failures (the 2580 InsufficientFunds all fire at ante.DeductFees before the gas meter engages), so the posture is "stay on strict / empty"
  • 45893f200 feat(hf-glue): add audit-realm-imports flagging dangling imports post-fork — scans every addpkg tx (historical + genesis-mode) for imports that no longer resolve against the current stdlib + examples tree. Filters test-only files. Result: 368 unique (realm, import) edges, 0 dangling

How to review

  • If you only care about the replay engine itself, stop at rc1.
  • For the v3 valset flow and why add-validator.sh finally works, review rc2 + rc3 together.
  • For the tooling / audit surface that would gate a production launch, rc4 + rc5 + rc6 are the interesting deltas.

aeddi and others added 30 commits April 21, 2026 12:41
PR gnolang#5486 commit bd3580d ("refactor: absorb misc/hardfork into 'gnogenesis
fork' subcommand") moved misc/hardfork/ into contribs/gnogenesis/internal/fork/
and rewired the CLI:

  misc/hardfork test  →  gnogenesis fork test

That refactor updated misc/deployments/gnoland-1/generate-genesis.sh but
missed the references in misc/hf-glue/. As a result, scripts/replay-log.sh
(invoked by `make replay-log` and `make reports`) still does:

  cd "$REPO/misc/hardfork"
  go run . test ...

which fails with "No such file or directory" because misc/hardfork no
longer exists on the PR head.

Update the cd target to contribs/gnogenesis and the subcommand to
`fork test` to match the new layout.

Other stale refs in misc/hf-glue/ (Makefile smoketest target,
fetch-from-dir.sh, README, lib/hf.sh comment) are also broken but
addressed in separate commits / left as docs.
scripts/report-replay.sh runs under `set -euo pipefail`. The assignment:

  missing_imports=$(grep -oE 'could not import gno\.land/[^ \"\\]+' "$tmp" \
    | sort | uniq -c | sort -rn | head -20)

fails the whole script when the [FAIL] log contains no "could not import"
lines: grep exits 1, pipefail propagates the non-zero status, and the
outer command substitution triggers set -e.

This is the happy path when a hardfork replay has no cascade failures —
e.g. the current run on chain/test13-base has exactly one root-cause
failure (r/sys/txfees storage deposit) and zero cascades, so the pipeline
returned empty and aborted the script before the report was written.

Append `|| true` to the pipeline so an empty result is treated as
"missing_imports is empty", consistent with how the adjacent
root_cause_fails assignment already handles it.
scripts/report-replay.sh runs under `set -u`. The line:

  echo "_Generated $(date ...) from $LOG_"

is parsed as expanding a variable named `LOG_` (trailing underscore is a
valid identifier character), not `LOG` followed by a literal underscore.
Since $LOG_ is unbound, set -u aborts the script with:

  line 58: LOG_: unbound variable

The trailing underscore was intended as the closing italic marker for
the markdown header `_Generated ... from /path/to/replay.log_`.

Wrap the variable in braces — `${LOG}_` — so bash parses it as $LOG
followed by a literal underscore, preserving the markdown emphasis.
… failures

Root cause
----------
master's storage-deposit code (4e1745a, gnolang#5415) locks a deposit from
msg.Creator for every addpkg, proportional to realm size. This feature
did not exist on gnoland1. The 85 genesis-mode addpkg txs in the
gnoland1 snapshot were all signed with max_deposit="" (the field did not
exist yet). Under the new SDK the empty field defaults to the parameter
ceiling (600 Mugnot), so the actual transfer is `diff × 100 ugnot` per
realm.

The creator g1r929wt2qplfawe4lvqv9zuwfdcz4vxdun7qh8l deploys 7
consecutive r/sys/* realms in genesis-mode, depleting its 58.7 Mugnot
balance. The 8th deploy (r/sys/txfees) then fails with insufficient
funds. Every downstream realm that imports r/sys/txfees cascades.

Why cherry-pick is not viable
------------------------------
The realm code on master is byte-for-byte identical to gnoland1's. The
gap is purely between old unsigned tx format and new SDK semantics.
There is nothing to cherry-pick: the fix would have to be a new feature
(e.g. a genesis-mode VM bypass), which is a larger PR outside this
testbed's scope. --skip-genesis-sig-verification is already set; leaving
the payload stable is the honest choice.

Design: hf_topup_balance
-------------------------
New DSL primitive in lib/hf.sh. Called from migrate.sh after the realm
patches section. Applied in a post-assembly Python pass that:

  • opens $OUT/genesis.json
  • increments the target address's balance line (or appends if absent)
  • writes the mutated genesis back in place
  • writes $OUT/TOPUP-REPORT.md — a persistent audit table with
    before / after / delta / reason for every synthetic change

The Python path uses an indexed O(1) lookup (dict keyed by addr+denom)
because app_state.balances is a multi-million-entry list on
mainnet-scale snapshots and a linear scan would be too slow.

After the pass, hf_assemble prints a red `hf_warn` banner:
  "N synthetic balance top-up(s) applied — see $OUT/TOPUP-REPORT.md"
This is intentionally noisy: synthetic state must not be silent.

Visibility in STATE-REPORT.md
------------------------------
check-state.sh now reads $OUT/TOPUP-REPORT.md (when present) and emits
a "⚠ Synthetic state modifications" table that probes the topped-up
addresses on both local and prod chains. A reviewer looking at
STATE-REPORT.md sees the full divergence picture: local/prod deltas for
these addresses are expected and documented, not replay bugs.

Files changed
-------------
scripts/lib/hf.sh    — new _HF_TOPUPS state var, hf_warn helper,
                        hf_topup_balance DSL function, _hf_apply_topups
                        internal, hf_assemble calls _hf_apply_topups
scripts/migrate.sh   — section 3b calls hf_topup_balance for the
                        r/sys/* creator with a 1 Bugnot top-up and a
                        documented reason
scripts/check-state.sh — synthetic modifications section in STATE-REPORT

Result: gnogenesis fork test now reports 2696/2696 OK, 0 failures.
alloc per-byte, first step to correct/improve gas assumption for memory
allocation.

(cherry picked from commit 5d5f921)
Safari iOS does not re-evaluate CSS custom properties in SVG inline
style attributes when variable values change dynamically. The logo fills
were invisible in dark mode because `fill: var(--s-logo-hat)` set via
inline style was never recalculated after the theme switch.

**Fix: moved fills to CSS stylesheet rules (`b-gnome/b-logo blocks`) so
the browser cascades correctly on theme change.**

Tested on Safari iOS (iPhone) in dark mode: logo now renders correctly
on initial load without requiring any repaint.

<img width="45%" height="1434" alt="IMG_4054"
src="https://github.com/user-attachments/assets/91dcc5e4-17b0-4f9c-8baa-84467185d390"
/>
<img width="45%" height="1434" alt="IMG_4053"
src="https://github.com/user-attachments/assets/2048fc94-71d6-458d-9de6-325c659b4264"
/>

(cherry picked from commit c1a785a)
…gnolang#5251)

Fix handshake validation to check the received peer NodeInfo (not local
NodeInfo), closing a path where malformed peer payloads could bypass
validation and trigger nil dereference later in handshake flow.

Also harden NodeInfo.Validate by rejecting nil NetAddress explicitly.

Tests:

- add NodeInfo.Validate regression for missing NetAddress

- add transport handshake regression for malformed peer node info

Context: NEWTENDG-161
(cherry picked from commit 553b1c7)
…ges (gnolang#5305)

- Static markdown pages (configured via
`--aliases=/path=static:file.md`) rendered **Source** and **Actions**
header buttons that returned errors when clicked, since there is no
realm backing the content.
- Add a `Static` bool field to `HeaderData` that suppresses
Source/Actions links, keeping only the Content button for static
markdown pages.
- Homepage general links (About, Docs, GitHub) remain unaffected.

(cherry picked from commit 7f6f798)
Co-authored-by: Morgan Bazalgette <morgan@morganbaz.com>
(cherry picked from commit cfe24df)
- Add compile-time validation of map key comparability
- Add compile-time detection of duplicate constant keys in map literals

(cherry picked from commit eef3b22)
fix gnolang#5270 & dup. gnolang#5273

---

It implements abstain vote in the gov/dao, it was already showned in the
UI but the business logic vote did not handle this kind of vote, i also
opened a PR with an alternative that remove the Abstain vote from the UI
(since the impl. does not handle it):
gnolang#5272

But this version is better IMO, abstain vote can be important
> ⚠️ Also, Should abstain votes reduce the effective denominator for
supermajority?
See this msg for more details:
gnolang#5271 (comment)

---------

Co-authored-by: Morgan <morgan@morganbaz.com>
(cherry picked from commit ce2ba67)
Plain byte-index assignment (bs[i] = v) on Data-backed []byte slices was
silently dropping mutations across transactions. PointerValue.Assign2
returned early for DataByteType after calling SetDataByte, never
reaching the rlm.DidUpdate call that marks the backing ArrayValue dirty.
The fix mirrors the pattern already used in op_inc_dec.go for data[i]++.

Adds zrealm_databyte0.gno filetest verifying the Realm diff shows the
backing Data array is now persisted after plain index assignment.

Reported-by: torxeon (HackenProof NEWTENDG-98)

Co-authored-by: ltzmaxwell <ltz.maxwell@gmail.com>
(cherry picked from commit a3a356e)
fix this:
https://github.com/gnolang/gno/actions/runs/17917650414/job/50943950259?pr=4040
by properly allocate while restoring objects from storage.

see comments in files.

---------

Co-authored-by: Morgan <morgan@morganbaz.com>
(cherry picked from commit e6da902)
…lang#5037)

This PR introduces "protected" Boards2 realm functions that can only be
called from sub-realms deployed under the `gno.land/r/gnoland/boards2`
namespace.

The "protected" functions are necessary to implement, or move, specific
board features to sub-realms. They could also be useful, for example, to
support migrating data between versions.

It also adds a new `hub` sub-realm with safe types and public functions
that can be called from other realms or off-chain apps to read Boards2
realm data. This realm initially allows Boards mobile app to better
integrate with Boards2 realm by being able to read boards data.

Moving or implementing new features as sub-realms would help keeping
Boards2 realm leaner and would potentially allow for a more "micro
services" oriented approach for new features.

---------

Co-authored-by: Rémi BARBERO <d4ryl00@gmail.com>
(cherry picked from commit 4d4478b)
GC and MemStats are only used in filetests
(gnovm/tests/files/alloc_*.gno) and have no legitimate use in on-chain
code. Keeping them in the production stdlibs would lock in
implementation details — specifically, the allocator's internal
byte-counting format — as part of the deterministic on-chain execution
environment. Moving them to gnovm/tests/stdlibs/runtime/ ensures they
are only available during testing and cannot be imported by realm or
package code deployed on-chain.

BREAKING CHANGE: Backward incompatible with gnoland1.

(cherry picked from commit afd7e48)
…gnolang#5280)

Boards2 is now initialized with a single address which is a multisig
address (`g1rp7cmetn27eqlpjpc4vuusf8kaj746tysc0qgh`).

This PR removes address `g16jpf0puufcpcjkph5nxueec8etpcldz7zwgydq` from
the initial list leaving only the multisig one.

The removed address has been replaced by the multisig in all tests.

(cherry picked from commit 4a84645)
Fix this HP issue:
https://dashboard.hackenproof.com/manager/companies/newtendermint/gno-dot-land/reports/NEWTENDG-164

---

This PR does 3 things:
- Fix int64() cast that caused large uint64 constants to bypass overflow
validation when converting to smaller types
- Improve overflow error message to match Go's format
- allow int -> string at runtime, was already allowed at preprocess
since convertTo at preprocess is called only on numeric target

(cherry picked from commit 6a6fc4c)
…gnolang#5315)

Audit and reclassify logger calls throughout the tm2, gno.land, and
contribs layers:

- Info → Debug: per-operation noise (mempool tx accept/reject/recheck,
RPC request/response per call, ABCI queries, consensus fast-sync message
filtering, consensus replay step messages, p2p connection message
details)
- Info → Warn: anomalous conditions that indicate protocol violations or
backpressure (unexpected block from peer, invalid peer, internal msg
queue full)
- Debug → Info: significant lifecycle events that operators need
visibility into (p2p listener closed, GnoVM packages preprocessed with
elapsed time)
- Remove per-packet Debug logs in MConnection (Send, TrySend, Received
bytes, Read PacketMsg) that are too noisy even at debug
- Fix switch dial loop to skip logging "dialing peer" for already-
connected peers (log was before the duplicate-check)
- Improve --log-level flag help text to list valid values

This should greatly improve the number of noisy logs from the node.

(cherry picked from commit a82373a)
gnolang#5228)

`SetPeerHeight` only ever raised `pool.maxPeerHeight`. If a peer
announced a high fake height and then corrected it, the pool's max
stayed permanently stuck, causing `IsCaughtUp()` to return `false`
forever and preventing the node from leaving fast-sync mode.

This PR fixes it by adding an `else if` branch in `SetPeerHeight` that
calls the existing `updateMaxPeerHeight()` when a peer's new height is
below the current maximum.

(cherry picked from commit 8d17f08)
closes gnolang#1611

---------

Co-authored-by: Morgan <morgan@morganbaz.com>
(cherry picked from commit 908d1f7)
fix: gnolang#5335

I've updated the static file and the home realm, and I assume the blog
post are gonna be uploaded later.
I've fixed the incorrect event redirection.
For the about and help, we should deploy the `r/gnoland/home` realm
(defined in `gno.land/pkg/gnoweb/app.go`), or we have to modify gnoweb
for this pre existing bug.

In my opinion, this static file provided by default should be as generic
as possible, which is why I deleted the different sections.

Do we want to keep `"gno.land/p/leon/svgbtn"`? (To reduce dependency)

Co-authored-by: moul <94029+moul@users.noreply.github.com>
(cherry picked from commit fe7e38b)
## Summary

- Add static markdown pages for all broken alias links on gno.land
(`/about`, `/gnolang`, `/ecosystem`, `/start`, `/license`,
`/contribute`, `/links`, `/partners`, `/events`)
- Content ported from the realm source code in
`examples/gno.land/r/gnoland/pages/` and staging events
- Update `home-alias` docker-compose to serve static pages via
`--aliases` flag
- Remove dead `/r/docs/home` link from homepage template
- Fix `/r/devrels/events` → `/events` alias path in homepage template

## Context

The current gno.land homepage has many dead links pointing to
`/r/gnoland/pages:p/*` realms that exist on staging but not on mainnet.
This PR creates static markdown files and configures gnoweb aliases to
serve them, restoring all broken pages.

### Pages added
| Path | Content |
|------|---------|
| `/about` | Platform overview |
| `/gnolang` | Gno language description |
| `/ecosystem` | Ecosystem projects |
| `/start` | Getting started |
| `/license` | GPL license text |
| `/contribute` | Contributor guide |
| `/links` | Important links |
| `/partners` | Fund & grants |
| `/events` | Past events list |

## Test plan
- [ ] Verify docker-compose starts gnoweb with all aliases
- [ ] Check each static page renders correctly
- [ ] Verify homepage links resolve (no more 404s)
- [ ] Run `gno test` on home package to verify filetest passes

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Signed-off-by: moul <94029+moul@users.noreply.github.com>
Co-authored-by: Nemanja Aleksic <kouteki@gmail.com>
(cherry picked from commit ccbcd67)
Add a beta banner that redirects users to the GitHub issues template for
feedback.
~The current text is somewhat random; we can likely find a better
option.~

added a generic env flag, to easily change the banner on purpose.

<img width="1271" height="606" alt="Screenshot 2026-03-19 at 14 28 25"
src="https://github.com/user-attachments/assets/ed2d2186-509d-4104-8b39-824e557a931a"
/>

(cherry picked from commit 3c9f07b)
Update the content on the static pages

Related to gnolang#5355

(cherry picked from commit 27ee208)
…nolang#5099)

Duplicate denoms in Coins is generally invalid. Currently, it simply
returns the first encountered Coin, but I think the more correct
behaviour is to panic so that execution doesn't continue with multiple
coins of the same value.

gnolang#5096 modifies NewCoins and Coins.Add to implement a behaviour I'm sure
some users will expect (joining coins together).

---------

Co-authored-by: David <60177543+Davphla@users.noreply.github.com>
(cherry picked from commit 81d9f80)
…nolang#5249)

Co-authored-by: Morgan Bazalgette <morgan@morganbaz.com>
(cherry picked from commit 2d7f193)
Related with gnolang#5374

---------

Signed-off-by: moul <94029+moul@users.noreply.github.com>
(cherry picked from commit 0886378)
…lang#5247)

Fixes:
[dashboard.hackenproof.com/manager/companies/newtendermint/gno-dot-land/reports/NEWTENDG-68](https://dashboard.hackenproof.com/manager/companies/newtendermint/gno-dot-land/reports/NEWTENDG-68).
Alternative to gnolang#5153.

The previous initialization algorithm used recursive depth-first
traversal of variable dependencies, which could produce incorrect
initialization order and was susceptible to non-determinism (map
iteration over dependency sets).

The Go specification mandates:
> Within a package, package-level variable initialization proceeds
stepwise, with each step selecting the variable earliest in declaration
order which has no dependencies on uninitialized variables.

## Changes

### Initialization order rewrite

**`preprocess.go`**: Replace the post-preprocessing `findDependentNames`
AST walker with a dependency-tracking approach that runs as a dedicated
coda pass (`codaInitOrderDeps`) after `preprocess1`:

- `addDependencyToTopDecl` — called from `codaInitOrderDeps` whenever a
`NameExpr` or `SelectorExpr` resolves to a package-level name in the
same package. It walks up the node stack to find the enclosing top-level
`ValueDecl` or `FuncDecl` and records the dependency as a `Name` in an
`ATTR_DECL_DEPS` attribute on that node. Method dependencies are encoded
as `"Type.Method"` so receiver bodies are tracked without re-walking the
AST.
- `resolveDeclDep` — given a dependency name (plain or `"Type.Method"`),
looks up the corresponding `Decl` using `GetLocalIndex` + `NameSources`
for correct, panic-safe `*DeclaredType` resolution.
- `resolveEffectiveDeps` — memoized DFS from all declarations, following
`ATTR_DECL_DEPS` edges and collapsing `FuncDecl` bodies transitively to
discover indirect `*ValueDecl` dependencies. O(V+E). Detects circular
variable dependencies and panics with the full chain (e.g. `circular
dependency: A -> f -> B -> A`).

**`machine.go`**: Replace the recursive `runDeclarationFor` +
`loopfindr` approach with a two-phase iterative algorithm directly
implementing the Go spec:

1. Build an ordered `[]Decl` pending list from all non-`FuncDecl`
declarations across all files, preserving source declaration order.
2. Kahn's topological sort with a min-heap keyed on declaration index to
always pick the earliest-in-declaration-order ready entry. O(V + E + V
log V).

This eliminates non-determinism from map iteration and incorrect
ordering from depth-first traversal.

**`nodes.go`**: Remove the now-unused `findDependentNames`,
`GetExternNames`, `addExternName`, and `isFile` helpers. The `Externs`
field on `StaticBlock` is retained for amino serialization
backward-compatibility but is no longer populated.

### Composite literal non-const key fix

**`preprocess.go`**: The old code allowed non-constant variables as
slice/array composite literal keys (e.g. `[]int{a: b, c: d}` where `a`
and `c` are runtime variables). This is invalid per the Go spec, which
requires constant index expressions. The new code panics with
`"slice/array literals may not contain non-const keys"`, matching Go's
`go/types` error.

**`uverse_test.go`**: `TestIssue1337PrintNilSliceAsUndefined`'s "print
composite slice" case used `a, b, c, d := 1, 2, 3, 4` as composite
literal keys. Updated to `const a, b, c, d = 1, 2, 3, 4` since these
must be constant expressions.

### Tests

- Add `var_initorder{1–26}.gno` filetests covering declaration chains,
multi-name specs, blank identifiers, method-receiver dependencies
(value, pointer, auto-addressed), cross-file transitive deps,
cross-package method guards, interface dispatch, recursive methods,
circular deps, embedded struct method promotion (value and pointer
receiver), func-literal-var circular deps, multiple blank decls with
side-effects, deep transitive FuncDecl→ValueDecl chains, variable
shadowing in function bodies, and doubly-nested closures.
- Add `var_initorder_crossfn.gno` and `var_initorder_xpkgmethod.gno` for
cross-file and cross-package cases.
- Add `TestInitOrderDeterminism` in `preprocess_test.go` which runs a
complex dependency graph 100 times to verify stable output.
- Add `TestCircDepDeterminism` which verifies circular-dependency error
messages are deterministic.
- Add post-Kahn's completeness assertion in `runFileDecls` that panics
if any declaration was not processed (guards against missed cycles or
reverse-dep notification gaps).
- Update `recursive10.gno`, `recursive11.gno`, and `closure.gno` for the
new circular-dependency error format.
- Update `composite15.gno` expected output for the new non-const key
error.

### Integration test updates

`atomicswap.txtar`: Gas amounts changed slightly (e.g. 454000 → 453800)
as a side effect of the new initialization order affecting the number of
VM operations executed during package loading.

---------

Co-authored-by: ltzmaxwell <ltz.maxwell@gmail.com>
(cherry picked from commit 50ee56e)
@Gno2D2
Copy link
Copy Markdown
Collaborator

Gno2D2 commented Apr 24, 2026

🛠 PR Checks Summary

🔴 Must not contain the "don't merge" label

Manual Checks (for Reviewers):
  • IGNORE the bot requirements for this PR (force green CI check)
Read More

🤖 This bot helps streamline PR reviews by verifying automated checks and providing guidance for contributors and reviewers.

✅ Automated Checks (for Contributors):

🔴 Must not contain the "don't merge" label

☑️ Contributor Actions:
  1. Fix any issues flagged by automated checks.
  2. Follow the Contributor Checklist to ensure your PR is ready for review.
    • Add new tests, or document why they are unnecessary.
    • Provide clear examples/screenshots, if necessary.
    • Update documentation, if required.
    • Ensure no breaking changes, or include BREAKING CHANGE notes.
    • Link related issues/PRs, where applicable.
☑️ Reviewer Actions:
  1. Complete manual checks for the PR, including the guidelines and additional checks if applicable.
📚 Resources:
Debug
Automated Checks
Must not contain the "don't merge" label

If

🟢 Condition met
└── 🟢 A label matches this pattern: don't merge (label: don't merge)

Then

🔴 Requirement not satisfied
└── 🔴 On no pull request

Manual Checks
**IGNORE** the bot requirements for this PR (force green CI check)

If

🟢 Condition met
└── 🟢 On every pull request

Can be checked by

  • Any user with comment edit permission

@aeddi aeddi marked this pull request as draft April 24, 2026 07:20
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 24, 2026

Codecov Report

❌ Patch coverage is 36.99422% with 109 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
contribs/tx-archive/backup/signerinfo.go 13.76% 89 Missing and 5 partials ⚠️
contribs/tx-archive/backup/backup.go 80.00% 6 Missing and 6 partials ⚠️
contribs/tx-archive/backup/options.go 0.00% 3 Missing ⚠️

📢 Thoughts on this report? Let us know!

@aeddi aeddi force-pushed the chain/test13-rc6 branch from f61fb6a to f121c37 Compare April 24, 2026 07:22
@aeddi aeddi changed the title Chain/test13 rc6 docs(test13): hardfork RC series review-only against chain/gnoland1 Apr 24, 2026
@aeddi aeddi changed the title docs(test13): hardfork RC series review-only against chain/gnoland1 docs(test13): hardfork RC series (review-only against chain/gnoland1) Apr 24, 2026
@aeddi aeddi added the don't merge Please don't merge this functionality temporarily label Apr 24, 2026
@aeddi aeddi marked this pull request as ready for review April 24, 2026 07:43
@aeddi aeddi requested review from ajnavarro, jaekwon, moul and thehowl April 24, 2026 07:44
@nemanjantic nemanjantic added a/gnops DevOps, Valopers, NetOps, Infra, Monitoring, Coordination team a/everyone Affects every team labels Apr 24, 2026
@aeddi aeddi closed this May 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

a/everyone Affects every team a/gnops DevOps, Valopers, NetOps, Infra, Monitoring, Coordination team amino Issues and PRs related to amino 📖 documentation Improvements or additions to documentation 🐳 devops don't merge Please don't merge this functionality temporarily 🛠️ gnodev 🌍 gnoweb Issues & PRs related to gnoweb and render 📦 🌐 tendermint v2 Issues or PRs tm2 related 📦 ⛰️ gno.land Issues or PRs gno.land package related 📦 🤖 gnovm Issues or PRs gnovm related 🧾 package/realm Tag used for new Realms or Packages.

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.