Skip to content

Write genesis data into legacy fields instead of extraConfig, fixing silently empty testnets on older nodes - #1420

Draft
carbolymer wants to merge 1 commit into
masterfrom
mgalazyn/genesis-extraconfig-compat
Draft

Write genesis data into legacy fields instead of extraConfig, fixing silently empty testnets on older nodes#1420
carbolymer wants to merge 1 commit into
masterfrom
mgalazyn/genesis-extraconfig-compat

Conversation

@carbolymer

@carbolymer carbolymer commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Reverts the writer side of #1384. genesis create, genesis create-staked and genesis create-testnet-data write initial funds, stake pools, stake credentials, delegations and initial DReps into the top-level initialFunds/staking (Shelley) and delegs/initialDReps (Conway) fields again, not into extraConfig.

Nodes built with cardano-ledger-shelley < 1.19 do not know extraConfig and silently ignore it, so a testnet created with cardano-cli 11.2.0.0 starts with an empty UTxO set and no genesis staking, without any error. The switch to extraConfig also brought no benefit: cardano-cli only ever emitted inline (EmbeddedInjection) data, which the ledger treats identically to the legacy fields. The streaming benefit only exists for file-based injection, which nothing uses yet.

The writers now set sgExtraConfig/cgExtraConfig to SNothing explicitly: cardano-api's genesis template defaults populate them with empty injections, which would otherwise leak an empty extraConfig block into the output. This also means a template-supplied extraConfig is discarded rather than passed through - it would conflict with the generated legacy fields at node startup.

Context

The bug was found when a nightly testnet run against cli master came up "healthy" but with initialFunds: {} and all funds under extraConfig.initialFunds.data, which the node silently dropped. The first visible symptom is a funding transaction failing much later with no TxIns available - there is no error anywhere in between.

The compatibility boundary: cardano-ledger-shelley 1.19.0.0 (which introduces extraConfig) reached CHaP on 2026-07-29 as part of the node 11.1 dependency batch, so nodes <= 11.0.x are affected, while nodes on ledger >= 1.19 read either shape. Older ledgers parse the genesis with plain aeson withObject, which never rejects unknown keys, hence the silence.

The revert is lossless for every ledger that exists today: ledger's resolveInjectionSource converts legacy fields into exactly the EmbeddedInjection value the cli was constructing by hand. The legacy fields carry no deprecation pragma and initialFunds remains a mandatory key in ledger's FromJSON. The ledger's dual-read path is marked as transitional (TODO: remove this once we move over to the extraConfig fields exclusively), so extraConfig output should be reintroduced behind an explicit option once file-based streaming injection is actually used and old nodes are no longer a concern. Ideally, when the ledger drops the legacy migration, populated legacy fields should become a startup error rather than being ignored, so this failure mode stays loud.

How to trust this PR

The source changes are a byte-for-byte reversal of a91953c's hunks (verified against git show a91953cd4), plus the four explicit SNothing field updates and the removal of now-unused imports and build-depends.

All 689 cardano-cli-golden and 72 cardano-cli-test tests pass via the nix checks, and the build is clean under -Werror.

To observe the fixed behaviour:

cardano-cli conway genesis create-testnet-data \
  --testnet-magic 42 --utxo-keys 2 --pools 1 --stake-delegators 1 \
  --total-supply 10000000000000 --out-dir /tmp/testnet-out
jq '{initialFunds: (.initialFunds | length), pools: (.staking.pools | length), stake: (.staking.stake | length), hasExtraConfig: has("extraConfig")}' /tmp/testnet-out/shelley-genesis.json

Expected: initialFunds: 3, pools: 1, stake: 1, hasExtraConfig: false (on 11.2.0.0 this yields initialFunds: 0 and hasExtraConfig: true). The conway genesis likewise no longer contains an extraConfig key.

A sweep of cardano-node, cardano-api and cardano-testnet found no consumer that hand-parses extraConfig from genesis JSON; all consumption goes through ledger types, which accept both shapes.

Checklist

  • Commit sequence broadly makes sense and commits have useful messages
  • New tests are added if needed and existing tests are updated. See Running tests for more details
  • Self-reviewed the diff
  • Changelog fragment added in .changes/

@carbolymer carbolymer self-assigned this Aug 17, 2026
@carbolymer carbolymer changed the title Write genesis data into legacy fields again instead of extraConfig Write genesis data into legacy fields instead of extraConfig, fixing silently empty testnets on older nodes Aug 17, 2026
Reverts the writer side of #1384. `genesis create`,
`genesis create-staked` and `genesis create-testnet-data` write initial
funds, stake pools, stake credentials, delegations and initial DReps
into the top-level `initialFunds`/`staking` (Shelley) and
`delegs`/`initialDReps` (Conway) fields again, not into `extraConfig`.

Nodes built with cardano-ledger-shelley < 1.19 do not know
`extraConfig` and silently ignore it, so a testnet created with
cardano-cli 11.2.0.0 starts with an empty UTxO set and no genesis
staking, without any error. The switch to `extraConfig` also brought no
benefit: cardano-cli only ever emitted inline (`EmbeddedInjection`)
data, which the ledger treats identically to the legacy fields. The
streaming benefit only exists for file-based injection, which nothing
uses yet.

The writers now set `sgExtraConfig`/`cgExtraConfig` to `SNothing`
explicitly: cardano-api's genesis template defaults populate them with
empty injections, which would otherwise leak an empty `extraConfig`
block into the output. This also means a template-supplied
`extraConfig` is discarded rather than passed through - it would
conflict with the generated legacy fields at node startup.
@carbolymer
carbolymer force-pushed the mgalazyn/genesis-extraconfig-compat branch from a9a45bd to be94c1c Compare August 17, 2026 14:15
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.

1 participant