zap_codec/zap_native: rip BE-fallback + LegacyEnabled, forward-only

Forward-only ZAP. No backwards-compat shims:
- zap_codec multi.go: drop BE-fallback read path
- zap_codec zapcodec.go: drop BE-fallback read path + peekVersionBE helper
- zap_native codec_select.go: rip LegacyEnabled var + LUXD_ENABLE_LEGACY_CODEC
  env knob + ErrLegacyCodecDisabled. Comment block simplified.

ZAPActivationUnix stays at 1766708400 (Dec 25 2025 16:20 PST — Quasar
activation timestamp, same as all other forks). ShouldUseZAPForWrite
returns true unconditionally — ZAP is the only wire format.

Tests cleaned: drop LegacyEnabled toggle scenarios in
TestShouldUseZAPForWrite and TestRed_V15. Pin ZAPActivationUnix value
to the Quasar timestamp.

Validators upgrading from v1.28.x DBs cannot mount in this layout
(pre-LP-023 BE-encoded P-chain state is unreadable). Mainnet/testnet/
devnet operators must rebootstrap from genesis + RLP re-import.
This commit is contained in:
Hanzo AI
2026-06-06 17:12:24 -07:00
parent a7014e0340
commit fe366196b6
5 changed files with 19 additions and 150 deletions
-18
View File
@@ -233,24 +233,6 @@ 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 {
-30
View File
@@ -186,18 +186,6 @@ 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 {
@@ -316,21 +304,3 @@ 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 (0N 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])
}
+8 -36
View File
@@ -78,44 +78,16 @@ func TestIsZAPBytes(t *testing.T) {
}
func TestShouldUseZAPForWrite(t *testing.T) {
// Default: native ZAP for every timestamp. Legacy is opt-in via
// LUXD_ENABLE_LEGACY_CODEC. The package-level LegacyEnabled is read
// once at init from the env var; tests assert the default-false path
// and the explicitly-toggled path.
defer func(prev bool) { LegacyEnabled = prev }(LegacyEnabled)
t.Run("default (legacy disabled): ZAP always", func(t *testing.T) {
LegacyEnabled = false
for _, ts := range []uint64{0, 1, 1782604800 - 1, 1782604800, 1782604800 + 86400} {
if !ShouldUseZAPForWrite(ts) {
t.Fatalf("ShouldUseZAPForWrite(%d) = false, want true (ZAP default)", ts)
}
for _, ts := range []uint64{0, 1, ZAPActivationUnix - 1, ZAPActivationUnix, ZAPActivationUnix + 86400} {
if !ShouldUseZAPForWrite(ts) {
t.Fatalf("ShouldUseZAPForWrite(%d) = false, want true (ZAP unconditional)", ts)
}
})
t.Run("legacy enabled: timestamp-gated", func(t *testing.T) {
// ZAPActivationUnix is now 0 (always-on). With activation=0, the
// "pre-activation" window is empty — every block timestamp satisfies
// blockTimestamp >= 0, so writes are always ZAP even when
// LegacyEnabled. LegacyEnabled remains meaningful only on the READ
// path (decoding pre-2026-06-02 archival linearcodec bytes); it has
// no semantic effect on writes once activation = 0.
LegacyEnabled = true
for _, ts := range []uint64{0, 1, 1782604800 - 1, 1782604800, 1782604800 + 86400} {
if !ShouldUseZAPForWrite(ts) {
t.Fatalf("ShouldUseZAPForWrite(%d) = false, want true (ZAP always-on regardless of LegacyEnabled when activation=0)", ts)
}
}
})
}
}
// TestZAPActivationUnixIsAlwaysOn pins the activation constant to 0 so any
// regression that re-introduces a forward-date guard fails this gate. Once
// LP-023 ZAP-native activation shipped (2026-06-02 cutover), the legacy
// timestamp gate is dead — the only legacy path is the explicit read-only
// LUXD_ENABLE_LEGACY_CODEC opt-in for archival linearcodec bytes.
func TestZAPActivationUnixIsAlwaysOn(t *testing.T) {
if ZAPActivationUnix != 0 {
t.Fatalf("ZAPActivationUnix = %d, want 0 (always-on per LP-023 cutover)", ZAPActivationUnix)
func TestZAPActivationUnix(t *testing.T) {
const want uint64 = 1766708400 // Dec 25 2025 16:20 PST — Quasar activation
if ZAPActivationUnix != want {
t.Fatalf("ZAPActivationUnix = %d, want %d", ZAPActivationUnix, want)
}
}
+6 -46
View File
@@ -3,52 +3,21 @@
package zap_native
import (
"errors"
"os"
)
// 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 to native ZAP.
//
// 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 decoderluxd 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.
// Activation is forward-only. No compat shims, no dual-mode read, no
// legacy linearcodecthe cut is clean. Pre-activation DBs (luxd
// v1.28.x and earlier) cannot be mounted; operators must rebootstrap.
const ZAPActivationUnix uint64 = 1766708400
// LegacyEnabled is true when the operator has set LUXD_ENABLE_LEGACY_CODEC=1.
//
// 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 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.
// Native ZAP is the default; legacy is opt-in.
var ErrLegacyCodecDisabled = errors.New("zap_native: legacy codec not enabled (set LUXD_ENABLE_LEGACY_CODEC=1 to read pre-activation blocks)")
// IsZAPBytes reports whether the byte buffer is a ZAP-encoded message
// (recognised by the 4-byte "ZAP\x00" magic). Cheap O(1) check.
//
// Callers use this to discriminate ZAP-encoded txs/blocks from legacy
// linearcodec-encoded ones during the cross-activation read window (which
// only happens when LUXD_ENABLE_LEGACY_CODEC=1).
func IsZAPBytes(b []byte) bool {
if len(b) < 4 {
return false
@@ -57,16 +26,7 @@ func IsZAPBytes(b []byte) bool {
}
// ShouldUseZAPForWrite reports whether new outgoing txs/blocks should be
// encoded as ZAP.
//
// Default behavior — native ZAP always. ZAPActivationUnix is now 0
// (always-on), so the timestamp gate is a no-op except when the operator
// explicitly opts back into legacy via LUXD_ENABLE_LEGACY_CODEC=1, in
// which case the original forward-date semantics are preserved for the
// LegacyEnabled path only.
// encoded as ZAP. Always true — ZAP is the only wire format post-Quasar.
func ShouldUseZAPForWrite(blockTimestamp uint64) bool {
if !LegacyEnabled {
return true // native ZAP default
}
return blockTimestamp >= ZAPActivationUnix
return true
}
+5 -20
View File
@@ -705,31 +705,16 @@ func TestRed_V14_CrossTypeConfusion(t *testing.T) {
// The owning layer is block/executor. Document the requirement.
func TestRed_V15_PreActivationZAPTxRejection(t *testing.T) {
// LP-023 cutover (2026-06-02): ZAPActivationUnix is now 0 — there is no
// pre-activation window. V15's threat model only applies in the legacy
// cutover-window scenario, which is closed. We pin the always-on
// invariant: regardless of LegacyEnabled, the write rule says ZAP for
// every timestamp.
atx := NewAdvanceTimeTx(0) // any timestamp; activation gate is dead
// Legacy linearcodec is gone. Every tx is ZAP. Pin the invariant.
atx := NewAdvanceTimeTx(0)
if !IsZAPBytes(atx.Bytes()) {
t.Fatal("V15: AdvanceTimeTx must be ZAP-encoded (wire layer is timestamp-agnostic post-LP-023)")
t.Fatal("V15: AdvanceTimeTx must be ZAP-encoded")
}
defer func(prev bool) { LegacyEnabled = prev }(LegacyEnabled)
LegacyEnabled = false
for _, ts := range []uint64{0, 1, 1782604800, 1782604800 + 86400} {
for _, ts := range []uint64{0, 1, ZAPActivationUnix, ZAPActivationUnix + 86400} {
if !ShouldUseZAPForWrite(ts) {
t.Fatalf("V15: legacy-disabled should write ZAP for ts=%d", ts)
t.Fatalf("V15: write rule must be ZAP for ts=%d", ts)
}
}
LegacyEnabled = true
for _, ts := range []uint64{0, 1, 1782604800, 1782604800 + 86400} {
if !ShouldUseZAPForWrite(ts) {
t.Fatalf("V15: legacy-enabled with activation=0 should still write ZAP for ts=%d (no pre-activation window exists)", ts)
}
}
t.Logf("V15 closed: LP-023 cutover (2026-06-02) makes ZAP unconditional on the write path. The legacy cutover-window threat model is no longer applicable; LegacyEnabled is now read-only-relevant.")
}
// -----------------------------------------------------------------------------