[V136] chore: bump to 1.136.0 and add chain-activation gate

Summary

Part 1 of the V136 chain-integration stack (BSC, BCH, TRON, SOL). Tracking issue: #325.

This branch targets develop. Each later branch targets the one before it:

# MR Branch Targets
00 !883 v136/00-chain-activation (this MR) develop
01 !884 v136/01-bifrost-utxo-hardening v136/00-chain-activation
02 !885 v136/02-bifrost-evm-hardening v136/01-bifrost-utxo-hardening
03 !886 v136/03-bsc v136/02-bifrost-evm-hardening
04 !887 v136/04-bch v136/03-bsc
05 !888 v136/05-tron v136/04-bch
06 !889 v136/06-sol v136/05-tron

This MR bumps the version to 1.136.0 and adds the chain-activation gate that every new chain uses. A chain in the support list stays inert, on chain and in Bifrost, until the network version reaches the chain's activation version. On its own the MR is behaviour-neutral: no predicate calls the gate until the chain branches add their chains.

It also carries one consensus change for the separate Bifrost MR !880 (merged): SkipSignerCache<chain> becomes an operational mimir from 1.136.0.

Commits

Commit Tag What
f51f085df V136 version 1.136.0, openapi version. Three store-migration tests that assumed store version 135 are pinned to a 1.135.0 network.
8214c3cfc V136 GetSupportChains: 1.136.0 uses SUPPORT_CHAINS_VCURRENT, 1.129.0 uses the frozen SUPPORT_CHAINS_V129 (byte copies of today's lists, same build tags).
4e9d94bf2 V136 common/chain_activation.go: activation table, Chain.IsActivated(), ChainActivationVersion, SetNetworkVersion. Mgrs.BeginBlock publishes the lowest active version every block. app.New turns enforcement on.
c7a9809a4 Version-unspecific Bifrost gate: keygen, observer, exinfo, block scanner and PostNetworkFee skip inactive chains. GetMayachainVersion returns an error instead of panicking on a bad version string.
3896b3c39 Version-unspecific docs/newchain.md: "Chain activation" rules and launch runbook.
648e8d84d V136 InitGenesis and ExportGenesis publish the network version, because neither runs a BeginBlock.
6f331b62b V136 isOperationalMimirV136 = V135 + SKIPSIGNERCACHE. V135 moves unchanged to keeper_mimir_archive.go.
3b1cd2c4e Version-unspecific Activation test counter gets a _total suffix and help text (promlinter).
4a628e537 Version-unspecific docs/newchain.md: the activation intro names the unversioned code a new chain case reaches, and why it splits consensus (review follow-up from !886).

Chain activation (common/chain_activation.go)

  • The table maps BSC, BCH, SOL and TRON to 1.136.0. Never remove an entry: replay needs it.

  • Chain.IsActivated():

    State Result
    Enforcement off (unit tests, Bifrost) true
    Chain not in the table true
    Enforcement on, version not yet published false (fail closed)
    Otherwise network version >= activation version
  • The lookup upper-cases the chain, so Chain("bsc") cannot bypass the gate.

  • Network version and enforcement are process-wide atomics. SetNetworkVersion and NetworkVersion deep-copy the semver slices.

  • Mgrs.BeginBlock calls SetNetworkVersion before the unchanged-version early return, so every block republishes it (restart, replay). It runs before the store-migration iterator, so a migrateStoreV136 already sees the new chains as active.

  • Only app.New enables enforcement. Its callers are mayanode start/replay/export and the build-tagged dry-run tool. No unit test builds app.New, so other packages' tests keep enforcement off.

The rule for chain branches, in docs/newchain.md: every new case <Chain> in a common predicate (GetEVMChains, GetUTXOChains, GetGasAsset, DustThreshold, MaxMemoLength, GetAddress, NewTxID, IsEVM, ...) is reached only when c.IsActivated(), and list functions filter inactive chains. Each chain branch has a test that, at 1.135.0, every predicate answers as for an unknown chain and MsgObservedTxIn/Out fail ValidateBasic.

Bifrost gate

mayaclient.IsChainActivated(bridge, chain) compares /mayachain/version current with the activation version. A chain outside the table returns true without a query. Any error means inactive (fail closed) for scanning, observations, exinfo and network fees. Keygen retries a failed lookup for up to five minutes and then sends no vote, and errata stay pending until the lookup succeeds. The bridge already caches the endpoint for one block.

Component Inactive chain
Keygen (sendKeygenToMayachain) left out of the MsgTssPool chain list; a failed lookup is retried for up to 5 minutes, then no vote is sent
Observer sendDeck deck held and retried each block, released after activation
Observer errata dropped; on a failed lookup or broadcast kept pending in the observer LevelDB and retried each block, also after a restart, until MAYANode says the errata no longer applies
Exinfo (solvency, halt reports) skipped
PostNetworkFee refused with ErrChainNotActivated; this covers every client, including custom scanners
Block scanner, mempool scan paused: no scan position change, scanner reported unhealthy

The SOL and TRON custom scanners call IsChainActivated themselves (branches 05 and 06).

SkipSignerCache operational mimir

SkipSignerCache<chain> gates the expired signer-cache path of !880 (merged). Every node must see the same value at the same height. As an operational mimir it is set by the operational node vote instead of an economic supermajority, is admin-lockable, and its node votes are purged on churn like the other operational keys. This is the consensus half of thornode!4770 (MR, a759cb4f99). No admin value set before 1.136 exists, so no lock migration is needed.

Versioning / consensus impact

  • [V136] commits: version bump, support-chain gate, activation table, genesis version publish, operational mimir.
  • Archived: isOperationalMimirV135 (unchanged) in x/mayachain/keeper/v1/keeper_mimir_archive.go.
  • No other versioned body changes. SUPPORT_CHAINS_V129 == SUPPORT_CHAINS_VCURRENT in this MR, so genesis is unchanged.
  • lint-versions passes without #check-lint-warning.

Tests

  • common/chain_activation_test.go: every state of IsActivated, case-insensitive lookup, copy safety.
  • support_chains_test.go: per network tag, versions below and from 1.136.0.
  • managers_test.go: BeginBlock publishes the version. genesis_test.go: import and export publish it.
  • Bifrost: chain_activation_test.go (mayaclient), sign_activation_test.go, observe_activation_test.go, dispatcher_activation_test.go, blockscanner_activation_test.go.
  • keeper_mimir_test.go TestIsOperationalMimirSkipSignerCache: operational from 1.136.0, not at 1.135.0.
  • make test, make test-network-specific, lint-versions, lint-handlers, lint-managers pass.
  • Regression: no export changes from this branch. The full suite on the stacked branches (03: 144/144, 04, 05, 06) includes this code.

Open items

  • Bifrost reads /mayachain/version current, which can lag the lowest active version by a few blocks after an upgrade. Bifrost may start a new chain slightly after the chain activates it. Liveness only.
  • A 1.135.x node (validator or not) restarted at the 1.136.0 upgrade height applies that block before it stops. Binaries from 1.136.0 on choose the BeginBlock path from the stored version. For this upgrade, operators follow the runbook in docs/newchain.md: upgrade before restarting, and roll back one height or resync a node that applied it.
  • Per-chain min_confirmations, fee bounds and EVM confirmations are node config but feed the vote, so they must match on every node. To document in node-launcher.

Review follow-up (round 4)

  • fbecf2bfc: after a restart, BeginBlock chooses its version path from the stored network version, and the binary check runs on the loaded version before the store migration. Restarted and running nodes now agree at the 1.136.0 upgrade block, and an old binary stops there. [Version-unspecific]: it cannot be version-gated, because the version is what is unknown at that point; it only changes what a freshly started node does in its first block, so that it matches a node that kept running. Runbook note in docs/newchain.md. Tests: module_test.go.
  • b22d3118b: keygen retries the activation lookup for up to five minutes and sends no vote if it never succeeds. A chain is excluded only when the lookup says it is inactive. Tests: sign_activation_test.go.
  • 9cfa6361a: errata are kept in a LevelDB outbox and retried every block and after a restart, until broadcast or confirmed inactive. Tests: observe_errata_test.go.

Follow-up after review round 4

  • f5b3b69c7: a pending errata is no longer retried forever. Before each attempt the observer asks MAYANode whether it still applies and drops it when this node is not Active (ErrataTxAnteHandler rejects every errata from a non-active node, the only lasting rejection) or when /mayachain/tx/details/<txid> reports the tx reverted (the errata already reached consensus, another vote is a no-op). A failed query never drops an entry. Adds the read-only bridge method GetTxDetails. Tests: observe_errata_test.go, TestGetTxDetails.

Follow-up after review round 6

  • 202db0950: app.New publishes the network version of the loaded state right after LoadLatestVersion, so a post-launch chain is active for CheckTx and queries after a restart, before the first new block. Read-only, the same value the next BeginBlock publishes. Test: TestRestartPublishesLoadedNetworkVersion.
  • a3b4b45fe: a pending errata stays until this node's vote is in a block with code 0 (new read-only bridge method GetCommittedTx, /cosmos/tx/v1beta1/txs/<hash>). The vote is sent again only if its tx failed in its block or is still not found 10 MAYAChain blocks after the broadcast, at most 3 times; a failed lookup waits, since a duplicate errata vote costs slash points. Tests: observe_errata_test.go, CommittedTxSuite.
Edited by Bitol Maya

Merge request reports

Loading
Loading