feat(runtime): gate the tblock correction on a runtime upgrade - #2002
Conversation
The tblock correction — verifying a block's first ledger transaction at `parent_block_time + 12s` so historical blocks still import — switched itself off at a hardcoded date. That date had to be pushed back indefinitely or syncing from genesis would break, and both it and the offset were consensus-critical values read from node config, where one validator with a different `default.toml` verifies historical blocks differently from its peers. Gate it on the on-chain runtime instead. Version 1 of the `Ledger8Bridge` `apply_transaction` and `validate_guaranteed_execution` host functions applies the correction; version 2, added here, does not. Historical blocks replay against whichever runtime was on-chain at that height, so pre-upgrade wasm imports v1 and still corrects, while every block from the `set_code` onward is verified against its own timestamp. The offset is hardcoded at 12s (`slot_duration_secs * (1 + MaxSkippedSlots)`, fixed for every chain the correction can apply to), and `tblock_correction_offset` / `tblock_correction_disable_after` are removed from node config along with the externalities extension that carried them. `spec_version` is bumped to 001_000_002. All validators must run node 1.0.2 before the upgrade is enacted — an older node cannot instantiate a runtime that imports `ext_ledger_8_bridge_apply_transaction_version_2`. Issue: #1924 Assisted-by: Claude:claude-opus-5 Signed-off-by: Oscar Bailey <79094698+ozgb@users.noreply.github.com>
Signed-off-by: Oscar Bailey <79094698+ozgb@users.noreply.github.com>
Signed-off-by: Tomasz Bartos <tomasz.bartos@shielded.io> Co-authored-by: Lech Głowiak <LGLO@users.noreply.github.com> Signed-off-by: Oscar Bailey <79094698+ozgb@users.noreply.github.com> # Conflicts: # metadata/static/midnight_metadata.scale # metadata/static/midnight_metadata_2.1.0.scale # runtime/src/lib.rs
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6b68321a31
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
Included accidentally via a bad merge conflict resolution Signed-off-by: Oscar Bailey <79094698+ozgb@users.noreply.github.com>
This comment has been minimized.
This comment has been minimized.
Signed-off-by: Oscar Bailey <79094698+ozgb@users.noreply.github.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4576164f43
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
Signed-off-by: Oscar Bailey <79094698+ozgb@users.noreply.github.com>
The strict cache key carried `block_context.tblock`, but `well_formed` is called with `well_formed_tblock(...)`, which skews a block's first transaction to `parent_block_time + 12s` under host-function v1 and leaves it alone under v2. Both versions can run in one process — historical replay against the pre-upgrade wasm alongside current blocks, and the pallet tests that call v1 directly while driving v2 through the current runtime — so whichever populated the cache first could hand the other a `VerifiedTransaction` verified at the wrong timestamp. Key on the resolved timestamp instead, via a single `strict_cache_key` helper shared by both key sites, and read the tblock back off the key when calling `well_formed` so the key and the verification cannot drift apart. The versions only get separate entries where they actually verify differently; when the correction is inert they still share one. Issue: #1924 Assisted-by: Claude:claude-opus-5 Signed-off-by: Oscar Bailey <79094698+ozgb@users.noreply.github.com>
Signed-off-by: Oscar Bailey <79094698+ozgb@users.noreply.github.com>
The pinned Debian versions (jq, curl, ca-certificates, vim, unzip) have been superseded in the bookworm archive, so apt-get install fails with exit code 100 and takes down the Local Environment Tests job. Cherry-picks the Dockerfile hunk from f04a980 (#1865) on main, which dropped the pins and bumped the node:22-slim digest. Assisted-by: Claude:claude-opus-5 Signed-off-by: Oscar Bailey <79094698+ozgb@users.noreply.github.com>
The pinned midnight-reserve-contracts revision invokes
`bunx @blaze-cardano/blueprint@latest`, which now resolves to 0.9.0. That
generator emits `import { TypedScript } from "@blaze-cardano/tx"`, a symbol
only present from tx@0.15.0 — but the contracts' bun.lock pins tx@0.14.1, so
`bun cli deploy` dies with "Export named 'TypedScript' not found".
Backport of midnight-reserve-contracts 424f7f94 until the pinned contracts
revision is bumped past it.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Oscar Bailey <79094698+ozgb@users.noreply.github.com>
Signed-off-by: Oscar Bailey <79094698+ozgb@users.noreply.github.com>
Signed-off-by: Oscar Bailey <79094698+ozgb@users.noreply.github.com>
…ghtntwrk#2031) * feat(runtime): gate the tblock correction on a runtime upgrade The tblock correction — verifying a block's first ledger transaction at `parent_block_time + 12s` so historical blocks still import — switched itself off at a hardcoded date. That date had to be pushed back indefinitely or syncing from genesis would break, and both it and the offset were consensus-critical values read from node config, where one validator with a different `default.toml` verifies historical blocks differently from its peers. Gate it on the on-chain runtime instead. Version 1 of the `Ledger8Bridge` `apply_transaction` and `validate_guaranteed_execution` host functions applies the correction; version 2, added here, does not. Historical blocks replay against whichever runtime was on-chain at that height, so pre-upgrade wasm imports v1 and still corrects, while every block from the `set_code` onward is verified against its own timestamp. The `Ledger9Bridge` host functions never skew: every ledger-9 block is produced after the upgrade that closed the loophole. The offset is hardcoded at 12s (`slot_duration_secs * (1 + MaxSkippedSlots)`, fixed for every chain the correction can apply to), and `tblock_correction_offset` / `tblock_correction_disable_after` are removed from node config along with the externalities extension that carried them. Backport of midnightntwrk#2002 onto main. `spec_version` is not bumped here — main's runtime is already at 002_001_000, a new spec version in its own right. All validators must run a node that exports `ext_ledger_8_bridge_apply_transaction_version_2` before that upgrade is enacted; an older node cannot instantiate a runtime importing it. The pallet's version-1 tests do not carry over: the pallet runs ledger 9, whose host functions have no version 1. The split is covered by the `well_formed_tblock` and `strict_cache_key` unit tests in `midnight-node-ledger`. Issue: midnightntwrk#1924 Assisted-by: Claude:claude-opus-5 Signed-off-by: Oscar Bailey <79094698+ozgb@users.noreply.github.com> * fix(ledger): key the strict cache on the tblock well_formed was given The strict cache key carried `block_context.tblock`, but `well_formed` is called with `well_formed_tblock(...)`, which skews a block's first transaction to `parent_block_time + 12s` under host-function v1 and leaves it alone under v2. Both versions can run in one process — historical replay against the pre-upgrade wasm alongside current blocks, and the pallet tests that call v1 directly while driving v2 through the current runtime — so whichever populated the cache first could hand the other a `VerifiedTransaction` verified at the wrong timestamp. Key on the resolved timestamp instead, via a single `strict_cache_key` helper shared by both key sites, and read the tblock back off the key when calling `well_formed` so the key and the verification cannot drift apart. The versions only get separate entries where they actually verify differently; when the correction is inert they still share one. Issue: midnightntwrk#1924 Assisted-by: Claude:claude-opus-5 Signed-off-by: Oscar Bailey <79094698+ozgb@users.noreply.github.com> * fix(toolkit): add support for the 1.0.3 runtime The block fetcher rejects any `spec_version` it does not know, so blocks produced by the 1.0.3 runtime (`001_000_003`) fail with `UnsupportedBlockVersion`. Bundle that runtime's metadata and map it like the others. Backport of midnightntwrk#2002 (189e581). Assisted-by: Claude:claude-opus-5 Signed-off-by: Oscar Bailey <79094698+ozgb@users.noreply.github.com> --------- Signed-off-by: Oscar Bailey <79094698+ozgb@users.noreply.github.com>
Overview
Modifies the fix for #1924 so it's triggered via a runtime upgrade rather than a timestamp.
Also includes #1900, to prepare for the upcoming hard-fork release (Node 2.1.0)
🗹 TODO before merging
📌 Submission Checklist
git commit -s) for the DCO🧪 Testing Evidence
Please describe any additional testing aside from CI:
🔱 Fork Strategy
Links
#1924