Files
node/mesh/peer/ip_pq_wire_test.go
zeekayandHanzo Dev 37e2940ead mesh is the peer layer, network is the validator set
Two packages were named for the same word and meant different things. network/
dialed peers, exchanged keys, throttled and tracked them; nets/ tracked which
chains share a validator set and when they have all bootstrapped. One letter
apart, and neither name said which was which.

The peer layer is mesh — the word this estate already uses for it, in
lux-rs/node's own mesh.rs. That frees network for the thing that is actually a
network: a set of chains sharing a validator set. A network holds chains; a
chain belongs to one network.

github.com/luxfi/p2p stays what it is, the gossip library the VMs use. Three
concerns, three names, none of them borrowed from another.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-07 21:17:09 -07:00

153 lines
5.2 KiB
Go

// Copyright (C) 2019-2026, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package peer
import (
"testing"
"github.com/stretchr/testify/require"
zappb "github.com/luxfi/proto/node/zap/p2p"
)
// TestHandshakeWire_LegacyFrameOmitsMLDSA verifies the wire-compat
// guarantee for the append-only IpMldsaSig field. A handshake frame
// emitted by a legacy peer (no IpMldsaSig at all) still round-trips
// through the new marshal/unmarshal pair, with IpMldsaSig reading as
// nil/empty.
func TestHandshakeWire_LegacyFrameOmitsMLDSA(t *testing.T) {
require := require.New(t)
// Build a handshake without ML-DSA.
hs := &zappb.Handshake{
NetworkId: 1337,
MyTime: 12345,
IpAddr: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4},
IpPort: 9650,
IpSigningTime: 12345,
IpNodeIdSig: []byte("legacy-tls-sig"),
TrackedNets: [][]byte{},
Client: &zappb.Client{
Name: "lux",
Major: 1,
Minor: 26,
Patch: 12,
},
SupportedLps: []uint32{},
ObjectedLps: []uint32{},
KnownPeers: &zappb.BloomFilter{
Filter: []byte{0x01, 0x02},
Salt: []byte{0x03, 0x04},
},
IpBlsSig: []byte("legacy-bls-sig"),
AllChains: false,
IpMldsaSig: nil,
}
// Wrap in a Message and round-trip via the codec.
msg := &zappb.Message{Message: &zappb.Message_Handshake{Handshake: hs}}
raw, err := zappb.Marshal(msg)
require.NoError(err)
var decoded zappb.Message
require.NoError(zappb.Unmarshal(raw, &decoded))
roundTripped, ok := decoded.Message.(*zappb.Message_Handshake)
require.True(ok, "decoded message must be a Handshake")
require.Empty(roundTripped.Handshake.IpMldsaSig,
"empty ML-DSA on the wire decodes to empty on the receiver")
require.Equal(hs.IpNodeIdSig, roundTripped.Handshake.IpNodeIdSig)
require.Equal(hs.IpBlsSig, roundTripped.Handshake.IpBlsSig)
}
// TestHandshakeWire_TruncatedLegacyFrameDecodes mimics a true legacy
// wire payload that ends after IpBlsSig with no trailing IpMldsaSig
// length-prefix. The new unmarshaller MUST tolerate this — that's the
// whole point of the HasMore() guard around the new field read. Without
// the guard, decoding would fail with io.ErrUnexpectedEOF and the peer
// handshake would refuse every legacy peer on the network.
func TestHandshakeWire_TruncatedLegacyFrameDecodes(t *testing.T) {
require := require.New(t)
// First produce a new-format frame, then strip the trailing 4-byte
// length prefix (the empty-bytes encoding of IpMldsaSig). The result
// is byte-for-byte what a pre-PQ binary would have produced.
hs := &zappb.Handshake{
NetworkId: 1337,
MyTime: 7777,
IpAddr: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4},
IpPort: 9650,
IpSigningTime: 7777,
IpNodeIdSig: []byte("legacy-tls"),
TrackedNets: [][]byte{},
Client: &zappb.Client{Name: "lux", Major: 1, Minor: 0, Patch: 0},
SupportedLps: []uint32{},
ObjectedLps: []uint32{},
KnownPeers: &zappb.BloomFilter{Filter: []byte{}, Salt: []byte{}},
IpBlsSig: []byte("legacy-bls"),
AllChains: false,
IpMldsaSig: nil,
}
msg := &zappb.Message{Message: &zappb.Message_Handshake{Handshake: hs}}
raw, err := zappb.Marshal(msg)
require.NoError(err)
// The last 4 bytes of the inner Handshake encoding are the empty
// IpMldsaSig length-prefix (0x00000000). Truncating them simulates
// a legacy peer that never wrote the field.
require.GreaterOrEqual(len(raw), 4, "marshalled frame must be larger than the trailing field")
truncated := raw[:len(raw)-4]
var decoded zappb.Message
require.NoError(zappb.Unmarshal(truncated, &decoded),
"truncated legacy frame must decode without error")
roundTripped, ok := decoded.Message.(*zappb.Message_Handshake)
require.True(ok)
require.Empty(roundTripped.Handshake.IpMldsaSig,
"absent field reads as empty")
require.Equal(hs.IpBlsSig, roundTripped.Handshake.IpBlsSig)
}
// TestHandshakeWire_NewFrameRoundTripsMLDSA verifies that a new-format
// frame (with a non-empty IpMldsaSig) round-trips intact.
func TestHandshakeWire_NewFrameRoundTripsMLDSA(t *testing.T) {
require := require.New(t)
mldsaSig := make([]byte, 3309) // ML-DSA-65 signature size
for i := range mldsaSig {
mldsaSig[i] = byte(i)
}
hs := &zappb.Handshake{
NetworkId: 1,
MyTime: 99999,
IpAddr: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 1},
IpPort: 9630,
IpSigningTime: 99999,
IpNodeIdSig: []byte("strict-pq-tls-sig"),
TrackedNets: [][]byte{},
Client: &zappb.Client{
Name: "lux",
Major: 1,
Minor: 26,
Patch: 13,
},
SupportedLps: []uint32{},
ObjectedLps: []uint32{},
KnownPeers: &zappb.BloomFilter{Filter: []byte{}, Salt: []byte{}},
IpBlsSig: []byte("strict-pq-bls-sig"),
AllChains: true,
IpMldsaSig: mldsaSig,
}
msg := &zappb.Message{Message: &zappb.Message_Handshake{Handshake: hs}}
raw, err := zappb.Marshal(msg)
require.NoError(err)
var decoded zappb.Message
require.NoError(zappb.Unmarshal(raw, &decoded))
roundTripped, ok := decoded.Message.(*zappb.Message_Handshake)
require.True(ok, "decoded message must be a Handshake")
require.Equal(mldsaSig, roundTripped.Handshake.IpMldsaSig,
"ML-DSA sig must round-trip byte-for-byte")
}