github.com/codingfuture/orig-energi3@v0.8.4/p2p/protocols/protocol_test.go (about) 1 // Copyright 2018 The Energi Core Authors 2 // Copyright 2018 The go-ethereum Authors 3 // This file is part of the Energi Core library. 4 // 5 // The Energi Core library is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Lesser General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // The Energi Core library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Lesser General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public License 16 // along with the Energi Core library. If not, see <http://www.gnu.org/licenses/>. 17 18 package protocols 19 20 import ( 21 "bytes" 22 "context" 23 "errors" 24 "fmt" 25 "testing" 26 "time" 27 28 "github.com/ethereum/go-ethereum/rlp" 29 30 "github.com/ethereum/go-ethereum/p2p" 31 "github.com/ethereum/go-ethereum/p2p/enode" 32 "github.com/ethereum/go-ethereum/p2p/simulations/adapters" 33 p2ptest "github.com/ethereum/go-ethereum/p2p/testing" 34 ) 35 36 // handshake message type 37 type hs0 struct { 38 C uint 39 } 40 41 // message to kill/drop the peer with nodeID 42 type kill struct { 43 C enode.ID 44 } 45 46 // message to drop connection 47 type drop struct { 48 } 49 50 /// protoHandshake represents module-independent aspects of the protocol and is 51 // the first message peers send and receive as part the initial exchange 52 type protoHandshake struct { 53 Version uint // local and remote peer should have identical version 54 NetworkID string // local and remote peer should have identical network id 55 } 56 57 // checkProtoHandshake verifies local and remote protoHandshakes match 58 func checkProtoHandshake(testVersion uint, testNetworkID string) func(interface{}) error { 59 return func(rhs interface{}) error { 60 remote := rhs.(*protoHandshake) 61 if remote.NetworkID != testNetworkID { 62 return fmt.Errorf("%s (!= %s)", remote.NetworkID, testNetworkID) 63 } 64 65 if remote.Version != testVersion { 66 return fmt.Errorf("%d (!= %d)", remote.Version, testVersion) 67 } 68 return nil 69 } 70 } 71 72 // newProtocol sets up a protocol 73 // the run function here demonstrates a typical protocol using peerPool, handshake 74 // and messages registered to handlers 75 func newProtocol(pp *p2ptest.TestPeerPool) func(*p2p.Peer, p2p.MsgReadWriter) error { 76 spec := &Spec{ 77 Name: "test", 78 Version: 42, 79 MaxMsgSize: 10 * 1024, 80 Messages: []interface{}{ 81 protoHandshake{}, 82 hs0{}, 83 kill{}, 84 drop{}, 85 }, 86 } 87 return func(p *p2p.Peer, rw p2p.MsgReadWriter) error { 88 peer := NewPeer(p, rw, spec) 89 90 // initiate one-off protohandshake and check validity 91 ctx, cancel := context.WithTimeout(context.Background(), time.Second) 92 defer cancel() 93 phs := &protoHandshake{42, "420"} 94 hsCheck := checkProtoHandshake(phs.Version, phs.NetworkID) 95 _, err := peer.Handshake(ctx, phs, hsCheck) 96 if err != nil { 97 return err 98 } 99 100 lhs := &hs0{42} 101 // module handshake demonstrating a simple repeatable exchange of same-type message 102 hs, err := peer.Handshake(ctx, lhs, nil) 103 if err != nil { 104 return err 105 } 106 107 if rmhs := hs.(*hs0); rmhs.C > lhs.C { 108 return fmt.Errorf("handshake mismatch remote %v > local %v", rmhs.C, lhs.C) 109 } 110 111 handle := func(ctx context.Context, msg interface{}) error { 112 switch msg := msg.(type) { 113 114 case *protoHandshake: 115 return errors.New("duplicate handshake") 116 117 case *hs0: 118 rhs := msg 119 if rhs.C > lhs.C { 120 return fmt.Errorf("handshake mismatch remote %v > local %v", rhs.C, lhs.C) 121 } 122 lhs.C += rhs.C 123 return peer.Send(ctx, lhs) 124 125 case *kill: 126 // demonstrates use of peerPool, killing another peer connection as a response to a message 127 id := msg.C 128 pp.Get(id).Drop(errors.New("killed")) 129 return nil 130 131 case *drop: 132 // for testing we can trigger self induced disconnect upon receiving drop message 133 return errors.New("dropped") 134 135 default: 136 return fmt.Errorf("unknown message type: %T", msg) 137 } 138 } 139 140 pp.Add(peer) 141 defer pp.Remove(peer) 142 return peer.Run(handle) 143 } 144 } 145 146 func protocolTester(pp *p2ptest.TestPeerPool) *p2ptest.ProtocolTester { 147 conf := adapters.RandomNodeConfig() 148 return p2ptest.NewProtocolTester(conf.ID, 2, newProtocol(pp)) 149 } 150 151 func protoHandshakeExchange(id enode.ID, proto *protoHandshake) []p2ptest.Exchange { 152 153 return []p2ptest.Exchange{ 154 { 155 Expects: []p2ptest.Expect{ 156 { 157 Code: 0, 158 Msg: &protoHandshake{42, "420"}, 159 Peer: id, 160 }, 161 }, 162 }, 163 { 164 Triggers: []p2ptest.Trigger{ 165 { 166 Code: 0, 167 Msg: proto, 168 Peer: id, 169 }, 170 }, 171 }, 172 } 173 } 174 175 func runProtoHandshake(t *testing.T, proto *protoHandshake, errs ...error) { 176 pp := p2ptest.NewTestPeerPool() 177 s := protocolTester(pp) 178 // TODO: make this more than one handshake 179 node := s.Nodes[0] 180 if err := s.TestExchanges(protoHandshakeExchange(node.ID(), proto)...); err != nil { 181 t.Fatal(err) 182 } 183 var disconnects []*p2ptest.Disconnect 184 for i, err := range errs { 185 disconnects = append(disconnects, &p2ptest.Disconnect{Peer: s.Nodes[i].ID(), Error: err}) 186 } 187 if err := s.TestDisconnected(disconnects...); err != nil { 188 t.Fatal(err) 189 } 190 } 191 192 type dummyHook struct { 193 peer *Peer 194 size uint32 195 msg interface{} 196 send bool 197 err error 198 waitC chan struct{} 199 } 200 201 type dummyMsg struct { 202 Content string 203 } 204 205 func (d *dummyHook) Send(peer *Peer, size uint32, msg interface{}) error { 206 d.peer = peer 207 d.size = size 208 d.msg = msg 209 d.send = true 210 return d.err 211 } 212 213 func (d *dummyHook) Receive(peer *Peer, size uint32, msg interface{}) error { 214 d.peer = peer 215 d.size = size 216 d.msg = msg 217 d.send = false 218 d.waitC <- struct{}{} 219 return d.err 220 } 221 222 func TestProtocolHook(t *testing.T) { 223 testHook := &dummyHook{ 224 waitC: make(chan struct{}, 1), 225 } 226 spec := &Spec{ 227 Name: "test", 228 Version: 42, 229 MaxMsgSize: 10 * 1024, 230 Messages: []interface{}{ 231 dummyMsg{}, 232 }, 233 Hook: testHook, 234 } 235 236 runFunc := func(p *p2p.Peer, rw p2p.MsgReadWriter) error { 237 peer := NewPeer(p, rw, spec) 238 ctx := context.TODO() 239 err := peer.Send(ctx, &dummyMsg{ 240 Content: "handshake"}) 241 242 if err != nil { 243 t.Fatal(err) 244 } 245 246 handle := func(ctx context.Context, msg interface{}) error { 247 return nil 248 } 249 250 return peer.Run(handle) 251 } 252 253 conf := adapters.RandomNodeConfig() 254 tester := p2ptest.NewProtocolTester(conf.ID, 2, runFunc) 255 err := tester.TestExchanges(p2ptest.Exchange{ 256 Expects: []p2ptest.Expect{ 257 { 258 Code: 0, 259 Msg: &dummyMsg{Content: "handshake"}, 260 Peer: tester.Nodes[0].ID(), 261 }, 262 }, 263 }) 264 if err != nil { 265 t.Fatal(err) 266 } 267 if testHook.msg == nil || testHook.msg.(*dummyMsg).Content != "handshake" { 268 t.Fatal("Expected msg to be set, but it is not") 269 } 270 if !testHook.send { 271 t.Fatal("Expected a send message, but it is not") 272 } 273 if testHook.peer == nil || testHook.peer.ID() != tester.Nodes[0].ID() { 274 t.Fatal("Expected peer ID to be set correctly, but it is not") 275 } 276 if testHook.size != 11 { //11 is the length of the encoded message 277 t.Fatalf("Expected size to be %d, but it is %d ", 1, testHook.size) 278 } 279 280 err = tester.TestExchanges(p2ptest.Exchange{ 281 Triggers: []p2ptest.Trigger{ 282 { 283 Code: 0, 284 Msg: &dummyMsg{Content: "response"}, 285 Peer: tester.Nodes[1].ID(), 286 }, 287 }, 288 }) 289 290 <-testHook.waitC 291 292 if err != nil { 293 t.Fatal(err) 294 } 295 if testHook.msg == nil || testHook.msg.(*dummyMsg).Content != "response" { 296 t.Fatal("Expected msg to be set, but it is not") 297 } 298 if testHook.send { 299 t.Fatal("Expected a send message, but it is not") 300 } 301 if testHook.peer == nil || testHook.peer.ID() != tester.Nodes[1].ID() { 302 t.Fatal("Expected peer ID to be set correctly, but it is not") 303 } 304 if testHook.size != 10 { //11 is the length of the encoded message 305 t.Fatalf("Expected size to be %d, but it is %d ", 1, testHook.size) 306 } 307 308 testHook.err = fmt.Errorf("dummy error") 309 err = tester.TestExchanges(p2ptest.Exchange{ 310 Triggers: []p2ptest.Trigger{ 311 { 312 Code: 0, 313 Msg: &dummyMsg{Content: "response"}, 314 Peer: tester.Nodes[1].ID(), 315 }, 316 }, 317 }) 318 319 <-testHook.waitC 320 321 time.Sleep(100 * time.Millisecond) 322 err = tester.TestDisconnected(&p2ptest.Disconnect{Peer: tester.Nodes[1].ID(), Error: testHook.err}) 323 if err != nil { 324 t.Fatalf("Expected a specific disconnect error, but got different one: %v", err) 325 } 326 327 } 328 329 //We need to test that if the hook is not defined, then message infrastructure 330 //(send,receive) still works 331 func TestNoHook(t *testing.T) { 332 //create a test spec 333 spec := createTestSpec() 334 //a random node 335 id := adapters.RandomNodeConfig().ID 336 //a peer 337 p := p2p.NewPeer(id, "testPeer", nil) 338 rw := &dummyRW{} 339 peer := NewPeer(p, rw, spec) 340 ctx := context.TODO() 341 msg := &perBytesMsgSenderPays{Content: "testBalance"} 342 //send a message 343 err := peer.Send(ctx, msg) 344 if err != nil { 345 t.Fatal(err) 346 } 347 //simulate receiving a message 348 rw.msg = msg 349 peer.handleIncoming(func(ctx context.Context, msg interface{}) error { 350 return nil 351 }) 352 //all should just work and not result in any error 353 } 354 355 func TestProtoHandshakeVersionMismatch(t *testing.T) { 356 runProtoHandshake(t, &protoHandshake{41, "420"}, errorf(ErrHandshake, errorf(ErrHandler, "(msg code 0): 41 (!= 42)").Error())) 357 } 358 359 func TestProtoHandshakeNetworkIDMismatch(t *testing.T) { 360 runProtoHandshake(t, &protoHandshake{42, "421"}, errorf(ErrHandshake, errorf(ErrHandler, "(msg code 0): 421 (!= 420)").Error())) 361 } 362 363 func TestProtoHandshakeSuccess(t *testing.T) { 364 runProtoHandshake(t, &protoHandshake{42, "420"}) 365 } 366 367 func moduleHandshakeExchange(id enode.ID, resp uint) []p2ptest.Exchange { 368 369 return []p2ptest.Exchange{ 370 { 371 Expects: []p2ptest.Expect{ 372 { 373 Code: 1, 374 Msg: &hs0{42}, 375 Peer: id, 376 }, 377 }, 378 }, 379 { 380 Triggers: []p2ptest.Trigger{ 381 { 382 Code: 1, 383 Msg: &hs0{resp}, 384 Peer: id, 385 }, 386 }, 387 }, 388 } 389 } 390 391 func runModuleHandshake(t *testing.T, resp uint, errs ...error) { 392 pp := p2ptest.NewTestPeerPool() 393 s := protocolTester(pp) 394 node := s.Nodes[0] 395 if err := s.TestExchanges(protoHandshakeExchange(node.ID(), &protoHandshake{42, "420"})...); err != nil { 396 t.Fatal(err) 397 } 398 if err := s.TestExchanges(moduleHandshakeExchange(node.ID(), resp)...); err != nil { 399 t.Fatal(err) 400 } 401 var disconnects []*p2ptest.Disconnect 402 for i, err := range errs { 403 disconnects = append(disconnects, &p2ptest.Disconnect{Peer: s.Nodes[i].ID(), Error: err}) 404 } 405 if err := s.TestDisconnected(disconnects...); err != nil { 406 t.Fatal(err) 407 } 408 } 409 410 func TestModuleHandshakeError(t *testing.T) { 411 runModuleHandshake(t, 43, fmt.Errorf("handshake mismatch remote 43 > local 42")) 412 } 413 414 func TestModuleHandshakeSuccess(t *testing.T) { 415 runModuleHandshake(t, 42) 416 } 417 418 // testing complex interactions over multiple peers, relaying, dropping 419 func testMultiPeerSetup(a, b enode.ID) []p2ptest.Exchange { 420 421 return []p2ptest.Exchange{ 422 { 423 Label: "primary handshake", 424 Expects: []p2ptest.Expect{ 425 { 426 Code: 0, 427 Msg: &protoHandshake{42, "420"}, 428 Peer: a, 429 }, 430 { 431 Code: 0, 432 Msg: &protoHandshake{42, "420"}, 433 Peer: b, 434 }, 435 }, 436 }, 437 { 438 Label: "module handshake", 439 Triggers: []p2ptest.Trigger{ 440 { 441 Code: 0, 442 Msg: &protoHandshake{42, "420"}, 443 Peer: a, 444 }, 445 { 446 Code: 0, 447 Msg: &protoHandshake{42, "420"}, 448 Peer: b, 449 }, 450 }, 451 Expects: []p2ptest.Expect{ 452 { 453 Code: 1, 454 Msg: &hs0{42}, 455 Peer: a, 456 }, 457 { 458 Code: 1, 459 Msg: &hs0{42}, 460 Peer: b, 461 }, 462 }, 463 }, 464 465 {Label: "alternative module handshake", Triggers: []p2ptest.Trigger{{Code: 1, Msg: &hs0{41}, Peer: a}, 466 {Code: 1, Msg: &hs0{41}, Peer: b}}}, 467 {Label: "repeated module handshake", Triggers: []p2ptest.Trigger{{Code: 1, Msg: &hs0{1}, Peer: a}}}, 468 {Label: "receiving repeated module handshake", Expects: []p2ptest.Expect{{Code: 1, Msg: &hs0{43}, Peer: a}}}} 469 } 470 471 func runMultiplePeers(t *testing.T, peer int, errs ...error) { 472 pp := p2ptest.NewTestPeerPool() 473 s := protocolTester(pp) 474 475 if err := s.TestExchanges(testMultiPeerSetup(s.Nodes[0].ID(), s.Nodes[1].ID())...); err != nil { 476 t.Fatal(err) 477 } 478 // after some exchanges of messages, we can test state changes 479 // here this is simply demonstrated by the peerPool 480 // after the handshake negotiations peers must be added to the pool 481 // time.Sleep(1) 482 tick := time.NewTicker(10 * time.Millisecond) 483 timeout := time.NewTimer(1 * time.Second) 484 WAIT: 485 for { 486 select { 487 case <-tick.C: 488 if pp.Has(s.Nodes[0].ID()) { 489 break WAIT 490 } 491 case <-timeout.C: 492 t.Fatal("timeout") 493 } 494 } 495 if !pp.Has(s.Nodes[1].ID()) { 496 t.Fatalf("missing peer test-1: %v (%v)", pp, s.Nodes) 497 } 498 499 // peer 0 sends kill request for peer with index <peer> 500 err := s.TestExchanges(p2ptest.Exchange{ 501 Triggers: []p2ptest.Trigger{ 502 { 503 Code: 2, 504 Msg: &kill{s.Nodes[peer].ID()}, 505 Peer: s.Nodes[0].ID(), 506 }, 507 }, 508 }) 509 510 if err != nil { 511 t.Fatal(err) 512 } 513 514 // the peer not killed sends a drop request 515 err = s.TestExchanges(p2ptest.Exchange{ 516 Triggers: []p2ptest.Trigger{ 517 { 518 Code: 3, 519 Msg: &drop{}, 520 Peer: s.Nodes[(peer+1)%2].ID(), 521 }, 522 }, 523 }) 524 525 if err != nil { 526 t.Fatal(err) 527 } 528 529 // check the actual discconnect errors on the individual peers 530 var disconnects []*p2ptest.Disconnect 531 for i, err := range errs { 532 disconnects = append(disconnects, &p2ptest.Disconnect{Peer: s.Nodes[i].ID(), Error: err}) 533 } 534 if err := s.TestDisconnected(disconnects...); err != nil { 535 t.Fatal(err) 536 } 537 // test if disconnected peers have been removed from peerPool 538 if pp.Has(s.Nodes[peer].ID()) { 539 t.Fatalf("peer test-%v not dropped: %v (%v)", peer, pp, s.Nodes) 540 } 541 542 } 543 func XTestMultiplePeersDropSelf(t *testing.T) { 544 runMultiplePeers(t, 0, 545 fmt.Errorf("subprotocol error"), 546 fmt.Errorf("Message handler error: (msg code 3): dropped"), 547 ) 548 } 549 550 func XTestMultiplePeersDropOther(t *testing.T) { 551 runMultiplePeers(t, 1, 552 fmt.Errorf("Message handler error: (msg code 3): dropped"), 553 fmt.Errorf("subprotocol error"), 554 ) 555 } 556 557 //dummy implementation of a MsgReadWriter 558 //this allows for quick and easy unit tests without 559 //having to build up the complete protocol 560 type dummyRW struct { 561 msg interface{} 562 size uint32 563 code uint64 564 } 565 566 func (d *dummyRW) WriteMsg(msg p2p.Msg) error { 567 return nil 568 } 569 570 func (d *dummyRW) ReadMsg() (p2p.Msg, error) { 571 enc := bytes.NewReader(d.getDummyMsg()) 572 return p2p.Msg{ 573 Code: d.code, 574 Size: d.size, 575 Payload: enc, 576 ReceivedAt: time.Now(), 577 }, nil 578 } 579 580 func (d *dummyRW) getDummyMsg() []byte { 581 r, _ := rlp.EncodeToBytes(d.msg) 582 var b bytes.Buffer 583 wmsg := WrappedMsg{ 584 Context: b.Bytes(), 585 Size: uint32(len(r)), 586 Payload: r, 587 } 588 rr, _ := rlp.EncodeToBytes(wmsg) 589 d.size = uint32(len(rr)) 590 return rr 591 }