github.com/theQRL/go-zond@v0.1.1/cmd/devp2p/internal/ethtest/helpers.go (about) 1 // Copyright 2021 The go-ethereum Authors 2 // This file is part of go-ethereum. 3 // 4 // go-ethereum is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU 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 // go-ethereum 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 General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. 16 17 package ethtest 18 19 import ( 20 "errors" 21 "fmt" 22 "net" 23 "reflect" 24 "strings" 25 "time" 26 27 "github.com/davecgh/go-spew/spew" 28 "github.com/theQRL/go-zond/common" 29 "github.com/theQRL/go-zond/core/types" 30 "github.com/theQRL/go-zond/crypto" 31 "github.com/theQRL/go-zond/internal/utesting" 32 "github.com/theQRL/go-zond/p2p" 33 "github.com/theQRL/go-zond/p2p/rlpx" 34 "github.com/theQRL/go-zond/zond/protocols/zond" 35 ) 36 37 var ( 38 pretty = spew.ConfigState{ 39 Indent: " ", 40 DisableCapacities: true, 41 DisablePointerAddresses: true, 42 SortKeys: true, 43 } 44 timeout = 20 * time.Second 45 ) 46 47 // dial attempts to dial the given node and perform a handshake, 48 // returning the created Conn if successful. 49 func (s *Suite) dial() (*Conn, error) { 50 // dial 51 fd, err := net.Dial("tcp", fmt.Sprintf("%v:%d", s.Dest.IP(), s.Dest.TCP())) 52 if err != nil { 53 return nil, err 54 } 55 conn := Conn{Conn: rlpx.NewConn(fd, s.Dest.Pubkey())} 56 // do encHandshake 57 conn.ourKey, _ = crypto.GenerateKey() 58 _, err = conn.Handshake(conn.ourKey) 59 if err != nil { 60 conn.Close() 61 return nil, err 62 } 63 // set default p2p capabilities 64 conn.caps = []p2p.Cap{ 65 {Name: "zond", Version: 66}, 66 {Name: "zond", Version: 67}, 67 {Name: "zond", Version: 68}, 68 } 69 conn.ourHighestProtoVersion = 68 70 return &conn, nil 71 } 72 73 // dialSnap creates a connection with snap/1 capability. 74 func (s *Suite) dialSnap() (*Conn, error) { 75 conn, err := s.dial() 76 if err != nil { 77 return nil, fmt.Errorf("dial failed: %v", err) 78 } 79 conn.caps = append(conn.caps, p2p.Cap{Name: "snap", Version: 1}) 80 conn.ourHighestSnapProtoVersion = 1 81 return conn, nil 82 } 83 84 // peer performs both the protocol handshake and the status message 85 // exchange with the node in order to peer with it. 86 func (c *Conn) peer(chain *Chain, status *Status) error { 87 if err := c.handshake(); err != nil { 88 return fmt.Errorf("handshake failed: %v", err) 89 } 90 if _, err := c.statusExchange(chain, status); err != nil { 91 return fmt.Errorf("status exchange failed: %v", err) 92 } 93 return nil 94 } 95 96 // handshake performs a protocol handshake with the node. 97 func (c *Conn) handshake() error { 98 defer c.SetDeadline(time.Time{}) 99 c.SetDeadline(time.Now().Add(10 * time.Second)) 100 // write hello to client 101 pub0 := crypto.FromECDSAPub(&c.ourKey.PublicKey)[1:] 102 ourHandshake := &Hello{ 103 Version: 5, 104 Caps: c.caps, 105 ID: pub0, 106 } 107 if err := c.Write(ourHandshake); err != nil { 108 return fmt.Errorf("write to connection failed: %v", err) 109 } 110 // read hello from client 111 switch msg := c.Read().(type) { 112 case *Hello: 113 // set snappy if version is at least 5 114 if msg.Version >= 5 { 115 c.SetSnappy(true) 116 } 117 c.negotiateEthProtocol(msg.Caps) 118 if c.negotiatedProtoVersion == 0 { 119 return fmt.Errorf("could not negotiate zond protocol (remote caps: %v, local zond version: %v)", msg.Caps, c.ourHighestProtoVersion) 120 } 121 // If we require snap, verify that it was negotiated 122 if c.ourHighestSnapProtoVersion != c.negotiatedSnapProtoVersion { 123 return fmt.Errorf("could not negotiate snap protocol (remote caps: %v, local snap version: %v)", msg.Caps, c.ourHighestSnapProtoVersion) 124 } 125 return nil 126 default: 127 return fmt.Errorf("bad handshake: %#v", msg) 128 } 129 } 130 131 // negotiateEthProtocol sets the Conn's zond protocol version to highest 132 // advertised capability from peer. 133 func (c *Conn) negotiateEthProtocol(caps []p2p.Cap) { 134 var highestEthVersion uint 135 var highestSnapVersion uint 136 for _, capability := range caps { 137 switch capability.Name { 138 case "zond": 139 if capability.Version > highestEthVersion && capability.Version <= c.ourHighestProtoVersion { 140 highestEthVersion = capability.Version 141 } 142 case "snap": 143 if capability.Version > highestSnapVersion && capability.Version <= c.ourHighestSnapProtoVersion { 144 highestSnapVersion = capability.Version 145 } 146 } 147 } 148 c.negotiatedProtoVersion = highestEthVersion 149 c.negotiatedSnapProtoVersion = highestSnapVersion 150 } 151 152 // statusExchange performs a `Status` message exchange with the given node. 153 func (c *Conn) statusExchange(chain *Chain, status *Status) (Message, error) { 154 defer c.SetDeadline(time.Time{}) 155 c.SetDeadline(time.Now().Add(20 * time.Second)) 156 157 // read status message from client 158 var message Message 159 loop: 160 for { 161 switch msg := c.Read().(type) { 162 case *Status: 163 if have, want := msg.Head, chain.blocks[chain.Len()-1].Hash(); have != want { 164 return nil, fmt.Errorf("wrong head block in status, want: %#x (block %d) have %#x", 165 want, chain.blocks[chain.Len()-1].NumberU64(), have) 166 } 167 if have, want := msg.TD.Cmp(chain.TD()), 0; have != want { 168 return nil, fmt.Errorf("wrong TD in status: have %v want %v", have, want) 169 } 170 if have, want := msg.ForkID, chain.ForkID(); !reflect.DeepEqual(have, want) { 171 return nil, fmt.Errorf("wrong fork ID in status: have %v, want %v", have, want) 172 } 173 if have, want := msg.ProtocolVersion, c.ourHighestProtoVersion; have != uint32(want) { 174 return nil, fmt.Errorf("wrong protocol version: have %v, want %v", have, want) 175 } 176 message = msg 177 break loop 178 case *Disconnect: 179 return nil, fmt.Errorf("disconnect received: %v", msg.Reason) 180 case *Ping: 181 c.Write(&Pong{}) // TODO (renaynay): in the future, this should be an error 182 // (PINGs should not be a response upon fresh connection) 183 default: 184 return nil, fmt.Errorf("bad status message: %s", pretty.Sdump(msg)) 185 } 186 } 187 // make sure zond protocol version is set for negotiation 188 if c.negotiatedProtoVersion == 0 { 189 return nil, errors.New("zond protocol version must be set in Conn") 190 } 191 if status == nil { 192 // default status message 193 status = &Status{ 194 ProtocolVersion: uint32(c.negotiatedProtoVersion), 195 NetworkID: chain.chainConfig.ChainID.Uint64(), 196 TD: chain.TD(), 197 Head: chain.blocks[chain.Len()-1].Hash(), 198 Genesis: chain.blocks[0].Hash(), 199 ForkID: chain.ForkID(), 200 } 201 } 202 if err := c.Write(status); err != nil { 203 return nil, fmt.Errorf("write to connection failed: %v", err) 204 } 205 return message, nil 206 } 207 208 // createSendAndRecvConns creates two connections, one for sending messages to the 209 // node, and one for receiving messages from the node. 210 func (s *Suite) createSendAndRecvConns() (*Conn, *Conn, error) { 211 sendConn, err := s.dial() 212 if err != nil { 213 return nil, nil, fmt.Errorf("dial failed: %v", err) 214 } 215 recvConn, err := s.dial() 216 if err != nil { 217 sendConn.Close() 218 return nil, nil, fmt.Errorf("dial failed: %v", err) 219 } 220 return sendConn, recvConn, nil 221 } 222 223 // readAndServe serves GetBlockHeaders requests while waiting 224 // on another message from the node. 225 func (c *Conn) readAndServe(chain *Chain, timeout time.Duration) Message { 226 start := time.Now() 227 for time.Since(start) < timeout { 228 c.SetReadDeadline(time.Now().Add(10 * time.Second)) 229 230 msg := c.Read() 231 switch msg := msg.(type) { 232 case *Ping: 233 c.Write(&Pong{}) 234 case *GetBlockHeaders: 235 headers, err := chain.GetHeaders(msg) 236 if err != nil { 237 return errorf("could not get headers for inbound header request: %v", err) 238 } 239 resp := &BlockHeaders{ 240 RequestId: msg.ReqID(), 241 BlockHeadersPacket: zond.BlockHeadersPacket(headers), 242 } 243 if err := c.Write(resp); err != nil { 244 return errorf("could not write to connection: %v", err) 245 } 246 default: 247 return msg 248 } 249 } 250 return errorf("no message received within %v", timeout) 251 } 252 253 // headersRequest executes the given `GetBlockHeaders` request. 254 func (c *Conn) headersRequest(request *GetBlockHeaders, chain *Chain, reqID uint64) ([]*types.Header, error) { 255 defer c.SetReadDeadline(time.Time{}) 256 c.SetReadDeadline(time.Now().Add(20 * time.Second)) 257 258 // write request 259 request.RequestId = reqID 260 if err := c.Write(request); err != nil { 261 return nil, fmt.Errorf("could not write to connection: %v", err) 262 } 263 264 // wait for response 265 msg := c.waitForResponse(chain, timeout, request.RequestId) 266 resp, ok := msg.(*BlockHeaders) 267 if !ok { 268 return nil, fmt.Errorf("unexpected message received: %s", pretty.Sdump(msg)) 269 } 270 headers := []*types.Header(resp.BlockHeadersPacket) 271 return headers, nil 272 } 273 274 func (c *Conn) snapRequest(msg Message, id uint64, chain *Chain) (Message, error) { 275 defer c.SetReadDeadline(time.Time{}) 276 c.SetReadDeadline(time.Now().Add(5 * time.Second)) 277 if err := c.Write(msg); err != nil { 278 return nil, fmt.Errorf("could not write to connection: %v", err) 279 } 280 return c.ReadSnap(id) 281 } 282 283 // headersMatch returns whether the received headers match the given request 284 func headersMatch(expected []*types.Header, headers []*types.Header) bool { 285 return reflect.DeepEqual(expected, headers) 286 } 287 288 // waitForResponse reads from the connection until a response with the expected 289 // request ID is received. 290 func (c *Conn) waitForResponse(chain *Chain, timeout time.Duration, requestID uint64) Message { 291 for { 292 msg := c.readAndServe(chain, timeout) 293 if msg.ReqID() == requestID { 294 return msg 295 } 296 } 297 } 298 299 // sendNextBlock broadcasts the next block in the chain and waits 300 // for the node to propagate the block and import it into its chain. 301 func (s *Suite) sendNextBlock() error { 302 // set up sending and receiving connections 303 sendConn, recvConn, err := s.createSendAndRecvConns() 304 if err != nil { 305 return err 306 } 307 defer sendConn.Close() 308 defer recvConn.Close() 309 if err = sendConn.peer(s.chain, nil); err != nil { 310 return fmt.Errorf("peering failed: %v", err) 311 } 312 if err = recvConn.peer(s.chain, nil); err != nil { 313 return fmt.Errorf("peering failed: %v", err) 314 } 315 // create new block announcement 316 nextBlock := s.fullChain.blocks[s.chain.Len()] 317 blockAnnouncement := &NewBlock{ 318 Block: nextBlock, 319 TD: s.fullChain.TotalDifficultyAt(s.chain.Len()), 320 } 321 // send announcement and wait for node to request the header 322 if err = s.testAnnounce(sendConn, recvConn, blockAnnouncement); err != nil { 323 return fmt.Errorf("failed to announce block: %v", err) 324 } 325 // wait for client to update its chain 326 if err = s.waitForBlockImport(recvConn, nextBlock); err != nil { 327 return fmt.Errorf("failed to receive confirmation of block import: %v", err) 328 } 329 // update test suite chain 330 s.chain.blocks = append(s.chain.blocks, nextBlock) 331 return nil 332 } 333 334 // testAnnounce writes a block announcement to the node and waits for the node 335 // to propagate it. 336 func (s *Suite) testAnnounce(sendConn, receiveConn *Conn, blockAnnouncement *NewBlock) error { 337 if err := sendConn.Write(blockAnnouncement); err != nil { 338 return fmt.Errorf("could not write to connection: %v", err) 339 } 340 return s.waitAnnounce(receiveConn, blockAnnouncement) 341 } 342 343 // waitAnnounce waits for a NewBlock or NewBlockHashes announcement from the node. 344 func (s *Suite) waitAnnounce(conn *Conn, blockAnnouncement *NewBlock) error { 345 for { 346 switch msg := conn.readAndServe(s.chain, timeout).(type) { 347 case *NewBlock: 348 if !reflect.DeepEqual(blockAnnouncement.Block.Header(), msg.Block.Header()) { 349 return fmt.Errorf("wrong header in block announcement: \nexpected %v "+ 350 "\ngot %v", blockAnnouncement.Block.Header(), msg.Block.Header()) 351 } 352 if !reflect.DeepEqual(blockAnnouncement.TD, msg.TD) { 353 return fmt.Errorf("wrong TD in announcement: expected %v, got %v", blockAnnouncement.TD, msg.TD) 354 } 355 return nil 356 case *NewBlockHashes: 357 hashes := *msg 358 if blockAnnouncement.Block.Hash() != hashes[0].Hash { 359 return fmt.Errorf("wrong block hash in announcement: expected %v, got %v", blockAnnouncement.Block.Hash(), hashes[0].Hash) 360 } 361 return nil 362 363 // ignore tx announcements from previous tests 364 case *NewPooledTransactionHashes66: 365 continue 366 case *NewPooledTransactionHashes: 367 continue 368 case *Transactions: 369 continue 370 371 default: 372 return fmt.Errorf("unexpected: %s", pretty.Sdump(msg)) 373 } 374 } 375 } 376 377 func (s *Suite) waitForBlockImport(conn *Conn, block *types.Block) error { 378 defer conn.SetReadDeadline(time.Time{}) 379 conn.SetReadDeadline(time.Now().Add(20 * time.Second)) 380 // create request 381 req := &GetBlockHeaders{ 382 GetBlockHeadersPacket: &zond.GetBlockHeadersPacket{ 383 Origin: zond.HashOrNumber{Hash: block.Hash()}, 384 Amount: 1, 385 }, 386 } 387 388 // loop until BlockHeaders response contains desired block, confirming the 389 // node imported the block 390 for { 391 requestID := uint64(54) 392 headers, err := conn.headersRequest(req, s.chain, requestID) 393 if err != nil { 394 return fmt.Errorf("GetBlockHeader request failed: %v", err) 395 } 396 // if headers response is empty, node hasn't imported block yet, try again 397 if len(headers) == 0 { 398 time.Sleep(100 * time.Millisecond) 399 continue 400 } 401 if !reflect.DeepEqual(block.Header(), headers[0]) { 402 return fmt.Errorf("wrong header returned: wanted %v, got %v", block.Header(), headers[0]) 403 } 404 return nil 405 } 406 } 407 408 func (s *Suite) oldAnnounce() error { 409 sendConn, receiveConn, err := s.createSendAndRecvConns() 410 if err != nil { 411 return err 412 } 413 defer sendConn.Close() 414 defer receiveConn.Close() 415 if err := sendConn.peer(s.chain, nil); err != nil { 416 return fmt.Errorf("peering failed: %v", err) 417 } 418 if err := receiveConn.peer(s.chain, nil); err != nil { 419 return fmt.Errorf("peering failed: %v", err) 420 } 421 // create old block announcement 422 oldBlockAnnounce := &NewBlock{ 423 Block: s.chain.blocks[len(s.chain.blocks)/2], 424 TD: s.chain.blocks[len(s.chain.blocks)/2].Difficulty(), 425 } 426 if err := sendConn.Write(oldBlockAnnounce); err != nil { 427 return fmt.Errorf("could not write to connection: %v", err) 428 } 429 // wait to see if the announcement is propagated 430 switch msg := receiveConn.readAndServe(s.chain, time.Second*8).(type) { 431 case *NewBlock: 432 block := *msg 433 if block.Block.Hash() == oldBlockAnnounce.Block.Hash() { 434 return fmt.Errorf("unexpected: block propagated: %s", pretty.Sdump(msg)) 435 } 436 case *NewBlockHashes: 437 hashes := *msg 438 for _, hash := range hashes { 439 if hash.Hash == oldBlockAnnounce.Block.Hash() { 440 return fmt.Errorf("unexpected: block announced: %s", pretty.Sdump(msg)) 441 } 442 } 443 case *Error: 444 errMsg := *msg 445 // check to make sure error is timeout (propagation didn't come through == test successful) 446 if !strings.Contains(errMsg.String(), "timeout") { 447 return fmt.Errorf("unexpected error: %v", pretty.Sdump(msg)) 448 } 449 default: 450 return fmt.Errorf("unexpected: %s", pretty.Sdump(msg)) 451 } 452 return nil 453 } 454 455 func (s *Suite) maliciousHandshakes(t *utesting.T) error { 456 conn, err := s.dial() 457 if err != nil { 458 return fmt.Errorf("dial failed: %v", err) 459 } 460 defer conn.Close() 461 462 // write hello to client 463 pub0 := crypto.FromECDSAPub(&conn.ourKey.PublicKey)[1:] 464 handshakes := []*Hello{ 465 { 466 Version: 5, 467 Caps: []p2p.Cap{ 468 {Name: largeString(2), Version: 64}, 469 }, 470 ID: pub0, 471 }, 472 { 473 Version: 5, 474 Caps: []p2p.Cap{ 475 {Name: "zond", Version: 64}, 476 {Name: "zond", Version: 65}, 477 }, 478 ID: append(pub0, byte(0)), 479 }, 480 { 481 Version: 5, 482 Caps: []p2p.Cap{ 483 {Name: "zond", Version: 64}, 484 {Name: "zond", Version: 65}, 485 }, 486 ID: append(pub0, pub0...), 487 }, 488 { 489 Version: 5, 490 Caps: []p2p.Cap{ 491 {Name: "zond", Version: 64}, 492 {Name: "zond", Version: 65}, 493 }, 494 ID: largeBuffer(2), 495 }, 496 { 497 Version: 5, 498 Caps: []p2p.Cap{ 499 {Name: largeString(2), Version: 64}, 500 }, 501 ID: largeBuffer(2), 502 }, 503 } 504 for i, handshake := range handshakes { 505 t.Logf("Testing malicious handshake %v\n", i) 506 if err := conn.Write(handshake); err != nil { 507 return fmt.Errorf("could not write to connection: %v", err) 508 } 509 // check that the peer disconnected 510 for i := 0; i < 2; i++ { 511 switch msg := conn.readAndServe(s.chain, 20*time.Second).(type) { 512 case *Disconnect: 513 case *Error: 514 case *Hello: 515 // Discard one hello as Hello's are sent concurrently 516 continue 517 default: 518 return fmt.Errorf("unexpected: %s", pretty.Sdump(msg)) 519 } 520 } 521 // dial for the next round 522 conn, err = s.dial() 523 if err != nil { 524 return fmt.Errorf("dial failed: %v", err) 525 } 526 } 527 return nil 528 } 529 530 func (s *Suite) maliciousStatus(conn *Conn) error { 531 if err := conn.handshake(); err != nil { 532 return fmt.Errorf("handshake failed: %v", err) 533 } 534 status := &Status{ 535 ProtocolVersion: uint32(conn.negotiatedProtoVersion), 536 NetworkID: s.chain.chainConfig.ChainID.Uint64(), 537 TD: largeNumber(2), 538 Head: s.chain.blocks[s.chain.Len()-1].Hash(), 539 Genesis: s.chain.blocks[0].Hash(), 540 ForkID: s.chain.ForkID(), 541 } 542 543 // get status 544 msg, err := conn.statusExchange(s.chain, status) 545 if err != nil { 546 return fmt.Errorf("status exchange failed: %v", err) 547 } 548 switch msg := msg.(type) { 549 case *Status: 550 default: 551 return fmt.Errorf("expected status, got: %#v ", msg) 552 } 553 554 // wait for disconnect 555 switch msg := conn.readAndServe(s.chain, timeout).(type) { 556 case *Disconnect: 557 return nil 558 case *Error: 559 return nil 560 default: 561 return fmt.Errorf("expected disconnect, got: %s", pretty.Sdump(msg)) 562 } 563 } 564 565 func (s *Suite) hashAnnounce() error { 566 // create connections 567 sendConn, recvConn, err := s.createSendAndRecvConns() 568 if err != nil { 569 return fmt.Errorf("failed to create connections: %v", err) 570 } 571 defer sendConn.Close() 572 defer recvConn.Close() 573 if err := sendConn.peer(s.chain, nil); err != nil { 574 return fmt.Errorf("peering failed: %v", err) 575 } 576 if err := recvConn.peer(s.chain, nil); err != nil { 577 return fmt.Errorf("peering failed: %v", err) 578 } 579 580 // create NewBlockHashes announcement 581 type anno struct { 582 Hash common.Hash // Hash of one particular block being announced 583 Number uint64 // Number of one particular block being announced 584 } 585 nextBlock := s.fullChain.blocks[s.chain.Len()] 586 announcement := anno{Hash: nextBlock.Hash(), Number: nextBlock.Number().Uint64()} 587 newBlockHash := &NewBlockHashes{announcement} 588 if err := sendConn.Write(newBlockHash); err != nil { 589 return fmt.Errorf("failed to write to connection: %v", err) 590 } 591 592 // Announcement sent, now wait for a header request 593 msg := sendConn.Read() 594 blockHeaderReq, ok := msg.(*GetBlockHeaders) 595 if !ok { 596 return fmt.Errorf("unexpected %s", pretty.Sdump(msg)) 597 } 598 if blockHeaderReq.Amount != 1 { 599 return fmt.Errorf("unexpected number of block headers requested: %v", blockHeaderReq.Amount) 600 } 601 if blockHeaderReq.Origin.Hash != announcement.Hash { 602 return fmt.Errorf("unexpected block header requested. Announced:\n %v\n Remote request:\n%v", 603 pretty.Sdump(announcement), 604 pretty.Sdump(blockHeaderReq)) 605 } 606 err = sendConn.Write(&BlockHeaders{ 607 RequestId: blockHeaderReq.ReqID(), 608 BlockHeadersPacket: zond.BlockHeadersPacket{nextBlock.Header()}, 609 }) 610 if err != nil { 611 return fmt.Errorf("failed to write to connection: %v", err) 612 } 613 614 // wait for block announcement 615 msg = recvConn.readAndServe(s.chain, timeout) 616 switch msg := msg.(type) { 617 case *NewBlockHashes: 618 hashes := *msg 619 if len(hashes) != 1 { 620 return fmt.Errorf("unexpected new block hash announcement: wanted 1 announcement, got %d", len(hashes)) 621 } 622 if nextBlock.Hash() != hashes[0].Hash { 623 return fmt.Errorf("unexpected block hash announcement, wanted %v, got %v", nextBlock.Hash(), 624 hashes[0].Hash) 625 } 626 627 case *NewBlock: 628 // node should only propagate NewBlock without having requested the body if the body is empty 629 nextBlockBody := nextBlock.Body() 630 if len(nextBlockBody.Transactions) != 0 || len(nextBlockBody.Uncles) != 0 { 631 return fmt.Errorf("unexpected non-empty new block propagated: %s", pretty.Sdump(msg)) 632 } 633 if msg.Block.Hash() != nextBlock.Hash() { 634 return fmt.Errorf("mismatched hash of propagated new block: wanted %v, got %v", 635 nextBlock.Hash(), msg.Block.Hash()) 636 } 637 // check to make sure header matches header that was sent to the node 638 if !reflect.DeepEqual(nextBlock.Header(), msg.Block.Header()) { 639 return fmt.Errorf("incorrect header received: wanted %v, got %v", nextBlock.Header(), msg.Block.Header()) 640 } 641 default: 642 return fmt.Errorf("unexpected: %s", pretty.Sdump(msg)) 643 } 644 // confirm node imported block 645 if err := s.waitForBlockImport(recvConn, nextBlock); err != nil { 646 return fmt.Errorf("error waiting for node to import new block: %v", err) 647 } 648 // update the chain 649 s.chain.blocks = append(s.chain.blocks, nextBlock) 650 return nil 651 }