mirror of
https://github.com/luxfi/node.git
synced 2026-10-30 15:24:05 +00:00
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]>
99 lines
3.3 KiB
Go
99 lines
3.3 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"
|
|
|
|
"github.com/luxfi/ids"
|
|
"github.com/luxfi/log"
|
|
)
|
|
|
|
// deriveStakingNodeID returns the canonical node-identity NodeID for an
|
|
// ML-DSA-65 public key under the primary-identity domain (ids.Empty) — the
|
|
// exact derivation node.Node uses for MyNodeID via
|
|
// StakingConfig.DeriveNodeID(ids.Empty). A peer that proves possession of
|
|
// this key MUST present exactly this NodeID.
|
|
func deriveStakingNodeID(t *testing.T, li *LocalIdentity) ids.NodeID {
|
|
t.Helper()
|
|
nid, _, err := ids.NodeIDSchemeMLDSA65.DeriveMLDSA(ids.Empty, packMLDSAPub(li.Public))
|
|
require.NoError(t, err)
|
|
return nid
|
|
}
|
|
|
|
func newAdoptTestPeer(tlsNodeID ids.NodeID) *peer {
|
|
return &peer{
|
|
Config: &Config{Log: log.NewNoOpLogger()},
|
|
id: tlsNodeID,
|
|
}
|
|
}
|
|
|
|
// TestAdoptVerifiedPQIdentity_BindsAndAdopts is the core of the strict-PQ
|
|
// block-production fix: when the NodeID a peer presents is the canonical
|
|
// derivation of the ML-DSA key it proved possession of, the peer's identity
|
|
// is switched from the transport-layer (TLS-cert) NodeID to the
|
|
// validator-set (ML-DSA) NodeID that consensus keys peers by.
|
|
func TestAdoptVerifiedPQIdentity_BindsAndAdopts(t *testing.T) {
|
|
require := require.New(t)
|
|
|
|
identity, err := NewLocalIdentity(ids.EmptyNodeID)
|
|
require.NoError(err)
|
|
mldsaNodeID := deriveStakingNodeID(t, identity)
|
|
|
|
tlsNodeID := ids.GenerateTestNodeID()
|
|
require.NotEqual(tlsNodeID, mldsaNodeID)
|
|
|
|
p := newAdoptTestPeer(tlsNodeID)
|
|
require.Equal(tlsNodeID, p.ID(), "precondition: peer starts on its TLS-cert NodeID")
|
|
|
|
result := &HandshakeResult{
|
|
PeerNodeID: mldsaNodeID,
|
|
PeerMLDSA: identity.Public,
|
|
}
|
|
|
|
require.NoError(p.adoptVerifiedPQIdentity(result))
|
|
require.Equal(mldsaNodeID, p.ID(),
|
|
"peer must adopt the ML-DSA validator NodeID after a bound handshake")
|
|
}
|
|
|
|
// TestAdoptVerifiedPQIdentity_RejectsUnboundNodeID covers the impersonation
|
|
// case the old ephemeral-key handshake silently allowed: a peer signs a
|
|
// SELF-ASSERTED NodeID (e.g. a real validator's) with a key that does NOT
|
|
// derive that NodeID. The binding check must reject it and leave the peer's
|
|
// identity untouched.
|
|
func TestAdoptVerifiedPQIdentity_RejectsUnboundNodeID(t *testing.T) {
|
|
require := require.New(t)
|
|
|
|
identity, err := NewLocalIdentity(ids.EmptyNodeID)
|
|
require.NoError(err)
|
|
|
|
tlsNodeID := ids.GenerateTestNodeID()
|
|
forgedNodeID := ids.GenerateTestNodeID() // NOT derived from identity.Public
|
|
require.NotEqual(forgedNodeID, deriveStakingNodeID(t, identity))
|
|
|
|
p := newAdoptTestPeer(tlsNodeID)
|
|
result := &HandshakeResult{
|
|
PeerNodeID: forgedNodeID,
|
|
PeerMLDSA: identity.Public,
|
|
}
|
|
|
|
err = p.adoptVerifiedPQIdentity(result)
|
|
require.Error(err)
|
|
require.Contains(err.Error(), "identity binding failed")
|
|
require.Equal(tlsNodeID, p.ID(),
|
|
"a failed binding must NOT mutate the peer identity")
|
|
}
|
|
|
|
// TestAdoptVerifiedPQIdentity_NilResult guards the defensive checks so a
|
|
// malformed/absent handshake result can never silently leave the peer on a
|
|
// stale identity.
|
|
func TestAdoptVerifiedPQIdentity_NilResult(t *testing.T) {
|
|
require := require.New(t)
|
|
p := newAdoptTestPeer(ids.GenerateTestNodeID())
|
|
require.Error(p.adoptVerifiedPQIdentity(nil))
|
|
require.Error(p.adoptVerifiedPQIdentity(&HandshakeResult{PeerNodeID: ids.GenerateTestNodeID()}))
|
|
}
|