[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,SOLandTRONto1.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.
SetNetworkVersionandNetworkVersiondeep-copy the semver slices. -
Mgrs.BeginBlockcallsSetNetworkVersionbefore the unchanged-version early return, so every block republishes it (restart, replay). It runs before the store-migration iterator, so amigrateStoreV136already sees the new chains as active. -
Only
app.Newenables enforcement. Its callers aremayanode start/replay/export and the build-tagged dry-run tool. No unit test buildsapp.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) inx/mayachain/keeper/v1/keeper_mimir_archive.go. - No other versioned body changes.
SUPPORT_CHAINS_V129 == SUPPORT_CHAINS_VCURRENTin this MR, so genesis is unchanged. lint-versionspasses without#check-lint-warning.
Tests
common/chain_activation_test.go: every state ofIsActivated, 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.goTestIsOperationalMimirSkipSignerCache: operational from 1.136.0, not at 1.135.0.make test,make test-network-specific,lint-versions,lint-handlers,lint-managerspass.- 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/versioncurrent, 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 EVMconfirmationsare 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,BeginBlockchooses 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 indocs/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 (ErrataTxAnteHandlerrejects 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 methodGetTxDetails. Tests:observe_errata_test.go,TestGetTxDetails.
Follow-up after review round 6
202db0950:app.Newpublishes the network version of the loaded state right afterLoadLatestVersion, 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 methodGetCommittedTx,/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.