mirror of
https://github.com/luxfi/lattice.git
synced 2026-09-24 00:25:44 +00:00
Merge pull request #10 from luxfi/feat/lp-107-wire-package
LP-107 Phase 3 (cycle-free): lattice consumes luxfi/math/codec (refile of #6)
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
// Copyright (c) 2026 Lux Industries Inc.
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
// Package wire is lattice's wire-format hardening boundary.
|
||||
//
|
||||
// LP-107 Phase 3 (cycle-free portion): lattice consumes
|
||||
// luxfi/math/codec for bounded decoding of untrusted wire data.
|
||||
// luxfi/math/codec does NOT import any lattice package, so this is a
|
||||
// safe one-way dependency.
|
||||
//
|
||||
// The full Phase 3 inversion (lattice/ring delegating to
|
||||
// luxfi/math/ntt for the canonical NTT body) requires moving ~5K LoC
|
||||
// of NTT/Montgomery code from lattice/ring into luxfi/math/ntt; that
|
||||
// migration is queued as a separate work-item, since its surface
|
||||
// touches every downstream consumer of lattice/ring.
|
||||
//
|
||||
// In the meantime, lattice/wire shows that lattice CAN consume the
|
||||
// math substrate where the dependency direction allows: codec
|
||||
// hardening lives once, in luxfi/math/codec, and is reused uniformly
|
||||
// across pulsar, lens, fhe, lattice.
|
||||
package wire
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"github.com/luxfi/math/codec"
|
||||
)
|
||||
|
||||
// MaxLatticeWireUintSliceLen — bound for any uint64 slice that
|
||||
// crosses the lattice wire boundary. Production NTT degrees max out
|
||||
// at 16384 (FHE PN13QP54); we cap at 4096 for the consensus-critical
|
||||
// Pulsar lane and provide a higher-cap helper for FHE.
|
||||
const MaxLatticeWireUintSliceLen = 4096
|
||||
|
||||
// LatticeConsensusWireLimits is the bounded codec.Limits suitable for
|
||||
// consensus-critical lattice frames (Pulsar Vector[Poly], threshold-
|
||||
// share blobs).
|
||||
var LatticeConsensusWireLimits = codec.Limits{
|
||||
MaxFrameBytes: 16 * 1024 * 1024,
|
||||
MaxUint16SliceLen: MaxLatticeWireUintSliceLen,
|
||||
MaxUint32SliceLen: MaxLatticeWireUintSliceLen,
|
||||
MaxUint64SliceLen: MaxLatticeWireUintSliceLen,
|
||||
MaxDepth: 4,
|
||||
}
|
||||
|
||||
// LatticeFHEWireLimits is the bounded codec.Limits for FHE-class
|
||||
// lattice frames (RNS chains, EvaluationKey blobs). Larger
|
||||
// MaxFrameBytes + slice cap reflect the deeper polynomial structure.
|
||||
var LatticeFHEWireLimits = codec.Limits{
|
||||
MaxFrameBytes: 32 * 1024 * 1024,
|
||||
MaxUint16SliceLen: 16384,
|
||||
MaxUint32SliceLen: 16384,
|
||||
MaxUint64SliceLen: 16384,
|
||||
MaxDepth: 5,
|
||||
}
|
||||
|
||||
// ValidateUintSliceFrame asserts the outer length prefix of a wire
|
||||
// frame is within the configured cap; returns an error wrapping
|
||||
// codec.ErrLimitExceeded when exceeded.
|
||||
//
|
||||
// Use LatticeConsensusWireLimits for Pulsar-class data, or
|
||||
// LatticeFHEWireLimits for FHE-class data.
|
||||
func ValidateUintSliceFrame(frame []byte, limits codec.Limits) error {
|
||||
r, err := codec.NewReader(bytes.NewReader(frame), limits)
|
||||
if err != nil {
|
||||
return fmt.Errorf("lattice/wire: NewReader: %w", err)
|
||||
}
|
||||
if _, err := r.ReadUint64Slice(); err != nil {
|
||||
return fmt.Errorf("lattice/wire: outer length: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
// Copyright (c) 2026 Lux Industries Inc.
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package wire
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/luxfi/math/codec"
|
||||
)
|
||||
|
||||
func encodeUvarint(out *bytes.Buffer, v uint64) {
|
||||
for v >= 0x80 {
|
||||
out.WriteByte(byte(v) | 0x80)
|
||||
v >>= 7
|
||||
}
|
||||
out.WriteByte(byte(v))
|
||||
}
|
||||
|
||||
// TestValidateUintSliceFrame_RejectsHugeLength is the regression test
|
||||
// for lattice issue #4 (Vector[T].ReadFrom unbounded allocation),
|
||||
// now centralized through luxfi/math/codec.Reader. The lattice repo
|
||||
// itself can now reject the same attack input class through the
|
||||
// substrate — completing the LP-107 cycle: pulsar, lens, fhe, AND
|
||||
// lattice all reject the same byte input identically.
|
||||
func TestValidateUintSliceFrame_RejectsHugeLength(t *testing.T) {
|
||||
const huge = uint64(70_368_955_777_453)
|
||||
var buf bytes.Buffer
|
||||
encodeUvarint(&buf, huge)
|
||||
|
||||
err := ValidateUintSliceFrame(buf.Bytes(), LatticeConsensusWireLimits)
|
||||
if err == nil {
|
||||
t.Fatal("ValidateUintSliceFrame returned nil for huge length")
|
||||
}
|
||||
if !errors.Is(err, codec.ErrLimitExceeded) {
|
||||
t.Errorf("err is not ErrLimitExceeded: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateUintSliceFrame_FHELimits_AcceptsLarger(t *testing.T) {
|
||||
// FHE limits accept up to 16384; consensus limits cap at 4096.
|
||||
// A frame requesting 8192 should pass FHE limits, fail consensus.
|
||||
var buf bytes.Buffer
|
||||
encodeUvarint(&buf, 8192)
|
||||
// Provide payload bytes (not strictly required if reading errors
|
||||
// before exhausting the buffer, but completeness).
|
||||
buf.Write(make([]byte, 8192*8))
|
||||
if err := ValidateUintSliceFrame(buf.Bytes(), LatticeFHEWireLimits); err != nil {
|
||||
t.Errorf("FHE limits should accept 8192: %v", err)
|
||||
}
|
||||
// Same bytes via consensus limits → should reject.
|
||||
if err := ValidateUintSliceFrame(buf.Bytes(), LatticeConsensusWireLimits); err == nil {
|
||||
t.Error("consensus limits should reject 8192")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user