github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/network/stub/network.go (about)

     1  package stub
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"sync"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/pkg/errors"
    11  	"github.com/stretchr/testify/mock"
    12  	"github.com/stretchr/testify/require"
    13  
    14  	"github.com/onflow/flow-go/model/flow"
    15  	"github.com/onflow/flow-go/network"
    16  	"github.com/onflow/flow-go/network/channels"
    17  	"github.com/onflow/flow-go/network/mocknetwork"
    18  	"github.com/onflow/flow-go/network/p2p/conduit"
    19  )
    20  
    21  // Network is a mocked Network layer made for testing engine's behavior.
    22  // It represents the Network layer of a single node. A node can attach several engines of
    23  // itself to the Network, and hence enabling them send and receive message.
    24  // When an engine is attached on a Network instance, the mocked Network delivers
    25  // all engine's events to others using an in-memory delivery mechanism.
    26  type Network struct {
    27  	mocknetwork.Network
    28  	ctx context.Context
    29  	sync.Mutex
    30  	myId           flow.Identifier                               // used to represent information of the attached node.
    31  	hub            *Hub                                          // used to attach Network layers of nodes together.
    32  	engines        map[channels.Channel]network.MessageProcessor // used to keep track of attached engines of the node.
    33  	seenEventIDs   map[string]struct{}                           // used to keep track of event IDs seen by attached engines.
    34  	qCD            chan struct{}                                 // used to stop continuous delivery mode of the Network.
    35  	conduitFactory network.ConduitFactory
    36  }
    37  
    38  func WithConduitFactory(factory network.ConduitFactory) func(*Network) {
    39  	return func(n *Network) {
    40  		n.conduitFactory = factory
    41  	}
    42  }
    43  
    44  var _ network.EngineRegistry = (*Network)(nil)
    45  var _ network.ConduitAdapter = (*Network)(nil)
    46  
    47  // NewNetwork create a mocked Network.
    48  // The committee has the identity of the node already, so only `committee` is needed
    49  // in order for a mock hub to find each other.
    50  func NewNetwork(t testing.TB, myId flow.Identifier, hub *Hub, opts ...func(*Network)) *Network {
    51  	net := &Network{
    52  		ctx:            context.Background(),
    53  		myId:           myId,
    54  		hub:            hub,
    55  		engines:        make(map[channels.Channel]network.MessageProcessor),
    56  		seenEventIDs:   make(map[string]struct{}),
    57  		qCD:            make(chan struct{}),
    58  		conduitFactory: conduit.NewDefaultConduitFactory(),
    59  	}
    60  
    61  	for _, opt := range opts {
    62  		opt(net)
    63  	}
    64  
    65  	// mocks the Start, Ready, and Done behavior of the network.
    66  	net.On("Start", mock.Anything).Return()
    67  	ready := make(chan struct{})
    68  	close(ready)
    69  	net.On("Ready", mock.Anything).Return(func() <-chan struct{} {
    70  		return ready
    71  	})
    72  
    73  	done := make(chan struct{})
    74  	close(done)
    75  	net.On("Done", mock.Anything).Return(func() <-chan struct{} {
    76  		return done
    77  	})
    78  
    79  	require.NoError(t, net.conduitFactory.RegisterAdapter(net))
    80  
    81  	// AddNetwork the Network to a hub so that Networks can find each other.
    82  	hub.AddNetwork(net)
    83  	return net
    84  }
    85  
    86  // GetID returns the identity of the attached node.
    87  func (n *Network) GetID() flow.Identifier {
    88  	return n.myId
    89  }
    90  
    91  // Register registers an Engine of the attached node to the channel via a Conduit, and returns the
    92  // Conduit instance.
    93  func (n *Network) Register(channel channels.Channel, engine network.MessageProcessor) (network.Conduit, error) {
    94  	n.Lock()
    95  	defer n.Unlock()
    96  	_, ok := n.engines[channel]
    97  	if ok {
    98  		return nil, errors.Errorf("channel already taken (%s)", channel)
    99  	}
   100  
   101  	c, err := n.conduitFactory.NewConduit(n.ctx, channel)
   102  	if err != nil {
   103  		return nil, fmt.Errorf("could not create a conduit on the channel: %w", err)
   104  	}
   105  
   106  	n.engines[channel] = engine
   107  
   108  	return c, nil
   109  }
   110  
   111  func (n *Network) UnRegisterChannel(channel channels.Channel) error {
   112  	n.Lock()
   113  	defer n.Unlock()
   114  	delete(n.engines, channel)
   115  	return nil
   116  }
   117  
   118  // submit is called when the attached Engine to the channel is sending an event to an
   119  // Engine attached to the same channel on another node or nodes.
   120  func (n *Network) submit(channel channels.Channel, event interface{}, targetIDs ...flow.Identifier) error {
   121  	m := &PendingMessage{
   122  		From:      n.GetID(),
   123  		Channel:   channel,
   124  		Event:     event,
   125  		TargetIDs: targetIDs,
   126  	}
   127  
   128  	n.buffer(m)
   129  
   130  	return nil
   131  }
   132  
   133  // unicast is called when the attached Engine to the channel is sending an event to a single target
   134  // Engine attached to the same channel on another node.
   135  func (n *Network) UnicastOnChannel(channel channels.Channel, event interface{}, targetID flow.Identifier) error {
   136  	m := &PendingMessage{
   137  		From:      n.GetID(),
   138  		Channel:   channel,
   139  		Event:     event,
   140  		TargetIDs: []flow.Identifier{targetID},
   141  	}
   142  
   143  	n.buffer(m)
   144  	return nil
   145  }
   146  
   147  // publish is called when the attached Engine is sending an event to a group of Engines attached to the
   148  // same channel on other nodes based on selector.
   149  // In this test helper implementation, publish uses submit method under the hood.
   150  func (n *Network) PublishOnChannel(channel channels.Channel, event interface{}, targetIDs ...flow.Identifier) error {
   151  
   152  	if len(targetIDs) == 0 {
   153  		return fmt.Errorf("publish found empty target ID list for the message")
   154  	}
   155  
   156  	return n.submit(channel, event, targetIDs...)
   157  }
   158  
   159  // multicast is called when an engine attached to the channel is sending an event to a number of randomly chosen
   160  // Engines attached to the same channel on other nodes. The targeted nodes are selected based on the selector.
   161  // In this test helper implementation, multicast uses submit method under the hood.
   162  func (n *Network) MulticastOnChannel(channel channels.Channel, event interface{}, num uint, targetIDs ...flow.Identifier) error {
   163  	var err error
   164  	targetIDs, err = flow.Sample(num, targetIDs...)
   165  	if err != nil {
   166  		return fmt.Errorf("sampling failed: %w", err)
   167  	}
   168  	return n.submit(channel, event, targetIDs...)
   169  }
   170  
   171  // buffer saves the message into the pending buffer of the Network hub.
   172  // Buffering process of a message imitates its transmission over an unreliable Network.
   173  // In specific, it emulates the process of dispatching the message out of the sender.
   174  func (n *Network) buffer(msg *PendingMessage) {
   175  	n.hub.Buffer.Save(msg)
   176  }
   177  
   178  // DeliverAll sends all pending messages to the receivers. The receivers
   179  // might be triggered to forward messages to its peers, so this function will
   180  // block until all receivers have done their forwarding, and there is no more message
   181  // in the Network to deliver.
   182  func (n *Network) DeliverAll(syncOnProcess bool) {
   183  	n.hub.Buffer.DeliverRecursive(func(m *PendingMessage) {
   184  		_ = n.sendToAllTargets(m, syncOnProcess)
   185  	})
   186  }
   187  
   188  // DeliverAllExcept flushes all pending messages in the buffer except
   189  // those that satisfy the shouldDrop predicate function. All messages that
   190  // satisfy the shouldDrop predicate are permanently dropped.
   191  // The message receivers might be triggered to forward some messages to their peers,
   192  // so this function will block until all receivers have done their forwarding,
   193  // and there is no more message in the Network to deliver.
   194  //
   195  // If syncOnProcess is true, the sender and receiver are synchronized on processing the message.
   196  // Otherwise they sync on delivery of the message.
   197  func (n *Network) DeliverAllExcept(syncOnProcess bool, shouldDrop func(*PendingMessage) bool) {
   198  	n.hub.Buffer.DeliverRecursive(func(m *PendingMessage) {
   199  		if shouldDrop(m) {
   200  			return
   201  		}
   202  		_ = n.sendToAllTargets(m, syncOnProcess)
   203  	})
   204  }
   205  
   206  // DeliverSome delivers all messages in the buffer that satisfy the
   207  // shouldDeliver predicate. Any messages that are not delivered remain in the
   208  // buffer.
   209  //
   210  // If syncOnProcess is true, the sender and receiver are synchronized on processing the message.
   211  // Otherwise they sync on delivery of the message.
   212  func (n *Network) DeliverSome(syncOnProcess bool, shouldDeliver func(*PendingMessage) bool) {
   213  	n.hub.Buffer.Deliver(func(m *PendingMessage) bool {
   214  		if shouldDeliver(m) {
   215  			return n.sendToAllTargets(m, syncOnProcess) != nil
   216  		}
   217  		return false
   218  	})
   219  }
   220  
   221  // sendToAllTargets send a message to all its targeted nodes if the targeted
   222  // node has not yet seen it.
   223  // sync parameter defines whether the sender and receiver are synced over processing or delivery of
   224  // message.
   225  // If syncOnProcess is set true, sender and receiver are synced over processing of the message, i.e., the method call
   226  // gets blocking till the message is processed at destination.
   227  // If syncOnProcess is set false, sender and receiver are synced over delivery of the message, i.e., the method call
   228  // returns once the message is delivered at destination (and not necessarily processed).
   229  func (n *Network) sendToAllTargets(m *PendingMessage, syncOnProcess bool) error {
   230  	key, err := eventKey(m.From, m.Channel, m.Event)
   231  	if err != nil {
   232  		return fmt.Errorf("could not generate event key for event: %w", err)
   233  	}
   234  
   235  	for _, nodeID := range m.TargetIDs {
   236  		// finds the Network of the targeted node
   237  		receiverNetwork, exist := n.hub.GetNetwork(nodeID)
   238  		if !exist {
   239  			continue
   240  		}
   241  
   242  		// finds the engine of the targeted Network
   243  		err := receiverNetwork.processWithEngine(syncOnProcess, key, m)
   244  		if err != nil {
   245  			return fmt.Errorf("could not process message for nodeID: %v, %w", nodeID, err)
   246  		}
   247  	}
   248  	return nil
   249  }
   250  
   251  func (n *Network) processWithEngine(syncOnProcess bool, key string, m *PendingMessage) error {
   252  	n.Lock()
   253  	defer n.Unlock()
   254  
   255  	// checks if the given engine already received the event.
   256  	// this prevents a node receiving the same event twice.
   257  	if _, ok := n.seenEventIDs[key]; ok {
   258  		return nil
   259  	}
   260  	n.seenEventIDs[key] = struct{}{}
   261  
   262  	receiverEngine, ok := n.engines[m.Channel]
   263  	if !ok {
   264  		return fmt.Errorf("could find engine ID: %v", m.Channel)
   265  	}
   266  
   267  	if syncOnProcess {
   268  		// sender and receiver are synced over processing the message
   269  		if err := receiverEngine.Process(m.Channel, m.From, m.Event); err != nil {
   270  			return fmt.Errorf("receiver engine failed to process event (%v): %w", m.Event, err)
   271  		}
   272  	} else {
   273  		// sender and receiver are synced over delivery of message
   274  		go func() {
   275  			_ = receiverEngine.Process(m.Channel, m.From, m.Event)
   276  		}()
   277  	}
   278  	return nil
   279  }
   280  
   281  // StartConDev starts the continuous delivery mode of the Network.
   282  // In this mode, the Network continuously checks the nodes' buffer
   283  // every `updateInterval` milliseconds, and delivers all the pending
   284  // messages. `recursive` determines whether the delivery is in recursive mode or not
   285  func (n *Network) StartConDev(updateInterval time.Duration, recursive bool) {
   286  	timer := time.NewTicker(updateInterval)
   287  
   288  	wg := sync.WaitGroup{}
   289  	wg.Add(1)
   290  
   291  	go func() {
   292  		wg.Done()
   293  		for {
   294  			select {
   295  			case <-timer.C:
   296  				n.DeliverAll(recursive)
   297  			case <-n.qCD:
   298  				// stops continuous delivery mode
   299  				return
   300  			}
   301  		}
   302  	}()
   303  
   304  	// waits till the internal goroutine starts
   305  	wg.Wait()
   306  }
   307  
   308  // StopConDev stops the continuous deliver mode of the Network.
   309  func (n *Network) StopConDev() {
   310  	close(n.qCD)
   311  }
   312  
   313  func (n *Network) ReportMisbehaviorOnChannel(_ channels.Channel, _ network.MisbehaviorReport) {
   314  	// no-op for stub network.
   315  }