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