github.com/MetalBlockchain/metalgo@v1.11.9/network/example_test.go (about)

     1  // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package network
     5  
     6  import (
     7  	"context"
     8  	"os"
     9  	"time"
    10  
    11  	"go.uber.org/zap"
    12  
    13  	"github.com/MetalBlockchain/metalgo/genesis"
    14  	"github.com/MetalBlockchain/metalgo/ids"
    15  	"github.com/MetalBlockchain/metalgo/message"
    16  	"github.com/MetalBlockchain/metalgo/snow/networking/router"
    17  	"github.com/MetalBlockchain/metalgo/snow/validators"
    18  	"github.com/MetalBlockchain/metalgo/utils/constants"
    19  	"github.com/MetalBlockchain/metalgo/utils/logging"
    20  	"github.com/MetalBlockchain/metalgo/utils/set"
    21  	"github.com/MetalBlockchain/metalgo/version"
    22  )
    23  
    24  var _ router.ExternalHandler = (*testExternalHandler)(nil)
    25  
    26  // Note: all of the external handler's methods are called on peer goroutines. It
    27  // is possible for multiple concurrent calls to happen with different NodeIDs.
    28  // However, a given NodeID will only be performing one call at a time.
    29  type testExternalHandler struct {
    30  	log logging.Logger
    31  }
    32  
    33  // Note: HandleInbound will be called with raw P2P messages, the networking
    34  // implementation does not implicitly register timeouts, so this handler is only
    35  // called by messages explicitly sent by the peer. If timeouts are required,
    36  // that must be handled by the user of this utility.
    37  func (t *testExternalHandler) HandleInbound(_ context.Context, message message.InboundMessage) {
    38  	t.log.Info(
    39  		"receiving message",
    40  		zap.Stringer("op", message.Op()),
    41  	)
    42  }
    43  
    44  func (t *testExternalHandler) Connected(nodeID ids.NodeID, version *version.Application, subnetID ids.ID) {
    45  	t.log.Info(
    46  		"connected",
    47  		zap.Stringer("nodeID", nodeID),
    48  		zap.Stringer("version", version),
    49  		zap.Stringer("subnetID", subnetID),
    50  	)
    51  }
    52  
    53  func (t *testExternalHandler) Disconnected(nodeID ids.NodeID) {
    54  	t.log.Info(
    55  		"disconnected",
    56  		zap.Stringer("nodeID", nodeID),
    57  	)
    58  }
    59  
    60  type testAggressiveValidatorManager struct {
    61  	validators.Manager
    62  }
    63  
    64  func (*testAggressiveValidatorManager) Contains(ids.ID, ids.NodeID) bool {
    65  	return true
    66  }
    67  
    68  func ExampleNewTestNetwork() {
    69  	log := logging.NewLogger(
    70  		"networking",
    71  		logging.NewWrappedCore(
    72  			logging.Info,
    73  			os.Stdout,
    74  			logging.Colors.ConsoleEncoder(),
    75  		),
    76  	)
    77  
    78  	// Needs to be periodically updated by the caller to have the latest
    79  	// validator set
    80  	validators := &testAggressiveValidatorManager{
    81  		Manager: validators.NewManager(),
    82  	}
    83  
    84  	// If we want to be able to communicate with non-primary network subnets, we
    85  	// should register them here.
    86  	trackedSubnets := set.Set[ids.ID]{}
    87  
    88  	// Messages and connections are handled by the external handler.
    89  	handler := &testExternalHandler{
    90  		log: log,
    91  	}
    92  
    93  	network, err := NewTestNetwork(
    94  		log,
    95  		constants.TahoeID,
    96  		validators,
    97  		trackedSubnets,
    98  		handler,
    99  	)
   100  	if err != nil {
   101  		log.Fatal(
   102  			"failed to create test network",
   103  			zap.Error(err),
   104  		)
   105  		return
   106  	}
   107  
   108  	// We need to initially connect to some nodes in the network before peer
   109  	// gossip will enable connecting to all the remaining nodes in the network.
   110  	bootstrappers := genesis.SampleBootstrappers(constants.TahoeID, 5)
   111  	for _, bootstrapper := range bootstrappers {
   112  		network.ManuallyTrack(bootstrapper.ID, bootstrapper.IP)
   113  	}
   114  
   115  	// Typically network.StartClose() should be called based on receiving a
   116  	// SIGINT or SIGTERM. For the example, we close the network after 15s.
   117  	go log.RecoverAndPanic(func() {
   118  		time.Sleep(15 * time.Second)
   119  		network.StartClose()
   120  	})
   121  
   122  	// network.Send(...) and network.Gossip(...) can be used here to send
   123  	// messages to peers.
   124  
   125  	// Calling network.Dispatch() will block until a fatal error occurs or
   126  	// network.StartClose() is called.
   127  	err = network.Dispatch()
   128  	log.Info(
   129  		"network exited",
   130  		zap.Error(err),
   131  	)
   132  }