feat(runtime): reduce block time from 12s to 6s#4042
Merged
Conversation
Halve the parachain block time by setting MILLISECS_PER_BLOCK to 6000, so one parachain block is produced per 6s relay slot (BLOCK_PROCESSING_VELOCITY stays 1; multi-block-per-slot / elastic scaling is left for a later step). Constant changes (heima + paseo): - primitives: MILLISECS_PER_BLOCK 12000 -> 6000 (DAYS/HOURS/... auto-double, so time-denominated period constants keep the same wall-clock duration) - runtime/common: UNINCLUDED_SEGMENT_CAPACITY 1 -> 2 (pipeline headroom) - ProposalLifetime: hard-coded 50400 -> 7 * DAYS (would otherwise silently halve the bridge proposal voting window to ~3.5 days) - spec_version 9261 -> 9270 One-shot migrations (migration/block_time_6s/, remove next release) that rescale stored state encoding absolute block numbers or per-block rates, which do not auto-rescale with the constant change: - vesting: SteppedMigration (multi-block; ~2770 schedules on mainnet) that keeps already-vested funds vested and drains the remainder at half rate from the upgrade block, preserving each schedule's wall-clock completion - parachain-staking: reset Round.length to DefaultBlocksPerRound, rebase first - score-staking: reset RoundConfig.interval (keep stake coefficients), rebase Round.start_block - scheduler: rebase anonymous non-periodic Agenda tasks to the same wall-clock firing time (the only kind present on-chain) Verified via cargo check (heima/paseo x prod/try-runtime), cargo fmt --check, and cargo test -p heima-runtime (incl. new vesting rescale unit tests).
try-runtime on-runtime-upgrade runs each migration twice to verify
idempotency; the scheduler/staking rebases in OnePassRescale changed the
storage root on the second run (a rebased scheduler task would be pushed out
again). Guard the whole migration with a once-only storage marker
(twox_128("BlockTime6s") ++ twox_128("OnePassRescaleDone")) so re-execution
is a no-op. The vesting MBM is already run-once via pallet_migrations.
Verified with try-runtime on-runtime-upgrade --checks=all against live
heima mainnet and Paseo state: storage root now identical across the
idempotency re-run, MBM completes, and all pallet try-state checks pass.
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
The `try-runtime.yml` workflow used `paritytech/try-runtime-gha`, which runs `cargo` from the repo root and fails here with "could not find Cargo.toml" because the workspace lives under `parachain/`. The action has no working-directory input. Replace it with a self-contained build+run that `cd parachain` first, mirroring the working try-runtime job in `check-runtime-upgrade.yml`: install the rust toolchain, download the try-runtime CLI, build the runtime with --features try-runtime, then run `on-runtime-upgrade --checks=all --blocktime 6000` against the live URIs in .github/runtime.json. Also add a path-filtered `pull_request` trigger so runtime/primitives/pallets changes get try-runtime coverage automatically, not only on manual dispatch. check-runtime-upgrade.yml: bump TRYRUNTIME_VERSION 0.8.0 -> 0.10.1 and pass the block time via a BLOCKTIME=6000 env var instead of the hard-coded 12000 (blocks are 6s after the spec 9270 upgrade).
Drop the pull_request trigger added in the previous commit. try-runtime compiles both runtimes from scratch and scrapes full live chain state (~10+ min per runtime), which is too heavy to run on every runtime PR. Keep workflow_dispatch so it can be run on demand on a feature branch when a migration warrants it. The parachain/ workspace-layout fix is retained.
Both try-runtime jobs passed a hard-coded --blocktime (6000), which is wrong whenever the workflow runs against a different runtime: the old 12s runtime, or a future elastic-scaling target with a shorter block time. Read it dynamically instead. Aura's slot duration is twice `Timestamp::MinimumPeriod`, so subwasm extracts that constant (a little-endian u64, in ms) from the built wasm's metadata and the job uses 2 * MinimumPeriod as the block time. This tracks whatever runtime is being tested with no manual update. Fails loudly if the constant can't be parsed. try-runtime.yml now installs subwasm for this; check-runtime-upgrade.yml already had it, so its BLOCKTIME env is dropped.
Add integration tests for OnePassRescale that schedule a real anonymous, non-periodic balances.burn (the same shape as the live heima on-chain burn: a Root-scheduled batchAll of dispatchAs + balances.burn) and run the actual migration: - scheduled_burn_keeps_wall_clock_expiry_after_block_time_halving: a task 100_000 blocks out is rebased to now + 2*(when-now), nothing is dropped or duplicated, and old (blocks*12s) == new (blocks*6s) wall-clock fire time. - migration_is_idempotent: a second on_runtime_upgrade run does not rebase again (the once-only guard holds). Needs sp-io as a dev-dependency for TestExternalities (matches runtime-common).
Kailai-Wang
force-pushed
the
feat/block-time-6s
branch
from
June 22, 2026 20:11
f2f1ffc to
f017653
Compare
Kailai-Wang
enabled auto-merge (squash)
June 22, 2026 20:21
BillyWooo
approved these changes
Jun 22, 2026
Kailai-Wang
disabled auto-merge
June 22, 2026 21:19
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Reduce the parachain block time from 12s to 6s, producing one parachain block per 6s relay slot. Async-backing infrastructure (
FixedVelocityConsensusHook, lookahead collator, 6s relay slot) is already in place, so this is a runtime-level change: a constant adjustment plus one-shot migrations to rescale stored state that encodes absolute block numbers or per-block rates.BLOCK_PROCESSING_VELOCITYstays1andAllowMultipleBlocksPerSlotstaysfalse— multi-block-per-slot / elastic scaling is intentionally left for a later step.Changes
Constants (heima + paseo)
primitives:MILLISECS_PER_BLOCK12000 → 6000.MINUTES/HOURS/DAYS/YEARSderive from it, so all time-denominated period constants (governance, treasury, staking round length default, identity, ...) auto-double and keep the same wall-clock duration.runtime/common:UNINCLUDED_SEGMENT_CAPACITY1 → 2 (pipeline headroom).ProposalLifetime: hard-coded50400→7 * DAYS. It does not auto-rescale; left unchanged it would have silently halved the bridge proposal voting window to ~3.5 days.spec_version9261 → 9270.One-shot migrations (
runtime/{heima,paseo}/src/migration/block_time_6s/, remove in the release after this one)Only stored state that does not auto-rescale is touched:
SteppedMigration(multi-block; ~2770 schedules on mainnet). Keeps already-vested funds vested and drains the remainder from the upgrade block at half the old rate, so each schedule completes at the same wall-clock time.per_blockclamped to ≥1; fully-vested schedules dropped.Round.lengthtoDefaultBlocksPerRound(the new doubled value), rebaseRound.first.RoundConfig.intervalto the new default (keeping the chain's existing stake coefficients), rebaseRound.start_block.Agendatasks to the same wall-clock firing time (the only kind present on-chain).OnePassRescalecarries a once-only storage guard so re-execution is a no-op (the rebases are not naturally idempotent). The vesting MBM is run-once viapallet_migrations.Scope confirmed against live state (RPC)
Scanned mainnet + Paseo before writing migrations. Surfaces needing rescale: vesting, parachain-staking round, score-staking, one anonymous scheduler task. Empty/not-applicable on both chains (no migration code): treasury spends, bounties (all
Proposed), democracy ongoing referenda & conviction locks, chain-bridge in-flight votes, proxy announcements, identity pending usernames.Collator impact
None required. The node reads
slot_durationfrom the runtime (sc_consensus_aura::slot_duration) and already uses the lookahead collator, so existing collators pick up 6s automatically after the runtime upgrade — no flag or binary change needed.Verification
cargo check— heima + paseo × (prod + try-runtime), all clean.cargo fmt --check,cargo test -p heima-runtime(incl. 4 new vesting rescale unit tests).on-runtime-upgrade --checks=allagainst live mainnet and Paseo state — both pass: idempotency re-run produces an identical storage root, the vesting MBM completes, all pallet try-state checks pass, full runtime state decodes, weight is 0.44% of max. (The first try-runtime run caught a non-idempotent rebase, fixed by the once-only guard.)Rollout
Recommended: merge → confirm CI try-runtime job green → Paseo runtime upgrade first (observe stable 6s, unchanged real-world period durations, vesting curve, staking round) → mainnet
setCode→ after MBM completes and is confirmed, remove the one-shot migrations next release.