github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/p2p/protocols/protocol_test.go (about) 1 2 //<developer> 3 // <name>linapex 曹一峰</name> 4 // <email>linapex@163.com</email> 5 // <wx>superexc</wx> 6 // <qqgroup>128148617</qqgroup> 7 // <url>https://jsq.ink</url> 8 // <role>pku engineer</role> 9 // <date>2019-03-16 19:16:41</date> 10 //</624450106002771968> 11 12 13 package protocols 14 15 import ( 16 "bytes" 17 "context" 18 "errors" 19 "fmt" 20 "testing" 21 "time" 22 23 "github.com/ethereum/go-ethereum/rlp" 24 25 "github.com/ethereum/go-ethereum/p2p" 26 "github.com/ethereum/go-ethereum/p2p/enode" 27 "github.com/ethereum/go-ethereum/p2p/simulations/adapters" 28 p2ptest "github.com/ethereum/go-ethereum/p2p/testing" 29 ) 30 31 //握手消息类型 32 type hs0 struct { 33 C uint 34 } 35 36 //用nodeid终止/删除对等机的消息 37 type kill struct { 38 C enode.ID 39 } 40 41 //断开连接的消息 42 type drop struct { 43 } 44 45 ///protochandshake表示协议与模块无关的方面,并且 46 //第一个消息对等端作为初始交换的一部分发送和接收 47 type protoHandshake struct { 48 Version uint //本地和远程对等机应具有相同的版本 49 NetworkID string //本地和远程对等机应具有相同的网络ID 50 } 51 52 //检查协议握手验证本地和远程协议握手是否匹配 53 func checkProtoHandshake(testVersion uint, testNetworkID string) func(interface{}) error { 54 return func(rhs interface{}) error { 55 remote := rhs.(*protoHandshake) 56 if remote.NetworkID != testNetworkID { 57 return fmt.Errorf("%s (!= %s)", remote.NetworkID, testNetworkID) 58 } 59 60 if remote.Version != testVersion { 61 return fmt.Errorf("%d (!= %d)", remote.Version, testVersion) 62 } 63 return nil 64 } 65 } 66 67 //新协议设置协议 68 //这里的run函数演示了使用peerpool和handshake的典型协议 69 //以及注册到处理程序的消息 70 func newProtocol(pp *p2ptest.TestPeerPool) func(*p2p.Peer, p2p.MsgReadWriter) error { 71 spec := &Spec{ 72 Name: "test", 73 Version: 42, 74 MaxMsgSize: 10 * 1024, 75 Messages: []interface{}{ 76 protoHandshake{}, 77 hs0{}, 78 kill{}, 79 drop{}, 80 }, 81 } 82 return func(p *p2p.Peer, rw p2p.MsgReadWriter) error { 83 peer := NewPeer(p, rw, spec) 84 85 //启动一次性协议握手并检查有效性 86 ctx, cancel := context.WithTimeout(context.Background(), time.Second) 87 defer cancel() 88 phs := &protoHandshake{42, "420"} 89 hsCheck := checkProtoHandshake(phs.Version, phs.NetworkID) 90 _, err := peer.Handshake(ctx, phs, hsCheck) 91 if err != nil { 92 return err 93 } 94 95 lhs := &hs0{42} 96 //模块握手演示同一类型消息的简单可重复交换 97 hs, err := peer.Handshake(ctx, lhs, nil) 98 if err != nil { 99 return err 100 } 101 102 if rmhs := hs.(*hs0); rmhs.C > lhs.C { 103 return fmt.Errorf("handshake mismatch remote %v > local %v", rmhs.C, lhs.C) 104 } 105 106 handle := func(ctx context.Context, msg interface{}) error { 107 switch msg := msg.(type) { 108 109 case *protoHandshake: 110 return errors.New("duplicate handshake") 111 112 case *hs0: 113 rhs := msg 114 if rhs.C > lhs.C { 115 return fmt.Errorf("handshake mismatch remote %v > local %v", rhs.C, lhs.C) 116 } 117 lhs.C += rhs.C 118 return peer.Send(ctx, lhs) 119 120 case *kill: 121 //演示使用对等池,终止另一个对等连接作为对消息的响应 122 id := msg.C 123 pp.Get(id).Drop(errors.New("killed")) 124 return nil 125 126 case *drop: 127 //对于测试,我们可以在接收到丢弃消息时触发自诱导断开。 128 return errors.New("dropped") 129 130 default: 131 return fmt.Errorf("unknown message type: %T", msg) 132 } 133 } 134 135 pp.Add(peer) 136 defer pp.Remove(peer) 137 return peer.Run(handle) 138 } 139 } 140 141 func protocolTester(t *testing.T, pp *p2ptest.TestPeerPool) *p2ptest.ProtocolTester { 142 conf := adapters.RandomNodeConfig() 143 return p2ptest.NewProtocolTester(t, conf.ID, 2, newProtocol(pp)) 144 } 145 146 func protoHandshakeExchange(id enode.ID, proto *protoHandshake) []p2ptest.Exchange { 147 148 return []p2ptest.Exchange{ 149 { 150 Expects: []p2ptest.Expect{ 151 { 152 Code: 0, 153 Msg: &protoHandshake{42, "420"}, 154 Peer: id, 155 }, 156 }, 157 }, 158 { 159 Triggers: []p2ptest.Trigger{ 160 { 161 Code: 0, 162 Msg: proto, 163 Peer: id, 164 }, 165 }, 166 }, 167 } 168 } 169 170 func runProtoHandshake(t *testing.T, proto *protoHandshake, errs ...error) { 171 pp := p2ptest.NewTestPeerPool() 172 s := protocolTester(t, pp) 173 //托多:多做一次握手 174 node := s.Nodes[0] 175 if err := s.TestExchanges(protoHandshakeExchange(node.ID(), proto)...); err != nil { 176 t.Fatal(err) 177 } 178 var disconnects []*p2ptest.Disconnect 179 for i, err := range errs { 180 disconnects = append(disconnects, &p2ptest.Disconnect{Peer: s.Nodes[i].ID(), Error: err}) 181 } 182 if err := s.TestDisconnected(disconnects...); err != nil { 183 t.Fatal(err) 184 } 185 } 186 187 type dummyHook struct { 188 peer *Peer 189 size uint32 190 msg interface{} 191 send bool 192 err error 193 waitC chan struct{} 194 } 195 196 type dummyMsg struct { 197 Content string 198 } 199 200 func (d *dummyHook) Send(peer *Peer, size uint32, msg interface{}) error { 201 d.peer = peer 202 d.size = size 203 d.msg = msg 204 d.send = true 205 return d.err 206 } 207 208 func (d *dummyHook) Receive(peer *Peer, size uint32, msg interface{}) error { 209 d.peer = peer 210 d.size = size 211 d.msg = msg 212 d.send = false 213 d.waitC <- struct{}{} 214 return d.err 215 } 216 217 func TestProtocolHook(t *testing.T) { 218 testHook := &dummyHook{ 219 waitC: make(chan struct{}, 1), 220 } 221 spec := &Spec{ 222 Name: "test", 223 Version: 42, 224 MaxMsgSize: 10 * 1024, 225 Messages: []interface{}{ 226 dummyMsg{}, 227 }, 228 Hook: testHook, 229 } 230 231 runFunc := func(p *p2p.Peer, rw p2p.MsgReadWriter) error { 232 peer := NewPeer(p, rw, spec) 233 ctx := context.TODO() 234 err := peer.Send(ctx, &dummyMsg{ 235 Content: "handshake"}) 236 237 if err != nil { 238 t.Fatal(err) 239 } 240 241 handle := func(ctx context.Context, msg interface{}) error { 242 return nil 243 } 244 245 return peer.Run(handle) 246 } 247 248 conf := adapters.RandomNodeConfig() 249 tester := p2ptest.NewProtocolTester(t, conf.ID, 2, runFunc) 250 err := tester.TestExchanges(p2ptest.Exchange{ 251 Expects: []p2ptest.Expect{ 252 { 253 Code: 0, 254 Msg: &dummyMsg{Content: "handshake"}, 255 Peer: tester.Nodes[0].ID(), 256 }, 257 }, 258 }) 259 if err != nil { 260 t.Fatal(err) 261 } 262 if testHook.msg == nil || testHook.msg.(*dummyMsg).Content != "handshake" { 263 t.Fatal("Expected msg to be set, but it is not") 264 } 265 if !testHook.send { 266 t.Fatal("Expected a send message, but it is not") 267 } 268 if testHook.peer == nil || testHook.peer.ID() != tester.Nodes[0].ID() { 269 t.Fatal("Expected peer ID to be set correctly, but it is not") 270 } 271 if testHook.size != 11 { //11是编码消息的长度 272 t.Fatalf("Expected size to be %d, but it is %d ", 1, testHook.size) 273 } 274 275 err = tester.TestExchanges(p2ptest.Exchange{ 276 Triggers: []p2ptest.Trigger{ 277 { 278 Code: 0, 279 Msg: &dummyMsg{Content: "response"}, 280 Peer: tester.Nodes[1].ID(), 281 }, 282 }, 283 }) 284 285 <-testHook.waitC 286 287 if err != nil { 288 t.Fatal(err) 289 } 290 if testHook.msg == nil || testHook.msg.(*dummyMsg).Content != "response" { 291 t.Fatal("Expected msg to be set, but it is not") 292 } 293 if testHook.send { 294 t.Fatal("Expected a send message, but it is not") 295 } 296 if testHook.peer == nil || testHook.peer.ID() != tester.Nodes[1].ID() { 297 t.Fatal("Expected peer ID to be set correctly, but it is not") 298 } 299 if testHook.size != 10 { //11是编码消息的长度 300 t.Fatalf("Expected size to be %d, but it is %d ", 1, testHook.size) 301 } 302 303 testHook.err = fmt.Errorf("dummy error") 304 err = tester.TestExchanges(p2ptest.Exchange{ 305 Triggers: []p2ptest.Trigger{ 306 { 307 Code: 0, 308 Msg: &dummyMsg{Content: "response"}, 309 Peer: tester.Nodes[1].ID(), 310 }, 311 }, 312 }) 313 314 <-testHook.waitC 315 316 time.Sleep(100 * time.Millisecond) 317 err = tester.TestDisconnected(&p2ptest.Disconnect{Peer: tester.Nodes[1].ID(), Error: testHook.err}) 318 if err != nil { 319 t.Fatalf("Expected a specific disconnect error, but got different one: %v", err) 320 } 321 322 } 323 324 //我们需要测试,如果钩子没有定义,那么消息基础结构 325 //(发送、接收)仍然有效 326 func TestNoHook(t *testing.T) { 327 //创建测试规范 328 spec := createTestSpec() 329 //随机节点 330 id := adapters.RandomNodeConfig().ID 331 //同辈 332 p := p2p.NewPeer(id, "testPeer", nil) 333 rw := &dummyRW{} 334 peer := NewPeer(p, rw, spec) 335 ctx := context.TODO() 336 msg := &perBytesMsgSenderPays{Content: "testBalance"} 337 //发信息 338 err := peer.Send(ctx, msg) 339 if err != nil { 340 t.Fatal(err) 341 } 342 //模拟接收消息 343 rw.msg = msg 344 peer.handleIncoming(func(ctx context.Context, msg interface{}) error { 345 return nil 346 }) 347 //一切都应该正常工作,不会导致任何错误。 348 } 349 350 func TestProtoHandshakeVersionMismatch(t *testing.T) { 351 runProtoHandshake(t, &protoHandshake{41, "420"}, errorf(ErrHandshake, errorf(ErrHandler, "(msg code 0): 41 (!= 42)").Error())) 352 } 353 354 func TestProtoHandshakeNetworkIDMismatch(t *testing.T) { 355 runProtoHandshake(t, &protoHandshake{42, "421"}, errorf(ErrHandshake, errorf(ErrHandler, "(msg code 0): 421 (!= 420)").Error())) 356 } 357 358 func TestProtoHandshakeSuccess(t *testing.T) { 359 runProtoHandshake(t, &protoHandshake{42, "420"}) 360 } 361 362 func moduleHandshakeExchange(id enode.ID, resp uint) []p2ptest.Exchange { 363 364 return []p2ptest.Exchange{ 365 { 366 Expects: []p2ptest.Expect{ 367 { 368 Code: 1, 369 Msg: &hs0{42}, 370 Peer: id, 371 }, 372 }, 373 }, 374 { 375 Triggers: []p2ptest.Trigger{ 376 { 377 Code: 1, 378 Msg: &hs0{resp}, 379 Peer: id, 380 }, 381 }, 382 }, 383 } 384 } 385 386 func runModuleHandshake(t *testing.T, resp uint, errs ...error) { 387 pp := p2ptest.NewTestPeerPool() 388 s := protocolTester(t, pp) 389 node := s.Nodes[0] 390 if err := s.TestExchanges(protoHandshakeExchange(node.ID(), &protoHandshake{42, "420"})...); err != nil { 391 t.Fatal(err) 392 } 393 if err := s.TestExchanges(moduleHandshakeExchange(node.ID(), resp)...); err != nil { 394 t.Fatal(err) 395 } 396 var disconnects []*p2ptest.Disconnect 397 for i, err := range errs { 398 disconnects = append(disconnects, &p2ptest.Disconnect{Peer: s.Nodes[i].ID(), Error: err}) 399 } 400 if err := s.TestDisconnected(disconnects...); err != nil { 401 t.Fatal(err) 402 } 403 } 404 405 func TestModuleHandshakeError(t *testing.T) { 406 runModuleHandshake(t, 43, fmt.Errorf("handshake mismatch remote 43 > local 42")) 407 } 408 409 func TestModuleHandshakeSuccess(t *testing.T) { 410 runModuleHandshake(t, 42) 411 } 412 413 //在多个对等点上测试复杂的交互、中继、丢弃 414 func testMultiPeerSetup(a, b enode.ID) []p2ptest.Exchange { 415 416 return []p2ptest.Exchange{ 417 { 418 Label: "primary handshake", 419 Expects: []p2ptest.Expect{ 420 { 421 Code: 0, 422 Msg: &protoHandshake{42, "420"}, 423 Peer: a, 424 }, 425 { 426 Code: 0, 427 Msg: &protoHandshake{42, "420"}, 428 Peer: b, 429 }, 430 }, 431 }, 432 { 433 Label: "module handshake", 434 Triggers: []p2ptest.Trigger{ 435 { 436 Code: 0, 437 Msg: &protoHandshake{42, "420"}, 438 Peer: a, 439 }, 440 { 441 Code: 0, 442 Msg: &protoHandshake{42, "420"}, 443 Peer: b, 444 }, 445 }, 446 Expects: []p2ptest.Expect{ 447 { 448 Code: 1, 449 Msg: &hs0{42}, 450 Peer: a, 451 }, 452 { 453 Code: 1, 454 Msg: &hs0{42}, 455 Peer: b, 456 }, 457 }, 458 }, 459 460 {Label: "alternative module handshake", Triggers: []p2ptest.Trigger{{Code: 1, Msg: &hs0{41}, Peer: a}, 461 {Code: 1, Msg: &hs0{41}, Peer: b}}}, 462 {Label: "repeated module handshake", Triggers: []p2ptest.Trigger{{Code: 1, Msg: &hs0{1}, Peer: a}}}, 463 {Label: "receiving repeated module handshake", Expects: []p2ptest.Expect{{Code: 1, Msg: &hs0{43}, Peer: a}}}} 464 } 465 466 func runMultiplePeers(t *testing.T, peer int, errs ...error) { 467 pp := p2ptest.NewTestPeerPool() 468 s := protocolTester(t, pp) 469 470 if err := s.TestExchanges(testMultiPeerSetup(s.Nodes[0].ID(), s.Nodes[1].ID())...); err != nil { 471 t.Fatal(err) 472 } 473 //在一些消息交换之后,我们可以测试状态变化 474 //在这里,这只是由Peerpool演示的 475 //握手后,必须将对等方添加到池中 476 //睡眠时间(1) 477 tick := time.NewTicker(10 * time.Millisecond) 478 timeout := time.NewTimer(1 * time.Second) 479 WAIT: 480 for { 481 select { 482 case <-tick.C: 483 if pp.Has(s.Nodes[0].ID()) { 484 break WAIT 485 } 486 case <-timeout.C: 487 t.Fatal("timeout") 488 } 489 } 490 if !pp.Has(s.Nodes[1].ID()) { 491 t.Fatalf("missing peer test-1: %v (%v)", pp, s.Nodes) 492 } 493 494 //peer 0发送索引为peer的kill请求<peer> 495 err := s.TestExchanges(p2ptest.Exchange{ 496 Triggers: []p2ptest.Trigger{ 497 { 498 Code: 2, 499 Msg: &kill{s.Nodes[peer].ID()}, 500 Peer: s.Nodes[0].ID(), 501 }, 502 }, 503 }) 504 505 if err != nil { 506 t.Fatal(err) 507 } 508 509 //未被杀死的对等机发送删除请求 510 err = s.TestExchanges(p2ptest.Exchange{ 511 Triggers: []p2ptest.Trigger{ 512 { 513 Code: 3, 514 Msg: &drop{}, 515 Peer: s.Nodes[(peer+1)%2].ID(), 516 }, 517 }, 518 }) 519 520 if err != nil { 521 t.Fatal(err) 522 } 523 524 //检查各个对等机上的实际DiscConnect错误 525 var disconnects []*p2ptest.Disconnect 526 for i, err := range errs { 527 disconnects = append(disconnects, &p2ptest.Disconnect{Peer: s.Nodes[i].ID(), Error: err}) 528 } 529 if err := s.TestDisconnected(disconnects...); err != nil { 530 t.Fatal(err) 531 } 532 //测试是否已从对等池中删除断开连接的对等机 533 if pp.Has(s.Nodes[peer].ID()) { 534 t.Fatalf("peer test-%v not dropped: %v (%v)", peer, pp, s.Nodes) 535 } 536 537 } 538 func XTestMultiplePeersDropSelf(t *testing.T) { 539 runMultiplePeers(t, 0, 540 fmt.Errorf("subprotocol error"), 541 fmt.Errorf("Message handler error: (msg code 3): dropped"), 542 ) 543 } 544 545 func XTestMultiplePeersDropOther(t *testing.T) { 546 runMultiplePeers(t, 1, 547 fmt.Errorf("Message handler error: (msg code 3): dropped"), 548 fmt.Errorf("subprotocol error"), 549 ) 550 } 551 552 //msgreadwriter的虚拟实现 553 //这允许快速简单的单元测试 554 //必须建立完整的协议 555 type dummyRW struct { 556 msg interface{} 557 size uint32 558 code uint64 559 } 560 561 func (d *dummyRW) WriteMsg(msg p2p.Msg) error { 562 return nil 563 } 564 565 func (d *dummyRW) ReadMsg() (p2p.Msg, error) { 566 enc := bytes.NewReader(d.getDummyMsg()) 567 return p2p.Msg{ 568 Code: d.code, 569 Size: d.size, 570 Payload: enc, 571 ReceivedAt: time.Now(), 572 }, nil 573 } 574 575 func (d *dummyRW) getDummyMsg() []byte { 576 r, _ := rlp.EncodeToBytes(d.msg) 577 var b bytes.Buffer 578 wmsg := WrappedMsg{ 579 Context: b.Bytes(), 580 Size: uint32(len(r)), 581 Payload: r, 582 } 583 rr, _ := rlp.EncodeToBytes(wmsg) 584 d.size = uint32(len(rr)) 585 return rr 586 } 587