Skip to content

Support forking a custom relay chain - #137

Merged
pepoviola merged 14 commits into
mainfrom
feat/custom-relay
Sep 3, 2026
Merged

Support forking a custom relay chain#137
pepoviola merged 14 commits into
mainfrom
feat/custom-relay

Conversation

@mordamax

@mordamax mordamax commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Stacked on #129, and includes #135's change (a custom relay has to be able to supply its endpoint, so the two overlap).

-r custom%<name>%<rpc_endpoint>%<chain_spec_path> forks a relay zombie-bite has no built-in knowledge of. The name is what artifacts are named after, the endpoint is what state and metadata are read from, and the spec is what the node is started with (--chain <spec> instead of a network name). There is no built-in host config for such a relay, so Configuration::ActiveConfig has to come from the endpoint — the bite fails with that reason if it can't.

A relay name outside polkadot/kusama/paseo/westend is now a custom relay instead of silently falling back to polkadot. That is what lets the helper subcommands (generate-artifacts, clean-up-dir), which only get the name back as a string, keep naming a custom relay's artifacts correctly — and a typo'd name now fails instead of biting the wrong chain.

The node stores its db under the chain-spec's own id, which need not match the artifact name, so the snapshot path is resolved from the spec.

Closes #133
Closes #134

Also fixes ParaScheduler::ValidatorGroups shape: one group per core with validators round-robined, instead of one group per validator. The old shape (always cores+1 groups) wedges the scheduler/approval subsystems on a live fork and finality stays pinned at the bite block - found by forking public paseo and comparing against preview-net's working bites. Requesting more cores than the dev validators can staff is now an error

@mordamax
mordamax marked this pull request as ready for review August 26, 2026 20:02
@mordamax
mordamax requested a review from pepoviola August 26, 2026 20:02
Base automatically changed from feat/metadata-overrides to main August 28, 2026 20:43
Comment thread src/cli.rs
Comment thread src/cli.rs
Comment thread src/doppelganger.rs
@mordamax
mordamax requested a review from pepoviola August 30, 2026 17:17
@mordamax

Copy link
Copy Markdown
Contributor Author

@pepoviola heads up, three more commits landed here after your review, all from actually forking public paseo with this branch (custom relay + PPN's next paras)

  • ValidatorGroups now one group per core with validators round-robined, was one group per validator which leaves the group count != num_cores
  • CoreDescriptors is encoded against the runtime's own type via metadata instead of the hand-rolled hex from zombienet-sdk
  • relay validators spawn with the doppelganger binary again. Stock polkadot's dispute coordinator scans ancestor headers a warp-synced bite doesn't have, never initializes, and grandpa stays capped at the bite block forever while blocks keep coming - the exact silent failure from Verify a spawned fork is actually a fork: finality, divergence, and usable bootNodes #119. The doppelganger binary honours ZOMBIE_DISPUTE_CANDIDATE_LIFETIME_AFTER_FINALIZATION which we already set, and with it the same bitten state goes from finality frozen for hours to "finality is advancing" in ~90s

worth a second look at those three before this merges

@mordamax

mordamax commented Aug 31, 2026

Copy link
Copy Markdown
Contributor Author

@pepoviola I ran this branch against real networks end to end

Kusama relay + Asset Hub forked in ~20 min:

  • finality advancing on both chains
  • both diverged from source
  • the unreleased fellowship v2004000 runtime enacted from the pre-baked authorization in 10s (2003002 -> 2004000, unsigned apply)
  • same pipeline earlier on paseo with two custom paras

four fixes came out of the runs, all in this PR:

  • per-core ValidatorGroups
  • CoreDescriptors encoded via metadata instead of hand-rolled hex
  • relay validators back on the doppelganger binary - stock polkadot's dispute coordinator never initializes on a warp-synced db and pins finality at the bite block
  • ZOMBIE_WARP_SKIP_PROOF set by default, without it an AH-kusama bite freezes at 37% exactly as doppelganger-wrapper#33 describes

@mordamax mordamax self-assigned this Sep 1, 2026
Comment thread src/doppelganger.rs Outdated
Comment on lines +340 to +349
cmd: context_relay.cmd(),
// The relay validators must run the doppelganger binary: it honours
// ZOMBIE_DISPUTE_CANDIDATE_LIFETIME_AFTER_FINALIZATION, without which
// the stock dispute coordinator scans ancestor headers a warp-synced
// bite does not have, never initializes, and caps finality at the bite
// block forever while blocks keep being produced.
cmd: context_relay.doppelganger_cmd(),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This is incorrect, the final config should use the polkakdot binary with ZOMBIE_DISPUTE_CANDIDATE_LIFETIME_AFTER_FINALIZATION set to 1 in env

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

reverted to the plain binary - my local polkadot was 1.22.0, one release before sdk#12247, hence the confusion. README now asks for v1.22.1+

Comment thread src/doppelganger.rs Outdated
if para.chain.contains("asset-hub") {
// Elastic scaling (more than one core) requires slot-based
// authoring, whatever the parachain is called.
if para.chain.contains("asset-hub") || para.cores > 1 {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

if the comment is true we should remove the para.chain.contains("asset-hub") part.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

removed, cores > 1 covers asset-hub anyway

Comment thread src/overrides.rs
// first session rotation re-elects the production validators - whose
// Session::NextKeys the bite has just replaced - and authoring halts an
// epoch in. Forcing::ForceNone keeps the dev set elected.
set.inject("Staking", "ForceEra", "02");

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

nice comment :) , my artisan hardcoded injection was not mention the cause :)

@pepoviola pepoviola left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

a few inline comments and then is good to merge 🚀

@pepoviola
pepoviola merged commit 6a00c50 into main Sep 3, 2026
4 checks passed
@pepoviola
pepoviola deleted the feat/custom-relay branch September 3, 2026 08:11
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.

'--rc-sync-url' is stored but never used Support forking a relay chain that is not one of the four known networks

2 participants