mirror of
https://github.com/luxfi/proto.git
synced 2026-07-26 22:54:49 +00:00
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:
@@ -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 {
|
||||
|
||||
@@ -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 (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])
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 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.
|
||||
// Activation is forward-only. No compat shims, no dual-mode read, no
|
||||
// legacy linearcodec — the 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
|
||||
}
|
||||
|
||||
@@ -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.")
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user