Files
node/network/example_test.go

174 lines
4.5 KiB
Go

// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package network
import (
"context"
"fmt"
"time"
"github.com/luxfi/log"
"github.com/luxfi/metric"
"github.com/luxfi/p2p"
validators "github.com/luxfi/validators"
"github.com/luxfi/constants"
"github.com/luxfi/ids"
"github.com/luxfi/math/set"
"github.com/luxfi/node/genesis/builder"
"github.com/luxfi/node/message"
"github.com/luxfi/node/version"
)
var _ ExternalHandler = (*testExternalHandler)(nil)
// Note: all of the external handler's methods are called on peer goroutines. It
// is possible for multiple concurrent calls to happen with different NodeIDs.
// However, a given NodeID will only be performing one call at a time.
type testExternalHandler struct {
log log.Logger
}
// Note: HandleInbound will be called with raw P2P messages, the networking
// implementation does not implicitly register timeouts, so this handler is only
// called by messages explicitly sent by the peer. If timeouts are required,
// that must be handled by the user of this utility.
func (t *testExternalHandler) HandleInbound(_ context.Context, msg message.InboundMessage) {
t.log.Info(
"receiving message",
"op", msg.Op(),
)
}
func (t *testExternalHandler) Connected(nodeID ids.NodeID, version *version.Application, netID ids.ID) {
t.log.Info(
"connected",
"nodeID", nodeID,
"version", version,
"netID", netID,
)
}
func (t *testExternalHandler) HandleGossip(_ context.Context, nodeID ids.NodeID, msg []byte) {
t.log.Info(
"received gossip",
"nodeID", nodeID,
"size", len(msg),
)
}
func (t *testExternalHandler) HandleTimeout(_ context.Context) {
t.log.Info("timeout occurred")
}
func (t *testExternalHandler) Disconnected(nodeID ids.NodeID) {
t.log.Info(
"disconnected",
"nodeID", nodeID,
)
}
func (t *testExternalHandler) Request(_ context.Context, nodeID ids.NodeID, requestID uint32, deadline time.Time, appRequestBytes []byte) error {
t.log.Info("Request", "nodeID", nodeID, "requestID", requestID)
return nil
}
func (t *testExternalHandler) RequestFailed(_ context.Context, nodeID ids.NodeID, requestID uint32, appErr *p2p.Error) error {
t.log.Info("RequestFailed", "nodeID", nodeID, "requestID", requestID)
return nil
}
func (t *testExternalHandler) Response(_ context.Context, nodeID ids.NodeID, requestID uint32, appResponseBytes []byte) error {
t.log.Info("Response", "nodeID", nodeID, "requestID", requestID)
return nil
}
func (t *testExternalHandler) Gossip(_ context.Context, nodeID ids.NodeID, appGossipBytes []byte) error {
t.log.Info("Gossip", "nodeID", nodeID)
return nil
}
type testAggressiveValidatorManager struct {
validators.Manager
}
func (*testAggressiveValidatorManager) Contains(ids.ID, ids.NodeID) bool {
return true
}
func ExampleNewTestNetwork() {
var log log.Logger
// Needs to be periodically updated by the caller to have the latest
// validator set
validators := &testAggressiveValidatorManager{
Manager: validators.NewManager(),
}
// If we want to be able to communicate with non-primary network nets, we
// should register them here.
trackedNets := make(set.Set[ids.ID])
// Messages and connections are handled by the external handler.
handler := &testExternalHandler{
log: log,
}
metrics := metric.NewRegistry()
cfg, err := NewTestNetworkConfig(
metrics,
constants.TestnetID,
validators,
trackedNets,
)
if err != nil {
panic(fmt.Sprintf("failed to create test network config: %v", err))
}
network, err := NewTestNetwork(
log,
metrics,
cfg,
handler,
)
if err != nil {
log.Error(
"failed to create test network",
"error", err,
)
return
}
// We need to initially connect to some nodes in the network before peer
// gossip will enable connecting to all the remaining nodes in the network.
bootstrappers, err := builder.SampleBootstrappers(constants.TestnetID, 5)
if err != nil {
log.Error(
"failed to get sample bootstrappers",
"error", err,
)
return
}
for _, bootstrapper := range bootstrappers {
network.ManuallyTrack(bootstrapper.ID, bootstrapper.Endpoint)
}
// Typically network.StartClose() should be called based on receiving a
// SIGINT or SIGTERM. For the example, we close the network after 15s.
go func() {
time.Sleep(15 * time.Second)
network.StartClose()
}()
// network.Send(...) and network.Gossip(...) can be used here to send
// messages to peers.
// Calling network.Dispatch() will block until a fatal error occurs or
// network.StartClose() is called.
err = network.Dispatch()
log.Info(
"network exited",
"error", err,
)
}