node-1.0.0
Midnight Node 1.0.0
Release date: 2026-05-20
Git tag: node-1.0.0
Tree hash: a748db6ce78ccbd9b6bb3571686b83f849c7dc59
Environment: All public networks at time of release. For the full compatibility matrix, see the release notes overview.
Docker Images
docker pull midnightntwrk/midnight-node:1.0.0
docker pull midnightntwrk/midnight-node-toolkit:1.0.0High-level summary
Midnight Node 1.0.0 is the mainnet GA release. It bumps spec_version to 1_000_000 and bumps transaction_version to 3. The runtime adopts TransactionExtension (replacing the deprecated SignedExtension), tightens the throttle pallet, lands the Midnight-side handler hooks for the Cardano-to-Midnight bridge (the bridge itself is not enabled at this release — preparatory plumbing only), and migrates to measured benchmark weights. The node aligns with the polkadot-stable2603 Substrate SDK, gains a self-describing rpc.discover OpenRPC v1.4 endpoint, and picks up midnight-ledger 8.1.0 along with a number of audit-driven hardenings. The toolkit ships as an independently versioned image with substantial new transaction-generation, caching and observability features. Runtime upgrade required. SDK consumers using polkadot.js or subxt see no breakage — the new AuthorizeCall and WeightReclaim extensions are zero-sized, so the encoded transaction bytes are unchanged; only the implicit transaction_version mixed into the signature hash differs, which live signers fetch from chain state on every sign. The only genuine breakage is pre-signed extrinsics held across the upgrade boundary, and that is true for any spec_version bump.
Audience
These release notes are intended for:
- Node operators (all networks): Required upgrade. Roll the binary, then submit the runtime upgrade. Re-check
networkIdagainst your chainspec (now validated on boot). - DApp developers / SDK consumers: No action required for live signing. polkadot.js / subxt fetch
transaction_versionandspec_versionfrom chain state on every sign, and the two newTransactionExtensionentries are zero-sized — encoded tx bytes are unchanged. Pre-signed extrinsics held across the upgrade need re-signing (same as anyspec_versionbump). Userpc.discoverif you generate clients from spec. - Toolkit users / load-test operators: Required upgrade. Toolkit is now independently versioned and ships a new structured-logging stack, new
show-block,batch-single-tx, and per-seed caching subcommands. - Governance signers: New
proposal_weight_boundparameter required onmotion_close. Toolkitruntime-upgrade/update-ledger-parametersnow accept long-form--council-members/--technical-committee-members. - Cardano bridge consumers: Heads up — the Cardano-to-Midnight bridge is not enabled at 1.0.0. The handler hooks ship in the runtime so you can develop against the on-chain shape (handlers return a value attached to bridge events, each transfer carries
McTxHash), but no transfers flow until the bridge is turned on in a later release.
What Changed
| Change | Upgrade Type | PR |
|---|---|---|
| Cardano-to-Midnight bridge handler implementation (events, McTxHash, handler return value) — preparatory only, bridge not enabled | Runtime upgrade | #1188 |
Migrate runtime from SignedExtension to TransactionExtension (adds AuthorizeCall, WeightReclaim); transaction_version 2→3 |
Runtime upgrade (wire-format change, SDK-transparent via metadata) | #597 |
Align node, runtime, relay and partner-chains with polkadot-stable2603 SDK |
Runtime upgrade | #1299 |
Align node and runtime with polkadot-stable2512-3 SDK |
Runtime upgrade | #1262 |
Throttle pallet: per-account transaction-count limit (MaxTxs); AccountUsage migration |
Runtime upgrade (storage migration) | #1060 |
motion_close gains proposal_weight_bound; promoted to DispatchClass::Operational |
Runtime upgrade (extrinsic signature change) | #1032 |
| Use measured benchmark weights across FRAME and local pallets | Runtime upgrade | #1495 |
Early block-weight check in midnight pallet pre_dispatch |
Runtime upgrade | #1305 |
Remove stale cost model stubs; re-enable test_get_mn_transaction_fee |
Runtime upgrade (audit) | #839 |
Bump midnight-ledger to 8.1.0 (storage-core 1.2.0 incremental GC; race-condition, panic, leak, lock-ordering fixes) |
Node upgrade | #1301, #1510 |
Bump midnight-storage-core to 1.2.0-rc.2 |
Node upgrade | #1388 |
rpc.discover endpoint serving OpenRPC v1.4 spec (16 custom + 52 Substrate methods, JSON Schema types) |
Node upgrade (new API) | #869 |
Validate networkId on node boot against genesis state |
Node upgrade | #1265 |
Reject unsupported system transaction types (no more wildcard "unknown" accept) |
Node upgrade (audit) | #840 |
| Validate genesis file type / size (>10 MB, symlinks, non-regular files rejected) | Node upgrade (audit) | #832 |
unsafe_allow_symlinks config option (default off) |
Node upgrade | #1372 |
| Redact DB host/port/dbname from error logs (full details at debug level) | Node upgrade | #1067 |
ssl_root_cert configuration option for Postgres |
Node upgrade | #1029 |
| Per-SQL-query Prometheus timing for midnight data-source queries | Node upgrade | #904 |
In-memory cache for multi_asset.id lookups (removes repeated JOIN multi_asset from cNight queries) |
Node upgrade (perf) | #934 |
Coarse tx / tx_out / ma_tx_out id-range bounds for cNight observation queries (prune before joins) |
Node upgrade (perf) | #1365 |
| Improve logging for malformed ledger transactions | Node upgrade | #961 |
| Reduce cNIGHT observation address logs from error to debug | Node upgrade | #905 |
| Fix chain-state truncation after unclean shutdown (explicit DB drop) | Node upgrade (correctness) | #1140 |
| Drop ledger default storage on graceful shutdown | Node upgrade | #886 |
Remove hard-fork test ledger version dependencies / cfg(hardfork_test) |
Node upgrade | #1024 |
| Point to midnightntwrk partner-chains fork | Node upgrade | #948 |
| Bump vulnerable deps (rustls-webpki, astral-tokio-tar) — RUSTSEC-2026-0049 / 0066 | Node upgrade (security) | #1079 |
| Bump node version to 1.0.0 | Node upgrade | #919 |
Toolkit images versioned independently (toolkit-X.Y.Z tags) |
Toolkit | #1261 |
Toolkit JSON log format change (drops structured_logger for tracing_subscriber) |
Toolkit (BREAKING — log format) | #899 |
Toolkit --log-json, --verbose, --quiet, --verbose-fetch, --verbose-ledger; pretty default |
Toolkit | #859, #899 |
Toolkit show-block diagnostic command |
Toolkit | #1068 |
Toolkit batch-single-tx bulk transaction generation; file-based caches; --seeds on fetch |
Toolkit (perf) | #820, #939 |
Toolkit support for multiple shielded coin inputs in single-tx / batch-single-tx / batches |
Toolkit | #1216 |
| Toolkit support for fallible contract calls and fallible inputs | Toolkit | #888, #966 |
| Toolkit zswap state chaining in batched intents | Toolkit | #879 |
Toolkit LEDGER_VERSION override for multiple ledger stacks |
Toolkit | #946 |
Toolkit contract_custom builder enabled for ledger 7 |
Toolkit | #864 |
| Toolkit batched block-number-to-hash RPC during sync | Toolkit (perf) | #1263 |
| Toolkit upgrade subxt v0.44 → v0.50 | Toolkit | #1229 |
Toolkit restore --council-members / --technical-committee-members long-form flags |
Toolkit | #875 |
| Toolkit cap intent file reads at 64 MB | Toolkit (security) | #874 |
| Toolkit checked arithmetic in offer creation and wallet seed increment (audit Issue AL) | Toolkit (audit) | #942, #1081 |
Toolkit cleanup nullifier/nonce on fork export path; redact new_authority in CLI logs |
Toolkit | #1074 |
| Toolkit cryptographically secure RNG for parent block hash fallback | Toolkit (audit) | #878 |
Toolkit error propagation in ledger state updates and save_intents_to_file |
Toolkit (audit) | #927, #873 |
Toolkit remove verbose println! from ledger helpers |
Toolkit (audit) | #936 |
| Toolkit fix DustWallet spend state propagation (audit Issue AO) | Toolkit (audit) | #877 |
| Toolkit fix using nullifier as nonce when encoding zswap state | Toolkit (correctness) | #895 |
| Toolkit regression tests for nonce/nullifier distinction (audit Issue E) | Toolkit (audit) | #1128 |
| Toolkit fix Dust address format to match specification | Toolkit (correctness) | #1059, midnight-architecture#190 |
Toolkit fix missing persist() and recursion depth on context fork |
Toolkit | #881 |
| Toolkit fix panic when first block has no midnight transactions | Toolkit | #1045 |
| Toolkit add support for node 0.20.1 runtime version | Toolkit | #827 |
Toolkit tracing for structured log fields; new diagnostic logs |
Toolkit | #1230, #1058, #1030 |
Slim node Docker image ~200 MB via multi-stage build; pin libfaketime 0.9.10 |
Infrastructure | #897 |
| Content-hash Docker tags (skip rebuild when tree unchanged) | Infrastructure | #783 |
| Cardano hard-fork script for local-env | Infrastructure | #1326 |
| CI permissions/workflow hardening; bot TOCTOU + expression-injection fixes; actionlint | Infrastructure (security) | #855, #848, #861 |
| Bump srtool image to Rust 1.93.0 | Infrastructure | #1497 |
| Migrate Renovate to org-wide hardened preset | Infrastructure | #1118 |
| Devnet chainspec rebuilt against runtime 0.22.0 to match mainnet baseline (devnet only) | Infrastructure (chainspec) | #1341 |
New Features
Cardano-to-Midnight bridge handler
⚠️ The C-to-M bridge is not yet ready for use and is not enabled at this release. The runtime ships the handler hooks so that the bridge plumbing is in place when it is turned on in a later release; nothing flows across the bridge today. Integrators should treat this as preparatory work, not a live feature.
Description: Implements the Midnight-side handler for the Cardano-to-Midnight (C-to-M) bridge. The bridge plumbing now emits events for each transfer, each call carries an McTxHash (Cardano transaction hash) so an inbound transfer would be traceable end-to-end, and the handler returns a value that is attached to the resulting bridge event. The runtime ships an implementation of this handler so that downstream products can develop against the on-chain shape ahead of the bridge being enabled. Tracking issue: #1083.
Runtime upgrade (preparatory — not enabled). — #1188
Self-describing JSON-RPC API (rpc.discover / OpenRPC v1.4)
Description: Registers a standards-compliant rpc.discover JSON-RPC method that returns a complete OpenRPC v1.4 document describing the node's API. 16 custom Midnight methods are fully documented with parameter types, return types, errors and descriptions; 52 standard Substrate methods are listed as reference entries; JSON Schema type definitions for every response type are generated via schemars; a static docs/openrpc.json is committed for offline access; CI drift-detection tests keep the schema in sync with registered methods. DApp developers, SDK authors and API tooling can now generate clients, validate requests and discover capabilities without reading the node source.
Node upgrade. — #869
Throttle pallet: per-account transaction-count limit
Description: The existing per-account throttle (byte limit, rolling window) is extended with a per-account transaction-count limit (MaxTxs) over the same rolling block window. AccountUsage storage migrates from a 2-field tuple to a UsageStats struct that adds txs_used; the old map is cleared on upgrade. Operators get a second axis to throttle pathologically-busy accounts; users see clearer error semantics when they trip the limit.
Runtime upgrade with storage migration. — #1060
Diagnostic show-block toolkit command
Description: New diagnostic subcommand to inspect individual blocks — metadata, deserialized transactions — with human-readable and JSON output. Reads from the fetch cache first and falls back to live node RPC on cache miss. Intended for support / triage workflows where you want to look at exactly one block without spinning up a fetcher.
Toolkit. — #1068
Bulk transaction generation: batch-single-tx, file-based caches
Description: New batch-single-tx subcommand for bulk transaction generation, plus file-based wallet and ledger-state caches (default cache directory moved to ./toolkit_cache). The fetch subcommand gains --seeds to allow caching wallet states alongside the fetch cache. Together these materially improve block-replay and transaction-generation throughput in load tests.
Per-SQL-query Prometheus timing for cNight data source
Description: Midnight-specific data sources (cNight observation, federated authority, candidates) record individual Prometheus timing histograms per SQL query against db-sync. 13 sub-query timers are exposed at :9615/metrics under midnight_data_source_query_time_elapsed{query_name=...}. Operators can finally pinpoint which query is the long pole in a given observation cycle without reaching for pg_stat_statements.
Node upgrade. — #904
motion_close weight bound
Description: The motion_close governance extrinsic now takes a proposal_weight_bound parameter following the pallet_collective::close pattern. The previous version declared a constant weight that did not account for the inner Root-dispatched call (Substrate can refund weight post-dispatch but never increase it). The extrinsic is also promoted to DispatchClass::Operational to match the rest of the governance extrinsics. Toolkit and upgrader are updated to pass the new parameter.
Runtime upgrade (extrinsic signature change). — #1032
New Features Requiring Configuration Updates
--max-tx-gas-cost transaction-pool filter (carried forward from 0.22.5)
Required updates:
- If you want to cap accepted transactions by estimated gas cost, set
--max-tx-gas-cost <limit>on the CLI orMAX_TX_GAS_COST=<limit>in the environment. CLI takes precedence when both are set. - Leaving both unset preserves unbounded behaviour.
Impact: Non-breaking when unset. When set, transactions exceeding the limit are rejected at the pool gateway before validation.
ssl_root_cert Postgres TLS option
Required updates:
- Operators using PostgreSQL over TLS may now configure
ssl_root_certin the cNight observation config to pin the root CA bundle.
Impact: Optional; existing configs continue to work.
unsafe_allow_symlinks file-loading option
Required updates:
- If you intentionally load chainspec / config files via symlinks, set
unsafe_allow_symlinks = true. The default isfalse(symlinks rejected) following the genesis-file audit fix in #832.
Impact: Default behaviour change for operators with symlinked config paths — they must either resolve the symlink or opt in explicitly.
networkId validation on boot
Required updates:
- The node now refuses to start if the chainspec's
networkIddisagrees with thenetworkIdbaked into the genesis state. Confirm both sides match before rolling the binary.
Impact: Misconfigured environments will now fail loud at boot instead of producing a chain that cannot be re-synced.
Improvements
polkadot-stable2603Substrate SDK alignment across node, runtime, relay and partner-chains. Pulls inSessionKeys::generate_session_keystaking an owner,sc_service::build_networktaking aspawn_essential_handle, and a swathe of upstream type renames (RuntimeDebugNoBound→DebugNoBound,BeefySignatureHasherremoval, etc). (#1299)polkadot-stable2512-3SDK alignment as an intermediate step:Core::execute_blockandBlockBuilder::check_inherentsnow useLazyBlock;SpawnTasksParamsrequirestracing_execute_block;MmrApiv3 gainsgenerate_ancestry_proof. (#1262)- Runtime now uses measured benchmark weights for GRANDPA, BEEFY MMR, timestamp, migrations, scheduler, preimage, tx-pause, council and technical committee collectives/memberships, session validator management, federated authority + observation, system parameters, and cNIGHT observation. (#1495)
- Early block-weight check in
pallet-midnight'spre_dispatchshort-circuits over-budget transactions before expensive ledger validation. (#1305) - Bumps midnight-ledger to 8.1.0 (storage-core 1.2.0 incremental GC, shared-ParityDB-backend access; fixes for
force_as_arcrace,Spserialization panic, pending-Updatesmemory leak, lock-ordering violation; finer-grained WASM wallet bindings). All midnight-ledger workspace crates resolved from crates.io at 8.1.0; the previous[patch.crates-io]block has been removed. (#1301, #1510) - Explicit DB-backend drop after tokio shutdown drains parity-db's WAL pipeline, preventing silent chain-state truncation after SIGTERM. (#1140, #886)
- DB connection strings, governance keys, and address-decoder noise no longer leak through error logs. (#1067, #1074, #905)
- Genesis-file loader rejects symlinks, non-regular files, and files larger than 10 MB by default (audit Issue AI). (#832)
- System-transaction handler now rejects unrecognized variants with an explicit error rather than silently labelling them
"unknown"in metrics (audit). (#840) - Toolkit hardening: checked arithmetic for offer / seed math, error propagation in ledger state updates and intent persistence, CSPRNG for parent-block-hash fallback, intent reads bounded at 64 MB (audit). (#942, #1081, #927, #873, #878, #874)
- DustWallet spend-state propagation fix prevents
utxos()returning already-spent outputs in consecutive spends (audit Issue AO). (#877) - Zswap state-chaining support in batched intents; multiple shielded coin inputs combine across
single-tx/batch-single-tx/batches. (#879, #1216) - Toolkit fetch sync batches block-number-to-hash RPC calls into single requests, materially reducing round trips. (#1263)
- cNight db-sync observation queries cache
multi_asset.idlookups, removing repeatedJOIN multi_assetfrom the registration, deregistration, asset-create, asset-spend and candidate-token paths. (#934) - cNight observation queries now pre-bound
tx/tx_out/ma_tx_outids for the requested block-range window, so Postgres can prune rows by primary key before doing the expensive joins. Same bounding extended to thetx_in-keyed spend / deregistration queries. (#1365) - Node Docker image trimmed ~200 MB by moving the build toolchain into a builder stage and pinning
libfaketimeto 0.9.10. Image tags now derive from a 12-char tree content hash so identical trees skip redundant rebuilds. (#897, #783) - Security advisories fixed via dep bumps (
rustls-webpki0.103.4→0.103.10 for RUSTSEC-2026-0049;astral-tokio-tar0.5.6→0.6.0 for RUSTSEC-2026-0066). (#1079) - Removed all
cfg(hardfork_test)infrastructure and*-hfledger deps; hard-fork e2e tests no longer require a separate node binary. (#1024) - Toolkit images are now released independently of the node (
toolkit-X.Y.Ztag format). (#1261)
Deprecations
SignedExtension / SignedExtra (runtime extension type)
Deprecated item: pallet-style SignedExtension / SignedExtra type alias
Starts: 1.0.0 (replacement landed)
Full removal: not scheduled — the upstream Substrate API has been replaced; downstream code that referenced the old type alias must move to TxExtension.
Replacement: TransactionExtension / TxExtension, with AuthorizeCall and WeightReclaim extensions; runtime now implements CreateTransaction, CreateBare, CreateSignedTransaction, and CreateAuthorizedTransaction.
Migration steps: Live signing through polkadot.js / subxt continues to work unchanged — the new extensions are zero-sized, so the encoded transaction byte format is identical; only the implicit transaction_version (2 → 3) and spec_version mixed into the payload hash change, and live signers fetch those from chain state. Custom Rust signers that built SignedPayload against the removed runtime::SignedExtra alias need to rename it to runtime::TxExtension and adopt the new positional tuple shown in PR #597.
Toolkit structured_logger (JSON log format)
Deprecated item: legacy structured_logger JSON output format
Starts: 1.0.0
Full removal: 1.0.0 (removed in this release)
Replacement: tracing-subscriber JSON output. Use --log-json to opt in.
Migration steps: Update any log-shipping pipelines that parsed the old structured_logger shape. The new shape (tracing-subscriber JSON layout — timestamp / level / fields.message / target) is illustrated in the "Toolkit JSON structured-log format changed" entry below.
Toolkit positional new_authority argument on maintain-contract
Deprecated item: positional new_authority arg on the maintain-contract subcommand
Starts: 1.0.0
Full removal: 1.0.0 (removed in this release)
Replacement: --new-authority named flag
Migration steps: Update scripts that invoke maintain-contract to pass --new-authority <key>.
*-hf ledger feature flags / cfg(hardfork_test) / cfg(hardfork_test_rollback)
Deprecated item: hard-fork-test conditional compilation infrastructure
Starts: 1.0.0
Full removal: 1.0.0
Replacement: hard-fork e2e tests no longer require a separately-built binary.
Migration steps: CI configs that special-cased a hard-fork build can drop that machinery.
Breaking Changes or Required Actions
⚠️ This release bumpsspec_versionfrom22_000to1_000_000andtransaction_versionfrom2to3. The encoded transaction byte format is unchanged — both newTransactionExtensionentries (AuthorizeCall,WeightReclaim) are zero-sized. The only signing-affecting change is the implicittransaction_versionvalue mixed into the payload hash; live signers fetch this from chain state and so handle it transparently. The path that breaks is pre-signed extrinsics held across the upgrade, which is true of anyspec_versionbump.
transaction_version bumped to 3 (SignedExtension → TransactionExtension)
What changed: The runtime migrated from the deprecated SignedExtra / SignedExtension pattern to TransactionExtension. AuthorizeCall<Runtime> is prepended and WeightReclaim<Runtime> is appended to the extension tuple. Both are PhantomData newtypes with type Implicit = () — they SCALE-encode to zero bytes in the extrinsic and contribute zero bytes to additional_signed. The runtime additionally implements CreateTransaction, CreateBare, CreateSignedTransaction, and CreateAuthorizedTransaction for offchain transaction creation. transaction_version was bumped from 2 to 3 to signal the metadata change to clients that gate on the version field.
What breaks (and what doesn't):
- Transparent for live signing. polkadot.js /
@polkadot/apiandsubxtreadtransaction_versionfrom chain state on every sign and embed it in the signing payload — the same way they handlespec_versionrotations. The encoded tx bytes are unchanged; only the implicit values mixed into the payload hash differ, and the SDK fetches the current values automatically. App code callingtx.signAndSend(account)keeps working without modification. - Needs re-signing. Extrinsics signed before the upgrade and held for later submission will fail signature verification (
BadProof) at the new runtime — same as any release that bumpsspec_version. - Needs a metadata refresh. Long-lived clients and offline signers should invalidate cached metadata and reconnect once the upgrade is observed. Hardware wallets that gate signing on metadata hash will reprompt the user — by design.
- Needs a code change. Custom Rust code that constructs
SignedPayloaddirectly (bypassingsubxtand the runtime'sCreateSignedTransactionimpl) must useruntime::TxExtensioninstead of the removedruntime::SignedExtraand add()slots at front and back of theadditional_signedtuple — they encode to nothing but their positions must match the type.
Required actions:
- Most SDK consumers: nothing — verify your client reconnects after the upgrade and confirm transactions still go through.
- Pre-signed transaction holders: re-sign against the 1.0.0 runtime.
- Long-lived daemons that cache metadata: refresh on reconnect.
- Custom encoders / out-of-tree forks of the runtime crate: rename
SignedExtra→TxExtensionand add the two new positional slots.
Reference: PR #597 contains the full runtime/src/lib.rs diff, the benchmarking-harness diff, and the custom-signer tuple shape; the upstream polkadot-stable2603 sources for AuthorizeCall and WeightReclaim confirm type Implicit = () (i.e. zero-sized contribution to both the extrinsic and additional_signed).
Cardano-to-Midnight bridge handler API changed
What changed: Bridge calls now carry McTxHash per transfer; the handler is now expected to return a value attached to the resulting bridge event. The Midnight runtime implements this handler in 1.0.0.
What breaks: Out-of-tree handler implementations that match the old call shape will fail to compile / run. Downstream consumers relying on the old event shape must update.
Required actions:
- Update any custom handler implementation to return an event-payload value.
- Update event consumers to read the new
McTxHashand handler-return fields.
Code example: see PR #1188 for the bridge call and handler-return-value shape.
motion_close gains required proposal_weight_bound parameter
What changed: motion_close now takes a proposal_weight_bound: Weight parameter and is dispatched as DispatchClass::Operational. Callers must pre-charge the inner Root call's weight.
What breaks: Any tooling that builds the old 2-argument motion_close call will fail to encode against the new metadata.
Required actions:
- Update toolkit / upgrader / governance scripts to pass
proposal_weight_bound. Toolkit and upgrader are updated in this release. - For external governance UIs, regenerate metadata bindings.
Code example: see PR #1032 (toolkit/upgrader call sites updated alongside the pallet change).
Throttle pallet AccountUsage storage migration
What changed: AccountUsage storage moves from a 2-field tuple to UsageStats (which adds txs_used). A migration clears the old map on upgrade.
What breaks: External indexers / consumers reading the raw AccountUsage storage layout will need to update.
Required actions:
- If you query throttle pallet storage directly, regenerate types from the 1.0.0 metadata after the runtime upgrade.
- No action required for normal users — accounts simply start with a fresh window post-migration.
Code example: see PR #1060 — the AccountUsage struct definition and the ClearAccountUsageV1 migration are in pallets/throttle/.
Toolkit JSON structured-log format changed
What changed: Toolkit drops structured_logger in favour of tracing_subscriber. JSON output now looks like:
{"timestamp":"2026-03-10T17:26:45.103688Z","level":"INFO","fields":{"message":"spawning 20 fetch workers","log.target":"midnight_node_toolkit::fetcher","log.module_path":"midnight_node_toolkit::fetcher","log.file":"util/toolkit/src/fetcher.rs","log.line":171},"target":"midnight_node_toolkit::fetcher"}Default log output is now human-readable; pass --log-json to opt back in to JSON.
What breaks: Any log-shipping or alerting pipeline that parsed the previous flat key/value structure must be updated for the new nested fields / target / timestamp / level shape.
Required actions:
- Update log parsers / dashboards.
- If your environment defaults to JSON, add
--log-jsonto the command line.
Code example: the new JSON shape is shown in the fenced block above; full context in PR #899.
Toolkit image tag scheme
What changed: The toolkit Docker image is now versioned from util/toolkit/Cargo.toml, independent of the node version. Toolkit-only releases use the toolkit-X.Y.Z tag format.
What breaks: CI that resolves the toolkit image by the node version will pull the wrong tag for toolkit-only releases.
Required actions:
- Pin the toolkit image by its own version (
midnightntwrk/midnight-node-toolkit:1.0.0for this release;toolkit-X.Y.Zfor later toolkit-only releases).
Known Issues
Error: config error: missing field unsafe_allow_symlinks
unsafe_allow_symlinks is a new config value that tells the node to follow symbolic links. This new field is required in this release, and will receive a default in a future release. Provide the argument to avoid the error, e.g. UNSAFE_ALLOW_SYMLINKS=true.
Initial sync performance
Description: Initial sync from genesis is still slow on some operator hardware (~0.2 BPS observed). Sync-perf optimisation work is in progress.
Issue: #1298
Workaround: None at present. Use a curated paritydb snapshot if one is published for your network; otherwise allow extra time on first bootstrap. Track the issue for progress.
Links and References
- PRs:
- Cardano-to-Midnight bridge handler — #1188
- TransactionExtension migration — #597
- polkadot-stable2603 alignment — #1299
- polkadot-stable2512-3 alignment — #1262
- Throttle pallet — per-account tx count limit — #1060
- motion_close weight bound — #1032
- Generated benchmark weights — #1495
- rpc.discover / OpenRPC v1.4 — #869
- midnight-ledger 8.1.0 — #1510
- networkId validation on boot — #1265
- Chain-state truncation fix — #1140
- Vulnerable deps bump (RUSTSEC-2026-0049/0066) — #1079
- show-block toolkit command — #1068
- batch-single-tx + file caches — #820, #939
- Cache multi_asset.id to avoid excessive joins — #934
- Coarse id-range bounds for cNight observation queries — #1365
- Devnet chainspec rebuilt with runtime 0.22.0 — #1341
- SDK docs:
docs/openrpc.json— committed OpenRPC v1.4 spec (16 custom + 52 Substrate methods) for offline client generationdocs/openrpc.md— readable companion to the spec
- Testing evidence: https://github.com/midnightntwrk/midnight-node/blob/main/docs/releases/1.0.0/test-evidence/test-evidence-1.0.0-rc.8.md
- Known issues board: https://github.com/midnightntwrk/midnight-node/issues?q=is%3Aissue+is%3Aopen+label%3Abug
Fixed Defects
| Defect | Summary |
|---|---|
| #895, PM-21923 | Toolkit: encoding zswap state used the nullifier as the nonce. Fixed; regression tests added (audit Issue E, #1128). |
| #1140 | Node: chain-state truncation after unclean shutdown. Explicit DB-backend drop drains parity-db's WAL. |
| #877, PM-20016 | Toolkit DustWallet: utxos() could return already-spent outputs across consecutive spends (audit Issue AO). |
| #881, PM-22124, PM-22253 | Toolkit: missing persist() and recursion-depth blow-up on context fork. |
| #1045, PM-22361 | Toolkit: panic when the first block contained no midnight transactions. |
| #1059 | Toolkit: Dust address format was off-spec; aligned with midnight-architecture#190. |
| #942, PM-20206 | Toolkit: silent overflow in offer-creation arithmetic now returns OfferBuildError (audit Issue AL). |
| #1081, PM-20017 | Toolkit: wallet seed increment could panic / collide; returns explicit error on overflow. |
| #874 | Toolkit: unbounded intent-file reads — capped at 64 MB. |
| #878, PM-20205 | Toolkit: parent-block-hash fallback used a non-CSPRNG. |
| #840, PM-19971 | Node: get_system_tx_type silently accepted unknown variants; rejects with explicit error. |
| #832, PM-19964 | Node: genesis loader accepted symlinks / >10 MB files (audit Issue AI). |
| #1067, PM-19904 | Node: DB connection details leaked through error logs. |
| #839, PM-19968 | Ledger: stale cost-model stubs removed; test_get_mn_transaction_fee integration test re-enabled. |
| #1079, PM-22035 | Security: rustls-webpki CRL-matching (RUSTSEC-2026-0049) and astral-tokio-tar PAX-extension (RUSTSEC-2026-0066) advisories addressed. |
| #1305 | Runtime: expensive ledger validation no longer runs for transactions that won't fit in the block; rejected early with ExhaustsResources. |
| #1032, PM-22326 | Governance: motion_close weight no longer underestimates the inner Root call (now passed proposal_weight_bound). |
| #1140, #886 | Shutdown: ledger default storages explicitly released on graceful shutdown. |
| #905 | cNIGHT: non-bech32 and no-delegation-part Cardano address logs demoted from error to debug. |
Full Change Details
Changed
Upgrade ledger from 8.0.2 to 8.1.0-rc.1 (#1301) (#node)
Bumps the midnight-ledger dependency from 8.0.2 to 8.1.0-rc.1, picking up
new ledger types and conversion support.
Add regression tests for nonce/nullifier distinction in zswap serialization (#1128, PM-22025) (#toolkit)
Add unit tests verifying that serialized zswap local state uses the coin
nonce (randomness), not the nullifier (spend identifier), for the nonce
field. Addresses Least Authority Q1 2026 Node DIFF audit Issue E.
PR: #1128
JIRA: https://shielded.atlassian.net/browse/PM-22025
Redact database connection details from error logs (#1067, PM-19904) (#node)
Database connection error messages no longer include the host, port, or database name at error level. Full connection details are available at debug log level for authorized troubleshooting.
PR: #1067
JIRA: https://shielded.atlassian.net/browse/PM-19904
Implements handler for C-to-M bridge (#1188) (#node, #runtime)
Updates bridge to emit events.
Updates call by adding McTxHash to each transfer.
Updates handler API: handler is expected to return a value that is attached to events.
Implements the handler in Midnight runtime.
Add validation for networkId on node boot to avoid mismatch with genesis state (#1265, PM-22422) (#node, #binary)
Adds validation to ensure the networkId set in the chainspec matches the
networkId used to generate the genesis state.
PR: #1265
Fix for: https://shielded.atlassian.net/browse/PM-22422
Fix DustWallet spend state propagation (#877, PM-20016) (#toolkit)
Fix DustWallet::speculative_spend to return the updated DustLocalState
alongside spends, and extend mark_spent to commit the state atomically
with nullifier recording. This ensures DustLocalState::spend's
pending_until flags are propagated, preventing utxos() from returning
already-spent outputs in consecutive spend operations.
Addresses Least Authority audit finding Issue AO.
PR: #877
JIRA: https://shielded.atlassian.net/browse/PM-20016
Align node and runtime with polkadot-stable2512-3 SDK (#1262) (#node, #runtime)
Bumps Substrate dependencies to the polkadot-stable2512-3 tag and updates call sites for breaking API changes: Core::execute_block and BlockBuilder::check_inherents now use LazyBlock; SpawnTasksParams requires tracing_execute_block (set to None unless trace RPC is wired); MmrApi v3 gains generate_ancestry_proof while BeefyApi no longer exposes it; pallet-version test mock implements Core with LazyBlock. Partner-chains and lockfiles are updated in line with the same SDK line.
Align node, runtime, relay, and partner-chains with polkadot-stable2603 SDK (#1299) (#node, #runtime, #partner-chains)
Bumps Substrate dependencies to the polkadot-stable2603 tag and updates call sites for breaking API changes.
Early weight check in midnight pallet pre_dispatch (#1305) (#node)
Add an early block weight check in ValidateUnsigned::pre_dispatch before
expensive ledger validation.
PR: #1305
Speed up toolkit syncing (#1263) (#toolkit)
Batch block-number-to-hash RPC calls into a single request.
PR: #1263
Speed up cNight db-sync observation queries (#1365) (#node)
Pre-query coarse tx / tx_out / ma_tx_out id bounds for the requested block-range window, then constrain the four cNight observation queries (registration, deregistration, asset create, asset spend) by primary-key range so postgres can prune rows before doing expensive joins. Extends the same tx.id bounding to the tx_in-keyed spend/deregistration queries.
PR: #1365
Cache multi_asset.id to avoid excessive joins (#934, PM-21995) (#node)
In-memory cache for multi_asset.id lookups, replacing repeated JOIN multi_asset with a single cached lookup per (policy, name) pair. Eliminates the multi_asset join from registration, deregistration, asset create/spend, and candidate token queries.
PR: #934
Reset devnet to runtime version 0.22.0 to match mainnet (#1341) (#chainspec)
Rebuilds the devnet chainspec against runtime version 0.22.0 so devnet matches the mainnet baseline ahead of the 1.0.0 runtime upgrade. Devnet only — no impact on mainnet / testnet operators.
PR: #1341
Add unsafe_allow_symlinks config option when loading files (#1372) (#node)
Off by default.
PR: #1372
Bump vulnerable dependencies (#1079, PM-22035) (#node, #toolkit)
RUSTSEC-2026-0049 (rustls-webpki) and RUSTSEC-2026-0066 (astral-tokio-tar).
PR: #1079
Reject unsupported system transaction types (#840, PM-19971) (#audit, #client)
Replaces wildcard "unknown" labeling with explicit error (code 204).
PR: #840
Remove stale cost model stubs and re-enable integration test (#839, PM-19968) (#audit, #ledger)
PR: #839
Clean up Postgresql connection (#1029, PM-19924) (#node)
Add ssl_root_cert configuration option.
PR: #1029
Improve logging for ledger transaction errors (#961, PM-22311) (#node)
Warning log for malformed transactions.
PR: #961
Harden bot workflows against TOCTOU and expression injection (#848, PM-22117) (#ci, #security)
PR: #848
Remaining CI/CD security hardening (permissions, data flow, actionlint) (#861, PM-22116) (#ci)
PR: #861
Use content hashes for Docker image tags (#783) (#node, #ci)
Replace 8-char commit hashes with 12-char tree content hashes; identical trees skip rebuilds; force_rebuild escape hatch.
PR: #783
Fix chain-state truncation after unclean shutdown (#1140) (#node)
Explicit DB-backend drop after tokio shutdown drains parity-db's WAL.
PR: #1140
Drop ledger default storage on node shutdown (#886, PM-22219) (#ledger, #node)
Call midnight_node_ledger::drop_all_default_storage() after run_node_until_exit returns.
PR: #886
Reduce cNIGHT observation address logging level (#905) (#node)
Non-bech32 and no-delegation-part address logs demoted error→debug.
PR: #905
Remove hard-fork test ledger version dependencies (#1024, PM-22109) (#node)
Drops *-hf ledger deps and all cfg(hardfork_test*) machinery.
PR: #1024
Slim down node Docker image by ~200 MB using multi-stage build (#897) (#node, #docker, #ci)
Confines build toolchain to a builder stage; pins libfaketime to v0.9.10.
PR: #897
Validate genesis file type and size before reading (#832, PM-19964) (#audit)
Reject symlinks, non-regular files, and oversized files (>10 MB) before reading genesis/config files. Audit Issue AI.
PR: #832
Add proposal_weight_bound parameter to motion_close (#1032, PM-22326) (#runtime)
Follows pallet_collective::close. Made DispatchClass::Operational. Toolkit/upgrader updated.
PR: #1032
Bump midnight-ledger from 8.1.0-rc.1 to 8.1.0 (#1510) (#node)
Promotes the Ledger 8 pin from rc.1 to 8.1.0. storage-core 1.2.0 — incremental GC, shared ParityDB access; force_as_arc race, Sp serialization panic, pending-Updates leak, lock-ordering fixes. All midnight-ledger crates from crates.io; [patch.crates-io] removed.
PR: #1510
Use generated benchmark weights in runtime (#1495) (#runtime)
Connects generated weights for GRANDPA, BEEFY MMR, timestamp, migrations, scheduler, preimage, tx-pause, council/technical-committee, session validator management, federated authority + observation, system parameters, and cNIGHT observation.
PR: #1495
Add per-account transaction count limit to throttle pallet (#1060, PM-22377) (#runtime)
Extends throttle with MaxTxs rolling-window cap. AccountUsage migrates 2-tuple → UsageStats struct (txs_used). Storage migration clears old map.
PR: #1060
Migrate from SignedExtension to TransactionExtension (#597) (#runtime)
Adds AuthorizeCall, WeightReclaim; implements CreateTransaction, CreateBare, CreateSignedTransaction, CreateAuthorizedTransaction.
PR: #597
Toolkit: --log-json, --verbose, --quiet, --verbose-fetch, --verbose-ledger (#859, #899) (#toolkit)
Pretty default; structured JSON via --log-json; new fine-grained verbosity flags; RUST_LOG override.
Enable contract_custom builder for ledger 7 (#864, PM-22229) (#toolkit)
Removes "not supported for ledger 7" error.
PR: #864
Add show-block command to toolkit (#1068, PM-22388) (#toolkit)
Diagnostic block inspector. Reads from cache, falls back to live RPC.
PR: #1068
Add option when generating intents to write out the contract on-chain state (#946, PM-22230) (#toolkit)
Adds multiple-Ledger-stack support to toolkit-js; LEDGER_VERSION env var override.
PR: #946
Replace unchecked arithmetic in offer creation with checked operations (#942, PM-20206) (#toolkit)
Audit Issue AL — as i128 casts and unchecked add/sub replaced with TryFrom/checked_add/checked_sub.
PR: #942
Replace unchecked addition in wallet seed increment with checked_add (#1081, PM-20017) (#toolkit)
Returns explicit error on overflow.
PR: #1081
Cleanup nullifier/nonce use in fork export path; redact new_authority in CLI logging (#1074) (#toolkit)
Mirrors fix from #895. maintain-contract new_authority becomes --new-authority named flag.
PR: #1074
Use cryptographically secure RNG for parent block hash fallback (#878, PM-20205) (#toolkit)
PR: #878
Replace expect calls with Result error propagation in ledger state updates (#927, PM-19977) (#toolkit, #security)
PR: #927
Restore long-form CLI flags for governance key arguments (#875, PM-22246)
--council-members / --technical-committee-members on runtime-upgrade and update-ledger-parameters.
PR: #875
Fix missing persist() on context fork causing "not in arena" errors (#881, PM-22124, PM-22253) (#toolkit)
Also fixes recursion-depth blow-up via get_lazy.
PR: #881
Fix panic if the first block doesn't have any midnight transactions (#1045, PM-22361) (#toolkit)
Scans all blocks; for RPC source queries get_network_id API.
PR: #1045
Change default cache location to ./toolkit_cache; add --seeds option to fetch (#939, PM-22103) (#toolkit)
Two separate caches (fetch + wallet-state).
PR: #939
Fix Dust address format to match the specification (#1059, midnight-architecture#190, PM-22375) (#toolkit)
PR: #1059
Toolkit images are now versioned independently (#1261) (#toolkit)
Versioned from util/toolkit/Cargo.toml; tags use toolkit-X.Y.Z.
PR: #1261
Drop structured_logger in favour of tracing_subscriber; Breaking JSON structured log format change (#899) (#toolkit)
New JSON shape — see migration guide.
PR: #899
Support multiple shielded coin input in single-tx, batch-single-tx and batches (#1216, PM-22405) (#toolkit)
PR: #1216
Improve toolkit block replay and transaction generation performance (#820, #939, PM-22103) (#toolkit)
Added
Add rpc.discover endpoint with OpenRPC v1.4 API specification (#869) (#client, #node, #rpc, #api)
16 custom + 52 standard Substrate methods; static docs/openrpc.json; CI drift detection.
PR: #869
Add per-SQL-query Prometheus timing for midnight data source queries (#904, PM-22100) (#node)
13 sub-query timers at midnight_data_source_query_time_elapsed{query_name=...}.
PR: #904
JIRA: https://shielded.atlassian.net/browse/PM-22100