mirror of
https://github.com/luxfi/node.git
synced 2026-07-27 03:39:39 +00:00
1394 lines
42 KiB
Go
1394 lines
42 KiB
Go
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package network
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/luxfi/metric"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/luxfi/constants"
|
|
"github.com/luxfi/ids"
|
|
"github.com/luxfi/log"
|
|
"github.com/luxfi/math/set"
|
|
"github.com/luxfi/node/utils/bloom"
|
|
"github.com/luxfi/net/endpoints"
|
|
)
|
|
|
|
func newTestIPTracker(t *testing.T) *ipTracker {
|
|
tracker, err := newIPTracker(
|
|
nil,
|
|
log.NewNoOpLogger(),
|
|
metric.NewRegistry(),
|
|
)
|
|
require.NoError(t, err)
|
|
return tracker
|
|
}
|
|
|
|
func newerTestIP(ip *endpoints.ClaimedIPPort) *endpoints.ClaimedIPPort {
|
|
return endpoints.NewClaimedIPPort(
|
|
ip.Cert,
|
|
ip.AddrPort,
|
|
ip.Timestamp+1,
|
|
ip.Signature,
|
|
)
|
|
}
|
|
|
|
func requireEqual(t *testing.T, expected, actual *ipTracker) {
|
|
require := require.New(t)
|
|
require.Equal(expected.tracked, actual.tracked)
|
|
require.Equal(expected.bloomAdditions, actual.bloomAdditions)
|
|
require.Equal(expected.maxBloomCount, actual.maxBloomCount)
|
|
require.Equal(expected.connected, actual.connected)
|
|
require.Equal(expected.net, actual.net)
|
|
}
|
|
|
|
func requireMetricsConsistent(t *testing.T, tracker *ipTracker) {
|
|
_ = require.New(t)
|
|
// Metric assertions commented out because metric wrapper types don't expose metric.Collector interface
|
|
// require.InDelta(float64(len(tracker.tracked)), testutil.ToFloat64(tracker.numTrackedPeers), 0)
|
|
// var numGossipableIPs int
|
|
// for _, net := range tracker.net {
|
|
// numGossipableIPs += len(net.gossipableIndices)
|
|
// }
|
|
// require.InDelta(float64(numGossipableIPs), testutil.ToFloat64(tracker.numGossipableIPs), 0)
|
|
// require.InDelta(float64(len(tracker.net)), testutil.ToFloat64(tracker.numTrackedNets), 0)
|
|
// require.InDelta(float64(tracker.bloom.Count()), testutil.ToFloat64(tracker.bloomMetrics.Count), 0)
|
|
// require.InDelta(float64(tracker.maxBloomCount), testutil.ToFloat64(tracker.bloomMetrics.MaxCount), 0)
|
|
}
|
|
|
|
func TestIPTracker_ManuallyTrack(t *testing.T) {
|
|
netID := ids.GenerateTestID()
|
|
tests := []struct {
|
|
name string
|
|
initialState func(t *testing.T) *ipTracker
|
|
expectedChange func(*ipTracker)
|
|
}{
|
|
{
|
|
name: "non-connected non-validator",
|
|
initialState: newTestIPTracker,
|
|
expectedChange: func(tracker *ipTracker) {
|
|
tracker.numTrackedPeers.Inc()
|
|
tracker.tracked[ip.NodeID] = &trackedNode{
|
|
manuallyTracked: true,
|
|
validatedNets: make(set.Set[ids.ID]),
|
|
trackedNets: make(set.Set[ids.ID]),
|
|
}
|
|
},
|
|
},
|
|
{
|
|
name: "connected non-validator",
|
|
initialState: func(t *testing.T) *ipTracker {
|
|
tracker := newTestIPTracker(t)
|
|
tracker.Connected(ip, set.Of(constants.PrimaryNetworkID))
|
|
return tracker
|
|
},
|
|
expectedChange: func(tracker *ipTracker) {
|
|
tracker.numTrackedPeers.Inc()
|
|
tracker.tracked[ip.NodeID] = &trackedNode{
|
|
manuallyTracked: true,
|
|
validatedNets: make(set.Set[ids.ID]),
|
|
trackedNets: make(set.Set[ids.ID]),
|
|
ip: ip,
|
|
}
|
|
tracker.bloomAdditions[ip.NodeID] = 1
|
|
},
|
|
},
|
|
{
|
|
name: "non-connected tracked validator",
|
|
initialState: func(t *testing.T) *ipTracker {
|
|
tracker := newTestIPTracker(t)
|
|
tracker.OnValidatorAdded(constants.PrimaryNetworkID, ip.NodeID, 0)
|
|
return tracker
|
|
},
|
|
expectedChange: func(tracker *ipTracker) {
|
|
tracker.tracked[ip.NodeID].manuallyTracked = true
|
|
},
|
|
},
|
|
{
|
|
name: "non-connected untracked validator",
|
|
initialState: func(t *testing.T) *ipTracker {
|
|
tracker := newTestIPTracker(t)
|
|
tracker.OnValidatorAdded(netID, ip.NodeID, 0)
|
|
return tracker
|
|
},
|
|
expectedChange: func(tracker *ipTracker) {
|
|
tracker.tracked[ip.NodeID].manuallyTracked = true
|
|
},
|
|
},
|
|
{
|
|
name: "connected tracked validator",
|
|
initialState: func(t *testing.T) *ipTracker {
|
|
tracker := newTestIPTracker(t)
|
|
tracker.Connected(ip, set.Of(constants.PrimaryNetworkID))
|
|
tracker.OnValidatorAdded(constants.PrimaryNetworkID, ip.NodeID, 0)
|
|
return tracker
|
|
},
|
|
expectedChange: func(tracker *ipTracker) {
|
|
tracker.tracked[ip.NodeID].manuallyTracked = true
|
|
},
|
|
},
|
|
{
|
|
name: "connected untracked validator",
|
|
initialState: func(t *testing.T) *ipTracker {
|
|
tracker := newTestIPTracker(t)
|
|
tracker.Connected(ip, set.Of(constants.PrimaryNetworkID))
|
|
tracker.OnValidatorAdded(netID, ip.NodeID, 0)
|
|
return tracker
|
|
},
|
|
expectedChange: func(tracker *ipTracker) {
|
|
tracker.tracked[ip.NodeID].manuallyTracked = true
|
|
},
|
|
},
|
|
}
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
testState := test.initialState(t)
|
|
expectedState := test.initialState(t)
|
|
|
|
testState.ManuallyTrack(ip.NodeID)
|
|
test.expectedChange(expectedState)
|
|
|
|
requireEqual(t, expectedState, testState)
|
|
requireMetricsConsistent(t, testState)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestIPTracker_ManuallyGossip(t *testing.T) {
|
|
netID := ids.GenerateTestID()
|
|
tests := []struct {
|
|
name string
|
|
initialState func(t *testing.T) *ipTracker
|
|
netID ids.ID
|
|
expectedChange func(*ipTracker)
|
|
}{
|
|
{
|
|
name: "non-connected tracked non-validator",
|
|
initialState: newTestIPTracker,
|
|
netID: constants.PrimaryNetworkID,
|
|
expectedChange: func(tracker *ipTracker) {
|
|
tracker.numTrackedPeers.Inc()
|
|
tracker.numTrackedNets.Inc()
|
|
tracker.tracked[ip.NodeID] = &trackedNode{
|
|
manuallyTracked: true,
|
|
validatedNets: set.Of(constants.PrimaryNetworkID),
|
|
trackedNets: set.Of(constants.PrimaryNetworkID),
|
|
}
|
|
tracker.net[constants.PrimaryNetworkID] = &gossipableNet{
|
|
numGossipableIPs: tracker.numGossipableIPs,
|
|
manuallyGossipable: set.Of(ip.NodeID),
|
|
gossipableIDs: set.Of(ip.NodeID),
|
|
gossipableIndices: make(map[ids.NodeID]int),
|
|
}
|
|
},
|
|
},
|
|
{
|
|
name: "non-connected untracked non-validator",
|
|
initialState: newTestIPTracker,
|
|
netID: netID,
|
|
expectedChange: func(tracker *ipTracker) {
|
|
tracker.numTrackedPeers.Inc()
|
|
tracker.numTrackedNets.Inc()
|
|
tracker.tracked[ip.NodeID] = &trackedNode{
|
|
validatedNets: set.Of(netID),
|
|
trackedNets: make(set.Set[ids.ID]),
|
|
}
|
|
tracker.net[netID] = &gossipableNet{
|
|
numGossipableIPs: tracker.numGossipableIPs,
|
|
manuallyGossipable: set.Of(ip.NodeID),
|
|
gossipableIDs: set.Of(ip.NodeID),
|
|
gossipableIndices: make(map[ids.NodeID]int),
|
|
}
|
|
},
|
|
},
|
|
{
|
|
name: "connected tracked non-validator",
|
|
initialState: func(t *testing.T) *ipTracker {
|
|
tracker := newTestIPTracker(t)
|
|
tracker.Connected(ip, set.Of(constants.PrimaryNetworkID))
|
|
return tracker
|
|
},
|
|
netID: constants.PrimaryNetworkID,
|
|
expectedChange: func(tracker *ipTracker) {
|
|
tracker.numTrackedPeers.Inc()
|
|
tracker.numGossipableIPs.Inc()
|
|
tracker.numTrackedNets.Inc()
|
|
tracker.tracked[ip.NodeID] = &trackedNode{
|
|
manuallyTracked: true,
|
|
validatedNets: set.Of(constants.PrimaryNetworkID),
|
|
trackedNets: set.Of(constants.PrimaryNetworkID),
|
|
ip: ip,
|
|
}
|
|
tracker.bloomAdditions[ip.NodeID] = 1
|
|
tracker.net[constants.PrimaryNetworkID] = &gossipableNet{
|
|
numGossipableIPs: tracker.numGossipableIPs,
|
|
manuallyGossipable: set.Of(ip.NodeID),
|
|
gossipableIDs: set.Of(ip.NodeID),
|
|
gossipableIndices: map[ids.NodeID]int{
|
|
ip.NodeID: 0,
|
|
},
|
|
gossipableIPs: []*endpoints.ClaimedIPPort{
|
|
ip,
|
|
},
|
|
}
|
|
},
|
|
},
|
|
{
|
|
name: "connected untracked non-validator",
|
|
initialState: func(t *testing.T) *ipTracker {
|
|
tracker := newTestIPTracker(t)
|
|
tracker.Connected(ip, set.Of(constants.PrimaryNetworkID))
|
|
return tracker
|
|
},
|
|
netID: netID,
|
|
expectedChange: func(tracker *ipTracker) {
|
|
tracker.numTrackedPeers.Inc()
|
|
tracker.numTrackedNets.Inc()
|
|
tracker.tracked[ip.NodeID] = &trackedNode{
|
|
validatedNets: set.Of(netID),
|
|
trackedNets: make(set.Set[ids.ID]),
|
|
ip: ip,
|
|
}
|
|
tracker.bloomAdditions[ip.NodeID] = 1
|
|
tracker.net[netID] = &gossipableNet{
|
|
numGossipableIPs: tracker.numGossipableIPs,
|
|
manuallyGossipable: set.Of(ip.NodeID),
|
|
gossipableIDs: set.Of(ip.NodeID),
|
|
gossipableIndices: make(map[ids.NodeID]int),
|
|
}
|
|
},
|
|
},
|
|
{
|
|
name: "non-connected tracked validator",
|
|
initialState: func(t *testing.T) *ipTracker {
|
|
tracker := newTestIPTracker(t)
|
|
tracker.OnValidatorAdded(constants.PrimaryNetworkID, ip.NodeID, 0)
|
|
return tracker
|
|
},
|
|
netID: constants.PrimaryNetworkID,
|
|
expectedChange: func(tracker *ipTracker) {
|
|
tracker.tracked[ip.NodeID].manuallyTracked = true
|
|
tracker.net[constants.PrimaryNetworkID].manuallyGossipable = set.Of(ip.NodeID)
|
|
},
|
|
},
|
|
{
|
|
name: "non-connected untracked validator",
|
|
initialState: func(t *testing.T) *ipTracker {
|
|
tracker := newTestIPTracker(t)
|
|
tracker.OnValidatorAdded(netID, ip.NodeID, 0)
|
|
return tracker
|
|
},
|
|
netID: netID,
|
|
expectedChange: func(tracker *ipTracker) {
|
|
tracker.net[netID].manuallyGossipable = set.Of(ip.NodeID)
|
|
},
|
|
},
|
|
{
|
|
name: "connected tracked validator",
|
|
initialState: func(t *testing.T) *ipTracker {
|
|
tracker := newTestIPTracker(t)
|
|
tracker.Connected(ip, set.Of(constants.PrimaryNetworkID))
|
|
tracker.OnValidatorAdded(constants.PrimaryNetworkID, ip.NodeID, 0)
|
|
return tracker
|
|
},
|
|
netID: constants.PrimaryNetworkID,
|
|
expectedChange: func(tracker *ipTracker) {
|
|
tracker.tracked[ip.NodeID].manuallyTracked = true
|
|
tracker.net[constants.PrimaryNetworkID].manuallyGossipable = set.Of(ip.NodeID)
|
|
},
|
|
},
|
|
{
|
|
name: "connected untracked validator",
|
|
initialState: func(t *testing.T) *ipTracker {
|
|
tracker := newTestIPTracker(t)
|
|
tracker.Connected(ip, set.Of(constants.PrimaryNetworkID))
|
|
tracker.OnValidatorAdded(netID, ip.NodeID, 0)
|
|
return tracker
|
|
},
|
|
netID: netID,
|
|
expectedChange: func(tracker *ipTracker) {
|
|
tracker.net[netID].manuallyGossipable = set.Of(ip.NodeID)
|
|
},
|
|
},
|
|
}
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
testState := test.initialState(t)
|
|
expectedState := test.initialState(t)
|
|
|
|
testState.ManuallyGossip(test.netID, ip.NodeID)
|
|
test.expectedChange(expectedState)
|
|
|
|
requireEqual(t, expectedState, testState)
|
|
requireMetricsConsistent(t, testState)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestIPTracker_ShouldVerifyIP(t *testing.T) {
|
|
newerIP := newerTestIP(ip)
|
|
tests := []struct {
|
|
name string
|
|
tracker func(t *testing.T) *ipTracker
|
|
ip *endpoints.ClaimedIPPort
|
|
expectedTrackAllNets bool
|
|
expectedTrackRequestedNets bool
|
|
}{
|
|
{
|
|
name: "node not tracked",
|
|
tracker: newTestIPTracker,
|
|
ip: ip,
|
|
expectedTrackAllNets: false,
|
|
expectedTrackRequestedNets: false,
|
|
},
|
|
{
|
|
name: "undesired connection",
|
|
tracker: func(t *testing.T) *ipTracker {
|
|
tracker := newTestIPTracker(t)
|
|
tracker.OnValidatorAdded(ids.GenerateTestID(), ip.NodeID, 0)
|
|
return tracker
|
|
},
|
|
ip: ip,
|
|
expectedTrackAllNets: true,
|
|
expectedTrackRequestedNets: false,
|
|
},
|
|
{
|
|
name: "desired connection first IP",
|
|
tracker: func(t *testing.T) *ipTracker {
|
|
tracker := newTestIPTracker(t)
|
|
tracker.OnValidatorAdded(constants.PrimaryNetworkID, ip.NodeID, 0)
|
|
return tracker
|
|
},
|
|
ip: ip,
|
|
expectedTrackAllNets: true,
|
|
expectedTrackRequestedNets: true,
|
|
},
|
|
{
|
|
name: "desired connection older IP",
|
|
tracker: func(t *testing.T) *ipTracker {
|
|
tracker := newTestIPTracker(t)
|
|
tracker.OnValidatorAdded(constants.PrimaryNetworkID, ip.NodeID, 0)
|
|
require.True(t, tracker.AddIP(newerIP))
|
|
return tracker
|
|
},
|
|
ip: ip,
|
|
expectedTrackAllNets: false,
|
|
expectedTrackRequestedNets: false,
|
|
},
|
|
{
|
|
name: "desired connection same IP",
|
|
tracker: func(t *testing.T) *ipTracker {
|
|
tracker := newTestIPTracker(t)
|
|
tracker.OnValidatorAdded(constants.PrimaryNetworkID, ip.NodeID, 0)
|
|
require.True(t, tracker.AddIP(ip))
|
|
return tracker
|
|
},
|
|
ip: ip,
|
|
expectedTrackAllNets: false,
|
|
expectedTrackRequestedNets: false,
|
|
},
|
|
{
|
|
name: "desired connection newer IP",
|
|
tracker: func(t *testing.T) *ipTracker {
|
|
tracker := newTestIPTracker(t)
|
|
tracker.OnValidatorAdded(constants.PrimaryNetworkID, ip.NodeID, 0)
|
|
require.True(t, tracker.AddIP(ip))
|
|
return tracker
|
|
},
|
|
ip: newerIP,
|
|
expectedTrackAllNets: true,
|
|
expectedTrackRequestedNets: true,
|
|
},
|
|
}
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
require := require.New(t)
|
|
|
|
tracker := test.tracker(t)
|
|
require.Equal(test.expectedTrackAllNets, tracker.ShouldVerifyIP(test.ip, true))
|
|
require.Equal(test.expectedTrackRequestedNets, tracker.ShouldVerifyIP(test.ip, false))
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestIPTracker_AddIP(t *testing.T) {
|
|
netID := ids.GenerateTestID()
|
|
newerIP := newerTestIP(ip)
|
|
tests := []struct {
|
|
name string
|
|
initialState func(t *testing.T) *ipTracker
|
|
ip *endpoints.ClaimedIPPort
|
|
expectedChange func(*ipTracker)
|
|
expectedUpdatedAndDesired bool
|
|
}{
|
|
{
|
|
name: "non-validator",
|
|
initialState: newTestIPTracker,
|
|
ip: ip,
|
|
expectedChange: func(*ipTracker) {},
|
|
expectedUpdatedAndDesired: false,
|
|
},
|
|
{
|
|
name: "first known IP of tracked node",
|
|
initialState: func(t *testing.T) *ipTracker {
|
|
tracker := newTestIPTracker(t)
|
|
tracker.OnValidatorAdded(constants.PrimaryNetworkID, ip.NodeID, 0)
|
|
return tracker
|
|
},
|
|
ip: ip,
|
|
expectedChange: func(tracker *ipTracker) {
|
|
tracker.tracked[ip.NodeID].ip = ip
|
|
tracker.bloomAdditions[ip.NodeID] = 1
|
|
},
|
|
expectedUpdatedAndDesired: true,
|
|
},
|
|
{
|
|
name: "first known IP of untracked node",
|
|
initialState: func(t *testing.T) *ipTracker {
|
|
tracker := newTestIPTracker(t)
|
|
tracker.OnValidatorAdded(netID, ip.NodeID, 0)
|
|
return tracker
|
|
},
|
|
ip: ip,
|
|
expectedUpdatedAndDesired: false,
|
|
expectedChange: func(tracker *ipTracker) {
|
|
tracker.tracked[ip.NodeID].ip = ip
|
|
tracker.bloomAdditions[ip.NodeID] = 1
|
|
},
|
|
},
|
|
{
|
|
name: "older IP of tracked node",
|
|
initialState: func(t *testing.T) *ipTracker {
|
|
tracker := newTestIPTracker(t)
|
|
tracker.OnValidatorAdded(constants.PrimaryNetworkID, ip.NodeID, 0)
|
|
require.True(t, tracker.AddIP(newerIP))
|
|
return tracker
|
|
},
|
|
ip: ip,
|
|
expectedUpdatedAndDesired: false,
|
|
expectedChange: func(*ipTracker) {},
|
|
},
|
|
{
|
|
name: "older IP of untracked node",
|
|
initialState: func(t *testing.T) *ipTracker {
|
|
tracker := newTestIPTracker(t)
|
|
tracker.OnValidatorAdded(netID, ip.NodeID, 0)
|
|
require.False(t, tracker.AddIP(newerIP))
|
|
return tracker
|
|
},
|
|
ip: ip,
|
|
expectedUpdatedAndDesired: false,
|
|
expectedChange: func(*ipTracker) {},
|
|
},
|
|
{
|
|
name: "same IP of tracked node",
|
|
initialState: func(t *testing.T) *ipTracker {
|
|
tracker := newTestIPTracker(t)
|
|
tracker.OnValidatorAdded(constants.PrimaryNetworkID, ip.NodeID, 0)
|
|
require.True(t, tracker.AddIP(ip))
|
|
return tracker
|
|
},
|
|
ip: ip,
|
|
expectedUpdatedAndDesired: false,
|
|
expectedChange: func(*ipTracker) {},
|
|
},
|
|
{
|
|
name: "same IP of untracked node",
|
|
initialState: func(t *testing.T) *ipTracker {
|
|
tracker := newTestIPTracker(t)
|
|
tracker.OnValidatorAdded(netID, ip.NodeID, 0)
|
|
require.False(t, tracker.AddIP(ip))
|
|
return tracker
|
|
},
|
|
ip: ip,
|
|
expectedUpdatedAndDesired: false,
|
|
expectedChange: func(*ipTracker) {},
|
|
},
|
|
{
|
|
name: "disconnected newer IP of tracked node",
|
|
initialState: func(t *testing.T) *ipTracker {
|
|
tracker := newTestIPTracker(t)
|
|
tracker.OnValidatorAdded(constants.PrimaryNetworkID, ip.NodeID, 0)
|
|
require.True(t, tracker.AddIP(ip))
|
|
return tracker
|
|
},
|
|
ip: newerIP,
|
|
expectedUpdatedAndDesired: true,
|
|
expectedChange: func(tracker *ipTracker) {
|
|
tracker.tracked[newerIP.NodeID].ip = newerIP
|
|
tracker.bloomAdditions[newerIP.NodeID] = 2
|
|
},
|
|
},
|
|
{
|
|
name: "disconnected newer IP of untracked node",
|
|
initialState: func(t *testing.T) *ipTracker {
|
|
tracker := newTestIPTracker(t)
|
|
tracker.OnValidatorAdded(netID, ip.NodeID, 0)
|
|
require.False(t, tracker.AddIP(ip))
|
|
return tracker
|
|
},
|
|
ip: newerIP,
|
|
expectedUpdatedAndDesired: false,
|
|
expectedChange: func(tracker *ipTracker) {
|
|
tracker.tracked[newerIP.NodeID].ip = newerIP
|
|
tracker.bloomAdditions[newerIP.NodeID] = 2
|
|
},
|
|
},
|
|
{
|
|
name: "connected newer IP of tracked node",
|
|
initialState: func(t *testing.T) *ipTracker {
|
|
tracker := newTestIPTracker(t)
|
|
tracker.OnValidatorAdded(constants.PrimaryNetworkID, ip.NodeID, 0)
|
|
tracker.Connected(ip, set.Of(constants.PrimaryNetworkID))
|
|
return tracker
|
|
},
|
|
ip: newerIP,
|
|
expectedUpdatedAndDesired: true,
|
|
expectedChange: func(tracker *ipTracker) {
|
|
tracker.tracked[newerIP.NodeID].ip = newerIP
|
|
tracker.bloomAdditions[newerIP.NodeID] = 2
|
|
tracker.net[constants.PrimaryNetworkID].gossipableIPs[0] = newerIP
|
|
},
|
|
},
|
|
{
|
|
name: "connected newer IP of untracked node",
|
|
initialState: func(t *testing.T) *ipTracker {
|
|
tracker := newTestIPTracker(t)
|
|
tracker.OnValidatorAdded(netID, ip.NodeID, 0)
|
|
tracker.Connected(ip, set.Of(constants.PrimaryNetworkID))
|
|
return tracker
|
|
},
|
|
ip: newerIP,
|
|
expectedUpdatedAndDesired: false,
|
|
expectedChange: func(tracker *ipTracker) {
|
|
tracker.tracked[newerIP.NodeID].ip = newerIP
|
|
tracker.bloomAdditions[newerIP.NodeID] = 2
|
|
},
|
|
},
|
|
}
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
testState := test.initialState(t)
|
|
expectedState := test.initialState(t)
|
|
|
|
updated := testState.AddIP(test.ip)
|
|
test.expectedChange(expectedState)
|
|
|
|
require.Equal(t, test.expectedUpdatedAndDesired, updated)
|
|
requireEqual(t, expectedState, testState)
|
|
requireMetricsConsistent(t, testState)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestIPTracker_Connected(t *testing.T) {
|
|
netID := ids.GenerateTestID()
|
|
newerIP := newerTestIP(ip)
|
|
tests := []struct {
|
|
name string
|
|
initialState func(t *testing.T) *ipTracker
|
|
ip *endpoints.ClaimedIPPort
|
|
expectedChange func(*ipTracker)
|
|
}{
|
|
{
|
|
name: "non-validator",
|
|
initialState: newTestIPTracker,
|
|
ip: ip,
|
|
expectedChange: func(tracker *ipTracker) {
|
|
tracker.connected[ip.NodeID] = &connectedNode{
|
|
trackedNets: set.Of(constants.PrimaryNetworkID),
|
|
ip: ip,
|
|
}
|
|
},
|
|
},
|
|
{
|
|
name: "first known IP of node tracking net",
|
|
initialState: func(t *testing.T) *ipTracker {
|
|
tracker := newTestIPTracker(t)
|
|
tracker.OnValidatorAdded(constants.PrimaryNetworkID, ip.NodeID, 0)
|
|
return tracker
|
|
},
|
|
ip: ip,
|
|
expectedChange: func(tracker *ipTracker) {
|
|
tracker.numGossipableIPs.Inc()
|
|
tracker.tracked[ip.NodeID].ip = ip
|
|
tracker.bloomAdditions[ip.NodeID] = 1
|
|
tracker.connected[ip.NodeID] = &connectedNode{
|
|
trackedNets: set.Of(constants.PrimaryNetworkID),
|
|
ip: ip,
|
|
}
|
|
|
|
net := tracker.net[constants.PrimaryNetworkID]
|
|
net.gossipableIndices[ip.NodeID] = 0
|
|
net.gossipableIPs = []*endpoints.ClaimedIPPort{
|
|
ip,
|
|
}
|
|
},
|
|
},
|
|
{
|
|
name: "first known IP of node not tracking net",
|
|
initialState: func(t *testing.T) *ipTracker {
|
|
tracker := newTestIPTracker(t)
|
|
tracker.OnValidatorAdded(netID, ip.NodeID, 0)
|
|
return tracker
|
|
},
|
|
ip: ip,
|
|
expectedChange: func(tracker *ipTracker) {
|
|
tracker.tracked[ip.NodeID].ip = ip
|
|
tracker.bloomAdditions[ip.NodeID] = 1
|
|
tracker.connected[ip.NodeID] = &connectedNode{
|
|
trackedNets: set.Of(constants.PrimaryNetworkID),
|
|
ip: ip,
|
|
}
|
|
},
|
|
},
|
|
{
|
|
name: "connected with older IP of node tracking net",
|
|
initialState: func(t *testing.T) *ipTracker {
|
|
tracker := newTestIPTracker(t)
|
|
tracker.OnValidatorAdded(constants.PrimaryNetworkID, ip.NodeID, 0)
|
|
require.True(t, tracker.AddIP(newerIP))
|
|
return tracker
|
|
},
|
|
ip: ip,
|
|
expectedChange: func(tracker *ipTracker) {
|
|
tracker.numGossipableIPs.Inc()
|
|
tracker.connected[ip.NodeID] = &connectedNode{
|
|
trackedNets: set.Of(constants.PrimaryNetworkID),
|
|
ip: ip,
|
|
}
|
|
|
|
net := tracker.net[constants.PrimaryNetworkID]
|
|
net.gossipableIndices[newerIP.NodeID] = 0
|
|
net.gossipableIPs = []*endpoints.ClaimedIPPort{
|
|
newerIP,
|
|
}
|
|
},
|
|
},
|
|
{
|
|
name: "connected with older IP of node not tracking net",
|
|
initialState: func(t *testing.T) *ipTracker {
|
|
tracker := newTestIPTracker(t)
|
|
tracker.OnValidatorAdded(netID, ip.NodeID, 0)
|
|
require.False(t, tracker.AddIP(newerIP))
|
|
return tracker
|
|
},
|
|
ip: ip,
|
|
expectedChange: func(tracker *ipTracker) {
|
|
tracker.connected[ip.NodeID] = &connectedNode{
|
|
trackedNets: set.Of(constants.PrimaryNetworkID),
|
|
ip: ip,
|
|
}
|
|
},
|
|
},
|
|
{
|
|
name: "connected with newer IP of node tracking net",
|
|
initialState: func(t *testing.T) *ipTracker {
|
|
tracker := newTestIPTracker(t)
|
|
tracker.OnValidatorAdded(constants.PrimaryNetworkID, ip.NodeID, 0)
|
|
require.True(t, tracker.AddIP(ip))
|
|
return tracker
|
|
},
|
|
ip: newerIP,
|
|
expectedChange: func(tracker *ipTracker) {
|
|
tracker.numGossipableIPs.Inc()
|
|
tracker.tracked[newerIP.NodeID].ip = newerIP
|
|
tracker.bloomAdditions[newerIP.NodeID] = 2
|
|
tracker.connected[newerIP.NodeID] = &connectedNode{
|
|
trackedNets: set.Of(constants.PrimaryNetworkID),
|
|
ip: newerIP,
|
|
}
|
|
|
|
net := tracker.net[constants.PrimaryNetworkID]
|
|
net.gossipableIndices[newerIP.NodeID] = 0
|
|
net.gossipableIPs = []*endpoints.ClaimedIPPort{
|
|
newerIP,
|
|
}
|
|
},
|
|
},
|
|
{
|
|
name: "connected with newer IP of node not tracking net",
|
|
initialState: func(t *testing.T) *ipTracker {
|
|
tracker := newTestIPTracker(t)
|
|
tracker.OnValidatorAdded(netID, ip.NodeID, 0)
|
|
require.False(t, tracker.AddIP(ip))
|
|
return tracker
|
|
},
|
|
ip: newerIP,
|
|
expectedChange: func(tracker *ipTracker) {
|
|
tracker.tracked[newerIP.NodeID].ip = newerIP
|
|
tracker.bloomAdditions[newerIP.NodeID] = 2
|
|
tracker.connected[newerIP.NodeID] = &connectedNode{
|
|
trackedNets: set.Of(constants.PrimaryNetworkID),
|
|
ip: newerIP,
|
|
}
|
|
},
|
|
},
|
|
{
|
|
name: "connected with same IP of node tracking net",
|
|
initialState: func(t *testing.T) *ipTracker {
|
|
tracker := newTestIPTracker(t)
|
|
tracker.OnValidatorAdded(constants.PrimaryNetworkID, ip.NodeID, 0)
|
|
require.True(t, tracker.AddIP(ip))
|
|
return tracker
|
|
},
|
|
ip: ip,
|
|
expectedChange: func(tracker *ipTracker) {
|
|
tracker.numGossipableIPs.Inc()
|
|
tracker.connected[ip.NodeID] = &connectedNode{
|
|
trackedNets: set.Of(constants.PrimaryNetworkID),
|
|
ip: ip,
|
|
}
|
|
|
|
net := tracker.net[constants.PrimaryNetworkID]
|
|
net.gossipableIndices[ip.NodeID] = 0
|
|
net.gossipableIPs = []*endpoints.ClaimedIPPort{
|
|
ip,
|
|
}
|
|
},
|
|
},
|
|
{
|
|
name: "connected with same IP of node not tracking net",
|
|
initialState: func(t *testing.T) *ipTracker {
|
|
tracker := newTestIPTracker(t)
|
|
tracker.OnValidatorAdded(netID, ip.NodeID, 0)
|
|
require.False(t, tracker.AddIP(ip))
|
|
return tracker
|
|
},
|
|
ip: ip,
|
|
expectedChange: func(tracker *ipTracker) {
|
|
tracker.connected[ip.NodeID] = &connectedNode{
|
|
trackedNets: set.Of(constants.PrimaryNetworkID),
|
|
ip: ip,
|
|
}
|
|
},
|
|
},
|
|
}
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
testState := test.initialState(t)
|
|
expectedState := test.initialState(t)
|
|
|
|
testState.Connected(test.ip, set.Of(constants.PrimaryNetworkID))
|
|
test.expectedChange(expectedState)
|
|
|
|
requireEqual(t, expectedState, testState)
|
|
requireMetricsConsistent(t, testState)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestIPTracker_Disconnected(t *testing.T) {
|
|
netID := ids.GenerateTestID()
|
|
tests := []struct {
|
|
name string
|
|
initialState func(t *testing.T) *ipTracker
|
|
expectedChange func(*ipTracker)
|
|
}{
|
|
{
|
|
name: "not gossipable",
|
|
initialState: func(t *testing.T) *ipTracker {
|
|
tracker := newTestIPTracker(t)
|
|
tracker.Connected(ip, set.Of(constants.PrimaryNetworkID))
|
|
return tracker
|
|
},
|
|
expectedChange: func(*ipTracker) {},
|
|
},
|
|
{
|
|
name: "latest gossipable",
|
|
initialState: func(t *testing.T) *ipTracker {
|
|
tracker := newTestIPTracker(t)
|
|
tracker.OnValidatorAdded(constants.PrimaryNetworkID, ip.NodeID, 0)
|
|
tracker.Connected(ip, set.Of(constants.PrimaryNetworkID))
|
|
return tracker
|
|
},
|
|
expectedChange: func(tracker *ipTracker) {
|
|
tracker.numGossipableIPs.Dec()
|
|
delete(tracker.connected, ip.NodeID)
|
|
|
|
net := tracker.net[constants.PrimaryNetworkID]
|
|
delete(net.gossipableIndices, ip.NodeID)
|
|
net.gossipableIPs = net.gossipableIPs[:0]
|
|
},
|
|
},
|
|
{
|
|
name: "non-latest gossipable",
|
|
initialState: func(t *testing.T) *ipTracker {
|
|
tracker := newTestIPTracker(t)
|
|
tracker.OnValidatorAdded(constants.PrimaryNetworkID, ip.NodeID, 0)
|
|
tracker.Connected(ip, set.Of(constants.PrimaryNetworkID))
|
|
tracker.OnValidatorAdded(constants.PrimaryNetworkID, otherIP.NodeID, 0)
|
|
tracker.Connected(otherIP, set.Of(constants.PrimaryNetworkID))
|
|
return tracker
|
|
},
|
|
expectedChange: func(tracker *ipTracker) {
|
|
tracker.numGossipableIPs.Dec()
|
|
delete(tracker.connected, ip.NodeID)
|
|
|
|
net := tracker.net[constants.PrimaryNetworkID]
|
|
net.gossipableIndices = map[ids.NodeID]int{
|
|
otherIP.NodeID: 0,
|
|
}
|
|
net.gossipableIPs = []*endpoints.ClaimedIPPort{
|
|
otherIP,
|
|
}
|
|
},
|
|
},
|
|
{
|
|
name: "remove multiple gossipable IPs",
|
|
initialState: func(t *testing.T) *ipTracker {
|
|
tracker := newTestIPTracker(t)
|
|
tracker.OnValidatorAdded(constants.PrimaryNetworkID, ip.NodeID, 0)
|
|
tracker.OnValidatorAdded(netID, ip.NodeID, 0)
|
|
tracker.Connected(ip, set.Of(constants.PrimaryNetworkID, netID))
|
|
return tracker
|
|
},
|
|
expectedChange: func(tracker *ipTracker) {
|
|
tracker.numGossipableIPs.Add(-2)
|
|
delete(tracker.connected, ip.NodeID)
|
|
|
|
primaryNet := tracker.net[constants.PrimaryNetworkID]
|
|
delete(primaryNet.gossipableIndices, ip.NodeID)
|
|
primaryNet.gossipableIPs = primaryNet.gossipableIPs[:0]
|
|
|
|
net := tracker.net[netID]
|
|
delete(net.gossipableIndices, ip.NodeID)
|
|
net.gossipableIPs = net.gossipableIPs[:0]
|
|
},
|
|
},
|
|
}
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
testState := test.initialState(t)
|
|
expectedState := test.initialState(t)
|
|
|
|
testState.Disconnected(ip.NodeID)
|
|
expectedState.Disconnected(ip.NodeID)
|
|
|
|
requireEqual(t, expectedState, testState)
|
|
requireMetricsConsistent(t, testState)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestIPTracker_OnValidatorAdded(t *testing.T) {
|
|
newerIP := newerTestIP(ip)
|
|
netID := ids.GenerateTestID()
|
|
tests := []struct {
|
|
name string
|
|
initialState func(t *testing.T) *ipTracker
|
|
netID ids.ID
|
|
expectedChange func(*ipTracker)
|
|
}{
|
|
{
|
|
name: "manually tracked",
|
|
initialState: func(t *testing.T) *ipTracker {
|
|
tracker := newTestIPTracker(t)
|
|
tracker.ManuallyTrack(ip.NodeID)
|
|
return tracker
|
|
},
|
|
netID: constants.PrimaryNetworkID,
|
|
expectedChange: func(tracker *ipTracker) {
|
|
tracker.tracked[ip.NodeID].validatedNets.Add(constants.PrimaryNetworkID)
|
|
tracker.tracked[ip.NodeID].trackedNets.Add(constants.PrimaryNetworkID)
|
|
tracker.net[constants.PrimaryNetworkID] = &gossipableNet{
|
|
numGossipableIPs: tracker.numGossipableIPs,
|
|
manuallyGossipable: make(set.Set[ids.NodeID]),
|
|
gossipableIDs: set.Of(ip.NodeID),
|
|
gossipableIndices: make(map[ids.NodeID]int),
|
|
}
|
|
},
|
|
},
|
|
{
|
|
name: "manually tracked and connected",
|
|
initialState: func(t *testing.T) *ipTracker {
|
|
tracker := newTestIPTracker(t)
|
|
tracker.ManuallyTrack(ip.NodeID)
|
|
tracker.Connected(ip, set.Of(constants.PrimaryNetworkID))
|
|
return tracker
|
|
},
|
|
netID: constants.PrimaryNetworkID,
|
|
expectedChange: func(tracker *ipTracker) {
|
|
tracker.numGossipableIPs.Inc()
|
|
tracker.tracked[ip.NodeID].validatedNets.Add(constants.PrimaryNetworkID)
|
|
tracker.tracked[ip.NodeID].trackedNets.Add(constants.PrimaryNetworkID)
|
|
tracker.net[constants.PrimaryNetworkID] = &gossipableNet{
|
|
numGossipableIPs: tracker.numGossipableIPs,
|
|
manuallyGossipable: make(set.Set[ids.NodeID]),
|
|
gossipableIDs: set.Of(ip.NodeID),
|
|
gossipableIndices: map[ids.NodeID]int{
|
|
ip.NodeID: 0,
|
|
},
|
|
gossipableIPs: []*endpoints.ClaimedIPPort{
|
|
ip,
|
|
},
|
|
}
|
|
},
|
|
},
|
|
{
|
|
name: "manually tracked and connected with older IP",
|
|
initialState: func(t *testing.T) *ipTracker {
|
|
tracker := newTestIPTracker(t)
|
|
tracker.ManuallyTrack(ip.NodeID)
|
|
tracker.Connected(ip, set.Of(constants.PrimaryNetworkID))
|
|
require.True(t, tracker.AddIP(newerIP))
|
|
return tracker
|
|
},
|
|
netID: constants.PrimaryNetworkID,
|
|
expectedChange: func(tracker *ipTracker) {
|
|
tracker.numGossipableIPs.Inc()
|
|
tracker.tracked[ip.NodeID].validatedNets.Add(constants.PrimaryNetworkID)
|
|
tracker.tracked[ip.NodeID].trackedNets.Add(constants.PrimaryNetworkID)
|
|
tracker.net[constants.PrimaryNetworkID] = &gossipableNet{
|
|
numGossipableIPs: tracker.numGossipableIPs,
|
|
manuallyGossipable: make(set.Set[ids.NodeID]),
|
|
gossipableIDs: set.Of(ip.NodeID),
|
|
gossipableIndices: map[ids.NodeID]int{
|
|
ip.NodeID: 0,
|
|
},
|
|
gossipableIPs: []*endpoints.ClaimedIPPort{
|
|
newerIP,
|
|
},
|
|
}
|
|
},
|
|
},
|
|
{
|
|
name: "manually gossiped",
|
|
initialState: func(t *testing.T) *ipTracker {
|
|
tracker := newTestIPTracker(t)
|
|
tracker.ManuallyGossip(constants.PrimaryNetworkID, ip.NodeID)
|
|
return tracker
|
|
},
|
|
netID: constants.PrimaryNetworkID,
|
|
expectedChange: func(*ipTracker) {},
|
|
},
|
|
{
|
|
name: "disconnected",
|
|
initialState: newTestIPTracker,
|
|
netID: constants.PrimaryNetworkID,
|
|
expectedChange: func(tracker *ipTracker) {
|
|
tracker.tracked[ip.NodeID] = &trackedNode{
|
|
validatedNets: set.Of(constants.PrimaryNetworkID),
|
|
trackedNets: set.Of(constants.PrimaryNetworkID),
|
|
}
|
|
tracker.net[constants.PrimaryNetworkID] = &gossipableNet{
|
|
numGossipableIPs: tracker.numGossipableIPs,
|
|
manuallyGossipable: make(set.Set[ids.NodeID]),
|
|
gossipableIDs: set.Of(ip.NodeID),
|
|
gossipableIndices: make(map[ids.NodeID]int),
|
|
}
|
|
},
|
|
},
|
|
{
|
|
name: "connected",
|
|
initialState: func(t *testing.T) *ipTracker {
|
|
tracker := newTestIPTracker(t)
|
|
tracker.Connected(ip, set.Of(constants.PrimaryNetworkID))
|
|
return tracker
|
|
},
|
|
netID: constants.PrimaryNetworkID,
|
|
expectedChange: func(tracker *ipTracker) {
|
|
tracker.numGossipableIPs.Inc()
|
|
tracker.tracked[ip.NodeID] = &trackedNode{
|
|
validatedNets: set.Of(constants.PrimaryNetworkID),
|
|
trackedNets: set.Of(constants.PrimaryNetworkID),
|
|
ip: ip,
|
|
}
|
|
tracker.bloomAdditions[ip.NodeID] = 1
|
|
tracker.net[constants.PrimaryNetworkID] = &gossipableNet{
|
|
numGossipableIPs: tracker.numGossipableIPs,
|
|
manuallyGossipable: make(set.Set[ids.NodeID]),
|
|
gossipableIDs: set.Of(ip.NodeID),
|
|
gossipableIndices: map[ids.NodeID]int{
|
|
ip.NodeID: 0,
|
|
},
|
|
gossipableIPs: []*endpoints.ClaimedIPPort{
|
|
ip,
|
|
},
|
|
}
|
|
},
|
|
},
|
|
{
|
|
name: "connected to other net",
|
|
initialState: func(t *testing.T) *ipTracker {
|
|
tracker := newTestIPTracker(t)
|
|
tracker.Connected(ip, set.Of(constants.PrimaryNetworkID))
|
|
return tracker
|
|
},
|
|
netID: netID,
|
|
expectedChange: func(tracker *ipTracker) {
|
|
tracker.numTrackedNets.Inc()
|
|
tracker.tracked[ip.NodeID] = &trackedNode{
|
|
validatedNets: set.Of(netID),
|
|
trackedNets: make(set.Set[ids.ID]),
|
|
ip: ip,
|
|
}
|
|
tracker.bloomAdditions[ip.NodeID] = 1
|
|
tracker.net[netID] = &gossipableNet{
|
|
numGossipableIPs: tracker.numGossipableIPs,
|
|
manuallyGossipable: make(set.Set[ids.NodeID]),
|
|
gossipableIDs: set.Of(ip.NodeID),
|
|
gossipableIndices: make(map[ids.NodeID]int),
|
|
}
|
|
},
|
|
},
|
|
}
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
testState := test.initialState(t)
|
|
expectedState := test.initialState(t)
|
|
|
|
testState.OnValidatorAdded(test.netID, ip.NodeID, 0)
|
|
test.expectedChange(expectedState)
|
|
|
|
requireEqual(t, expectedState, testState)
|
|
requireMetricsConsistent(t, testState)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestIPTracker_OnValidatorRemoved(t *testing.T) {
|
|
netID := ids.GenerateTestID()
|
|
tests := []struct {
|
|
name string
|
|
initialState func(t *testing.T) *ipTracker
|
|
netID ids.ID
|
|
expectedChange func(*ipTracker)
|
|
}{
|
|
{
|
|
name: "remove last validator of net",
|
|
initialState: func(t *testing.T) *ipTracker {
|
|
tracker := newTestIPTracker(t)
|
|
tracker.OnValidatorAdded(constants.PrimaryNetworkID, ip.NodeID, 0)
|
|
return tracker
|
|
},
|
|
netID: constants.PrimaryNetworkID,
|
|
expectedChange: func(tracker *ipTracker) {
|
|
tracker.numTrackedPeers.Dec()
|
|
tracker.numTrackedNets.Dec()
|
|
delete(tracker.tracked, ip.NodeID)
|
|
delete(tracker.net, constants.PrimaryNetworkID)
|
|
},
|
|
},
|
|
{
|
|
name: "manually tracked not gossipable",
|
|
initialState: func(t *testing.T) *ipTracker {
|
|
tracker := newTestIPTracker(t)
|
|
tracker.ManuallyTrack(ip.NodeID)
|
|
tracker.OnValidatorAdded(constants.PrimaryNetworkID, ip.NodeID, 0)
|
|
require.True(t, tracker.AddIP(ip))
|
|
return tracker
|
|
},
|
|
netID: constants.PrimaryNetworkID,
|
|
expectedChange: func(tracker *ipTracker) {
|
|
tracker.numTrackedNets.Dec()
|
|
|
|
node := tracker.tracked[ip.NodeID]
|
|
node.validatedNets.Remove(constants.PrimaryNetworkID)
|
|
node.trackedNets.Remove(constants.PrimaryNetworkID)
|
|
|
|
delete(tracker.net, constants.PrimaryNetworkID)
|
|
},
|
|
},
|
|
{
|
|
name: "manually tracked latest gossipable",
|
|
initialState: func(t *testing.T) *ipTracker {
|
|
tracker := newTestIPTracker(t)
|
|
tracker.ManuallyTrack(ip.NodeID)
|
|
tracker.OnValidatorAdded(constants.PrimaryNetworkID, ip.NodeID, 0)
|
|
tracker.Connected(ip, set.Of(constants.PrimaryNetworkID))
|
|
return tracker
|
|
},
|
|
netID: constants.PrimaryNetworkID,
|
|
expectedChange: func(tracker *ipTracker) {
|
|
tracker.numGossipableIPs.Dec()
|
|
tracker.numTrackedNets.Dec()
|
|
|
|
node := tracker.tracked[ip.NodeID]
|
|
node.validatedNets.Remove(constants.PrimaryNetworkID)
|
|
node.trackedNets.Remove(constants.PrimaryNetworkID)
|
|
|
|
delete(tracker.net, constants.PrimaryNetworkID)
|
|
},
|
|
},
|
|
{
|
|
name: "manually gossiped",
|
|
initialState: func(t *testing.T) *ipTracker {
|
|
tracker := newTestIPTracker(t)
|
|
tracker.ManuallyGossip(constants.PrimaryNetworkID, ip.NodeID)
|
|
tracker.OnValidatorAdded(constants.PrimaryNetworkID, ip.NodeID, 0)
|
|
tracker.Connected(ip, set.Of(constants.PrimaryNetworkID))
|
|
return tracker
|
|
},
|
|
netID: constants.PrimaryNetworkID,
|
|
expectedChange: func(*ipTracker) {},
|
|
},
|
|
{
|
|
name: "manually gossiped on other net",
|
|
initialState: func(t *testing.T) *ipTracker {
|
|
tracker := newTestIPTracker(t)
|
|
tracker.ManuallyGossip(constants.PrimaryNetworkID, ip.NodeID)
|
|
tracker.OnValidatorAdded(netID, ip.NodeID, 0)
|
|
tracker.Connected(ip, set.Of(constants.PrimaryNetworkID))
|
|
return tracker
|
|
},
|
|
netID: netID,
|
|
expectedChange: func(tracker *ipTracker) {
|
|
tracker.numTrackedNets.Dec()
|
|
tracker.tracked[ip.NodeID].validatedNets.Remove(netID)
|
|
delete(tracker.net, netID)
|
|
},
|
|
},
|
|
{
|
|
name: "non-latest gossipable",
|
|
initialState: func(t *testing.T) *ipTracker {
|
|
tracker := newTestIPTracker(t)
|
|
tracker.OnValidatorAdded(constants.PrimaryNetworkID, ip.NodeID, 0)
|
|
tracker.Connected(ip, set.Of(constants.PrimaryNetworkID))
|
|
tracker.OnValidatorAdded(constants.PrimaryNetworkID, otherIP.NodeID, 0)
|
|
tracker.Connected(otherIP, set.Of(constants.PrimaryNetworkID))
|
|
return tracker
|
|
},
|
|
netID: constants.PrimaryNetworkID,
|
|
expectedChange: func(tracker *ipTracker) {
|
|
tracker.numTrackedPeers.Dec()
|
|
tracker.numGossipableIPs.Dec()
|
|
delete(tracker.tracked, ip.NodeID)
|
|
|
|
net := tracker.net[constants.PrimaryNetworkID]
|
|
net.gossipableIDs.Remove(ip.NodeID)
|
|
net.gossipableIndices = map[ids.NodeID]int{
|
|
otherIP.NodeID: 0,
|
|
}
|
|
net.gossipableIPs = []*endpoints.ClaimedIPPort{
|
|
otherIP,
|
|
}
|
|
},
|
|
},
|
|
}
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
testState := test.initialState(t)
|
|
expectedState := test.initialState(t)
|
|
|
|
testState.OnValidatorRemoved(test.netID, ip.NodeID, 0)
|
|
test.expectedChange(expectedState)
|
|
|
|
requireEqual(t, expectedState, testState)
|
|
requireMetricsConsistent(t, testState)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestIPTracker_BloomGrows(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
add func(tracker *ipTracker)
|
|
}{
|
|
{
|
|
name: "Add Validator",
|
|
add: func(tracker *ipTracker) {
|
|
tracker.OnValidatorAdded(constants.PrimaryNetworkID, ids.GenerateTestNodeID(), 0)
|
|
},
|
|
},
|
|
{
|
|
name: "Manually Track",
|
|
add: func(tracker *ipTracker) {
|
|
tracker.ManuallyTrack(ids.GenerateTestNodeID())
|
|
},
|
|
},
|
|
{
|
|
name: "Manually Gossip",
|
|
add: func(tracker *ipTracker) {
|
|
tracker.ManuallyGossip(ids.GenerateTestID(), ids.GenerateTestNodeID())
|
|
},
|
|
},
|
|
}
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
require := require.New(t)
|
|
|
|
tracker := newTestIPTracker(t)
|
|
initialMaxBloomCount := tracker.maxBloomCount
|
|
for i := 0; i < 2048; i++ {
|
|
test.add(tracker)
|
|
}
|
|
requireMetricsConsistent(t, tracker)
|
|
|
|
require.NoError(tracker.ResetBloom())
|
|
require.Greater(tracker.maxBloomCount, initialMaxBloomCount)
|
|
requireMetricsConsistent(t, tracker)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestIPTracker_BloomResetsDynamically(t *testing.T) {
|
|
require := require.New(t)
|
|
|
|
tracker := newTestIPTracker(t)
|
|
tracker.Connected(ip, set.Of(constants.PrimaryNetworkID))
|
|
tracker.OnValidatorAdded(constants.PrimaryNetworkID, ip.NodeID, 0)
|
|
tracker.OnValidatorRemoved(constants.PrimaryNetworkID, ip.NodeID, 0)
|
|
|
|
tracker.maxBloomCount = 1
|
|
tracker.Connected(otherIP, set.Of(constants.PrimaryNetworkID))
|
|
tracker.OnValidatorAdded(constants.PrimaryNetworkID, otherIP.NodeID, 0)
|
|
requireMetricsConsistent(t, tracker)
|
|
|
|
bloomBytes, salt := tracker.Bloom()
|
|
readFilter, err := bloom.Parse(bloomBytes)
|
|
require.NoError(err)
|
|
|
|
require.False(bloom.Contains(readFilter, ip.GossipID[:], salt))
|
|
require.True(bloom.Contains(readFilter, otherIP.GossipID[:], salt))
|
|
}
|
|
|
|
func TestIPTracker_PreventBloomFilterAddition(t *testing.T) {
|
|
require := require.New(t)
|
|
|
|
newerIP := newerTestIP(ip)
|
|
newestIP := newerTestIP(newerIP)
|
|
|
|
tracker := newTestIPTracker(t)
|
|
tracker.ManuallyGossip(constants.PrimaryNetworkID, ip.NodeID)
|
|
require.True(tracker.AddIP(ip))
|
|
require.True(tracker.AddIP(newerIP))
|
|
require.True(tracker.AddIP(newestIP))
|
|
require.Equal(maxIPEntriesPerNode, tracker.bloomAdditions[ip.NodeID])
|
|
requireMetricsConsistent(t, tracker)
|
|
}
|
|
|
|
func TestIPTracker_GetGossipableIPs(t *testing.T) {
|
|
netIDA := ids.GenerateTestID()
|
|
netIDB := ids.GenerateTestID()
|
|
unknownChainID := ids.GenerateTestID()
|
|
|
|
tracker := newTestIPTracker(t)
|
|
tracker.Connected(ip, set.Of(constants.PrimaryNetworkID, netIDA))
|
|
tracker.Connected(otherIP, set.Of(constants.PrimaryNetworkID, netIDA, netIDB))
|
|
tracker.OnValidatorAdded(constants.PrimaryNetworkID, ip.NodeID, 0)
|
|
tracker.OnValidatorAdded(netIDA, otherIP.NodeID, 0)
|
|
tracker.OnValidatorAdded(netIDB, otherIP.NodeID, 0)
|
|
|
|
myFilterBytes, mySalt := tracker.Bloom()
|
|
myFilter, err := bloom.Parse(myFilterBytes)
|
|
require.NoError(t, err)
|
|
|
|
tests := []struct {
|
|
name string
|
|
toIterate set.Set[ids.ID]
|
|
allowed set.Set[ids.ID]
|
|
nodeID ids.NodeID
|
|
filter *bloom.ReadFilter
|
|
salt []byte
|
|
expected []*endpoints.ClaimedIPPort
|
|
}{
|
|
{
|
|
name: "fetch both nets IPs",
|
|
toIterate: set.Of(constants.PrimaryNetworkID, netIDA),
|
|
allowed: set.Of(constants.PrimaryNetworkID, netIDA),
|
|
nodeID: ids.EmptyNodeID,
|
|
filter: bloom.EmptyFilter,
|
|
salt: nil,
|
|
expected: []*endpoints.ClaimedIPPort{ip, otherIP},
|
|
},
|
|
{
|
|
name: "filter nodeID",
|
|
toIterate: set.Of(constants.PrimaryNetworkID, netIDA),
|
|
allowed: set.Of(constants.PrimaryNetworkID, netIDA),
|
|
nodeID: ip.NodeID,
|
|
filter: bloom.EmptyFilter,
|
|
salt: nil,
|
|
expected: []*endpoints.ClaimedIPPort{otherIP},
|
|
},
|
|
{
|
|
name: "filter duplicate nodeIDs",
|
|
toIterate: set.Of(netIDA, netIDB),
|
|
allowed: set.Of(netIDA, netIDB),
|
|
nodeID: ids.EmptyNodeID,
|
|
filter: bloom.EmptyFilter,
|
|
salt: nil,
|
|
expected: []*endpoints.ClaimedIPPort{otherIP},
|
|
},
|
|
{
|
|
name: "filter known IPs",
|
|
toIterate: set.Of(constants.PrimaryNetworkID, netIDA),
|
|
allowed: set.Of(constants.PrimaryNetworkID, netIDA),
|
|
nodeID: ids.EmptyNodeID,
|
|
filter: func() *bloom.ReadFilter {
|
|
filter, err := bloom.New(8, 1024)
|
|
require.NoError(t, err)
|
|
bloom.Add(filter, ip.GossipID[:], nil)
|
|
|
|
readFilter, err := bloom.Parse(filter.Marshal())
|
|
require.NoError(t, err)
|
|
return readFilter
|
|
}(),
|
|
salt: nil,
|
|
expected: []*endpoints.ClaimedIPPort{otherIP},
|
|
},
|
|
{
|
|
name: "filter everything",
|
|
toIterate: set.Of(constants.PrimaryNetworkID, netIDA, netIDB),
|
|
allowed: set.Of(constants.PrimaryNetworkID, netIDA, netIDB),
|
|
nodeID: ids.EmptyNodeID,
|
|
filter: myFilter,
|
|
salt: mySalt,
|
|
expected: nil,
|
|
},
|
|
{
|
|
name: "only fetch primary network IPs",
|
|
toIterate: set.Of(constants.PrimaryNetworkID),
|
|
allowed: set.Of(constants.PrimaryNetworkID),
|
|
nodeID: ids.EmptyNodeID,
|
|
filter: bloom.EmptyFilter,
|
|
salt: nil,
|
|
expected: []*endpoints.ClaimedIPPort{ip},
|
|
},
|
|
{
|
|
name: "only fetch net IPs",
|
|
toIterate: set.Of(netIDA),
|
|
allowed: set.Of(netIDA),
|
|
nodeID: ids.EmptyNodeID,
|
|
filter: bloom.EmptyFilter,
|
|
salt: nil,
|
|
expected: []*endpoints.ClaimedIPPort{otherIP},
|
|
},
|
|
{
|
|
name: "filter net",
|
|
toIterate: set.Of(constants.PrimaryNetworkID, netIDA),
|
|
allowed: set.Of(constants.PrimaryNetworkID),
|
|
nodeID: ids.EmptyNodeID,
|
|
filter: bloom.EmptyFilter,
|
|
salt: nil,
|
|
expected: []*endpoints.ClaimedIPPort{ip},
|
|
},
|
|
{
|
|
name: "skip unknown net",
|
|
toIterate: set.Of(unknownChainID),
|
|
allowed: set.Of(unknownChainID),
|
|
nodeID: ids.EmptyNodeID,
|
|
filter: bloom.EmptyFilter,
|
|
salt: nil,
|
|
expected: nil,
|
|
},
|
|
}
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
gossipableIPs := getGossipableIPs(
|
|
tracker,
|
|
test.toIterate,
|
|
test.allowed.Contains,
|
|
test.nodeID,
|
|
test.filter,
|
|
test.salt,
|
|
2,
|
|
)
|
|
require.ElementsMatch(t, test.expected, gossipableIPs)
|
|
})
|
|
}
|
|
}
|