github.com/koko1123/flow-go-1@v0.29.6/network/internal/testutils/testUtil.go (about)

     1  package testutils
     2  
     3  import (
     4  	"context"
     5  	"reflect"
     6  	"runtime"
     7  	"strings"
     8  	"sync"
     9  	"testing"
    10  	"time"
    11  
    12  	dht "github.com/libp2p/go-libp2p-kad-dht"
    13  	"github.com/libp2p/go-libp2p/core/connmgr"
    14  	"github.com/libp2p/go-libp2p/core/host"
    15  	p2pNetwork "github.com/libp2p/go-libp2p/core/network"
    16  	"github.com/libp2p/go-libp2p/core/peer"
    17  	pc "github.com/libp2p/go-libp2p/core/protocol"
    18  	"github.com/libp2p/go-libp2p/core/routing"
    19  	"github.com/rs/zerolog"
    20  	"github.com/stretchr/testify/require"
    21  
    22  	"github.com/koko1123/flow-go-1/model/flow"
    23  	"github.com/koko1123/flow-go-1/model/flow/filter"
    24  	libp2pmessage "github.com/koko1123/flow-go-1/model/libp2p/message"
    25  	"github.com/koko1123/flow-go-1/module"
    26  	"github.com/koko1123/flow-go-1/module/id"
    27  	"github.com/koko1123/flow-go-1/module/irrecoverable"
    28  	"github.com/koko1123/flow-go-1/module/metrics"
    29  	"github.com/koko1123/flow-go-1/module/mock"
    30  	"github.com/koko1123/flow-go-1/module/observable"
    31  	"github.com/koko1123/flow-go-1/network"
    32  	netcache "github.com/koko1123/flow-go-1/network/cache"
    33  	"github.com/koko1123/flow-go-1/network/channels"
    34  	"github.com/koko1123/flow-go-1/network/codec/cbor"
    35  	"github.com/koko1123/flow-go-1/network/p2p"
    36  	"github.com/koko1123/flow-go-1/network/p2p/connection"
    37  	p2pdht "github.com/koko1123/flow-go-1/network/p2p/dht"
    38  	"github.com/koko1123/flow-go-1/network/p2p/middleware"
    39  	"github.com/koko1123/flow-go-1/network/p2p/p2pbuilder"
    40  	"github.com/koko1123/flow-go-1/network/p2p/subscription"
    41  	"github.com/koko1123/flow-go-1/network/p2p/translator"
    42  	"github.com/koko1123/flow-go-1/network/p2p/unicast"
    43  	"github.com/koko1123/flow-go-1/network/p2p/unicast/ratelimit"
    44  	"github.com/koko1123/flow-go-1/network/slashing"
    45  	"github.com/koko1123/flow-go-1/utils/unittest"
    46  	"github.com/onflow/flow-go/crypto"
    47  )
    48  
    49  var sporkID = unittest.IdentifierFixture()
    50  
    51  type PeerTag struct {
    52  	Peer peer.ID
    53  	Tag  string
    54  }
    55  
    56  type TagWatchingConnManager struct {
    57  	*connection.ConnManager
    58  	observers map[observable.Observer]struct{}
    59  	obsLock   sync.RWMutex
    60  }
    61  
    62  func (cwcm *TagWatchingConnManager) Subscribe(observer observable.Observer) {
    63  	cwcm.obsLock.Lock()
    64  	defer cwcm.obsLock.Unlock()
    65  	var void struct{}
    66  	cwcm.observers[observer] = void
    67  }
    68  
    69  func (cwcm *TagWatchingConnManager) Unsubscribe(observer observable.Observer) {
    70  	cwcm.obsLock.Lock()
    71  	defer cwcm.obsLock.Unlock()
    72  	delete(cwcm.observers, observer)
    73  }
    74  
    75  func (cwcm *TagWatchingConnManager) Protect(id peer.ID, tag string) {
    76  	cwcm.obsLock.RLock()
    77  	defer cwcm.obsLock.RUnlock()
    78  	cwcm.ConnManager.Protect(id, tag)
    79  	for obs := range cwcm.observers {
    80  		go obs.OnNext(PeerTag{Peer: id, Tag: tag})
    81  	}
    82  }
    83  
    84  func (cwcm *TagWatchingConnManager) Unprotect(id peer.ID, tag string) bool {
    85  	cwcm.obsLock.RLock()
    86  	defer cwcm.obsLock.RUnlock()
    87  	res := cwcm.ConnManager.Unprotect(id, tag)
    88  	for obs := range cwcm.observers {
    89  		go obs.OnNext(PeerTag{Peer: id, Tag: tag})
    90  	}
    91  	return res
    92  }
    93  
    94  func NewTagWatchingConnManager(log zerolog.Logger, metrics module.LibP2PConnectionMetrics) *TagWatchingConnManager {
    95  	cm := connection.NewConnManager(log, metrics)
    96  	return &TagWatchingConnManager{
    97  		ConnManager: cm,
    98  		observers:   make(map[observable.Observer]struct{}),
    99  		obsLock:     sync.RWMutex{},
   100  	}
   101  }
   102  
   103  // GenerateIDs is a test helper that generate flow identities with a valid port and libp2p nodes.
   104  func GenerateIDs(t *testing.T, logger zerolog.Logger, n int, opts ...func(*optsConfig)) (flow.IdentityList,
   105  	[]p2p.LibP2PNode,
   106  	[]observable.Observable) {
   107  	libP2PNodes := make([]p2p.LibP2PNode, n)
   108  	tagObservables := make([]observable.Observable, n)
   109  
   110  	o := &optsConfig{peerUpdateInterval: connection.DefaultPeerUpdateInterval}
   111  	for _, opt := range opts {
   112  		opt(o)
   113  	}
   114  
   115  	identities := unittest.IdentityListFixture(n, unittest.WithAllRoles())
   116  
   117  	for _, identity := range identities {
   118  		for _, idOpt := range o.idOpts {
   119  			idOpt(identity)
   120  		}
   121  	}
   122  
   123  	idProvider := id.NewFixedIdentityProvider(identities)
   124  
   125  	// generates keys and address for the node
   126  	for i, id := range identities {
   127  		// generate key
   128  		key, err := generateNetworkingKey(id.NodeID)
   129  		require.NoError(t, err)
   130  
   131  		var opts []nodeBuilderOption
   132  
   133  		opts = append(opts, withDHT(o.dhtPrefix, o.dhtOpts...))
   134  		opts = append(opts, withPeerManagerOptions(connection.ConnectionPruningEnabled, o.peerUpdateInterval))
   135  
   136  		libP2PNodes[i], tagObservables[i] = generateLibP2PNode(t, logger, key, idProvider, opts...)
   137  
   138  		_, port, err := libP2PNodes[i].GetIPPort()
   139  		require.NoError(t, err)
   140  
   141  		identities[i].Address = unittest.IPPort(port)
   142  		identities[i].NetworkPubKey = key.PublicKey()
   143  	}
   144  
   145  	return identities, libP2PNodes, tagObservables
   146  }
   147  
   148  // GenerateMiddlewares creates and initializes middleware instances for all the identities
   149  func GenerateMiddlewares(t *testing.T,
   150  	logger zerolog.Logger,
   151  	identities flow.IdentityList,
   152  	libP2PNodes []p2p.LibP2PNode,
   153  	codec network.Codec,
   154  	consumer slashing.ViolationsConsumer,
   155  	opts ...func(*optsConfig)) ([]network.Middleware, []*UpdatableIDProvider) {
   156  	mws := make([]network.Middleware, len(identities))
   157  	idProviders := make([]*UpdatableIDProvider, len(identities))
   158  	bitswapmet := metrics.NewNoopCollector()
   159  	o := &optsConfig{
   160  		peerUpdateInterval:  connection.DefaultPeerUpdateInterval,
   161  		unicastRateLimiters: ratelimit.NoopRateLimiters(),
   162  		networkMetrics:      metrics.NewNoopCollector(),
   163  	}
   164  
   165  	for _, opt := range opts {
   166  		opt(o)
   167  	}
   168  
   169  	total := len(identities)
   170  	for i := 0; i < total; i++ {
   171  		// casts libP2PNode instance to a local variable to avoid closure
   172  		node := libP2PNodes[i]
   173  		nodeId := identities[i].NodeID
   174  
   175  		idProviders[i] = NewUpdatableIDProvider(identities)
   176  
   177  		// creating middleware of nodes
   178  		mws[i] = middleware.NewMiddleware(
   179  			logger,
   180  			node,
   181  			nodeId,
   182  			bitswapmet,
   183  			sporkID,
   184  			middleware.DefaultUnicastTimeout,
   185  			translator.NewIdentityProviderIDTranslator(idProviders[i]),
   186  			codec,
   187  			consumer,
   188  			middleware.WithUnicastRateLimiters(o.unicastRateLimiters))
   189  	}
   190  	return mws, idProviders
   191  }
   192  
   193  // GenerateNetworks generates the network for the given middlewares
   194  func GenerateNetworks(t *testing.T,
   195  	log zerolog.Logger,
   196  	ids flow.IdentityList,
   197  	mws []network.Middleware,
   198  	sms []network.SubscriptionManager) []network.Network {
   199  	count := len(ids)
   200  	nets := make([]network.Network, 0)
   201  
   202  	for i := 0; i < count; i++ {
   203  
   204  		// creates and mocks me
   205  		me := &mock.Local{}
   206  		me.On("NodeID").Return(ids[i].NodeID)
   207  		me.On("NotMeFilter").Return(filter.Not(filter.HasNodeID(me.NodeID())))
   208  		me.On("Address").Return(ids[i].Address)
   209  
   210  		receiveCache := netcache.NewHeroReceiveCache(p2p.DefaultReceiveCacheSize, log, metrics.NewNoopCollector())
   211  
   212  		// create the network
   213  		net, err := p2p.NewNetwork(&p2p.NetworkParameters{
   214  			Logger:              log,
   215  			Codec:               cbor.NewCodec(),
   216  			Me:                  me,
   217  			MiddlewareFactory:   func() (network.Middleware, error) { return mws[i], nil },
   218  			Topology:            unittest.NetworkTopology(),
   219  			SubscriptionManager: sms[i],
   220  			Metrics:             metrics.NewNoopCollector(),
   221  			IdentityProvider:    id.NewFixedIdentityProvider(ids),
   222  			ReceiveCache:        receiveCache,
   223  		})
   224  		require.NoError(t, err)
   225  
   226  		nets = append(nets, net)
   227  	}
   228  
   229  	return nets
   230  }
   231  
   232  // GenerateIDsAndMiddlewares returns nodeIDs, libp2pNodes, middlewares, and observables which can be subscirbed to in order to witness protect events from pubsub
   233  func GenerateIDsAndMiddlewares(t *testing.T,
   234  	n int,
   235  	logger zerolog.Logger,
   236  	codec network.Codec,
   237  	consumer slashing.ViolationsConsumer,
   238  	opts ...func(*optsConfig)) (flow.IdentityList, []p2p.LibP2PNode, []network.Middleware, []observable.Observable, []*UpdatableIDProvider) {
   239  
   240  	ids, libP2PNodes, protectObservables := GenerateIDs(t, logger, n, opts...)
   241  	mws, providers := GenerateMiddlewares(t, logger, ids, libP2PNodes, codec, consumer, opts...)
   242  	return ids, libP2PNodes, mws, protectObservables, providers
   243  }
   244  
   245  type optsConfig struct {
   246  	idOpts              []func(*flow.Identity)
   247  	dhtPrefix           string
   248  	dhtOpts             []dht.Option
   249  	unicastRateLimiters *ratelimit.RateLimiters
   250  	peerUpdateInterval  time.Duration
   251  	networkMetrics      module.NetworkMetrics
   252  }
   253  
   254  func WithIdentityOpts(idOpts ...func(*flow.Identity)) func(*optsConfig) {
   255  	return func(o *optsConfig) {
   256  		o.idOpts = idOpts
   257  	}
   258  }
   259  
   260  func WithDHT(prefix string, dhtOpts ...dht.Option) func(*optsConfig) {
   261  	return func(o *optsConfig) {
   262  		o.dhtPrefix = prefix
   263  		o.dhtOpts = dhtOpts
   264  	}
   265  }
   266  
   267  func WithPeerUpdateInterval(interval time.Duration) func(*optsConfig) {
   268  	return func(o *optsConfig) {
   269  		o.peerUpdateInterval = interval
   270  	}
   271  }
   272  
   273  func WithUnicastRateLimiters(limiters *ratelimit.RateLimiters) func(*optsConfig) {
   274  	return func(o *optsConfig) {
   275  		o.unicastRateLimiters = limiters
   276  	}
   277  }
   278  
   279  func WithNetworkMetrics(m module.NetworkMetrics) func(*optsConfig) {
   280  	return func(o *optsConfig) {
   281  		o.networkMetrics = m
   282  	}
   283  }
   284  
   285  func GenerateIDsMiddlewaresNetworks(t *testing.T,
   286  	n int,
   287  	log zerolog.Logger,
   288  	codec network.Codec,
   289  	consumer slashing.ViolationsConsumer,
   290  	opts ...func(*optsConfig)) (flow.IdentityList, []p2p.LibP2PNode, []network.Middleware, []network.Network, []observable.Observable) {
   291  	ids, libp2pNodes, mws, observables, _ := GenerateIDsAndMiddlewares(t, n, log, codec, consumer, opts...)
   292  	sms := GenerateSubscriptionManagers(t, mws)
   293  	networks := GenerateNetworks(t, log, ids, mws, sms)
   294  
   295  	return ids, libp2pNodes, mws, networks, observables
   296  }
   297  
   298  // GenerateEngines generates MeshEngines for the given networks
   299  func GenerateEngines(t *testing.T, nets []network.Network) []*MeshEngine {
   300  	count := len(nets)
   301  	engs := make([]*MeshEngine, count)
   302  	for i, n := range nets {
   303  		eng := NewMeshEngine(t, n, 100, channels.TestNetworkChannel)
   304  		engs[i] = eng
   305  	}
   306  	return engs
   307  }
   308  
   309  // StartNodesAndNetworks starts the provided networks and libp2p nodes, returning the irrecoverable error channel
   310  func StartNodesAndNetworks(ctx irrecoverable.SignalerContext, t *testing.T, nodes []p2p.LibP2PNode, nets []network.Network, duration time.Duration) {
   311  	// start up networks (this will implicitly start middlewares)
   312  	for _, net := range nets {
   313  		net.Start(ctx)
   314  		unittest.RequireComponentsReadyBefore(t, duration, net)
   315  	}
   316  
   317  	// start up nodes and Peer managers
   318  	StartNodes(ctx, t, nodes, duration)
   319  }
   320  
   321  // StartNodes starts the provided nodes and their peer managers using the provided irrecoverable context
   322  func StartNodes(ctx irrecoverable.SignalerContext, t *testing.T, nodes []p2p.LibP2PNode, duration time.Duration) {
   323  	for _, node := range nodes {
   324  		node.Start(ctx)
   325  		unittest.RequireComponentsReadyBefore(t, duration, node)
   326  
   327  		pm := node.PeerManagerComponent()
   328  		pm.Start(ctx)
   329  		unittest.RequireComponentsReadyBefore(t, duration, pm)
   330  	}
   331  }
   332  
   333  // StopComponents stops ReadyDoneAware instances in parallel and fails the test if they could not be stopped within the
   334  // duration.
   335  func StopComponents[R module.ReadyDoneAware](t *testing.T, rda []R, duration time.Duration) {
   336  	comps := make([]module.ReadyDoneAware, 0, len(rda))
   337  	for _, c := range rda {
   338  		comps = append(comps, c)
   339  	}
   340  
   341  	unittest.RequireComponentsDoneBefore(t, duration, comps...)
   342  }
   343  
   344  type nodeBuilderOption func(p2pbuilder.NodeBuilder)
   345  
   346  func withDHT(prefix string, dhtOpts ...dht.Option) nodeBuilderOption {
   347  	return func(nb p2pbuilder.NodeBuilder) {
   348  		nb.SetRoutingSystem(func(c context.Context, h host.Host) (routing.Routing, error) {
   349  			return p2pdht.NewDHT(c, h, pc.ID(unicast.FlowDHTProtocolIDPrefix+prefix), zerolog.Nop(), metrics.NewNoopCollector(), dhtOpts...)
   350  		})
   351  	}
   352  }
   353  
   354  func withPeerManagerOptions(connectionPruning bool, updateInterval time.Duration) nodeBuilderOption {
   355  	return func(nb p2pbuilder.NodeBuilder) {
   356  		nb.SetPeerManagerOptions(connectionPruning, updateInterval)
   357  	}
   358  }
   359  
   360  // generateLibP2PNode generates a `LibP2PNode` on localhost using a port assigned by the OS
   361  func generateLibP2PNode(t *testing.T,
   362  	logger zerolog.Logger,
   363  	key crypto.PrivateKey,
   364  	idProvider module.IdentityProvider,
   365  	opts ...nodeBuilderOption) (p2p.LibP2PNode, observable.Observable) {
   366  
   367  	noopMetrics := metrics.NewNoopCollector()
   368  
   369  	// Inject some logic to be able to observe connections of this node
   370  	connManager := NewTagWatchingConnManager(logger, noopMetrics)
   371  
   372  	builder := p2pbuilder.NewNodeBuilder(
   373  		logger,
   374  		metrics.NewNoopCollector(),
   375  		unittest.DefaultAddress,
   376  		key,
   377  		sporkID,
   378  		p2pbuilder.DefaultResourceManagerConfig()).
   379  		SetConnectionManager(connManager).
   380  		SetResourceManager(NewResourceManager(t))
   381  
   382  	for _, opt := range opts {
   383  		opt(builder)
   384  	}
   385  
   386  	libP2PNode, err := builder.Build()
   387  	require.NoError(t, err)
   388  
   389  	return libP2PNode, connManager
   390  }
   391  
   392  // OptionalSleep introduces a sleep to allow nodes to heartbeat and discover each other (only needed when using PubSub)
   393  func OptionalSleep(send ConduitSendWrapperFunc) {
   394  	sendFuncName := runtime.FuncForPC(reflect.ValueOf(send).Pointer()).Name()
   395  	if strings.Contains(sendFuncName, "Multicast") || strings.Contains(sendFuncName, "Publish") {
   396  		time.Sleep(2 * time.Second)
   397  	}
   398  }
   399  
   400  // generateNetworkingKey generates a Flow ECDSA key using the given seed
   401  func generateNetworkingKey(s flow.Identifier) (crypto.PrivateKey, error) {
   402  	seed := make([]byte, crypto.KeyGenSeedMinLenECDSASecp256k1)
   403  	copy(seed, s[:])
   404  	return crypto.GeneratePrivateKey(crypto.ECDSASecp256k1, seed)
   405  }
   406  
   407  // GenerateSubscriptionManagers creates and returns a ChannelSubscriptionManager for each middleware object.
   408  func GenerateSubscriptionManagers(t *testing.T, mws []network.Middleware) []network.SubscriptionManager {
   409  	require.NotEmpty(t, mws)
   410  
   411  	sms := make([]network.SubscriptionManager, len(mws))
   412  	for i, mw := range mws {
   413  		sms[i] = subscription.NewChannelSubscriptionManager(mw)
   414  	}
   415  	return sms
   416  }
   417  
   418  // NetworkPayloadFixture creates a blob of random bytes with the given size (in bytes) and returns it.
   419  // The primary goal of utilizing this helper function is to apply stress tests on the network layer by
   420  // sending large messages to transmit.
   421  func NetworkPayloadFixture(t *testing.T, size uint) []byte {
   422  	// reserves 1000 bytes for the message headers, encoding overhead, and libp2p message overhead.
   423  	overhead := 1000
   424  	require.Greater(t, int(size), overhead, "could not generate message below size threshold")
   425  	emptyEvent := &libp2pmessage.TestMessage{
   426  		Text: "",
   427  	}
   428  
   429  	// encodes the message
   430  	codec := cbor.NewCodec()
   431  	empty, err := codec.Encode(emptyEvent)
   432  	require.NoError(t, err)
   433  
   434  	// max possible payload size
   435  	payloadSize := int(size) - overhead - len(empty)
   436  	payload := make([]byte, payloadSize)
   437  
   438  	// populates payload with random bytes
   439  	for i := range payload {
   440  		payload[i] = 'a' // a utf-8 char that translates to 1-byte when converted to a string
   441  	}
   442  
   443  	event := emptyEvent
   444  	event.Text = string(payload)
   445  	// encode Event the way the network would encode it to get the size of the message
   446  	// just to do the size check
   447  	encodedEvent, err := codec.Encode(event)
   448  	require.NoError(t, err)
   449  
   450  	require.InDelta(t, len(encodedEvent), int(size), float64(overhead))
   451  
   452  	return payload
   453  }
   454  
   455  // NewResourceManager creates a new resource manager for testing with no limits.
   456  func NewResourceManager(t *testing.T) p2pNetwork.ResourceManager {
   457  	return p2pNetwork.NullResourceManager
   458  }
   459  
   460  // NewConnectionGater creates a new connection gater for testing with given allow listing filter.
   461  func NewConnectionGater(allowListFilter p2p.PeerFilter) connmgr.ConnectionGater {
   462  	filters := []p2p.PeerFilter{allowListFilter}
   463  	return connection.NewConnGater(unittest.Logger(),
   464  		connection.WithOnInterceptPeerDialFilters(filters),
   465  		connection.WithOnInterceptSecuredFilters(filters))
   466  }