node-2.1.0-rc.2
Pre-releaseMidnight Node 2.1.0-rc.2
Metadata
- Type of release: major — the last final release is
node-1.0.1;2.0.0never shipped a final, so2.1.0is the first release of a new major line. - Date: 2026-09-17
- Ships in bundle: TBD — standalone pre-release (not bundled). No bundle in
midnightntwrk/midnight-network-opsreferences this tag;releases/components/latest-component-releases.mdstill lists2.1.0-rc.1as the line's latest pre-release and needs updating. - Git tag: node-2.1.0-rc.2
- Environment: devnet, qanet. For the full compatibility matrix, see the release notes overview.
- Upgrade scope: binary + runtime
- Reset required: No — with one bootstrap caveat for preview and devnet, see Deployment information.
- Governance action required: Yes — no new action in this delta; the
set_codeenactment carried from2.1.0-beta.1still applies. - Sister-line note: This release is on the
2.1.xline. Operators on the1.0.xmaintenance line should tracknode-1.0.2. The fork-from baseline is the1.0.300runtime that devnet actually ran before the hard fork;2.1.0-beta.1and2.1.0-rc.1named1.0.3, which is still pending.
High-level summary
2.1.0-rc.2 is a release-engineering candidate: five commits on top of 2.1.0-rc.1, no new protocol behaviour, no spec_version move, and no metadata change. It clears the two things that stood between the line and a publishable final. First, the deterministic srtool runtime WASM is published again — 2.1.0-rc.2 is the first release of the 2.1.0 line to ship a *.wasm asset and an srtool-digest.json, which resolves the headline known issue from rc.1 and makes a verifiable governance set_code artefact available. Second, the 2.1.0 benchmark weights are re-run on the reference machine, correcting a real under-pricing: every call in pallet_c2m_bridge and pallet_partner_chains_bridge was budgeted at roughly half its cost on validator hardware. Alongside those, two packaged chain-spec corrections fix a defect where a preview or devnet node brought up from an empty disk computed the wrong genesis hash and could not peer.
Audience
These notes are written for:
- Full Node Operators (FNOs) bringing up preview or devnet nodes from an empty disk, or running validators on the
2.1.xline. - Shielded Technologies engineering, SRE and release engineering, who own the srtool artefact chain and the environment rollout.
- Midnight Foundation release coordination, for the governance
set_codeartefact. - Developers using the toolkit to replay devnet history or generate transactions against devnet.
- External integrators whose clients, SDKs or indexers decode against the node's metadata — for the one thing they can now stop worrying about, see Breaking changes.
Dependencies
- Rust 1.98.1 — the toolchain moves from
1.95across the workspace, thesubxtand nightly cNIGHT e2e container images, and the srtool image used for deterministic runtime WASM builds (PR #2166, backported as #2172). No consumer-facing dependency follows from this; it is recorded because it is what made the srtool build work again. - midnight-ledger 8.1.2 and 9.1.0.0-rc.5 — unchanged from
2.1.0-rc.1; the node binary provides both via host calls. - The chain must already be running the 1.0.300 runtime (
spec_version001_000_300) — this is what devnet ran before the 2.1.0 hard fork, and what PRs #2161 and #2175 in this delta are written against. It supersedes the1.0.3baseline named in2.1.0-beta.1and2.1.0-rc.1; the two have an identical pallet metadata surface, so nothing previously documented about the fork surface changes. The measured diff is in runtime-diff.md. - Ledger 7 remains fully removed on this line, so this node cannot decode or replay chain history predating the ledger 7 → 8 hardfork.
Downstream impact (cascading effects).
- The runtime WASM blob changes (new weights, new compiler) while
spec_versionstays at002_001_000— the third distinct blob to claim that version on this line. Its metadata is identical torc.1's, sorc.2adds no client-facing surface thatrc.1did not already have — a property of the artefacts, not an upgrade path between them. Clients fork in from1.0.300, where metadata does change and must be refreshed. See Breaking changes for the evidence and the narrow case that does still bite. - Packaged network configuration changes for preview and devnet:
res/preview/andres/devnet/chain-specs and genesis artefacts are replaced. Running nodes are unaffected — their genesis is on disk — but anything that bootstraps fromres/must use this release's copy. See Deployment information. - The measured bridge weights are roughly 1.92x–2.51x the previous values. Blocks carrying bridge activity will account more weight per call than they did under the
rc.1runtime, so effective per-block bridge throughput falls to match the real cost.handle_transfersis aMandatoryinherent, where under-weighting was the unsafe direction.
For all other interop questions, see the bundle dependency matrix.
Tested-with versions
These are build pins — not QA-verified — read from the workspace lockfile and pin files at the release tag, so they say what this release was compiled against rather than what it was tested against. Only the Rust toolchain moved relative to
2.1.0-rc.1.
| Component | Tested-with version |
|---|---|
| Rust toolchain | 1.98.1 — changed from 1.95 (PR #2166) |
| srtool | ghcr.io/shieldedtech/srtool:1.98.1-0.18.5 — changed from paritytech/srtool:1.93.0-0.18.4 (PR #2166) |
midnight-ledger (ledger 8) |
8.1.2 |
midnight-ledger (ledger 9) |
9.1.0.0-rc.5, per-crate tags |
polkadot-sdk |
polkadot-stable2606 |
partner-chains |
1.8.1 |
compactc |
0.33.0-rc.1 |
subxt |
0.50.0 and 0.44.3 |
parity-db |
0.5.4 |
midnight-indexer |
TODO: not yet available |
Deployment information
- Upgrade scope: binary + runtime — a new binary and image, plus the coordinated on-chain runtime upgrade. The supported path into this release is a hard fork from a
1.0.300chain. There is no supported upgrade path from2.1.0-rc.1or2.1.0-beta.1: those are pre-releases on the way to this one, not deployment baselines to step forward from. - Reset required: No for a running node. Yes for a preview or devnet node that was bootstrapped from
2.1.0-rc.1or earlierres/artefacts — such a node computed a genesis hash that no live bootnode recognises, was rejected as a different chain, and has been sitting at block #0 with no peers. Wipe its base path and re-bootstrap from this release'sres/. Nodes already synced on either network are unaffected; their genesis is on disk. A chain running2.1.0-rc.1or2.1.0-beta.1is re-forked from1.0.300, not stepped forward onto this release. Nothing else in this delta calls for a state wipe, re-sync or re-index. - Governance action required: Yes, unchanged — the runtime is swapped by a governance
set_code, and this is the first release of the line that publishes the deterministic blob and digest that action should reference. Enabling the C2M bridge additionally needs a governance action to set itsMainChainScriptsand data checkpoint; without it the bridge's inherent data provider staysInert. - Downtime / coordination: Coordinated, and unchanged from
2.1.0-beta.1— this is a hard fork from1.0.300, not a rolling upgrade of an existing 2.x chain. Roll every validator onto the new binary first, confirm the whole set is importing and finalizing, then enactset_code; Cardano observation is gated for the duration of the DUST replay. Only one 2.1.0 pre-release binary should ever be present on a given chain: the cNIGHT UTXO ordering rule (PR #2123) landed mid-series whilespec_versionstayed at002_001_000, so mixed pre-release binaries on one 2.x chain reject each other's blocks.
Artifacts
ghcr.io/midnightntwrk/midnight-node:2.1.0-rc.2— node image; index digestsha256:55335420d20dddb57e0c03370e9b690546f5c09347b1c2edfc3e7e6e0fedd072(linux/amd64sha256:068a27fcd2c50ca35a0ab3dd3626f42997de7e2ba65e86eb4fa40749722c93ca, linux/arm64sha256:d1afcf24084d3f860a3c66d87e9600383aa200094c04f028eb7c9a7f6703e3c2)ghcr.io/midnightntwrk/midnight-node-toolkit:2.1.0-rc.2— toolkit image; index digestsha256:16a14dd347c1a2476162bbe1949b8e2638506cae9db4189da921347fdc4af9c2(linux/amd64sha256:f24d89f8a5b1367a2f4c0f9788b404ddb558be0f3ebe9f339955131d7077e8b0, linux/arm64sha256:4c0005ed1a25604990229d3bd4c07832f6298a43b42cd5a7b70e1548058d7578)midnight_node_runtime-2.1.0-rc.2.compact.compressed.wasm— deterministic runtime blob, new on this line;sha256:cb1df875b0adc841893860ca64f2f0fa441dda06703834deec2dcdb2815400cf, blake2-2560xb15f7383617c41cdb1b74b1ea4c8db0f4d0cde5e8438708a89e9f1df343a01b3, 556,781 bytes.system.setCodeproposal hash0x661ab01fc058cdd0bccb33b0372868eae3e6f8ef3c4263a7a63f93e6d2c5fed3;authorizeUpgradeproposal hash0x1b7e536898fe8ef38d698374849745729b661fbd743458a06d1f0e39edc90c0b. This is the artefact a governanceset_codeshould reference.midnight_node_runtime-2.1.0-rc.2.compact.wasm—sha256:0aae87cecacc26f4cc349187204b8cff408207aae9823ef6304b84b8628460a5midnight_node_runtime-2.1.0-rc.2.wasm—sha256:efeb98c35982834218cd56308f88c8f9713959804109198126b0d61699e6c8e9srtool-digest.json— build provenance; srtoolv0.18.5,rustc 1.98.1 (48a229cea 2026-09-01), built2026-09-17T15:02:40Zmidnight-node-2.1.0-rc.2-linux-amd64.tar.gz—sha256:4cf53431f30d7b3caa909daa71b04186b8a670f43819f4fbcee2d2ed8f9e54bdmidnight-node-2.1.0-rc.2-linux-arm64.tar.gz—sha256:0e223177ecd8708e75c5ab0f4615e80fc5a286ca6192a66798b56b24ac5f0106midnight-node-toolkit-2.1.0-rc.2-linux-amd64.tar.gz—sha256:54f73db3a69711ca0441ed44683cb6319441aa35fb13fa0784bdb300e28ec6b3midnight-node-toolkit-2.1.0-rc.2-linux-arm64.tar.gz—sha256:f45c17f0b1bc52ed10aa80b90c0917dfd9c72659aa04bd7e0ba69efd7c0e0174SHA256SUMS— checksums for all seven binary and WASM assets- Git tree hash:
cacad186a1c0edf9118af1800231255e792e9d4d(tagnode-2.1.0-rc.2, commit4d045ab33; sibling tagsruntime-2.1.0-rc.2andtoolkit-2.1.0-rc.2)
docker pull ghcr.io/midnightntwrk/midnight-node:2.1.0-rc.2
docker pull ghcr.io/midnightntwrk/midnight-node-toolkit:2.1.0-rc.2What changed
The list below is the delta against node-2.1.0-rc.1 — five commits. Everything the 2.x line introduced relative to 1.0.x is in the 2.1.0-rc.1, 2.1.0-beta.1 and 2.0.0-alpha.1 notes and is not restated.
- The deterministic srtool runtime WASM is published again, for the first time on the 2.1.0 line, along with its build digest.
- The whole toolchain moves to Rust 1.98.1 — which is what unblocked the srtool build.
- 2.1.0 benchmark weights are re-run on the reference machine, and four previously unbenchmarked surfaces gain measured weights.
- Bridge pallet weights are corrected upward by 1.92x–2.51x; they had been generated on a developer workstation.
- Preview and devnet ship the chain-spec and genesis artefacts the live networks actually run, fixing a bootstrap-from-empty-disk failure on both.
- The toolkit can replay devnet history again, instead of aborting on pre-fork blocks.
| Change | Upgrade Type | PR |
|---|---|---|
Deterministic srtool runtime WASM published, with srtool-digest.json |
Runtime upgrade | #2166 |
| Update Rust toolchain to 1.98.1 across node, toolkit and runtime | Node upgrade + Toolkit + Runtime upgrade | #2166, #2172 |
| 2.1.0 benchmark weights re-run on the reference machine | Runtime upgrade | #2160 |
Close benchmark coverage gaps: frame_system_extensions, pallet_safe_mode, pallet_version, one cNIGHT setter |
Runtime upgrade | #2160 |
| Restore the preview chain-spec and genesis that live preview actually runs | Node upgrade | #2165 |
| Copy the devnet chain-spec and genesis from 1.0.300 after the devnet reset | Node upgrade | #2175 |
| Accept spec version 1.0.300 when replaying chain history | Toolkit | #2161 |
| Runtime metadata diff (subwasm), 1.0.300 → 2.1.0-rc.2 — first measured on this line | Runtime upgrade | runtime-diff.md |
New features
None. This delta ships no new functionality; see Improvements and Fixed defect list.
New features requiring configuration updates
None.
Improvements
Deterministic runtime WASM is published again
Description: 2.1.0-rc.2 is the first release on the 2.1.0 line to publish *.wasm assets and an srtool-digest.json. 2.1.0-beta.1 and 2.1.0-rc.1 shipped none, and rc.1 recorded that as a known issue blocking the final, because a governance set_code wants a blob whose hash is independently reproducible — not one extracted from inside a node image. The blocker was the srtool image being pinned to an older Rust (paritytech/srtool:1.93.0-0.18.4) than the repo builds with; upstream publishes no 1.98.x tag, so the build now uses ghcr.io/shieldedtech/srtool:1.98.1-0.18.5. The resulting artefact is reproducible from the recorded digest, and the first measured metadata diff for the line is in runtime-diff.md. Runtime upgrade.
PR: #2166
Rust toolchain 1.98.1
Description: Updates Rust to 1.98.1 across the workspace rust-toolchain.toml, the subxt and nightly cNIGHT e2e container base images, and the srtool image. Two build fixes were needed: ethnum moves to 1.5.3 (1.5.2 transmuted () into TryFromIntError, which 1.98.1 rejects), and WASM_BUILD_RUSTFLAGS restores --allow-undefined for the runtime WASM link, which 1.98.1 dropped from the wasm32v1-none target spec and which Substrate's sp_io host-function imports rely on. New clippy lints (useless_borrows_in_formatting, unnecessary_get_then_check) and unused-import warnings account for the rest of the diff across the node, toolkit and the vendored partner-chains tree. Node upgrade + Toolkit + Runtime upgrade.
PR: #2166, backported as #2172
2.1.0 benchmark weights, measured rather than estimated
Description: benchmark pallet --pallet '*' was silently skipping four surfaces, so the runtime carried estimates for them. All four are now registered and measured on the reference machine (c7i.xlarge, Intel Xeon Platinum 8488C — the same CPU as the 2026-05-08 run) at STEPS=50, REPEAT=20:
frame_system_extensions— theTxExtensionchecks every signed transaction pays for.frame_system::Config::ExtensionsWeightInfohad been falling back to upstream's()impl, whose numbers are Parity's reference hardware and RocksDB; this runtime is ParityDb.pallet_safe_mode— the crate feature was enabled but the pallet was never listed, so the runtime used upstreamSubstrateWeight. Safe mode became load-bearing when #2079 made it the failed-multi-block-migration handler.pallet_version— gains aruntime-benchmarksfeature and anon_initializebenchmark. Its productionWeightInforeturnedWeight::zero()behind a TODO while the hook appends a digest log on every block.pallet_cnight_observation::set_mapping_validator_contract_address— the one call in the pallet still priced by a hardcodedDbWeight::writes(1).
Outside those, everything moved within noise except the three pallet_cnight_observation setters and pallet_timestamp::on_finalize, which fell; the cNIGHT setters were hand-written 10ms placeholders now measured at ~3.8ms. Runtime upgrade.
PR: #2160
Bridge pallet weights corrected upward
Description: Every function in pallet_c2m_bridge and pallet_partner_chains_bridge rose 1.92x–2.51x. Those two weight files had previously been generated on a developer workstation (AMD Ryzen 9 9950X) rather than the reference machine, so the runtime had been budgeting roughly half the real cost on validator hardware. handle_transfers moves from 121µs to 233µs and is a Mandatory inherent, where under-weighting is the unsafe direction — a Mandatory call is executed regardless of the remaining block weight budget, so an under-priced one can push a block past its limit rather than being deferred. Runtime upgrade.
PR: #2160
Deprecations
None in this delta. The StandardTrasactionInfo alias deprecated in 2.1.0-beta.1 is unchanged.
Breaking changes
A third distinct runtime blob now claims
spec_version002_001_000. Operators enacting aset_codemust reference the blob by hash, not by version — see runtime-diff.md for the measured metadata and the per-hash identifiers.
Runtime WASM changes again without a spec_version bump — but metadata does not
What changed: The runtime blob differs from 2.1.0-rc.1's (re-measured weights, new compiler) while spec_version stays 002_001_000 and transaction_version stays 4. This narrows — and largely retires — the equivalent breaking change announced in 2.1.0-rc.1, which was about metadata drifting under a frozen spec_version.
What breaks: Less than in rc.1. Runtime metadata is unchanged between rc.1 and rc.2, measured directly. rc.1 published no WASM asset, but its node image carries one, so the two blobs can be compared: subwasm diff reports No change detected, and both advertise the same 26 runtime APIs at the same versions. The checked-in metadata/static/midnight_metadata.scale is byte-identical at both tags (144,075 bytes) and agrees. This is expected — V14 metadata encodes types, calls, events, errors, storage and constants, not weights, and PR #2160's runtime changes are WeightInfo associated-type swaps plus define_benchmarks! registrations behind cfg(feature = "runtime-benchmarks"). Read that as a property of the blobs, not as upgrade guidance: it says rc.2 adds no client-facing surface that rc.1 did not already have, which is what a release candidate should be able to demonstrate. It is not a statement that a chain can move from rc.1 to rc.2 — no such path is supported. The client-facing action is the one driven by 1.0.300 → 2.1.0 and it is unchanged: refresh runtime metadata and re-sign. The reproduction is in runtime-diff.md.
What does still bite is artefact identity: three blobs on this line now advertise the same spec_version, so spec_version alone no longer identifies which runtime a chain is running or which one a proposal enacts.
Required actions:
- Identify the runtime by blob hash, not by
spec_version. For this release: blake2-2560xb15f7383617c41cdb1b74b1ea4c8db0f4d0cde5e8438708a89e9f1df343a01b3,system.setCodeproposal hash0x661ab01fc058cdd0bccb33b0372868eae3e6f8ef3c4263a7a63f93e6d2c5fed3. - Do not pin production tooling against
rc.2metadata:002_001_000is not yet released onto any chain and is still being filled in on this line. - Do not treat the unchanged metadata as a path between pre-releases: a chain on
2.1.0-rc.1or2.1.0-beta.1is re-forked from1.0.300, not stepped forward onto this release.
Code example: — (no client-facing API surface changes)
Carried forward, unchanged
The consensus-affecting cNIGHT UTXO ordering change (PR #2123) announced in 2.1.0-rc.1 still applies in full: a 2.1.0-beta.1 binary and any 2.1.0-rc.x binary must not validate the same 2.x chain concurrently, because the ordering rule changed mid-series while spec_version stayed at 002_001_000. The operative rule for this release is simpler than the flag-day procedure rc.1 described: fork from 1.0.300 with one pre-release binary across the whole validator set, and re-fork rather than step a chain forward from an earlier 2.1.0 pre-release. See the 2.1.0-rc.1 notes for the full description of the ordering change.
Known issues
Known issues are not freshly assessed at pre-release stage. The one item below is carried forward from 2.1.0-rc.1 because it is still true; the other rc.1 item — no runtime WASM asset — is resolved by this release.
Issue Sync from genesis remains slow
Description: The 16x reduction in cNIGHT observation UTXO over-fetch remains reverted for security reasons, so the follower is still on the higher Postgres round-trip volume during block import, on top of the sliding-window cache revert announced in 2.1.0-beta.1. Nothing in this delta changes it.
Issue: see the known issues board
Workaround (if any): Restore from a database snapshot rather than syncing from genesis where that option exists. The toolkit's own sync and replay path is a separate, and substantially faster, code path from node block import.
Links and references
- PRs: #2166, #2172, #2160, #2165, #2175, #2161
- Engineering docs: Runtime diff 1.0.300 → 2.1.0-rc.2, Ledger 9 and the C2M bridge, Storage separation config
- Migration guides: Hardfork from the 1.0.x line — unchanged by this delta. This link resolves once PR #2063, which adds the
2.1.0-beta.1notes folder, has merged. - Previous releases on this line: node-2.1.0-rc.1, node-2.1.0-beta.1, 2.0.0-alpha.1 release notes
- Known issues board: open bugs
- GitHub release: node-2.1.0-rc.2
Fixed defect list
| Defect number | Description |
|---|---|
| #2061 | The srtool image was pinned to an older Rust than the repo builds with, so no deterministic runtime WASM could be produced and neither 2.1.0-beta.1 nor 2.1.0-rc.1 published one. Fixed in effect by PR #2166; the tracking issue is still open at the time of this release |
| #2126 | The 2.1.0 weights run had not been performed, and benchmark pallet --pallet '*' was silently skipping four surfaces, leaving the runtime on estimates — including bridge weights generated on a developer workstation at roughly half the real cost on validator hardware (PR #2160) |
| #1690 | Preview was reset in June 2026 and the regenerated chain-spec and genesis landed only on release/node-1.0.1; the 2.x line branched before that, so a preview node brought up from an empty disk computed genesis 0x801d…b880 instead of live preview's 0x3c096de2…6dd13796, was rejected by every bootnode as a different chain, and sat at block #0 with no peers (PR #2165) |
| #2159 | res/devnet/ still held the pre-reset chain-spec and genesis, whose Locked pool was empty; the post-reset 1.0.300 artefacts are copied across (PR #2175) |
| #2158 | RuntimeVersion::try_from knew 001_000_003 but not 001_000_300, which devnet ran before the 2.1.0 hard fork, so any replay reaching a pre-fork devnet block aborted with UnsupportedBlockVersion(1000300) — breaking show-night-pools, show-wallet on a cold cache, and generate-txs without a warm one. Both versions are ledger-8 era and now share a decoder (PR #2161) |
Other Changes
Three change fragments present on this branch have never appeared in a release note on this line, because they carry no PR number and so were missed by the beta.1 and rc.1 passes. They are not new in rc.2 — they predate 2.1.0-rc.1 — and are listed here to close the gap:
- Fix
fork-networkruntime-upgrade mode hanging until the job timeout at "Connecting to node at ws://localhost:9950" on runners where the host-loopback to published-port path is black-holed. Theruntimestep now connects to whichever RPC endpoint actually answers, preferring the published port and falling back to node1's docker bridge IP, andcreateApifails fast after a boundedAPI_CONNECT_TIMEOUT_MS.fullmode still relies on the published port and needs a follow-up tooling change. - Use
apply_post_block_updateinpallet_midnight'son_finalize, which has one fewer theoretical failure mode. In practice the removed error could not occur, since block fullness is checked for each included transaction. - Clear the backlog of pending major dependency updates:
actions/cachev6.1.0 andazure/setup-kubectlv5.1.0 (both commit-pinned), Docker Compose v5.5.0 plus the buildx plugin it now delegatesbuild:to, the local-env contract-compiler base onnode:24-slim, eslint v10, and@types/node24 inutil/toolkit-js.