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