From ff277f4b44e5995d37d1cb8a89e2e86e5f0649fc Mon Sep 17 00:00:00 2001 From: Hanzo AI Date: Mon, 1 Jun 2026 01:43:00 -0700 Subject: [PATCH] fix(config): invalidate cached genesis on parse failure instead of CrashLooping MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The cached `/genesis.bytes` file is written on first start to hold hash stability across restarts. On a binary upgrade that adds new codec types (multi-version v0+v1 dispatcher, etc.), the old cached blob may carry type IDs the new binary doesn't recognise. The existing code surfaced this as `resolve X-Chain asset ID from cached genesis: unmarshal interface: unknown type ID N` and returned an error — wedging the node in CrashLoop with no automatic recovery. Drop the cache and rebuild from the `--genesis-file` instead. Hash stability is forfeit for that single restart (intentional — the alternative is a permanent outage on every binary bump that changes codec types). Subsequent restarts re-establish stability against the fresh cache. Surfaced today on Liquidity testnet+mainnet bumping lqd v1.9.x → v1.10.8: every pod hit "unknown type ID 29" on the stale v1.9.x codec cache and CrashLoopBackOff'd. --- config/config.go | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/config/config.go b/config/config.go index 4af7d4895d..415278c79e 100644 --- a/config/config.go +++ b/config/config.go @@ -1092,15 +1092,28 @@ func getGenesisData(v *viper.Viper, networkID uint32, stakingCfg *builder.Stakin cacheFile := filepath.Join(dataDir, "genesis.bytes") if cachedBytes, err := loadCachedGenesisBytes(cacheFile); err == nil && len(cachedBytes) > 0 { utxoAssetID, err := resolveXAssetID(networkID, cachedBytes) - if err != nil { - return nil, ids.Empty, fmt.Errorf("resolve X-Chain asset ID from cached genesis: %w", err) + if err == nil { + log.Info("loaded cached genesis bytes for hash stability", + "cacheFile", cacheFile, + "size", len(cachedBytes), + "utxoAssetID", utxoAssetID, + ) + return cachedBytes, utxoAssetID, nil } - log.Info("loaded cached genesis bytes for hash stability", + // Cache parse failed — almost certainly a codec mismatch + // after a binary upgrade (e.g. v1-codec bytes persisted by + // an older luxd, multi-version v0+v1 dispatcher in this + // luxd doesn't recognise a type the cached blob still + // uses). Drop the cache and rebuild from the file rather + // than wedging in a CrashLoop. Hash stability is forfeit + // for this single restart — intentional, the alternative + // is a permanent outage on every binary bump. + log.Warn("cached genesis bytes failed to parse — invalidating cache and rebuilding from genesis-file", "cacheFile", cacheFile, "size", len(cachedBytes), - "utxoAssetID", utxoAssetID, + "error", err, ) - return cachedBytes, utxoAssetID, nil + _ = os.Remove(cacheFile) } // No cache or invalid cache - build from file and cache the result genesisBytes, utxoAssetID, err := builder.FromFile(networkID, genesisFileName, stakingCfg)