Skip to content

Support forking a custom relay chain - #137

Open
mordamax wants to merge 13 commits into
mainfrom
feat/custom-relay
Open

Support forking a custom relay chain#137
mordamax wants to merge 13 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 on lines +32 to +34
/// For a relay that is not a public network use:
/// custom%<name>%<rpc_endpoint>%<chain_spec_path>
#[arg(short = 'r', long = "rc", verbatim_doc_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.

We are dropping the help msj here with the possible values. I think we should include it.

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.

restored, lists the four networks plus the custom format

Comment thread src/cli.rs
{
let relaychain = if relay_network.starts_with("custom%") {
resolve_custom_relaychain(&relay_network, relay_runtime.clone(), relay_bite_at)?
} else if relay_runtime.is_some() || rc_sync_url.is_some() || relay_bite_at.is_some() {

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.

But here we support more strings that may not start with custom%, I think we need to ensure is a valid rc.

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.

now bails on anything that isn't a known network or custom%. Kept the permissive mapping only in Relaychain::new for the helper subcommands that only get a name back

Comment thread src/doppelganger.rs
Comment on lines +462 to +468
/// `id` of a chain-spec, which is the directory the node stores its db under.
async fn spec_chain_id(spec_path: &str) -> Option<String> {
let content = fs::read_to_string(spec_path).await.ok()?;
let spec: serde_json::Value = serde_json::from_str(&content).ok()?;
spec["id"].as_str().map(str::to_string)
}

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 looks weird, we are returning an Option and do not emitting errors when we can't read the chain-spec from the spec_path and if we can read but is not a valid json format. I think if any of those two fails we should report the error since that will make something fail down the road.
WDYT?

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.

made it a Result with separate errors for unreadable / bad json / missing id, propagated at the call site

@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

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