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 }