feat(chain): add freeze_height to the unified schema - #49
Conversation
seid reads freeze-height from app.toml to stop executing at a height while still serving query RPC. The key was absent from the unified schema, so ApplyOverrides rejected it as an unknown key and no caller could set it declaratively — the controller forwards spec.overrides verbatim and the sidecar resolves them through this package. Modelled on halt_height: unified key chain.freeze_height, legacy app.toml key freeze-height, both legacy mappings, and field metadata. No default entry, because zero already means disabled, as with halt_height. Validation mirrors seid's ValidateFreeze so an invalid config is reported here instead of failing at node start: freeze_height must fit in an int64, and it cannot be combined with halt_height or halt_time. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
PR SummaryLow Risk Overview The field is wired through Reviewed by Cursor Bugbot for commit ea127f7. Bugbot is set up for automated code reviews on this repo. Configure here. |
Carries #49, which threads `freeze_height` through both schema layers, both legacy mapping directions, field metadata, and validation. The key is unreachable until it ships in a tagged version: sei-k8s-controller pins v0.0.22, which predates it, so a SeiNode setting freeze_height today has ApplyOverrides reject it as an unknown key. The key matters because seid reads `freeze-height` from app.toml to stop executing at a height while still serving query RPC, and the config file is the only route in. sei-k8s-controller builds a fixed `seid start --home <dataDir>` argv with no flag escape hatch, and carries no CRD field for freeze. So without this key there is no declarative way to stand up a frozen node at all. Unlike v0.0.26, the consuming seid build already exists. Freeze mode is on release/v6.6: the flag is registered in the vendored sei-cosmos (server/start.go), sei-tendermint refuses to freeze a validator, and sei-chain#4006 disables mempool traffic in freeze mode. This release closes the half of the path that was missing, rather than staging a knob ahead of its build. Consumed next by sei-sidecar, which resolves and validates overrides during config-apply and is therefore what makes this real on a running node, and then by sei-k8s-controller to keep its pin in sync. The controller needs no code or CRD change: it uses this library for types and constants, merges spec.overrides, and forwards the map verbatim. Releasing the key does not by itself change any node's behaviour. freeze_height defaults to 0, which means disabled, and validation rejects combining it with halt_height or halt_time. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
What this does
Adds
chain.freeze_heightto the unified schema, so a caller can set seid'sfreeze-heightdeclaratively.seidreadsfreeze-heightfromapp.tomlto stop executing at a height whilestill serving query RPC. The key was missing from this schema, so
ApplyOverridesrejected it as an unknown key (io.go) and no caller could setit. That closes the only declarative route:
sei-k8s-controllerbuilds a fixedseid start --home <dataDir>argv with no flag escape hatch, so the config fileis the only way in.
Why it is shaped like
halt_heighthalt_heightis the direct precedent — it sits besidefreeze-heightin thesame sei-cosmos
BaseConfig. This change mirrors it exactly:config.goFreezeHeight uint64onChainConfig, unified keychain.freeze_heightlegacy.goapp.tomlkeyfreeze-heightonlegacyAppConfiglegacy.gotoLegacyApp()andfromLegacy()enrichments.goblocksunitvalidate.gofreeze_test.goTwo judgment calls
No
baseDefaults()entry.HaltHeightandHaltTimehave none either —zero already means disabled, and the zero value delivers that. The repo guide
says to add a default for a new field; the established pattern for this exact
kind of field says otherwise, so I followed the pattern. Say the word if you
want it stated explicitly instead.
Tests in a new file.
config_test.gois at 884 lines, past the ~500-linethreshold in the repo guide, so the new coverage went to
freeze_test.go.Validation
validate.gonow mirrors seid's ownValidateFreeze, so an invalid combinationis reported here instead of failing at node start:
freeze_heightmust fit in anint64. seid converts it and rejects anover-range value.
freeze_heightcannot be combined withhalt_heightorhalt_time. Freezekeeps serving queries; halt exits the process.
I did not port seid's third rule —
freeze-heightcannot be used withgrpc-only. That one reads a CLI flag, not config, so it has no representationhere.
Testing
make testgo vet ./...staticcheck ./...gofmt -l .golangci-lint runorigin/main, identical breakdown — none introducedI checked out
origin/mainto take that lint baseline rather than assume it.Note the repo does not pass golangci-lint 2.12.2 even on
main, so CI ispinning an older version.
New coverage:
TestFreezeHeight_RoundTrip— asserts the renderedapp.tomlcarries thefreeze-heightspelling seid actually reads, then reads it back.TestFreezeHeight_Override— thechain.freeze_heightoverride path thecontroller and seictl use.
TestFreezeHeight_Validate— 6 cases: unset, freeze alone, halt alone, freezewith halt height, freeze with halt time, and int64 overflow.
Follow-ups, not in this PR
version.json, per the convention here.sei-sidecarbumps this library. That is the load-bearing consumer — it runsthe override resolution and validation.
sei-k8s-controllerbumps it for hygiene only. It never validates or writesconfig; it uses this library for types and constants, merges
spec.overrides,and forwards the map verbatim. No CRD change is needed, because
spec.overridesis already a free-formmap[string]string.🤖 Generated with Claude Code