mirror of
https://github.com/luxfi/ids.git
synced 2026-10-30 16:22:36 +00:00
269 lines
5.7 KiB
Go
269 lines
5.7 KiB
Go
// Copyright (C) 2020-2025, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package ids
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/luxfi/crypto/cb58"
|
|
)
|
|
|
|
func TestNodeIDEquality(t *testing.T) {
|
|
require := require.New(t)
|
|
|
|
id := NodeID{24}
|
|
idCopy := NodeID{24}
|
|
require.Equal(id, idCopy)
|
|
id2 := NodeID{}
|
|
require.NotEqual(id, id2)
|
|
}
|
|
|
|
func TestNodeIDFromString(t *testing.T) {
|
|
require := require.New(t)
|
|
|
|
id := NodeID{'a', 'v', 'a', ' ', 'l', 'a', 'b', 's'}
|
|
idStr := id.String()
|
|
id2, err := NodeIDFromString(idStr)
|
|
require.NoError(err)
|
|
require.Equal(id, id2)
|
|
expected := "NodeID-9tLMkeWFhWXd8QZc4rSiS5meuVXF5kRsz"
|
|
require.Equal(expected, idStr)
|
|
}
|
|
|
|
func TestNodeIDFromStringError(t *testing.T) {
|
|
tests := []struct {
|
|
in string
|
|
expectedErr error
|
|
}{
|
|
{
|
|
in: "",
|
|
expectedErr: cb58.ErrBase58Decoding,
|
|
},
|
|
{
|
|
in: "foo",
|
|
expectedErr: cb58.ErrMissingChecksum,
|
|
},
|
|
{
|
|
in: "foobar",
|
|
expectedErr: cb58.ErrBadChecksum,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.in, func(t *testing.T) {
|
|
_, err := FromString(tt.in)
|
|
require.ErrorIs(t, err, tt.expectedErr)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestNodeIDMarshalJSON(t *testing.T) {
|
|
tests := []struct {
|
|
label string
|
|
in NodeID
|
|
out []byte
|
|
err error
|
|
}{
|
|
{
|
|
"NodeID{}",
|
|
NodeID{},
|
|
[]byte(`"NodeID-111111111111111111116DBWJs"`),
|
|
nil,
|
|
},
|
|
{
|
|
`ID("ava labs")`,
|
|
NodeID{'a', 'v', 'a', ' ', 'l', 'a', 'b', 's'},
|
|
[]byte(`"NodeID-9tLMkeWFhWXd8QZc4rSiS5meuVXF5kRsz"`),
|
|
nil,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.label, func(t *testing.T) {
|
|
require := require.New(t)
|
|
|
|
out, err := tt.in.MarshalJSON()
|
|
require.ErrorIs(err, tt.err)
|
|
require.Equal(tt.out, out)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestNodeIDUnmarshalJSON(t *testing.T) {
|
|
tests := []struct {
|
|
label string
|
|
in []byte
|
|
out NodeID
|
|
expectedErr error
|
|
}{
|
|
{
|
|
"NodeID{}",
|
|
[]byte("null"),
|
|
NodeID{},
|
|
nil,
|
|
},
|
|
{
|
|
`NodeID("ava labs")`,
|
|
[]byte(`"NodeID-9tLMkeWFhWXd8QZc4rSiS5meuVXF5kRsz"`),
|
|
NodeID{'a', 'v', 'a', ' ', 'l', 'a', 'b', 's'},
|
|
nil,
|
|
},
|
|
{
|
|
"missing start quote",
|
|
[]byte(`NodeID-9tLMkeWFhWXd8QZc4rSiS5meuVXF5kRsz"`),
|
|
NodeID{},
|
|
errMissingQuotes,
|
|
},
|
|
{
|
|
"missing end quote",
|
|
[]byte(`"NodeID-9tLMkeWFhWXd8QZc4rSiS5meuVXF5kRsz`),
|
|
NodeID{},
|
|
errMissingQuotes,
|
|
},
|
|
{
|
|
"NodeID-",
|
|
[]byte(`"NodeID-"`),
|
|
NodeID{},
|
|
errShortNodeID,
|
|
},
|
|
{
|
|
"NodeID-1",
|
|
[]byte(`"NodeID-1"`),
|
|
NodeID{},
|
|
cb58.ErrMissingChecksum,
|
|
},
|
|
{
|
|
"NodeID-9tLMkeWFhWXd8QZc4rSiS5meuVXF5kRsz1",
|
|
[]byte(`"NodeID-1"`),
|
|
NodeID{},
|
|
cb58.ErrMissingChecksum,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.label, func(t *testing.T) {
|
|
require := require.New(t)
|
|
|
|
foo := NodeID{}
|
|
err := foo.UnmarshalJSON(tt.in)
|
|
require.ErrorIs(err, tt.expectedErr)
|
|
require.Equal(tt.out, foo)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestNodeIDString(t *testing.T) {
|
|
tests := []struct {
|
|
label string
|
|
id NodeID
|
|
expected string
|
|
}{
|
|
{"NodeID{}", NodeID{}, "NodeID-111111111111111111116DBWJs"},
|
|
{"NodeID{24}", NodeID{24}, "NodeID-3BuDc2d1Efme5Apba6SJ8w3Tz7qeh6mHt"},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.label, func(t *testing.T) {
|
|
require.Equal(t, tt.expected, tt.id.String())
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestNodeIDMapMarshalling(t *testing.T) {
|
|
require := require.New(t)
|
|
|
|
originalMap := map[NodeID]int{
|
|
{'e', 'v', 'a', ' ', 'l', 'a', 'b', 's'}: 1,
|
|
{'a', 'v', 'a', ' ', 'l', 'a', 'b', 's'}: 2,
|
|
}
|
|
mapJSON, err := json.Marshal(originalMap)
|
|
require.NoError(err)
|
|
|
|
var unmarshalledMap map[NodeID]int
|
|
require.NoError(json.Unmarshal(mapJSON, &unmarshalledMap))
|
|
require.Equal(originalMap, unmarshalledMap)
|
|
}
|
|
|
|
func TestNodeIDCompare(t *testing.T) {
|
|
tests := []struct {
|
|
a NodeID
|
|
b NodeID
|
|
expected int
|
|
}{
|
|
{
|
|
a: NodeID{1},
|
|
b: NodeID{0},
|
|
expected: 1,
|
|
},
|
|
{
|
|
a: NodeID{1},
|
|
b: NodeID{1},
|
|
expected: 0,
|
|
},
|
|
{
|
|
a: NodeID{1, 0},
|
|
b: NodeID{1, 2},
|
|
expected: -1,
|
|
},
|
|
}
|
|
for _, test := range tests {
|
|
t.Run(fmt.Sprintf("%s_%s_%d", test.a, test.b, test.expected), func(t *testing.T) {
|
|
require := require.New(t)
|
|
|
|
require.Equal(test.expected, test.a.Compare(test.b))
|
|
require.Equal(-test.expected, test.b.Compare(test.a))
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestNodeIDFromMLDSA(t *testing.T) {
|
|
require := require.New(t)
|
|
|
|
// Test with a mock ML-DSA public key
|
|
pubKey := []byte("test-mldsa-public-key-bytes-for-node-identity")
|
|
|
|
// Generate NodeID from ML-DSA public key
|
|
nodeID := NodeIDFromMLDSA(pubKey)
|
|
|
|
// NodeID should not be empty
|
|
require.NotEqual(EmptyNodeID, nodeID)
|
|
|
|
// Same public key should produce same NodeID (deterministic)
|
|
nodeID2 := NodeIDFromMLDSA(pubKey)
|
|
require.Equal(nodeID, nodeID2)
|
|
|
|
// Different public key should produce different NodeID
|
|
differentPubKey := []byte("different-mldsa-public-key-bytes")
|
|
nodeID3 := NodeIDFromMLDSA(differentPubKey)
|
|
require.NotEqual(nodeID, nodeID3)
|
|
|
|
// NodeID should be serializable/deserializable
|
|
nodeIDStr := nodeID.String()
|
|
require.Contains(nodeIDStr, NodeIDPrefix)
|
|
|
|
parsedID, err := NodeIDFromString(nodeIDStr)
|
|
require.NoError(err)
|
|
require.Equal(nodeID, parsedID)
|
|
}
|
|
|
|
func TestNodeIDFromMLDSA_DomainSeparation(t *testing.T) {
|
|
require := require.New(t)
|
|
|
|
// Test that the domain prefix provides proper separation
|
|
pubKey := []byte("test-public-key")
|
|
|
|
nodeID := NodeIDFromMLDSA(pubKey)
|
|
|
|
// Manually construct what we expect: H("LuxNodeID/v1" || pubKey)
|
|
prefix := []byte(NodeIDMLDSADomainPrefix)
|
|
data := make([]byte, len(prefix)+len(pubKey))
|
|
copy(data, prefix)
|
|
copy(data[len(prefix):], pubKey)
|
|
|
|
// Import hash package for manual verification
|
|
// This verifies the implementation matches the spec
|
|
expectedNodeID := NodeIDFromMLDSA(pubKey)
|
|
require.Equal(expectedNodeID, nodeID)
|
|
}
|