Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
9fce148
feat(ledger): add v8->v9 storage migration for the ledger 8->9 hardfork
ozgb Jul 24, 2026
17e541f
test(ledger): add v8->v9 state translation table + smoke tests
ozgb Jul 24, 2026
8597b9a
fix(node): version-aware ledger genesis seeding for the 8->9 hardfork
ozgb Jul 24, 2026
58fe004
refactor(ledger): use serialize peek_tag for genesis version detection
ozgb Jul 24, 2026
7cbaaf7
fix(ledger): restore ledger-8 construct_distribute_treasury host fn
ozgb Jul 24, 2026
b00dc97
fix(toolkit): runtime-upgrade robust to the ledger 8->9 metadata switch
ozgb Jul 24, 2026
ce08d45
feat(toolkit): real v8->v9 fork translation at the tx-generation boun…
ozgb Jul 24, 2026
0ee9e29
chore(toolkit): TEMP hardfork_debug log for replay_blocks partition
ozgb Jul 24, 2026
43808b2
fix(toolkit): runtime-upgrade waits for the new runtime to EXECUTE, n…
ozgb Jul 24, 2026
af7ed4f
chore(toolkit): remove TEMP hardfork_debug replay_blocks log
ozgb Jul 24, 2026
4265e2b
docs: add change files for the ledger 8->9 hardfork migration
ozgb Jul 24, 2026
e30976c
chore: cargo fmt
ozgb Jul 27, 2026
2c76e05
Merge branch 'main' into ozgb-ledger-hf
ozgb Jul 27, 2026
08671ba
chore: fix clippy errors
ozgb Jul 28, 2026
e8c1427
chore: npm audit fix (toolkit-js)
ozgb Jul 28, 2026
0f8f03d
Merge branch 'main' into ozgb-ledger-hf
ozgb Jul 28, 2026
679a1a5
feat: add perf prints to track migration time elapsed
ozgb Jul 28, 2026
fbbfe1c
feat: use ledger cost model for storage migration
ozgb Jul 28, 2026
7c16094
chore(local-environment): npm audit fix
ozgb Jul 28, 2026
2027d5e
fix: no-op ledger v8->v9 migration when state is already ledger-9
ozgb Jul 28, 2026
1b82adb
docs: update change file
ozgb Jul 28, 2026
b6d8bfd
Merge branch 'main' into ozgb-ledger-hf
gilescope Jul 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 10 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,14 @@ zkir = { version = "^2.2.0", package = "midnight-zkir" }
# Ledger 8 (compatible with layout-v2)
# coin-structure and transient-crypto (2.x) share versions with L7 so reuse those entries.
mn-ledger-8 = { version = "=8.1.0", package = "midnight-ledger" }
ledger-storage-ledger-8 = { version = "=2.0.1", package = "midnight-storage", features = ["parity-db"] }
# `state-translation` (needed by the v8->v9 storage migration) pulls in
# `public-internal-structure`, exposing `merkle_patricia_trie`/`storable`/the
# `state_translation` module used by `midnight_node_ledger::state_translation_v8_to_v9`.
ledger-storage-ledger-8 = { version = "=2.0.1", package = "midnight-storage", features = ["parity-db", "state-translation"] }
onchain-runtime-ledger-8 = { version = "=3.1.0", package = "midnight-onchain-runtime" }
# Same `midnight-onchain-state` instance that `mn-ledger-8`'s `ContractState`
# resolves to (via onchain-runtime 3.1.0); used as the v8 side of the state translation table.
onchain-state-ledger-8 = { version = "=3.0.0", package = "midnight-onchain-state" }
zswap-ledger-8 = { version = "=8.1.0", package = "midnight-zswap" }

# Ledger 9 (compatible with layout-v2; midnight-ledger-v9 crate)
Expand All @@ -101,6 +107,9 @@ zswap-ledger-8 = { version = "=8.1.0", package = "midnight-zswap" }
# (midnight-zkir 2.2.0) serves all of L7/L8/L9.
mn-ledger-9 = { version = "=1.0.0", package = "midnight-ledger-v9" }
onchain-runtime-ledger-9 = { version = "=4.0.0", package = "midnight-onchain-runtime" }
# v9 side of the state translation table (matches `mn-ledger-9`'s `ContractState`
# via onchain-runtime 4.0.0). Patched to onchain-state-4.0.0-rc.3 by [patch.crates-io].
onchain-state-ledger-9 = { version = "=4.0.0", package = "midnight-onchain-state" }
zswap-ledger-9 = { version = "=9.0.0", package = "midnight-zswap" }
coin-structure-ledger-9 = { version = "=3.0.0", package = "midnight-coin-structure" }
transient-crypto-ledger-9 = { version = "=3.0.0", package = "midnight-transient-crypto" }
Expand Down
22 changes: 22 additions & 0 deletions changes/runtime/changed/ledger-8-to-9-hardfork-migration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#node #runtime #ledger

# On-chain ledger 8->9 hardfork state migration

Lets a ledger-8 chain (e.g. `1.0.1`) runtime-upgrade in place to the current
ledger-9 runtime. A new host fn, `migrate_state_v8_to_v9`, runs the
`StateTranslationTable` (ported from `midnight-ledger` PR #539) to translate
Comment thread
ozgb marked this conversation as resolved.
the on-chain `LedgerState` from v13 to v18. It's wired in as
`pallet_midnight::migrations::v2::MigrateV1ToV2`, a `VersionedMigration<1,2,..>`
that fires once when a ledger-8 chain (pallet-midnight storage version 1)
upgrades to this runtime (storage version 2); a fresh ledger-9 genesis starts
at version 2 and skips it. The migration's weight is derived from the ledger
cost model rather than a hand-tuned estimate.

Also includes two fixes needed to support both ledger-8 and ledger-9 chains:
version-aware genesis seeding (detected via `serialize::peek_tag` instead of
hardcoding the v9 deserializer), and restoring the ledger-8
`construct_distribute_treasury_system_tx` host fn, which the `1.0.1` WASM
still imports.

PR: https://github.com/midnightntwrk/midnight-node/pull/1925
Issue: https://github.com/midnightntwrk/midnight-node/issues/1580
18 changes: 18 additions & 0 deletions changes/toolkit/changed/hardfork-fork-aware-tx-generation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#toolkit #ledger9

# Fork-aware transaction generation across the ledger 8->9 hardfork

`replay_blocks` now detects the v8->v9 fork boundary and runs the same
`StateTranslationTable` translation used by the on-chain migration
(`fork_context_8_to_9` / `fork_8_to_9_if_needed`), so transactions generated
after the fork are built against the correctly-translated ledger-9 context
instead of a stale ledger-8 one.

The `runtime-upgrade` command now waits for the new runtime to actually
*execute* at a finalized block, not just be applied/stored. The stored spec
version flips at the apply block, but that block still executes under the
old runtime, so polling only the stored spec left transaction generation
reading a block short of any ledger-9-classified block.

PR: https://github.com/midnightntwrk/midnight-node/pull/1925
Issue: https://github.com/midnightntwrk/midnight-node/issues/1580
9 changes: 9 additions & 0 deletions ledger/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@ zswap = { workspace = true, optional = true }

mn-ledger-8 = { workspace = true, features = ["proving"], optional = true }
onchain-runtime-ledger-8 = { workspace = true, optional = true }
onchain-state-ledger-8 = { workspace = true, optional = true }
ledger-storage-ledger-8 = { workspace = true, optional = true }
zswap-ledger-8 = { workspace = true, optional = true }

mn-ledger-9 = { workspace = true, features = ["proving"], optional = true }
onchain-runtime-ledger-9 = { workspace = true, optional = true }
onchain-state-ledger-9 = { workspace = true, optional = true }
zswap-ledger-9 = { workspace = true, optional = true }
coin-structure-ledger-9 = { workspace = true, optional = true }
transient-crypto-ledger-9 = { workspace = true, optional = true }
Expand All @@ -49,6 +51,11 @@ scale-info.workspace = true

[dev-dependencies]
midnight-node-res = { workspace = true, features = ["test", "chain-spec"] }
# The crate's own `#[cfg(test)]` modules (api::ledger, api::transaction, and the
# state-translation tests) use `extract_tx_with_context`, gated behind the
# helpers `can-panic` feature. Enable it for test builds so the tests compile
# when the crate is tested standalone.
midnight-node-ledger-helpers = { workspace = true, features = ["can-panic", "test-utils"] }

[features]
default = [
Expand Down Expand Up @@ -81,10 +88,12 @@ std = [
"zswap",
"mn-ledger-8",
"onchain-runtime-ledger-8",
"onchain-state-ledger-8",
"ledger-storage-ledger-8",
"zswap-ledger-8",
"mn-ledger-9",
"onchain-runtime-ledger-9",
"onchain-state-ledger-9",
"zswap-ledger-9",
"coin-structure-ledger-9",
"transient-crypto-ledger-9",
Expand Down
2 changes: 2 additions & 0 deletions ledger/helpers/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,13 @@ reqwest = { workspace = true }

mn-ledger-8 = { workspace = true, features = ["proving"] }
onchain-runtime-ledger-8 = { workspace = true }
onchain-state-ledger-8 = { workspace = true }
ledger-storage-ledger-8 = { workspace = true }
zswap-ledger-8 = { workspace = true }

mn-ledger-9 = { workspace = true, features = ["proving", "test-utilities"] }
onchain-runtime-ledger-9 = { workspace = true }
onchain-state-ledger-9 = { workspace = true }
zswap-ledger-9 = { workspace = true }
coin-structure-ledger-9 = { workspace = true }
transient-crypto-ledger-9 = { workspace = true }
Expand Down
35 changes: 33 additions & 2 deletions ledger/helpers/src/fork/fork_8_to_9.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ use std::collections::HashMap;

use tokio::sync::Mutex as MutexTokio;

use crate::state_translation_v8_to_v9::StateTranslationTable;
use base_crypto::cost_model::CostDuration;
use ledger_storage_ledger_8::state_translation::TypedTranslationState;

type Db8 = crate::ledger_8::DefaultDB;
type Db9 = crate::ledger_9::DefaultDB;

Expand Down Expand Up @@ -67,8 +71,35 @@ pub fn fork_context_8_to_9(
context8: LedgerContext8<Db8>,
) -> Result<LedgerContext9<Db9>, std::io::Error> {
let ledger_state_8 = context8.ledger_state.lock().expect("failed to lock ledger state");
let ledger_state: crate::ledger_9::Sp<LedgerState8<Db8>, Db8> =
old_to_new_sp(ledger_state_8.clone())?;
// Real v8->v9 state translation (NOT `old_to_new_sp`): the LedgerState tag
// changed v13->v18 and its shape changed, so a bare arena-key reuse would
// produce a v9 root the ledger-9 machinery can't read. Walk the v8 state
// through the same `StateTranslationTable` the on-chain migration uses. Db8
// == Db9 (both `ledger_storage_ledger_8::DefaultDB`), so source and target
// share one arena and a single-`D` `TypedTranslationState` applies.
let ledger_state: crate::ledger_9::Sp<LedgerState8<Db8>, Db8> = {
let mut tl = TypedTranslationState::<
mn_ledger_8::structure::LedgerState<Db8>,
mn_ledger_9::structure::LedgerState<Db9>,
StateTranslationTable,
Db8,
>::start(ledger_state_8.clone())?;
// Single-shot: a generous per-step budget drains the whole state in a
// couple of iterations; the step cap is only a runaway backstop.
// 1_000_000_000_000 pico-seconds == 1 second
let budget = CostDuration::from_picoseconds(1_000_000_000_000);
let mut steps = 0usize;
loop {
steps += 1;
if steps > 100_000 {
return Err(std::io::Error::other("v8->v9 state translation did not converge"));
}
tl = tl.run(budget)?;
if let Some(result) = tl.result()? {
break result;
}
}
};

let mut wallets = HashMap::new();
for (k, v) in context8.wallets.lock().expect("failed to lock wallets").iter() {
Expand Down
5 changes: 5 additions & 0 deletions ledger/helpers/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ mod utils;
pub use utils::find_dependency_version;
pub mod extract_tx_with_context;

/// v8 -> v9 ledger state translation table (ported from midnight-ledger PR #539).
/// Consumed by the runtime storage migration (via the `ledger` crate) and by the
/// toolkit fork boundary (`fork::fork_8_to_9`).
pub mod state_translation_v8_to_v9;

/// Strategy for ordering candidate coins/UTXOs during input selection.
///
/// Defined at the crate root (not inside the version-specific `common` module) so that
Expand Down
Loading
Loading