Skip to content

fix: base validate_unsigned DUST validity window on wall-clock time#1877

Open
ozgb wants to merge 3 commits into
mainfrom
ozgb-dust-validity-window
Open

fix: base validate_unsigned DUST validity window on wall-clock time#1877
ozgb wants to merge 3 commits into
mainfrom
ozgb-dust-validity-window

Conversation

@ozgb

@ozgb ozgb commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Overview

pallet_midnight's validate_unsigned (the tx-pool validation path) computed the DUST
validity-window reference time tblock from the last produced block's on-chain timestamp,
plus a MaxSkippedSlots margin. When block production stalls (network hiccup, missed AURA
slots beyond MaxSkippedSlots), that stored timestamp goes stale, so tblock lags real time.
A transaction whose creation time (ctime) is near "now" is then wrongly rejected during pool
validation with OutOfDustValidityWindow (ctime > tblock) — even though the next block,
whenever produced, would carry a timestamp ≈ now and accept it.

This change bases tblock on the true expected next block time: the current wall-clock
time rounded up to the next AURA slot boundary, so pool validation tracks real time
regardless of how long production has stalled. The MaxSkippedSlots margin is dropped.

How

  • New wall_clock::now_millis() host function (ledger/src/host_api/clock.rs) reading the
    node's system clock. A host function is required because the runtime's validate_transaction
    path registers no offchain extension, so sp_io::offchain::timestamp() would panic there.
  • Registered in both the node's HostFunctions executor tuples (node/src/service.rs).
  • Injected into the pallet via a new Config associated type WallClockMillis: Get<u64>
    (production wires it to the host fn; mocks return the deterministic on-chain mock timestamp).
  • validate_unsigned now sets tblock = wall-clock now rounded up to the next slot boundary.

Determinism is preserved: the wall-clock bump lives only in validate_unsigned (pool
path). Block import/execution goes through the pallet's own pre_dispatch, which uses the raw,
unbumped on-chain timestamp — deterministic across nodes. A non-deterministic wall-clock in
validate_unsigned does not affect consensus.

Removes the now-unused MaxSkippedSlots storage item (it only ever held its default; no setter
exists). This is a metadata change.

Two explanatory TODO comments were added deliberately, noting that once a custom transaction
pool exists, this host fn should be replaced by a timestamp (offchain) extension scoped to the
validation call.

🗹 TODO before merging

  • Rebuild runtime metadata (/bot rebuild-metadata) — MaxSkippedSlots storage removed
  • End-to-end check: submit a send_mn_transaction on a dev node, confirm no OutOfDustValidityWindow
  • Add PR link to the two change files (follow-up commit)
  • Ready

📌 Submission Checklist

  • All commits are signed off (git commit -s) for the DCO
  • Changes are backward-compatible (or flagged if breaking) — see Fork Strategy (node must upgrade with/before runtime)
  • Pull request description explains why the change is needed
  • Self-reviewed the diff
  • I have included a change file, or skipped for this reason:
  • If the changes introduce a new feature, I have bumped the node minor version
  • Update documentation (if relevant)
  • Updated AGENTS.md if build commands, architecture, or workflows changed
  • No new todos introduced

🧪 Testing Evidence

  • cargo check — 0 errors across midnight-node-ledger, pallet-midnight,
    pallet-cnight-observation-mock, midnight-node-runtime, midnight-node (and
    midnight-node --features runtime-benchmarks, covering the benchmark HostFunctions tuple).
  • cargo fmt --check — clean on all modified crates.
  • cargo test -p pallet-midnight — 21 passed, 2 ignored. test_validation_works,
    test_validation_fails, and test_pre_dispatch_* all pass (no fixture adjustment needed;
    the new tblock lands closer to ctime than the old +12s, staying inside the window).
  • cargo test -p pallet-cnight-observation — 43 passed, 1 ignored (exercises the modified mock).

Please describe any additional testing aside from CI:

  • Additional tests are provided (if possible)

🔱 Fork Strategy

  • Node Runtime Update
  • Node Client Update
  • Other:
  • N/A

Adding a host function means a node must have it registered before running a runtime that calls
it. An old node importing/validating with a new runtime would fail to resolve the
wall_clock::now_millis wasm import — upgrade nodes with (or before) the runtime.

Links

Closes #1856

pallet_midnight's validate_unsigned (tx-pool path) computed the DUST
validity-window reference time `tblock` from the last produced block's
on-chain timestamp plus a MaxSkippedSlots margin. When block production
stalls, that stored timestamp goes stale, so `tblock` lags real time and
transactions with a creation time near "now" are wrongly rejected with
OutOfDustValidityWindow, even though the next block would accept them.

Base `tblock` on the true expected next block time instead: the current
wall-clock time rounded up to the next AURA slot boundary. Wall-clock time
is supplied via a new `wall_clock::now_millis()` host function (the runtime's
validate path registers no offchain extension, so sp_io::offchain::timestamp()
is unavailable there) and injected into the pallet through a new Config type
`WallClockMillis: Get<u64>`. Determinism is preserved: the bump lives only in
validate_unsigned; block import/execution goes through pre_dispatch, which uses
the unbumped on-chain timestamp.

Removes the now-unused MaxSkippedSlots storage (metadata change).

Issue: #1856

Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Oscar Bailey <79094698+ozgb@users.noreply.github.com>
@ozgb ozgb requested a review from a team as a code owner July 14, 2026 14:16
@ozgb ozgb added the ai-assisted AI (LLM) assisted contribution label Jul 14, 2026
@jina-simulation

Copy link
Copy Markdown

Jina Review

Jina was blocked while reviewing this PR.

Review: https://app.usejina.com/reviews/763a68d1-d38c-400e-afa7-afb23336832c

Item Status
Review Blocked
Findings Insufficient credits

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Credits must be used to enable repository wide code reviews.

Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Oscar Bailey <79094698+ozgb@users.noreply.github.com>
@jina-simulation

Copy link
Copy Markdown

Jina Review

Jina was blocked while reviewing this PR.

Review: https://app.usejina.com/reviews/a52aebb1-ffbb-471f-8cbb-17032fa16ef2

Item Status
Review Blocked
Findings Insufficient credits

@ozgb

ozgb commented Jul 14, 2026

Copy link
Copy Markdown
Contributor Author

/bot rebuild-metadata

1 similar comment
@ozgb

ozgb commented Jul 14, 2026

Copy link
Copy Markdown
Contributor Author

/bot rebuild-metadata

@datadog-official

This comment has been minimized.

@ozgb ozgb added bot:ai-assisted Authored or substantially edited by an AI agent and removed ai-assisted AI (LLM) assisted contribution labels Jul 14, 2026
Signed-off-by: Oscar Bailey <79094698+ozgb@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bot:ai-assisted Authored or substantially edited by an AI agent

Projects

None yet

Development

Successfully merging this pull request may close these issues.

OutOfDustValidityWindow (171): txpool tblock margin too tight under network lag

1 participant