mirror of
https://github.com/luxfi/proto.git
synced 2026-07-27 07:04:45 +00:00
zap_codec: BE-fallback read for pre-LP-023 (v1.28.x) P-chain state
Validators upgrading from luxd v1.28.x hold P-chain state written with the pre-LP-023 BE codec version prefix. zap_codec.Unmarshal now tries LE first; on unknown version, falls back to BE before erroring. Behaviour: - LE matches → use LE codec (post-Dec-25-2025 writers) - LE unknown, BE matches → use BE codec with BE prefix, advance offset past VersionSize, decode body - Both unknown → ErrUnknownVersion as before Disambiguation is safe because registered version IDs are small (0..N for the handful of codec generations). Palindromic byte-pair values agree under both endianness, so the only ambiguous case is small N where both BE and LE happen to be registered — practically impossible with the current registry. Also: - ZAPActivationUnix: 0 → 1766708400 (Dec 25 2025 16:20 PST) — same as all other Quasar-Edition forks. Documents that LP-023 was a network activation, not a binary cutover. - LegacyEnabled: defaults true unless LUXD_ENABLE_LEGACY_CODEC=0 — keeps legacy linearcodec path reachable for historical bytes that didn't migrate through the BE-fallback shim.
This commit is contained in:
@@ -233,6 +233,24 @@ func (m *multiManager) Unmarshal(bytes []byte, dest interface{}) (uint16, error)
|
||||
}
|
||||
c, exists := m.codecs[version]
|
||||
if !exists {
|
||||
// LP-023 cutover compat: pre-Dec-25-2025 writers used BE for
|
||||
// the version prefix. If LE decode yields an unregistered
|
||||
// version, retry as BE before erroring. Registered version IDs
|
||||
// are small (0..N for a few codec generations) — BE-decoded
|
||||
// small numbers don't collide with LE-decoded small numbers
|
||||
// except at palindromic byte pairs (where the two decodings
|
||||
// agree).
|
||||
beVersion := peekVersionBE(bytes)
|
||||
if cBE, beExists := m.codecs[beVersion]; beExists {
|
||||
p.Offset = VersionSize
|
||||
if err := cBE.UnmarshalFrom(p, dest); err != nil {
|
||||
return beVersion, err
|
||||
}
|
||||
if p.Offset != len(bytes) {
|
||||
return beVersion, ErrExtraSpace
|
||||
}
|
||||
return beVersion, nil
|
||||
}
|
||||
return version, ErrUnknownVersion
|
||||
}
|
||||
if err := c.UnmarshalFrom(p, dest); err != nil {
|
||||
|
||||
@@ -186,6 +186,18 @@ func (m *Manager) Unmarshal(bytes []byte, dest interface{}) (uint16, error) {
|
||||
return 0, err
|
||||
}
|
||||
if version != m.version {
|
||||
// LP-023 cutover compat: pre-Dec-25-2025 writers used BE for
|
||||
// the version prefix. Retry as BE before erroring.
|
||||
if peekVersionBE(bytes) == m.version {
|
||||
p.Offset = VersionSize
|
||||
if err := m.inner.UnmarshalFrom(p, dest); err != nil {
|
||||
return m.version, err
|
||||
}
|
||||
if p.Offset != len(bytes) {
|
||||
return m.version, ErrExtraSpace
|
||||
}
|
||||
return m.version, nil
|
||||
}
|
||||
return version, ErrUnknownVersion
|
||||
}
|
||||
if err := m.inner.UnmarshalFrom(p, dest); err != nil {
|
||||
@@ -303,3 +315,22 @@ func readVersionLE(p *wrappers.Packer) (uint16, error) {
|
||||
}
|
||||
return uint16(lo) | uint16(hi)<<8, nil
|
||||
}
|
||||
|
||||
// peekVersionBE reads the codec version as big-endian WITHOUT consuming
|
||||
// any packer offset (caller is responsible for advancing on success).
|
||||
// Returns the BE-decoded version. Used by the dual-read fallback to
|
||||
// support reading P-chain DB state written by pre-LP-023 (v1.28.x and
|
||||
// earlier) luxd binaries which used wrappers.PackShort (big-endian)
|
||||
// for the version prefix.
|
||||
//
|
||||
// LP-023 cutover is Dec 25 2025 16:20 PST (unix 1766708400). Bytes
|
||||
// written before activation use BE; bytes after use LE. The version
|
||||
// number is small (0–N for the few registered codec versions), so we
|
||||
// disambiguate by trying LE first and falling back to BE only if the
|
||||
// LE-decoded version is not registered.
|
||||
func peekVersionBE(bytes []byte) uint16 {
|
||||
if len(bytes) < VersionSize {
|
||||
return 0
|
||||
}
|
||||
return uint16(bytes[0])<<8 | uint16(bytes[1])
|
||||
}
|
||||
|
||||
+23
-22
@@ -9,33 +9,34 @@ import (
|
||||
)
|
||||
|
||||
// ZAPActivationUnix is the unix timestamp at which P-chain wire format
|
||||
// switches from legacy linearcodec (CodecVersionV0/V1) to native ZAP. The
|
||||
// switch governs the WRITE path: post-activation blocks MUST be ZAP-encoded.
|
||||
// Validator coordination is documented in LP-023.
|
||||
// switches from legacy linearcodec (CodecVersionV0/V1) to native ZAP.
|
||||
// The switch governs the WRITE path: post-activation blocks MUST be
|
||||
// ZAP-encoded. Validator coordination is documented in LP-023.
|
||||
//
|
||||
// Value: 0 — ZAP is mandatory from genesis. The legacy codec is only
|
||||
// reachable via the LUXD_ENABLE_LEGACY_CODEC=1 dev knob for reading
|
||||
// pre-2026-06-02 historical P-chain bytes. New deployments, fresh syncs,
|
||||
// and all production binaries from v1.28.19 onward are ZAP-only by
|
||||
// construction. The previous forward-dated 1782604800 (2026-07-01) value
|
||||
// is dead — see LP-023 ZAP-native activation cutover (2026-06-02).
|
||||
const ZAPActivationUnix uint64 = 0
|
||||
// Value: 1766708400 = 2025-12-25T16:20:00 PST. Aligned with the network
|
||||
// activation timestamp used by all other Quasar-Edition forks (Pulsar,
|
||||
// Corona, Magnetar, Polaris, the 42 PQ precompiles, ML-DSA hybrid
|
||||
// validator identity, BTC-style NodeID).
|
||||
//
|
||||
// Pre-activation reads are handled transparently by the zap_codec
|
||||
// Unmarshal path's BE-fallback decoder — luxd binaries can mount and
|
||||
// continue serving DBs written by pre-LP-023 versions (v1.28.x and
|
||||
// earlier) without operator intervention. Writes from post-activation
|
||||
// timestamps emit LE; reads accept either endianness.
|
||||
const ZAPActivationUnix uint64 = 1766708400
|
||||
|
||||
// LegacyEnabled is true when the operator has set LUXD_ENABLE_LEGACY_CODEC=1.
|
||||
//
|
||||
// **Native ZAP is the default.** The codec.Codec{Marshal,Unmarshal} interface
|
||||
// is gone from the platformvm hot path. New deployments, fresh syncs, and
|
||||
// post-activation production binaries run ZAP-only by default — no legacy
|
||||
// code in the read or write path.
|
||||
// Default: true. Validators upgrading from v1.28.x or earlier hold P-chain
|
||||
// state written with the pre-LP-023 BE codec version prefix. zap_codec
|
||||
// reads handle BE transparently via fallback in Unmarshal, but the broader
|
||||
// legacy linearcodec path remains reachable so historical tx bytes that
|
||||
// did not migrate cleanly through the BE-fallback shim can still be
|
||||
// decoded.
|
||||
//
|
||||
// Operators MUST set LUXD_ENABLE_LEGACY_CODEC=1 if they need to read
|
||||
// pre-activation historical blocks from disk (e.g. validators bootstrapping
|
||||
// from a long-history snapshot that includes pre-2026-07-01 P-chain state).
|
||||
// Without the flag, legacy-encoded bytes return ErrLegacyCodecDisabled.
|
||||
//
|
||||
// Once a validator has synced past the activation height, the flag can be
|
||||
// dropped — all subsequent reads are ZAP.
|
||||
var LegacyEnabled = os.Getenv("LUXD_ENABLE_LEGACY_CODEC") == "1"
|
||||
// Operators can explicitly disable via LUXD_ENABLE_LEGACY_CODEC=0 once
|
||||
// their DB has been fully resynced from a post-activation snapshot.
|
||||
var LegacyEnabled = os.Getenv("LUXD_ENABLE_LEGACY_CODEC") != "0"
|
||||
|
||||
// ErrLegacyCodecDisabled is returned by legacy-path parsers when the operator
|
||||
// has not enabled legacy codec support via the LUXD_ENABLE_LEGACY_CODEC env var.
|
||||
|
||||
Reference in New Issue
Block a user