Support for 3 async cores#131
Open
ilchu wants to merge 22 commits into
Open
Conversation
Switch both runtimes from 6s blocks to 2s blocks with 3 cores assigned per parachain, mirroring polkadot-bulletin-chain PRs #417 (slot-based authoring) and #231 (2s + 3 cores). Closes #113. Runtime changes (both web3-storage and Paseo): - MILLISECS_PER_BLOCK: 6000 -> 2000 - BLOCK_PROCESSING_VELOCITY: 1 -> 3 (3 parachain blocks per relay slot) - UNINCLUDED_SEGMENT_CAPACITY: 3 -> (3 + RELAY_PARENT_OFFSET) * VELOCITY = 12 - New RELAY_PARENT_OFFSET = 1, wired into cumulus_pallet_parachain_system::Config and the RelayParentOffsetApi runtime impl (slot-based authoring requirement) - RELAY_CHAIN_SLOT_DURATION_MILLIS stays 6000 (relay slot is independent) - spec_version: 1 -> 2 Storage-pallet checkpoint cadence is now wall-clock-coupled so it survives block-time changes: DefaultCheckpointInterval = 10 * MINUTES, DefaultCheckpointGrace = 2 * MINUTES (resolves to the prior 100/20 blocks at 6s and 300/60 at 2s — same ~10 min cadence either way). All other timeouts in storage.rs (ChallengeTimeout, SettlementTimeout, RequestTimeout, DeregisterAnnouncementPeriod) auto-rescale via HOURS. Zombienet topology (zombienet.toml + zombienet/storage-paseo-local.toml): - 6 relay validators (alice, bob, charlie, dave, eve, ferdie) to support 3-core scheduling - Relay genesis patched with scheduler_params.num_cores = 3 and async_backing_params (allowed_ancestry_len = 6, max_candidate_depth = 6) - Parachain num_cores = 3; second collator (bob) added - Both collators run with --authoring=slot-based New examples/papi/assign-cores.js + `just assign-cores` recipe: zombienet's `num_cores=3` only sets the relay-side budget; actual assignment of cores to a para needs an explicit Coretime::assign_core extrinsic. The script sudo-batches one assign_core per core (mirrors polkadot-bulletin-chain's examples/assign_cores.js). Use after `just start-chain` to realize full 3-core throughput locally. Verified: - cargo fmt / check / clippy --all-features clean on both runtimes - RPC reports AuraApi::slot_duration() = 2000 ms and RelayParentOffsetApi::relay_parent_offset() = 1 - Relay availability_cores shows para 4000 occupying cores 0/1/2 after assign-cores - ~2.5-2.85s avg parachain block production (sampled over 60-91s windows) - fs-demo-ci (Rust subxt L1) passes end-to-end Known limitation: The PAPI demos under examples/papi/ are still on polkadot-api 1.23 while the rest of the repo is on 2.x. At 2s blocks PAPI 1.23's chainHead pin window evicts the runtime-context block before `getValue()` can resolve, producing BlockNotPinnedError. Migrating examples/papi/ to PAPI 2.x is substantive (event payload reshape, H256 -> SizedHex<N>, signature enum form, etc.) and tracked as follow-up; just demo will be red on dev until that lands.
Brings examples/papi in line with the rest of the repo (the UIs are
already on polkadot-api@^2.1.0 + @polkadot-api/cli@^0.20.4) and gets the
PAPI demo working under 2-second blocks.
The previous full-flow.js failure modes on this branch (BlockNotPinnedError
on the first storage query after a tx) turned out to be two separate
issues, both of which are addressed here:
- Event watching destabilises chainHead. On PAPI 2.x, a global
`api.event.X.Y.watch().subscribe()` interferes with the pinned-block
set; subsequent `api.query.X.Y.getValue(...)` then throws
`BlockNotPinnedError ... (getRuntimeCtx)`. `watchDefendedEvents` is
removed — `respondToChallenge` already returns the ChallengeDefended
event extracted from its own extrinsic events, so the demo counts
those instead.
- finalizedBlock$ is now a BehaviorSubject. `waitForNextBlock` /
`waitForBlock` declared `const sub = papi.finalizedBlock$.subscribe(...)`
and referenced `sub` from inside the callback. On 2.x the BehaviorSubject
fires synchronously on subscribe, before the `const` is assigned — TDZ
ReferenceError. Switched to a `let sub; sub = papi.finalizedBlock$.subscribe(...)`
pattern guarded by a `resolved` flag.
Type/shape adjustments to match the 2.x generated descriptors
(`SizedHex<N>` replaces `Binary` for `H256` / `[u8; N]` fields,
anonymous enums take `{ type, value }` object form):
- `mmr_root`, `data_root`, peaks, siblings: drop
`Binary.fromBytes(hexToBytes(...))`, pass the raw hex string straight
through.
- `provider_signature`: drop `Enum("Sr25519", Binary.fromBytes(...))`,
use `{ type: "Sr25519", value: hexSignature }` (MultiSignature is an
AnonymousEnum in the descriptors).
- `respond_to_challenge` response: same — `{ type: "Proof", value: proof }`.
- `chunk_data`: `Uint8Array` field, pass raw `Uint8Array` instead of
`Binary.fromBytes(...)` (the descriptor type check now rejects Binary
where Uint8Array is required).
Other plumbing for the version bump:
- `requireOneEvent` now returns `matched[0].payload` — 2.x wraps filtered
events as `{ original, payload }`.
- Import path `polkadot-api/ws-provider` → `polkadot-api/ws` (and in
assign-cores.js).
- `papi:generate` script: trailing `&& papi` is a no-op on 2.x; replace
with `&& papi generate`.
Verified end-to-end against a running zombienet (chain producing at
~2.5–2.8 s/block after `just assign-cores`):
- `just demo`: all 8 steps green, "PASSED: Provider received payment!"
- `just drain-tx-pool-then fs-demo-ci`: passes
- `just drain-tx-pool-then s3-demo-ci`: passes
Collaborator
The 'Merge branch dev into ic/2s-blocks-3-cores' conflict resolution kept both the feature branch's @polkadot-api/cli bump (^0.20.4) and dev's new c8 dependency, but dropped the comma between them, producing invalid JSON.
# Conflicts: # runtimes/web3-storage-paseo/src/storage.rs
…c_version, document UNINCLUDED_SEGMENT_CAPACITY - examples/papi/common.js: drop undeclared `resolved` reference (ReferenceError) and redundant unsubscribe in waitForNextBlock/waitForBlock - paseo runtime: bump spec_version 4_001 -> 4_002 for the consensus change - runtimes: replace magic 3 in UNINCLUDED_SEGMENT_CAPACITY with a named RELAY_SLOTS_OF_CAPACITY constant + derivation comment
Collaborator
|
@ilchu please, you can continue, after W3S presentation (today 15:00 CET), we can go with this to PreviewNet for sure |
The migration check builds one post-upgrade block on a snapshot of the live chain and mocks its Aura slot as snapshot_timestamp / blocktime. previewnet paseo runs at slot_duration 6000, so the stored Aura slot is timestamp/6000 and pallet-aura requires the new slot not to decrease, i.e. blocktime <= 6000. The 24000 value made the mocked slot ~4x smaller and tripped "Slot must not decrease". 2000 matches the new SLOT_DURATION and stays valid once previewnet itself moves to 2s blocks.
…line The 2.x migration bumped polkadot-api and @polkadot-api/cli but left @polkadot-api/substrate-bindings at ^0.16.5, a 1.x-era codec line. With no committed lockfile, that floor hoists into the generated descriptors, so their checksums are computed on a different codec than polkadot-api@2.x uses at runtime — failing every StorageProvider call with "Incompatible runtime entry". Bump to ^0.20.3 (what polkadot-api@2.1.6 and the user-interfaces workspace already use) and pin polkadot-api ^2.1.6.
The 2.x migration (0894b92) bumped polkadot-api/cli but left the Binary value-class API in place; substrate-bindings@0.16.5 still shipped Binary.fromBytes so it ran locally, masking the break. With bindings on the 2.x line (0.20.x) that API is gone — Vec<u8>/[u8;N] fields are plain Uint8Array, AccountId is SS58String, and H160 is a hex string. - write sites: drop the Binary.fromBytes(x) wrapper; pass the Uint8Array directly (including the inline (await import(...)).Binary.fromBytes form in the e2e specs) - read sites: drop .asBytes()/.asText() on Uint8Array fields; decode multiaddr via TextDecoder (the defensive `x.asBytes ? ... : x` guards were already 2.x-safe) - revive: dest is SizedHex<20> (hex string, as returned by instantiated.contract); code/data are Uint8Array - drop now-unused Binary imports Verified against live previewnet: Providers reads yield Uint8Array multiaddr/public_key; update_provider_multiaddr encodes from a raw Uint8Array.
…Array The previous commit converted all byte fields to raw Uint8Array, but that is only correct for variable Vec<u8> (multiaddr, name, key, content_type, code, data). Fixed-size [u8; N] fields are SizedHex<N> in polkadot-api 2.x — i.e. 0x-prefixed hex strings — and passing a Uint8Array there fails the arg-shape check as "Incompatible runtime entry": - establish_storage_agreement sig (MultiSignature, [u8; 64]) — hex - put_object_metadata cid (blake2-256, [u8; 32]) — hex; the read-side comparison now compares the SizedHex strings directly provider-supplied fixed fields (mmr_root, provider_signature, mmr proof hashes) already arrive as hex from the provider node, and the revive H160 dest is the hex string returned by instantiated.contract — all unchanged. Verified against live previewnet with the CI dependency set: establish (hex sig) and put_object_metadata (hex cid) encode COMPATIBLE; the old Uint8Array forms reproduce the "Incompatible runtime entry" error.
respondToChallenge returned the extracted ChallengeDefended payload, but every consumer that uses the return (full-flow recordDefended, e2e/05) reads result.events — which is undefined on a payload, so PAPI's eventDescriptor.filter(undefined) threw "Cannot read properties of undefined (reading 'filter')" right after the on-chain respond succeeded. Return the full tx result instead; keep the requireOneEvent call only to assert the challenge was defended (the sc-flow/sc-coverage callers ignore the return and rely on that throw).
In polkadot-api 2.x the Revive contract address (Instantiated.contract, ContractEmitted.contract) and the Revive.call `dest` are SizedHex<20> hex strings, not byte arrays: - deployContract: keep instantiated.contract as the hex string (toHex on a hex string coerces it to an array length and throws RangeError at deploy) - h160ToSubstrate: accept hex or raw bytes for publicKey.set() - decodeContractEmitted: feed viem hex data/topics without re-encoding the already-hex values - sc-coverage: precompile addresses are SizedHex<20>, used directly as Revive.call dest (drop the now-unused hexToBytes) Verified locally on an omni-node dev chain + provider: sc-flow, sc-coverage, sc-team-drive, sc-token-gated and the L0 demo all pass end to end.
# Conflicts: # examples/papi/api.js # examples/papi/common.js # examples/papi/e2e/01-provider-registration.ts # examples/papi/e2e/03-s3-bucket-and-objects.ts # examples/papi/package.json # examples/papi/sc-api.js # examples/papi/sc-coverage.ts
After merging dev (PR #160 migrated examples/papi fully to TS), assign-cores was the only remaining .js file in the repo: run via bare `node` and excluded from `tsc --noEmit`. Convert it to .ts so it matches the sibling papi scripts and gets typechecked. - type the two implicit-any params (argv, seed) - replace the push loop with Array.from to avoid an evolving any[] under strict - run via `node --import tsx` in the justfile recipe Keeps its self-contained makeSigner: it targets the relay chain via the untyped getUnsafeApi(), so it deliberately avoids the parachain SDK/descriptors. tsc --noEmit passes under strict.
PR #131 now targets #268 (relay-block denomination) instead of dev. Conflict resolutions: - runtimes/*/storage.rs: took the relay-block-provider side wholesale. This PR's DefaultCheckpointInterval/Grace rework (10*MINUTES/2*MINUTES of parachain time) is obsolete — the constants are relay-denominated now (100/20 relay blocks = the same ~10 min / ~2 min wall-clock) and no longer rescale with parachain block time, which was the whole point of that rework. - runtimes/*/lib.rs spec_version: both sides bumped independently from the merge base, so take the union: local runtime 2 vs 3 -> 4, paseo 4_002 vs 4_003 -> 4_004, with lineage comments. - Everything else (2s consensus constants, velocity 3, RelayParentOffset, zombienet topology, assign-cores) merged clean.
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
Switches both runtimes from 6-second to 2-second blocks with 3 relay cores per parachain, matching what polkadot-bulletin-chain PRs #417 (slot-based authoring) and #231 (2 s + 3 cores) did for Bulletin.
Stacked on #268 (relay-chain block denomination, #233). That ordering makes this PR a pure throughput/consensus change: every storage-pallet timeout, expiry and checkpoint window is denominated in relay blocks and reads its clock from
RelaychainDataProvider, so nothing here touches storage timing at all — the constants keep their wall-clock meaning at any parachain block time.Closes #113.
Runtime changes (
runtimes/web3-storage-local/,runtimes/web3-storage-paseo/)MILLISECS_PER_BLOCK: 6000 → 2000 (SLOT_DURATIONfollows)BLOCK_PROCESSING_VELOCITY: 1 → 3 (three parachain blocks per relay slot)RELAY_PARENT_OFFSET = 1, wired intocumulus_pallet_parachain_system::Config::RelayParentOffsetand theRelayParentOffsetApiruntime impl — required by slot-based authoringUNINCLUDED_SEGMENT_CAPACITYderived from those two:(3 + RELAY_PARENT_OFFSET) * BLOCK_PROCESSING_VELOCITY = 12RELAY_CHAIN_SLOT_DURATION_MILLISstays 6000 (the relay sets its own slot time; it is also what therelay_timeconstants module from Measure pallet timeouts in relay chain blocks, not parachain blocks #268 anchors to, which is why storage timing is untouched)spec_version: local runtime → 4, paseo → 4_004 (both sides of the Measure pallet timeouts in relay chain blocks, not parachain blocks #268 merge had bumped independently, so the merge takes the union)scripts/runtimes-matrix.json: try-runtime--blocktime6000 → 2000pallet_aura::AllowMultipleBlocksPerSlot = true,FixedVelocityConsensusHook, and theRelayParentOffsetApishell were already in place — they just needed the new constants threaded through.Zombienet topology (
zombienet.toml,zombienet/storage-paseo-local.toml)3-core scheduling needs more validators and explicit core capacity: 6 relay validators (alice…ferdie), relay genesis patched with
scheduler_params.num_cores = 3andasync_backing_params { allowed_ancestry_len = 6, max_candidate_depth = 6 },num_cores = 3on the parachain block, and two collators (alice, bob) running--authoring=slot-based.examples/papi/assign-cores.ts+just assign-coresnum_cores = 3in genesis only sets the relay-side budget; cores still have to be assigned to the para via the relay'sCoretime::assign_coreextrinsic before the parachain can actually use them — without it block production stays at ~12 s/block. The script sudo-batches oneassign_coreper core (same pattern as Bulletin'sexamples/assign_cores.js). Afterjust start-chain, runjust assign-coresonce and the parachain settles at ~2 s blocks.Plus a one-line drive-by in
provider-discovery.ts: decodemultiaddrviaTextDecoder(papi 2.x returns raw bytes here, not aBinary).No longer in this PR (superseded since the original version)
DefaultCheckpointInterval/Gracere-expression as10 * MINUTES/2 * MINUTES— dropped in the Measure pallet timeouts in relay chain blocks, not parachain blocks #268 merge. Those constants are relay-denominated now (100/20 relay blocks ≈ the same 10 min / 2 min) and no longer rescale with parachain block time, which was the entire reason for that rework.examples/papimigration to polkadot-api 2.x — landed ondevindependently and was absorbed via earlier merges.Verified
The original 6s→2s bring-up was verified live against zombienet with
just assign-coresapplied:AuraApi::slot_duration() = 2000 ms,RelayParentOffsetApi::relay_parent_offset() = 1, para 4000 occupying relay cores 0/1/2, ~2–2.9 s/block sustained, andjust demo/fs-demo-ci/s3-demo-cipassing end-to-end. Post-retarget onto #268: both runtimes compile, all paseo runtime tests (incl. XCM e2e andintegrity_test) pass under the 2s constants; the live 2s zombienet run has not yet been repeated on the stacked branch.