Skip to content

feat(api): plumb spec.genesis.overrides to sidecar assemble-genesis task#270

Merged
bdchatham merged 2 commits into
mainfrom
feat/genesis-overrides
May 18, 2026
Merged

feat(api): plumb spec.genesis.overrides to sidecar assemble-genesis task#270
bdchatham merged 2 commits into
mainfrom
feat/genesis-overrides

Conversation

@bdchatham
Copy link
Copy Markdown
Collaborator

@bdchatham bdchatham commented May 18, 2026

Summary

Wires user-supplied genesis parameter overrides from SeiNodeDeployment.spec.genesis.overrides into the sidecar's assemble-and-upload-genesis task payload, so a fresh test cluster can boot with custom cosmos params (e.g. staking.params.unbonding_time = 600s) instead of the cosmos defaults.

Motivation

SIP-3 testing requires a chain configured for fast-iteration unbonding (10 minutes vs 21 days). The CRD field spec.genesis.overrides existed today but was dead-end:

  • Its value type was map[string]string, which can't carry numbers, durations-as-strings, or nested cosmos param objects without forcing operators to handcraft escaped JSON.
  • The controller copied it into a per-node GenesisCeremonyNodeConfig.GenesisParams JSON string that no consumer reads — the sidecar's generate-gentx handler ignores the field, and the assemble-genesis task wasn't given the overrides at all.

The companion seictl PR (see "Linked work" below) extends the sidecar's AssembleGenesisRequest with Overrides map[string]json.RawMessage; this PR sends the matching field from the controller side.

API changes

  • SeiNodeDeployment.spec.genesis.overrides value type reshaped from map[string]string to map[string]apiextensionsv1.JSON. Keys remain dotted snake_case paths (cosmos-encoded), values are now arbitrary JSON.
  • spec.genesis is now CRD-immutable after creation (CEL self == oldSelf). Genesis-ceremony semantics make in-place edits meaningless; better to fail fast at the API server.
  • GenesisCeremonyNodeConfig.GenesisParams (per-node JSON string) dropped along with the controller-side marshalOverrides helper and the planner's GenesisParams: gc.GenesisParams propagation — all three were the dead path.

Compatibility note

This is a CRD field-type reshape (map value string -> JSON), normally a breaking change. Safe in this case because no live consumer reads the field today — the sidecar drops it, the controller stamped it onto a per-node field nothing reads. Behavior of existing SeiNodeDeployment objects that omit overrides is unchanged.

Wire path

spec.genesis.overrides (CRD)
    -> planner ForGroup() (internal/planner/group.go)
        -> task.AssembleAndUploadGenesisTask{Overrides: ...} (internal/task/assemble_genesis.go)
            -> TaskRequest.Params["overrides"] = {...}
                -> seictl sidecar consumes from AssembleGenesisRequest.Overrides

task.AssembleAndUploadGenesisTask is a thin wrapper that embeds the upstream sidecar.AssembleAndUploadGenesisTask and adds Overrides. Mirrors the existing configApplyTask pattern. Folds back into the upstream type once seictl releases — there's no schema obligation to keep the wrapper after that.

Test plan

  • make test passes (added planner-level overrides-propagation + omits-when-unset assertions in internal/planner/group_test.go)
  • make manifests generate regenerates manifests/crds/sei.io_seinodedeployments.yaml with the new CEL immutability rule and x-kubernetes-preserve-unknown-fields: true on the overrides map values
  • Existing assemble-genesis tests still green (TestBuildGroupAssemblyPlan, TestBuildGroupAssemblyPlan_DefaultS3, TestBuildPlan_PropagatesAccounts, etc.)
  • e2e smoke against the SIP-3 test cluster once the companion seictl release ships

Linked work

Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com

Companion seictl PR: sei-protocol/seictl#181

Reshape SeiNodeDeployment.spec.genesis.overrides to map[string]JSON so it
can carry typed values (strings, numbers, objects) for the sidecar's
genesis-defaults merge, and route it through the assemble-and-upload-genesis
task at SND level. Required for SIP-3 testing, where the test cluster needs
non-default cosmos params (e.g. staking.params.unbonding_time = 600s).

API changes:
- GenesisCeremonyConfig.Overrides: map[string]string -> map[string]apiextensionsv1.JSON
- spec.genesis is now immutable after creation (CEL: self == oldSelf)
- Drop dead GenesisCeremonyNodeConfig.GenesisParams (per-node JSON string
  that nothing consumed)

Plumbing:
- Planner ForGroup now copies group.Spec.Genesis.Overrides into the
  assemble-genesis task params; on the wire those flatten into the
  TaskRequest under the "overrides" key the sidecar reads.
- Introduce task.AssembleAndUploadGenesisTask, a thin wrapper that embeds
  the upstream sidecar struct and adds the Overrides field. Mirrors the
  existing configApplyTask pattern. Folds back into the upstream type once
  the companion seictl PR releases.

The value-type reshape is a CRD field rewrite; safe because no controller
or sidecar today actually consumes the existing map[string]string (sidecar
drops it on the floor, controller stamped it into a per-node JSON string
that nothing read).

Companion seictl PR: TBD (parallel)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@cursor
Copy link
Copy Markdown

cursor Bot commented May 18, 2026

You have used all Bugbot PR reviews included in your free trial for your GitHub account on this workspace.

To continue using Bugbot reviews, enable Bugbot for your team in the Cursor dashboard.

bdchatham added a commit to sei-protocol/seictl that referenced this pull request May 18, 2026
…bled genesis.json (#181)

## Summary

SIP-3 testing needs a fresh test cluster whose
`staking.params.unbonding_time` is `600s`, not the cosmos default of 21
days. The controller already accepts
`SeiNodeDeployment.spec.genesis.overrides` and propagates them through,
but the sidecar was dropping them on the floor at the assemble-genesis
step. This PR closes that gap.

## Wire contract

`AssembleGenesisRequest` gains:

```go
Overrides map[string]json.RawMessage `json:"overrides,omitempty"`
```

Keys are dotted snake_case paths into `genesis.app_state`. The first
segment is the cosmos module name (a top-level key in `app_state`);
subsequent segments walk into that module's JSON tree and replace the
leaf verbatim. Values are raw JSON, so callers can override scalars,
numbers, or whole objects:

| key | value |
| --- | --- |
| `staking.params.unbonding_time` | `"600s"` |
| `gov.params.max_deposit_period` | `"60s"` |
| `staking.params.max_validators` | `50` |
| `gov.params.voting_params` | `{"voting_period":"60s","quorum":"0.4"}`
|

## Validator-side flow change

`GenesisAssembler.Handler()` order is now:

```
downloadGentxFiles
  -> addMissingGenesisAccounts
  -> addExternalGenesisAccounts
  -> collectGentxs
  -> applyOverrides   <-- new
  -> uploadGenesis
  -> uploadPeers
```

Overrides are applied **after** collect-gentxs so any validator-derived
state and persistent peers baked in by `genutil.GenAppStateFromConfig`
are already in place — the override step is an in-place JSON patch on
the final assembled `genesis.json`.

## Design anchors (from the coral round)

- Single SND-level dispatch; no per-node fan-out for overrides.
- Bootstrap-only — the sidecar doesn't enforce immutability; the
controller does that via CEL.
- Fails loudly on bad keys (empty, single-token, trailing/double dot,
empty value, unknown module, non-object intermediates) so
misconfiguration surfaces as a `FailedTaskDetail` on the owning
`SeiNodeDeployment` rather than as a silently-ignored field.
- No cosmos-sdk module allowlist — the chain will fail loud on garbage
at `InitGenesis`.

## Other changes

- Drops the dead `GenesisParams` field from `GenerateGentxRequest` and
its client-side mirror. The "reserved for future genesis customization"
comment is now the dedicated `Overrides` path on the SND-level assemble
task.
- New helper `applyGenesisOverrides` lives in
`sidecar/tasks/genesis_overrides.go`; `GenesisAssembler.applyOverrides`
is the disk-I/O glue that re-reads `config/genesis.json`, calls the
helper, and writes back via `genutil.ExportGenesisFile`.

## Companion change

The controller-side wiring (populating `assembleParams.Overrides` from
`SeiNodeDeployment.spec.genesis.overrides` in
`internal/planner/group.go`) is being implemented in parallel in
`sei-protocol/sei-k8s-controller`. See
sei-protocol/sei-k8s-controller#270.

## Test plan

- [x] `go test ./sidecar/tasks/... -count=1` passes
- [x] `make test` passes
- [x] `make lint` clean
- [x] Unit coverage in `genesis_overrides_test.go`: string/number/object
leaves, deep nesting, cross-module, missing intermediate, idempotency,
all error surfaces
- [x] Disk-level coverage in `assemble_genesis_test.go`:
`applyOverrides` mutates `genesis.json` and bubbles bad-key errors
- [x] Wire round-trip test for `AssembleGenesisRequest.Overrides`
- [ ] Integration: SIP-3 test cluster boots with `unbonding_time=600s`
and observes the resulting validator un-bonding behavior end-to-end
…0.50

Now that seictl v0.0.50 ships AssembleAndUploadGenesisTask with the
Overrides map[string]json.RawMessage field natively, the temporary
wrapper at internal/task/assemble_genesis.go is no longer needed.

- Bump go.mod: seictl v0.0.47 -> v0.0.50
- Delete the wrapper; register sidecar.AssembleAndUploadGenesisTask
  directly in the deserializer registry
- Planner builds the upstream task directly; new toRawMessages helper
  converts spec.genesis.overrides values (apiextensionsv1.JSON) to the
  wire type (encoding/json.RawMessage). Same raw bytes, different Go type.
- Tests now unmarshal into the upstream type. Override leaf assertions
  read sidecar's RawMessage instead of the apiextensionsv1.JSON.Raw shape.
@cursor
Copy link
Copy Markdown

cursor Bot commented May 18, 2026

You have used all Bugbot PR reviews included in your free trial for your GitHub account on this workspace.

To continue using Bugbot reviews, enable Bugbot for your team in the Cursor dashboard.

@bdchatham
Copy link
Copy Markdown
Collaborator Author

Cleanup commit pushed:

  • go.mod bumped: sei-protocol/seictl v0.0.47v0.0.50 (seictl#181 + 6 other commits since v0.0.49 — see sei-protocol/seictl@b37c69a8)
  • Deleted internal/task/assemble_genesis.go (the temporary wrapper)
  • Registered sidecar.AssembleAndUploadGenesisTask directly in the task registry
  • Added toRawMessages helper in internal/planner/group.go to convert CRD apiextensionsv1.JSON values to upstream's encoding/json.RawMessage wire type
  • Tests updated to read the upstream type

The go.sum delta is large because the seictl bump pulled in transitive deps (ProjectZKM/Ziren go-runtime, go-eth-kzg, c-kzg-4844). Not introduced by this PR's logic — go mod transitively resolved them.

Full test suite green; go vet clean. Pre-existing golangci-lint findings on nodes.go / networking.go are unchanged on main.

@bdchatham bdchatham merged commit 3cf2887 into main May 18, 2026
2 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.

1 participant