github.com/intfoundation/intchain@v0.0.0-20220727031208-4316ad31ca73/p2p/protocols/protocol_test.go (about) 1 // Copyright 2017 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package protocols 18 19 import ( 20 "context" 21 "errors" 22 "fmt" 23 "testing" 24 "time" 25 26 "github.com/intfoundation/intchain/p2p" 27 "github.com/intfoundation/intchain/p2p/discover" 28 "github.com/intfoundation/intchain/p2p/simulations/adapters" 29 p2ptest "github.com/intfoundation/intchain/p2p/testing" 30 ) 31 32 // handshake message type 33 type hs0 struct { 34 C uint 35 } 36 37 // message to kill/drop the peer with nodeID 38 type kill struct { 39 C discover.NodeID 40 } 41 42 // message to drop connection 43 type drop struct { 44 } 45 46 /// protoHandshake represents module-independent aspects of the protocol and is 47 // the first message peers send and receive as part the initial exchange 48 type protoHandshake struct { 49 Version uint // local and remote peer should have identical version 50 NetworkID string // local and remote peer should have identical network id 51 } 52 53 // checkProtoHandshake verifies local and remote protoHandshakes match 54 func checkProtoHandshake(testVersion uint, testNetworkID string) func(interface{}) error { 55 return func(rhs interface{}) error { 56 remote := rhs.(*protoHandshake) 57 if remote.NetworkID != testNetworkID { 58 return fmt.Errorf("%s (!= %s)", remote.NetworkID, testNetworkID) 59 } 60 61 if remote.Version != testVersion { 62 return fmt.Errorf("%d (!= %d)", remote.Version, testVersion) 63 } 64 return nil 65 } 66 } 67 68 // newProtocol sets up a protocol 69 // the run function here demonstrates a typical protocol using peerPool, handshake 70 // and messages registered to handlers 71 func newProtocol(pp *p2ptest.TestPeerPool) func(*p2p.Peer, p2p.MsgReadWriter) error { 72 spec := &Spec{ 73 Name: "test", 74 Version: 42, 75 MaxMsgSize: 10 * 1024, 76 Messages: []interface{}{ 77 protoHandshake{}, 78 hs0{}, 79 kill{}, 80 drop{}, 81 }, 82 } 83 return func(p *p2p.Peer, rw p2p.MsgReadWriter) error { 84 peer := NewPeer(p, rw, spec) 85 86 // initiate one-off protohandshake and check validity 87 ctx, cancel := context.WithTimeout(context.Background(), time.Second) 88 defer cancel() 89 phs := &protoHandshake{42, "420"} 90 hsCheck := checkProtoHandshake(phs.Version, phs.NetworkID) 91 _, err := peer.Handshake(ctx, phs, hsCheck) 92 if err != nil { 93 return err 94 } 95 96 lhs := &hs0{42} 97 // module handshake demonstrating a simple repeatable exchange of same-type message 98 hs, err := peer.Handshake(ctx, lhs, nil) 99 if err != nil { 100 return err 101 } 102 103 if rmhs := hs.(*hs0); rmhs.C > lhs.C { 104 return fmt.Errorf("handshake mismatch remote %v > local %v", rmhs.C, lhs.C) 105 } 106 107 handle := func(msg interface{}) error { 108 switch msg := msg.(type) { 109 110 case *protoHandshake: 111 return errors.New("duplicate handshake") 112 113 case *hs0: 114 rhs := msg 115 if rhs.C > lhs.C { 116 return fmt.Errorf("handshake mismatch remote %v > local %v", rhs.C, lhs.C) 117 } 118 lhs.C += rhs.C 119 return peer.Send(lhs) 120 121 case *kill: 122 // demonstrates use of peerPool, killing another peer connection as a response to a message 123 id := msg.C 124 pp.Get(id).Drop(errors.New("killed")) 125 return nil 126 127 case *drop: 128 // for testing we can trigger self induced disconnect upon receiving drop message 129 return errors.New("dropped") 130 131 default: 132 return fmt.Errorf("unknown message type: %T", msg) 133 } 134 } 135 136 pp.Add(peer) 137 defer pp.Remove(peer) 138 return peer.Run(handle) 139 } 140 } 141 142 func protocolTester(t *testing.T, pp *p2ptest.TestPeerPool) *p2ptest.ProtocolTester { 143 conf := adapters.RandomNodeConfig() 144 return p2ptest.NewProtocolTester(t, conf.ID, 2, newProtocol(pp)) 145 } 146 147 func protoHandshakeExchange(id discover.NodeID, proto *protoHandshake) []p2ptest.Exchange { 148 149 return []p2ptest.Exchange{ 150 { 151 Expects: []p2ptest.Expect{ 152 { 153 Code: 0, 154 Msg: &protoHandshake{42, "420"}, 155 Peer: id, 156 }, 157 }, 158 }, 159 { 160 Triggers: []p2ptest.Trigger{ 161 { 162 Code: 0, 163 Msg: proto, 164 Peer: id, 165 }, 166 }, 167 }, 168 } 169 } 170 171 func runProtoHandshake(t *testing.T, proto *protoHandshake, errs ...error) { 172 pp := p2ptest.NewTestPeerPool() 173 s := protocolTester(t, pp) 174 // TODO: make this more than one handshake 175 id := s.IDs[0] 176 if err := s.TestExchanges(protoHandshakeExchange(id, proto)...); err != nil { 177 t.Fatal(err) 178 } 179 var disconnects []*p2ptest.Disconnect 180 for i, err := range errs { 181 disconnects = append(disconnects, &p2ptest.Disconnect{Peer: s.IDs[i], Error: err}) 182 } 183 if err := s.TestDisconnected(disconnects...); err != nil { 184 t.Fatal(err) 185 } 186 } 187 188 func TestProtoHandshakeVersionMismatch(t *testing.T) { 189 runProtoHandshake(t, &protoHandshake{41, "420"}, errorf(ErrHandshake, errorf(ErrHandler, "(msg code 0): 41 (!= 42)").Error())) 190 } 191 192 func TestProtoHandshakeNetworkIDMismatch(t *testing.T) { 193 runProtoHandshake(t, &protoHandshake{42, "421"}, errorf(ErrHandshake, errorf(ErrHandler, "(msg code 0): 421 (!= 420)").Error())) 194 } 195 196 func TestProtoHandshakeSuccess(t *testing.T) { 197 runProtoHandshake(t, &protoHandshake{42, "420"}) 198 } 199 200 func moduleHandshakeExchange(id discover.NodeID, resp uint) []p2ptest.Exchange { 201 202 return []p2ptest.Exchange{ 203 { 204 Expects: []p2ptest.Expect{ 205 { 206 Code: 1, 207 Msg: &hs0{42}, 208 Peer: id, 209 }, 210 }, 211 }, 212 { 213 Triggers: []p2ptest.Trigger{ 214 { 215 Code: 1, 216 Msg: &hs0{resp}, 217 Peer: id, 218 }, 219 }, 220 }, 221 } 222 } 223 224 func runModuleHandshake(t *testing.T, resp uint, errs ...error) { 225 pp := p2ptest.NewTestPeerPool() 226 s := protocolTester(t, pp) 227 id := s.IDs[0] 228 if err := s.TestExchanges(protoHandshakeExchange(id, &protoHandshake{42, "420"})...); err != nil { 229 t.Fatal(err) 230 } 231 if err := s.TestExchanges(moduleHandshakeExchange(id, resp)...); err != nil { 232 t.Fatal(err) 233 } 234 var disconnects []*p2ptest.Disconnect 235 for i, err := range errs { 236 disconnects = append(disconnects, &p2ptest.Disconnect{Peer: s.IDs[i], Error: err}) 237 } 238 if err := s.TestDisconnected(disconnects...); err != nil { 239 t.Fatal(err) 240 } 241 } 242 243 func TestModuleHandshakeError(t *testing.T) { 244 runModuleHandshake(t, 43, fmt.Errorf("handshake mismatch remote 43 > local 42")) 245 } 246 247 func TestModuleHandshakeSuccess(t *testing.T) { 248 runModuleHandshake(t, 42) 249 } 250 251 // testing complex interactions over multiple peers, relaying, dropping 252 func testMultiPeerSetup(a, b discover.NodeID) []p2ptest.Exchange { 253 254 return []p2ptest.Exchange{ 255 { 256 Label: "primary handshake", 257 Expects: []p2ptest.Expect{ 258 { 259 Code: 0, 260 Msg: &protoHandshake{42, "420"}, 261 Peer: a, 262 }, 263 { 264 Code: 0, 265 Msg: &protoHandshake{42, "420"}, 266 Peer: b, 267 }, 268 }, 269 }, 270 { 271 Label: "module handshake", 272 Triggers: []p2ptest.Trigger{ 273 { 274 Code: 0, 275 Msg: &protoHandshake{42, "420"}, 276 Peer: a, 277 }, 278 { 279 Code: 0, 280 Msg: &protoHandshake{42, "420"}, 281 Peer: b, 282 }, 283 }, 284 Expects: []p2ptest.Expect{ 285 { 286 Code: 1, 287 Msg: &hs0{42}, 288 Peer: a, 289 }, 290 { 291 Code: 1, 292 Msg: &hs0{42}, 293 Peer: b, 294 }, 295 }, 296 }, 297 298 {Label: "alternative module handshake", Triggers: []p2ptest.Trigger{{Code: 1, Msg: &hs0{41}, Peer: a}, 299 {Code: 1, Msg: &hs0{41}, Peer: b}}}, 300 {Label: "repeated module handshake", Triggers: []p2ptest.Trigger{{Code: 1, Msg: &hs0{1}, Peer: a}}}, 301 {Label: "receiving repeated module handshake", Expects: []p2ptest.Expect{{Code: 1, Msg: &hs0{43}, Peer: a}}}} 302 } 303 304 func runMultiplePeers(t *testing.T, peer int, errs ...error) { 305 pp := p2ptest.NewTestPeerPool() 306 s := protocolTester(t, pp) 307 308 if err := s.TestExchanges(testMultiPeerSetup(s.IDs[0], s.IDs[1])...); err != nil { 309 t.Fatal(err) 310 } 311 // after some exchanges of messages, we can test state changes 312 // here this is simply demonstrated by the peerPool 313 // after the handshake negotiations peers must be added to the pool 314 // time.Sleep(1) 315 tick := time.NewTicker(10 * time.Millisecond) 316 timeout := time.NewTimer(1 * time.Second) 317 WAIT: 318 for { 319 select { 320 case <-tick.C: 321 if pp.Has(s.IDs[0]) { 322 break WAIT 323 } 324 case <-timeout.C: 325 t.Fatal("timeout") 326 } 327 } 328 if !pp.Has(s.IDs[1]) { 329 t.Fatalf("missing peer test-1: %v (%v)", pp, s.IDs) 330 } 331 332 // peer 0 sends kill request for peer with index <peer> 333 err := s.TestExchanges(p2ptest.Exchange{ 334 Triggers: []p2ptest.Trigger{ 335 { 336 Code: 2, 337 Msg: &kill{s.IDs[peer]}, 338 Peer: s.IDs[0], 339 }, 340 }, 341 }) 342 343 if err != nil { 344 t.Fatal(err) 345 } 346 347 // the peer not killed sends a drop request 348 err = s.TestExchanges(p2ptest.Exchange{ 349 Triggers: []p2ptest.Trigger{ 350 { 351 Code: 3, 352 Msg: &drop{}, 353 Peer: s.IDs[(peer+1)%2], 354 }, 355 }, 356 }) 357 358 if err != nil { 359 t.Fatal(err) 360 } 361 362 // check the actual discconnect errors on the individual peers 363 var disconnects []*p2ptest.Disconnect 364 for i, err := range errs { 365 disconnects = append(disconnects, &p2ptest.Disconnect{Peer: s.IDs[i], Error: err}) 366 } 367 if err := s.TestDisconnected(disconnects...); err != nil { 368 t.Fatal(err) 369 } 370 // test if disconnected peers have been removed from peerPool 371 if pp.Has(s.IDs[peer]) { 372 t.Fatalf("peer test-%v not dropped: %v (%v)", peer, pp, s.IDs) 373 } 374 375 } 376 377 func TestMultiplePeersDropSelf(t *testing.T) { 378 runMultiplePeers(t, 0, 379 fmt.Errorf("subprotocol error"), 380 fmt.Errorf("Message handler error: (msg code 3): dropped"), 381 ) 382 } 383 384 func TestMultiplePeersDropOther(t *testing.T) { 385 runMultiplePeers(t, 1, 386 fmt.Errorf("Message handler error: (msg code 3): dropped"), 387 fmt.Errorf("subprotocol error"), 388 ) 389 }