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