github.com/koko1123/flow-go-1@v0.29.6/consensus/integration/network_test.go (about)

     1  package integration_test
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"time"
     7  
     8  	"github.com/hashicorp/go-multierror"
     9  
    10  	"github.com/koko1123/flow-go-1/model/flow"
    11  	"github.com/koko1123/flow-go-1/network"
    12  	"github.com/koko1123/flow-go-1/network/channels"
    13  	"github.com/koko1123/flow-go-1/network/mocknetwork"
    14  )
    15  
    16  // TODO replace this type with `network/stub/hub.go`
    17  // Hub is a test helper that mocks a network overlay.
    18  // It maintains a set of network instances and enables them to directly exchange message
    19  // over the memory.
    20  type Hub struct {
    21  	networks   map[flow.Identifier]*Network
    22  	filter     BlockOrDelayFunc
    23  	identities flow.IdentityList
    24  }
    25  
    26  // NewNetworkHub creates and returns a new Hub instance.
    27  func NewNetworkHub() *Hub {
    28  	return &Hub{
    29  		networks:   make(map[flow.Identifier]*Network),
    30  		identities: flow.IdentityList{},
    31  	}
    32  }
    33  
    34  // WithFilter is an option method that sets filter of the Hub instance.
    35  func (h *Hub) WithFilter(filter BlockOrDelayFunc) *Hub {
    36  	h.filter = filter
    37  	return h
    38  }
    39  
    40  // AddNetwork stores the reference of the Network in the Hub, in order for networks to find
    41  // other networks to send events directly.
    42  func (h *Hub) AddNetwork(originID flow.Identifier, node *Node) *Network {
    43  	net := &Network{
    44  		ctx:      context.Background(),
    45  		hub:      h,
    46  		originID: originID,
    47  		conduits: make(map[channels.Channel]*Conduit),
    48  		node:     node,
    49  	}
    50  	h.networks[originID] = net
    51  	h.identities = append(h.identities, node.id)
    52  	return net
    53  }
    54  
    55  // TODO replace this type with `network/stub/network.go`
    56  // Network is a mocked Network layer made for testing engine's behavior.
    57  // It represents the Network layer of a single node. A node can attach several engines of
    58  // itself to the Network, and hence enabling them send and receive message.
    59  // When an engine is attached on a Network instance, the mocked Network delivers
    60  // all engine's events to others using an in-memory delivery mechanism.
    61  type Network struct {
    62  	ctx      context.Context
    63  	hub      *Hub
    64  	node     *Node
    65  	originID flow.Identifier
    66  	conduits map[channels.Channel]*Conduit
    67  	mocknetwork.Network
    68  }
    69  
    70  // Register registers an Engine of the attached node to the channel via a Conduit, and returns the
    71  // Conduit instance.
    72  func (n *Network) Register(channel channels.Channel, engine network.MessageProcessor) (network.Conduit, error) {
    73  	ctx, cancel := context.WithCancel(n.ctx)
    74  	con := &Conduit{
    75  		ctx:     ctx,
    76  		cancel:  cancel,
    77  		net:     n,
    78  		channel: channel,
    79  		queue:   make(chan message, 1024),
    80  	}
    81  
    82  	go func() {
    83  		for msg := range con.queue {
    84  			go func(m message) {
    85  				_ = engine.Process(channel, m.originID, m.event)
    86  			}(msg)
    87  		}
    88  	}()
    89  
    90  	n.conduits[channel] = con
    91  	return con, nil
    92  }
    93  
    94  // unregister unregisters the engine associated with the given channel and closes the conduit queue.
    95  func (n *Network) unregister(channel channels.Channel) error {
    96  	con := n.conduits[channel]
    97  	close(con.queue)
    98  	delete(n.conduits, channel)
    99  	return nil
   100  }
   101  
   102  // submit is called when the attached Engine to the channel is sending an event to an
   103  // Engine attached to the same channel on another node or nodes.
   104  // This implementation uses unicast under the hood.
   105  func (n *Network) submit(event interface{}, channel channels.Channel, targetIDs ...flow.Identifier) error {
   106  	var sendErrors *multierror.Error
   107  	for _, targetID := range targetIDs {
   108  		if err := n.unicast(event, channel, targetID); err != nil {
   109  			sendErrors = multierror.Append(sendErrors, fmt.Errorf("could not unicast the event: %w", err))
   110  		}
   111  	}
   112  	return sendErrors.ErrorOrNil()
   113  }
   114  
   115  // unicast is called when the attached Engine to the channel is sending an event to a single target
   116  // Engine attached to the same channel on another node.
   117  func (n *Network) unicast(event interface{}, channel channels.Channel, targetID flow.Identifier) error {
   118  	net, found := n.hub.networks[targetID]
   119  	if !found {
   120  		return fmt.Errorf("could not find target network on hub: %x", targetID)
   121  	}
   122  	con, found := net.conduits[channel]
   123  	if !found {
   124  		return fmt.Errorf("invalid channel (%d) for target ID (%x)", targetID, channel)
   125  	}
   126  
   127  	sender, receiver := n.node, net.node
   128  	block, delay := n.hub.filter(channel, event, sender, receiver)
   129  	// block the message
   130  	if block {
   131  		return nil
   132  	}
   133  
   134  	// no delay, push to the receiver's message queue right away
   135  	if delay == 0 {
   136  		con.queue <- message{originID: n.originID, event: event}
   137  		return nil
   138  	}
   139  
   140  	// use a goroutine to wait and send
   141  	go func(delay time.Duration, senderID flow.Identifier, receiver *Conduit, event interface{}) {
   142  		// sleep in order to simulate the network delay
   143  		time.Sleep(delay)
   144  		con.queue <- message{originID: senderID, event: event}
   145  	}(delay, n.originID, con, event)
   146  
   147  	return nil
   148  }
   149  
   150  // publish is called when the attached Engine is sending an event to a group of Engines attached to the
   151  // same channel on other nodes based on selector.
   152  // In this test helper implementation, publish uses submit method under the hood.
   153  func (n *Network) publish(event interface{}, channel channels.Channel, targetIDs ...flow.Identifier) error {
   154  	return n.submit(event, channel, targetIDs...)
   155  }
   156  
   157  // multicast is called when an Engine attached to the channel is sending an event to a number of randomly chosen
   158  // Engines attached to the same channel on other nodes. The targeted nodes are selected based on the selector.
   159  // In this test helper implementation, multicast uses submit method under the hood.
   160  func (n *Network) multicast(event interface{}, channel channels.Channel, num uint, targetIDs ...flow.Identifier) error {
   161  	targetIDs = flow.Sample(num, targetIDs...)
   162  	return n.submit(event, channel, targetIDs...)
   163  }
   164  
   165  type Conduit struct {
   166  	ctx     context.Context
   167  	cancel  context.CancelFunc
   168  	net     *Network
   169  	channel channels.Channel
   170  	queue   chan message
   171  }
   172  
   173  func (c *Conduit) Submit(event interface{}, targetIDs ...flow.Identifier) error {
   174  	if c.ctx.Err() != nil {
   175  		return fmt.Errorf("conduit closed")
   176  	}
   177  	return c.net.submit(event, c.channel, targetIDs...)
   178  }
   179  
   180  func (c *Conduit) Publish(event interface{}, targetIDs ...flow.Identifier) error {
   181  	if c.ctx.Err() != nil {
   182  		return fmt.Errorf("conduit closed")
   183  	}
   184  	return c.net.publish(event, c.channel, targetIDs...)
   185  }
   186  
   187  func (c *Conduit) Unicast(event interface{}, targetID flow.Identifier) error {
   188  	if c.ctx.Err() != nil {
   189  		return fmt.Errorf("conduit closed")
   190  	}
   191  	return c.net.unicast(event, c.channel, targetID)
   192  }
   193  
   194  func (c *Conduit) Multicast(event interface{}, num uint, targetIDs ...flow.Identifier) error {
   195  	if c.ctx.Err() != nil {
   196  		return fmt.Errorf("conduit closed")
   197  	}
   198  	return c.net.multicast(event, c.channel, num, targetIDs...)
   199  }
   200  
   201  func (c *Conduit) Close() error {
   202  	if c.ctx.Err() != nil {
   203  		return fmt.Errorf("conduit closed")
   204  	}
   205  	c.cancel()
   206  	return c.net.unregister(c.channel)
   207  }
   208  
   209  type message struct {
   210  	originID flow.Identifier
   211  	event    interface{}
   212  }