Skip to content

Polkadot-Kusama Bridge support#1029

Merged
xlc merged 11 commits into
AcalaNetwork:masterfrom
karolk91:kk-bridge-support
Jun 26, 2026
Merged

Polkadot-Kusama Bridge support#1029
xlc merged 11 commits into
AcalaNetwork:masterfrom
karolk91:kk-bridge-support

Conversation

@karolk91

@karolk91 karolk91 commented May 15, 2026

Copy link
Copy Markdown
Contributor

Adds bridge-message relaying between two forked bridge hubs, so XCM-over-bridge scenarios (e.g. Polkadot↔Kusama) can be tested end-to-end in chopsticks.

Usage

Library:

const handle = await connectBridgeHubs(sourceApi, destApi, { signer })
// drive source blocks; new messages are delivered to dest automatically
await handle.disconnect()

CLI:

chopsticks bridge -r polkadot -p polkadot-bridge-hub -R kusama -P kusama-bridge-hub

Both spin up two independent chopsticks instances and wire delivery in both directions (one relayer account, funded on both hubs).

How it works

connectBridgeHubs never builds blocks — it reads state, builds storage proofs, writes the destination's pallet_bridge_parachains state, and pushes extrinsics to pools, then reacts to the heads produced by whatever builds blocks (Instant/Batch auto-build, or a host driving dev_newBlock under Manual). So it behaves identically across build modes.

source bridge hub                              destination bridge hub
  │ on source head:                              │
  │   track lane latest_generated_nonce          │
  │   when dest ready (live last_delivered):     │
  │     dev_getReadProof(OutboundLanes+Messages) ┼─► dev_setStorage: ParasInfo /
  │       → { proof, stateRoot }                 │     ImportedParaHeads(stateRoot) / Hashes
  │                                              │   receive_messages_proof(...) → dest POOL
  │                                              │   (auto-build / host applies → InboundLanes advances;
  │                                              │    a dest head clears the in-flight marker, pushes next)
  │   dev_setStorage: ParasInfo / ◄──────────────┼─ on dest head:
  │     ImportedParaHeads(stateRoot) / Hashes    │     dev_getReadProof(InboundLanes) → { proof, stateRoot }
  │   receive_messages_delivery_proof(...) → source POOL
  │   (a source block applies it → OutboundLanes.latest_received advances, dest prunes)

Two reactive loops:

  • deliver (reacts to source and destination heads): push receive_messages_proof for [last_delivered+1 .. latest_generated] (chunked to ≤4096) to the destination pool, with ParasInfo/ImportedParaHeads/ImportedParaHashes written from the proof's recomputed root. Starting at the live last_delivered+1 gives the pallet's is_obsolete check its contiguity, so nothing is re-delivered; an in-flight chunk blocks the next push until a destination head shows it landed.
  • confirm (reacts to destination heads): prove the destination's InboundLanes, derive UnrewardedRelayersState as the pallet's From<&InboundLaneData> does, and push receive_messages_delivery_proof to the source pool, advancing latest_received_nonce so the destination prunes its relayer queue.

ImportedParaHeads is written directly (finality isn't forged), so only the messages-pallet calls run through pallet logic. Pallet names and para ids are auto-detected from runtime metadata (override via config for runtimes with multiple instances). signer must hold a balance on both hubs; one signer covers both directions since every submission goes through the pool.

New RPC methods

  • state_getReadProof — spec-shaped { at, proof }. Composed from an upstream base plus local overrides via the executor's createProof (chopsticks has no trie of its own).
  • dev_getReadProof{ at, proof, stateRoot }: the same proof plus its recomputed root, which diverges from chain_getHeader(at).state_root once overrides are applied. Verifiers check against this root; kept separate so state_getReadProof stays spec-compliant.

References

Testing

  • packages/e2e/src/state.test.tsstate_getReadProof: proof reflects local overrides, rejects child-storage keys, rejects empty key list.
  • packages/e2e/src/bridge.test.ts — against live Polkadot/Kusama bridge-hub forks: continuous delivery, back-to-back source blocks, a multi-nonce range in one block, delivery under each build mode (Manual / Instant / Batch), idempotent confirmation relay across rounds, and state-root-binding proofs. All assert the destination's inbound last_delivered_nonce.
  • packages/e2e/src/bridge-detect.test.ts — pallet/para-id auto-detection guards, including ambiguity on every side and override-bypass (no live forks).
  • packages/chopsticks/src/bridge.test.ts — unit tests for the UnrewardedRelayersState derivation across multi-entry / multi-message relayer queues (the count>1 cases the single live confirmation can't reach), checked against the pallet's From<&InboundLaneData>

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Found an issue where source.query queries the latest state instead of the state at the specific block hash, which can lead to proof generation failures if the chain has advanced.

Comment thread packages/chopsticks/src/bridge.ts Outdated
@karolk91 karolk91 changed the title [WIP] Polkadot-Kusama Bridge support Polkadot-Kusama Bridge support Jun 25, 2026
@karolk91
karolk91 marked this pull request as ready for review June 25, 2026 11:08

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR successfully implements bridge-message relaying between two forked bridge hubs. However, there is a potential runtime error when interacting with runtimes that do not have the importedParaHashes storage item in their bridge parachains pallet.

Comment thread packages/chopsticks/src/bridge.ts
@xlc
xlc merged commit b9e7725 into AcalaNetwork:master Jun 26, 2026
4 of 5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add support for P<>K bridge

2 participants