Skip to content

release: v2.5.0 - Zanzibar on MainNet at 53155801 - #339

Merged
guo merged 16 commits into
masterfrom
rc_2.5.0
Sep 16, 2026
Merged

guo merged 16 commits into
masterfrom
rc_2.5.0

Conversation

@envestcc

@envestcc envestcc commented Aug 17, 2026 •

Copy link
Copy Markdown
Member

Summary

Prepares the v2.5.0 MainNet release and records the completed TestNet rollout.

MainNet

Zanzibar, Beta, and Gamma activate together as one MainNet hardfork:

Network Activation height Estimated time
MainNet 53155801 2026-10-08 02:00 UTC / 10:00 CST

MainNet has not activated any Zanzibar generation yet, so all three internal feature gates use the same height. Operators and exchanges only need to coordinate one hardfork height.

This is a mandatory binary upgrade to v2.5.0. MainNet carries the fork heights and both IIP-59 contract addresses in the binary; genesis_mainnet.yaml and config_mainnet.yaml are unchanged.

Depends on iotex-core#5009 being merged and tagged as v2.5.0.

TestNet

The staged rollout has completed:

Fork Height Activation time
Zanzibar 46880641 2026-08-21
Zanzibar Beta 47141281 2026-08-29
Zanzibar Gamma 47468161 2026-09-07 10:00 UTC

TestNet operators must use v2.5.0-rc2 with the current genesis_testnet.yaml. Binary-first ordering is required because rc1 cannot parse zanzibarGammaHeight.

Changes

  • Updates MainNet delegate documentation and the v2.5.0 release note to height 53155801 and the estimated UTC/CST activation time.
  • Updates TestNet documentation and scripts from rc1 to rc2.
  • Records the already-activated TestNet Gamma height consistently with genesis_testnet.yaml.
  • Keeps MainNet genesis and config files unchanged.

Verification

  • MainNet height is on the existing epoch grid: (53155801 - 51918841) % 1440 == 0.
  • Zanzibar, Beta, and Gamma use the same MainNet height.
  • TestNet block 47468161 is confirmed on-chain at 2026-09-07 10:00:40 UTC.
  • Documentation is consistent with the new MainNet schedule.

envestcc and others added 12 commits August 17, 2026 11:56
Zanzibar is the next fork after Yap. It activates IIP-59 on-chain voter
reward distribution, the BLS proof-of-possession requirement at
candidate register/update, the in-contract transfer log topic fix, and
the GetCommittedState prestate fix.

46850041 starts an epoch ((h-1) % 360 == 0, testnet epoch is
numDelegates 24 * numSubEpochs 15) and sits roughly three days ahead of
the chain tip at the time of writing (46750267, ~2.6s/block).

epochsPerRewardEra (24) and voterBudgetPerBlock (256) are left at their
iotex-core defaults; the genesis loader layers this YAML over them.

Requires iotex-core v2.5.0.
- genesis_testnet.yaml: autoDepositContractAddress and
  delegateProfileContractAddress, the two contracts IIP-59 reads. The
  AutoDepositRegister was deployed on TestNet for this release from the
  same runtime bytecode as MainNet's, so the storage layout the
  direct-slot reader depends on matches; DelegateProfile already existed.
- changelog/v2.5.0-release-note.md.
- v2.4.4 -> v2.5.0 across READMEs, archive-node.md and the all_in_one
  scripts.

config_mainnet.yaml / config_testnet.yaml are untouched: v2.5.0 adds no
new node-config fields, only the Zanzibar height-ordering check, which
lives in genesis.

MainNet has no zanzibarHeight, so v2.5.0 is a no-op upgrade there.
Zanzibar is scheduled on TestNet only, so v2.5.0 is a TestNet release and
MainNet operators should not upgrade yet. Reverts the version bump in the
MainNet-facing files -- README.md, README_CN.md, archive-node.md and
all_in_one_mainnet.sh -- which also un-does an incorrect rewrite of
README.md's transaction-log patch section, where the patch and its release
note genuinely belong to v2.4.4.

TestNet docs, the all_in_one_testnet.sh docker tags and the changelog stay
on v2.5.0. CLAUDE.md and the release note now state the split explicitly.
…ibar

IIP-59 only pays out for candidates with VoterRewardOnchainOptIn set. The
fork-block migration sets it automatically for candidates whose reward
address is one of hermesRewardVaultAddresses, but genesis_testnet.yaml
does not override that list, so it inherits the two MainNet Hermes vaults
-- and every TestNet delegate sampled uses its own address as its reward
address. The migration therefore matches nobody on TestNet and delegates
have to send SetVoterRewardOptIn themselves.

Paired with the existing note about DelegateProfile commission portions,
since either one being unset leaves the distribution path with nothing to
pay out.
Fifth change on the fork. Notes that the height moved out of node config
into genesis, so blackListRemovalHeight in config.yaml is no longer read.
Sixth change on the fork. Spells out which ABI entry a BLS-less
registration uses, since the amount travels in a different field there.
…r fixes

The opt-in section claimed the fork-block migration "matches nobody" on
TestNet. That was written from a four-delegate sample and is wrong: three
of the 35 delegates route rewards through a Hermes vault and will be
opted in automatically. Replaced with the full survey -- 3 vault-routed,
2 with commission configured, 1 with both -- which makes the same point
without overstating it, plus the ioctl commands to check any delegate.

Also records what landed in v2.5.0 after this note was first written:
the three fixes carried over from master (#4910 SELFDESTRUCT transaction
log, #4921 sender eviction on a mint panic, #4937 block-level gasUsed),
the abandoned-drain terminal state, and the new ioctl surface.

Notes that the DelegateProfile write must come from the candidate's
identifier address, since sending it from the operator address writes a
record nothing reads.
Height moves from 46850041 to 46880641, an era's first epoch rather than
a point 12 epochs into one. Both are safe -- the constraint that matters
is leaving enough room before the settling epoch, and 46850041 had 12
epochs of it -- but starting an era means the first settlement Zanzibar
takes part in is one it has been active for from the beginning, with the
full 23 epochs of margin.

  height          46880641
  epoch aligned   (h-1) % 360 == 0, epoch 130225
  era start       130225 % 24 == 1
  settles at      epoch 130248, 23 epochs later
  ETA             2026-08-21 ~10:00 CST

Note the era's *first* epoch is epochNum % 24 == 1, not == 0: the latter
is where IsEraBoundary fires, which is the epoch the era settles *at the
end of*. Scheduling activation there is the one case that loses that
era's rewards, because the freeze it needs happens ~1.5 epochs earlier,
before the fork is live.

TestNet docs, the all_in_one_testnet script and the release note's
upgrade steps now name v2.5.0-rc0, which is what is tagged and what
delegates will run; v2.5.0 proper is tagged after the fork is observed.
MainNet docs stay on v2.4.4.
…osit

Both addresses previously named deployments whose owner keys are not
available to this release.

autoDepositContractAddress named a deployment whose `owner()` is the zero
address — its bytecode carries pause()/unpause()/transferOwnership, so
those are permanently uncallable. It also differed from the address
iotex-hub writes compound registrations to, which would have left the
protocol reading one contract while users registered against another:
compound preferences silently ignored, rewards credited to unclaimed
balance instead.

The replacements were deployed by replaying the mainnet creation
transactions, so both runtimes are byte-identical to mainnet's and the
constructors ran normally, leaving the owner set. Verified on chain:
owner is the release key, paused is false, DelegateProfile has the three
reward-portion fields registered against the existing PermyriadVerifier,
and an arbitrary address can publish portions.

The three delegates holding published portions on the old DelegateProfile
were migrated across and their values reconciled value by value.

  delegateProfile  io19l8qpk08rw0jr4vwguyufva9w4t6aq75q2kt90
                   0x2fcE00d9E71B9f21D58e4709c4B3A57557ae83D4
  autoDeposit      io1pvlpc02xft2va4f38ae2nvgqglcxtfytez75c4
                   0x0b3e1C3d464AD4CED5313f72A9b10047f065A48B

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…he fork

Three things were out of sync with genesis_testnet.yaml.

The contract addresses. 02b0476 replaced both deployments; the note still
named the old ones, including in a yaml block operators might copy. It now
carries both the bech32 and 0x forms and explains what changed: replaying
the MainNet creation transactions keeps each runtime byte-identical --
which is what AutoDepositRegister needs, since IIP-59 reads its storage
slots directly -- while letting the constructors run, so the owner is set.
It also records that the AutoDeposit address is the one iotex-hub writes
to, which is the property the previous deployment lacked.

A stale claim. The note told operators to refresh config_testnet.yaml as
well. Only the genesis changed.

The READMEs. README_testnet.md and README_CN_testnet.md -- the docs a
delegate actually follows -- said nothing about the fork. Bumping the image
tag there was not enough: an operator upgrading an existing node has no
reason to re-download the genesis, and nothing told them there is a
deadline. Both now carry a notice above the body with the height, the date,
the requirement to re-download the genesis specifically (it changed twice,
so a stale copy is plausible), and the two ioctl steps for IIP-59.

Drops the "two things to do before the fork" section: the ioctl section
below already covers opt-in and the reward-portion setup, and the survey
numbers in it were a snapshot that would go stale before the fork.
Both land at height 47141281 — epoch 32737's first block, which every other
fork height on this chain is also aligned to, and roughly 06:00 CST on
2026-08-29, about six hours after this is written.

zanzibarBetaHeight carries the three corrections that landed after v2.5.0-rc0:
the epoch-settlement fault handling, the DelegateProfile requirement for the
Hermes opt-in migration, and the era freeze log. They correct behaviour
Zanzibar already turned on, so they cannot share Zanzibar's height — this
chain committed 250k blocks under the pre-correction rules and re-deciding
them would change those blocks' receipt roots. Verified locally: a node given
zanzibarBetaHeight == zanzibarHeight diverges at the activation block itself.

testnetGrants credits 1e9 IOTX to a fresh address so the delegate set can be
re-registered. The credit goes through the state manager, so it reaches the
Erigon secondary store as well as the trie, and it adds to whatever the
account already holds rather than overwriting it.

Both heights must stay ahead of the tip until every node is upgraded. Note
that a v2.5.0-rc0 node cannot read this file at all — it rejects the unknown
zanzibarBetaHeight key and refuses to start — so the binary has to be rolled
before this genesis is distributed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Zanzibar activated on TestNet at 46880641 on 2026-08-21. Zanzibar Beta
follows at 47141281 with three corrections to what it turned on, plus the
scheduled balance grant, so every version reference moves to v2.5.0-rc1.

Both READMEs and the release note now lead with the upgrade ordering
requirement: rc0 cannot parse the new genesis at all -- it rejects the
zanzibarBetaHeight and account.testnetGrants keys and refuses to start --
so operators must pull the new binary before swapping the genesis in.
Verified by loading this genesis under both rc0 (rejects, with the two
field errors quoted in the release note) and rc1 (accepts, all four
values as scheduled).

The release note also records that the Hermes opt-in migration guard is
permanently inert on TestNet: it runs only in the block at Zanzibar's
height, which TestNet has passed, so a later Beta height cannot restore
it. Chains that have not yet activated Zanzibar should set
zanzibarBetaHeight equal to zanzibarHeight and get all three corrections.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@envestcc envestcc changed the title release: v2.5.0 — Zanzibar hardfork on TestNet release: v2.5.0-rc1 — Zanzibar Beta + testnet grant on TestNet Aug 28, 2026
Zanzibar, Beta and Gamma all land on block 52813081, around 2026-09-28
10:30 CST. This release stops being TestNet-only, so the MainNet docs,
scripts and archive guide move from v2.4.4 to v2.5.0 and the release note
covers both networks.

The banners lead with what makes a MainNet upgrade different: there is no
genesis change. MainNet keeps its fork heights and both IIP-59 contract
addresses in the binary rather than in genesis_mainnet.yaml, so operators
pull the image and restart -- none of the re-download-and-order-it-right
care the TestNet rollout needed. Verified by loading the unmodified
genesis_mainnet.yaml against the scheduling change: all three heights, both
contract addresses, epochsPerRewardEra, voterBudgetPerBlock and the two
Hermes vaults come through from the code defaults.

Adds a Gamma section, which the note had no entry for at all. Its four
corrections are gated together, and letting Gamma trail Zanzibar costs
something concrete: EnforceBLSPoP rides Zanzibar and candidate register
writes its self-stake bucket before verifying the proof, so a rejected
proof would leave that bucket behind until Gamma activates. It also records
that TestNet has not scheduled Gamma -- it activated Zanzibar and Beta on
builds predating the fork, so those four corrections stay off there until a
height is set.

The delegate-facing part is the Hermes migration, which runs in exactly the
activation block and never again. Against the live candidate set at block
52,004,121: 88 of 123 candidates have a Hermes vault reward address and
complete DelegateProfile portions and will be migrated, 9 have the vault
but incomplete portions and are left on the Hermes path, and 26 are
unaffected. Being left behind breaks nothing but forfeits on-chain
distribution with no second chance, so both READMEs and the note tell
Hermes delegates to check both portion fields before the fork.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@envestcc envestcc changed the title release: v2.5.0-rc1 — Zanzibar Beta + testnet grant on TestNet release: v2.5.0 — Zanzibar on MainNet (52813081) and TestNet (activated) Sep 4, 2026
envestcc and others added 2 commits September 7, 2026 10:52
TestNet activated Zanzibar at 46880641 and Beta at 47141281 on builds that
predate Gamma, so its four corrections -- ValidateHeaderGasUsed,
CorrectStakeMigrationGas, CheckedBlockGasDeduction and
RevertStakingStateOnFailedReceipt -- have been off since IIP-59 went live
there. This schedules them.

47468161 starts an epoch: (h - 47141281) % 1440 == 0, exactly 227 epochs
past Beta and on the same grid zanzibarHeight and zanzibarBetaHeight sit
on. It is ~11,200 blocks ahead of the tip at the time of writing
(47,456,979 on 2026-09-07 10:13 CST), which at 2.507 s/block lands around
18:00 CST the same day.

Verified by loading this file under v2.5.0-rc2 (30714c91e): it reports
46880641/47141281/47468161 with the grant entry intact.

Upgrade the binary before distributing this file. v2.5.0-rc1 rejects it --
"field zanzibarGammaHeight not found in type genesis.Blockchain" -- and
refuses to start, the same trap the rc0-to-rc1 rollout had with
zanzibarBetaHeight. Roll every node to v2.5.0-rc2 first, where Gamma stays
unscheduled and behaviour is unchanged, then replace the genesis.

The deferred-Gamma case this puts TestNet in was rehearsed locally at the
same commit: a chain with Zanzibar and Beta at 200 and Gamma at 400 crossed
400 and kept producing, five nodes agreeing, zero digest mismatches. Every
prior local round had activated all three together, so that path was
untested until now.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@guo guo changed the title release: v2.5.0 — Zanzibar on MainNet (52813081) and TestNet (activated) release: v2.5.0 - Zanzibar on MainNet at 53155801 Sep 15, 2026
# Conflicts:
#	CLAUDE.md
#	README.md
#	README_CN.md
#	README_CN_testnet.md
#	README_testnet.md
#	archive-node.md
#	scripts/all_in_one_mainnet.sh
#	scripts/all_in_one_testnet.sh
@guo
guo merged commit 2c9e215 into master Sep 16, 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.

2 participants