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 }