Files
node/chains/catchup_frame_test.go
zeekay 7cf87b230b node-unified is the node
The module already called itself github.com/luxfi/node while containing no node
in Go: cmd/luxd was twenty-one lines that printed the size of a few types, and
the Go column of the matrix measured a VM plugin. A repo that fetches the thing
it claims to be has not replaced it, so the Go sources move in — 1515 files,
the daemon and everything under it — and the build reaches outside this module
for nothing.

Rust and C++ sit beside it unchanged and are still measured against it by the
differential. That is the point of one repo: the reference implementation and
the two ports are the same checkout, so parity is a thing you can run rather
than a thing you assert.

Verified: builds, and the closure carries no lux-private — checked at build time
rather than believed. The daemon starts twelve chains on a local network
(P X C Q Z A B G K M F, plus D through its plugin) and reports healthy.
2026-09-07 16:15:25 -07:00

111 lines
4.6 KiB
Go

// Copyright (C) 2019-2026, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// catchup_frame_test.go — the CERT-CARRYING catch-up wire format. These prove the
// The property under test: a v2 (block,cert) entry round-trips, an entry with no cert
// routes to the vote path, and a cross-version exchange fails CLEANLY (a legacy
// decoder cannot misparse a v2 frame, and the v2 decoder treats a legacy raw block
// as legacy — never a partial/garbage parse). The cert-accept SEMANTICS are proven
// in the consensus engine tests; here we pin only the framing.
package chains
import (
"bytes"
"encoding/binary"
"testing"
)
func TestCatchupEntry_RoundTrip(t *testing.T) {
block := []byte("\xf9\x02\x00 a realistic-ish block body")
cert := []byte("a-marshaled-quorum-cert")
blk, crt, ok := decodeCatchupEntry(encodeCatchupEntry(block, cert))
if !ok {
t.Fatal("a v2 entry must decode as a v2 entry")
}
if !bytes.Equal(blk, block) {
t.Fatalf("block bytes corrupted: got %q want %q", blk, block)
}
if !bytes.Equal(crt, cert) {
t.Fatalf("cert bytes corrupted: got %q want %q", crt, cert)
}
}
func TestCatchupEntry_EmptyCertRoutesToVotePath(t *testing.T) {
// A still-pending block served on the live missing-parent path carries no cert.
// It must decode as a v2 entry with an EMPTY cert — the requester then votes on
// it (handleContext routes certLen==0 to Put), the legacy behaviour.
block := []byte("pending-block-no-cert")
blk, crt, ok := decodeCatchupEntry(encodeCatchupEntry(block, nil))
if !ok {
t.Fatal("a v2 entry with empty cert must still decode as v2")
}
if !bytes.Equal(blk, block) {
t.Fatalf("block bytes corrupted: got %q", blk)
}
if len(crt) != 0 {
t.Fatalf("cert must be empty, got %d bytes", len(crt))
}
}
func TestCatchupEntry_LegacyRawBlockIsNotV2(t *testing.T) {
// A legacy responder sends the raw block as the container (no magic). The v2
// decoder must report ok=false so handleContext treats it as a raw block (Put),
// never as a malformed v2 entry. Cover several real block-prefix shapes.
for _, raw := range [][]byte{
{0xf9, 0x02, 0x00, 0x11, 0x22}, // EVM/RLP list header
{0x00, 0x00, 0x00, 0x2a}, // P/X-chain codec version prefix
{}, // empty
[]byte("LCU"), // 3 bytes — too short to even hold the magic
} {
if _, _, ok := decodeCatchupEntry(raw); ok {
t.Fatalf("legacy raw block %x must NOT decode as a v2 entry", raw)
}
}
}
func TestCatchupEntry_MagicIsNotAPlausibleLength(t *testing.T) {
// CROSS-VERSION SAFETY (new responder → legacy requester): a legacy decoder reads
// the first 4 bytes of a v2 entry as a uint32 length. The magic must be so large
// that it always exceeds the remaining buffer, so the legacy loop rejects the
// frame (0 blocks processed) rather than consuming garbage. 0x4C435532 ≈ 1.28 GB.
asLen := binary.BigEndian.Uint32(catchupEntryMagic[:])
if asLen < (1 << 30) {
t.Fatalf("magic read as a length (%d) is too small — a legacy decoder could misparse a v2 frame", asLen)
}
// And a full v2 frame's leading length-word (the magic) dwarfs the frame itself,
// so the legacy `blockLen > remaining` guard always fires.
frame := encodeCatchupEntry([]byte("blk"), []byte("crt"))
if uint64(binary.BigEndian.Uint32(frame[:4])) <= uint64(len(frame)) {
t.Fatal("magic-as-length must exceed the frame length so a legacy decoder self-rejects")
}
}
func TestCatchupEntry_CorruptV2FramesRejected(t *testing.T) {
good := encodeCatchupEntry([]byte("block-body"), []byte("cert-body"))
// Truncations inside the v2 structure must fail to ok=false (never a partial
// parse): drop the trailing cert byte, drop the certLen word, etc.
for _, bad := range [][]byte{
good[:len(good)-1], // last cert byte missing → certLen != remaining
good[:len(good)-5], // certLen word + cert missing
good[:8], // magic + blockLen only, block missing
good[:6], // magic + 2 bytes of blockLen
append(append([]byte(nil), good...), 0x00), // trailing byte → does not consume exactly
} {
if _, _, ok := decodeCatchupEntry(bad); ok {
t.Fatalf("a corrupt v2 frame (len %d) must be rejected, not partial-parsed", len(bad))
}
}
// An overflowing blockLen (claims more block than the buffer holds) is rejected.
overflow := append([]byte(nil), catchupEntryMagic[:]...)
var u32 [4]byte
binary.BigEndian.PutUint32(u32[:], 0xFFFFFFFF)
overflow = append(overflow, u32[:]...)
overflow = append(overflow, []byte("tiny")...)
if _, _, ok := decodeCatchupEntry(overflow); ok {
t.Fatal("a v2 frame with an overflowing blockLen must be rejected")
}
}