github.com/ethw3/go-ethereuma@v0.0.0-20221013053120-c14602a4c23c/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/ethw3/go-ethereuma/common" 28 "github.com/ethw3/go-ethereuma/core/types" 29 "github.com/ethw3/go-ethereuma/crypto" 30 "github.com/ethw3/go-ethereuma/eth/protocols/eth" 31 "github.com/ethw3/go-ethereuma/internal/utesting" 32 "github.com/ethw3/go-ethereuma/p2p" 33 "github.com/ethw3/go-ethereuma/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 case *NewPooledTransactionHashes: 361 // ignore tx announcements from previous tests 362 continue 363 default: 364 return fmt.Errorf("unexpected: %s", pretty.Sdump(msg)) 365 } 366 } 367 } 368 369 func (s *Suite) waitForBlockImport(conn *Conn, block *types.Block) error { 370 defer conn.SetReadDeadline(time.Time{}) 371 conn.SetReadDeadline(time.Now().Add(20 * time.Second)) 372 // create request 373 req := &GetBlockHeaders{ 374 GetBlockHeadersPacket: ð.GetBlockHeadersPacket{ 375 Origin: eth.HashOrNumber{Hash: block.Hash()}, 376 Amount: 1, 377 }, 378 } 379 380 // loop until BlockHeaders response contains desired block, confirming the 381 // node imported the block 382 for { 383 requestID := uint64(54) 384 headers, err := conn.headersRequest(req, s.chain, requestID) 385 if err != nil { 386 return fmt.Errorf("GetBlockHeader request failed: %v", err) 387 } 388 // if headers response is empty, node hasn't imported block yet, try again 389 if len(headers) == 0 { 390 time.Sleep(100 * time.Millisecond) 391 continue 392 } 393 if !reflect.DeepEqual(block.Header(), headers[0]) { 394 return fmt.Errorf("wrong header returned: wanted %v, got %v", block.Header(), headers[0]) 395 } 396 return nil 397 } 398 } 399 400 func (s *Suite) oldAnnounce() error { 401 sendConn, receiveConn, err := s.createSendAndRecvConns() 402 if err != nil { 403 return err 404 } 405 defer sendConn.Close() 406 defer receiveConn.Close() 407 if err := sendConn.peer(s.chain, nil); err != nil { 408 return fmt.Errorf("peering failed: %v", err) 409 } 410 if err := receiveConn.peer(s.chain, nil); err != nil { 411 return fmt.Errorf("peering failed: %v", err) 412 } 413 // create old block announcement 414 oldBlockAnnounce := &NewBlock{ 415 Block: s.chain.blocks[len(s.chain.blocks)/2], 416 TD: s.chain.blocks[len(s.chain.blocks)/2].Difficulty(), 417 } 418 if err := sendConn.Write(oldBlockAnnounce); err != nil { 419 return fmt.Errorf("could not write to connection: %v", err) 420 } 421 // wait to see if the announcement is propagated 422 switch msg := receiveConn.readAndServe(s.chain, time.Second*8).(type) { 423 case *NewBlock: 424 block := *msg 425 if block.Block.Hash() == oldBlockAnnounce.Block.Hash() { 426 return fmt.Errorf("unexpected: block propagated: %s", pretty.Sdump(msg)) 427 } 428 case *NewBlockHashes: 429 hashes := *msg 430 for _, hash := range hashes { 431 if hash.Hash == oldBlockAnnounce.Block.Hash() { 432 return fmt.Errorf("unexpected: block announced: %s", pretty.Sdump(msg)) 433 } 434 } 435 case *Error: 436 errMsg := *msg 437 // check to make sure error is timeout (propagation didn't come through == test successful) 438 if !strings.Contains(errMsg.String(), "timeout") { 439 return fmt.Errorf("unexpected error: %v", pretty.Sdump(msg)) 440 } 441 default: 442 return fmt.Errorf("unexpected: %s", pretty.Sdump(msg)) 443 } 444 return nil 445 } 446 447 func (s *Suite) maliciousHandshakes(t *utesting.T) error { 448 conn, err := s.dial() 449 if err != nil { 450 return fmt.Errorf("dial failed: %v", err) 451 } 452 defer conn.Close() 453 454 // write hello to client 455 pub0 := crypto.FromECDSAPub(&conn.ourKey.PublicKey)[1:] 456 handshakes := []*Hello{ 457 { 458 Version: 5, 459 Caps: []p2p.Cap{ 460 {Name: largeString(2), Version: 64}, 461 }, 462 ID: pub0, 463 }, 464 { 465 Version: 5, 466 Caps: []p2p.Cap{ 467 {Name: "eth", Version: 64}, 468 {Name: "eth", Version: 65}, 469 }, 470 ID: append(pub0, byte(0)), 471 }, 472 { 473 Version: 5, 474 Caps: []p2p.Cap{ 475 {Name: "eth", Version: 64}, 476 {Name: "eth", Version: 65}, 477 }, 478 ID: append(pub0, pub0...), 479 }, 480 { 481 Version: 5, 482 Caps: []p2p.Cap{ 483 {Name: "eth", Version: 64}, 484 {Name: "eth", Version: 65}, 485 }, 486 ID: largeBuffer(2), 487 }, 488 { 489 Version: 5, 490 Caps: []p2p.Cap{ 491 {Name: largeString(2), Version: 64}, 492 }, 493 ID: largeBuffer(2), 494 }, 495 } 496 for i, handshake := range handshakes { 497 t.Logf("Testing malicious handshake %v\n", i) 498 if err := conn.Write(handshake); err != nil { 499 return fmt.Errorf("could not write to connection: %v", err) 500 } 501 // check that the peer disconnected 502 for i := 0; i < 2; i++ { 503 switch msg := conn.readAndServe(s.chain, 20*time.Second).(type) { 504 case *Disconnect: 505 case *Error: 506 case *Hello: 507 // Discard one hello as Hello's are sent concurrently 508 continue 509 default: 510 return fmt.Errorf("unexpected: %s", pretty.Sdump(msg)) 511 } 512 } 513 // dial for the next round 514 conn, err = s.dial() 515 if err != nil { 516 return fmt.Errorf("dial failed: %v", err) 517 } 518 } 519 return nil 520 } 521 522 func (s *Suite) maliciousStatus(conn *Conn) error { 523 if err := conn.handshake(); err != nil { 524 return fmt.Errorf("handshake failed: %v", err) 525 } 526 status := &Status{ 527 ProtocolVersion: uint32(conn.negotiatedProtoVersion), 528 NetworkID: s.chain.chainConfig.ChainID.Uint64(), 529 TD: largeNumber(2), 530 Head: s.chain.blocks[s.chain.Len()-1].Hash(), 531 Genesis: s.chain.blocks[0].Hash(), 532 ForkID: s.chain.ForkID(), 533 } 534 535 // get status 536 msg, err := conn.statusExchange(s.chain, status) 537 if err != nil { 538 return fmt.Errorf("status exchange failed: %v", err) 539 } 540 switch msg := msg.(type) { 541 case *Status: 542 default: 543 return fmt.Errorf("expected status, got: %#v ", msg) 544 } 545 546 // wait for disconnect 547 switch msg := conn.readAndServe(s.chain, timeout).(type) { 548 case *Disconnect: 549 return nil 550 case *Error: 551 return nil 552 default: 553 return fmt.Errorf("expected disconnect, got: %s", pretty.Sdump(msg)) 554 } 555 } 556 557 func (s *Suite) hashAnnounce() error { 558 // create connections 559 sendConn, recvConn, err := s.createSendAndRecvConns() 560 if err != nil { 561 return fmt.Errorf("failed to create connections: %v", err) 562 } 563 defer sendConn.Close() 564 defer recvConn.Close() 565 if err := sendConn.peer(s.chain, nil); err != nil { 566 return fmt.Errorf("peering failed: %v", err) 567 } 568 if err := recvConn.peer(s.chain, nil); err != nil { 569 return fmt.Errorf("peering failed: %v", err) 570 } 571 572 // create NewBlockHashes announcement 573 type anno struct { 574 Hash common.Hash // Hash of one particular block being announced 575 Number uint64 // Number of one particular block being announced 576 } 577 nextBlock := s.fullChain.blocks[s.chain.Len()] 578 announcement := anno{Hash: nextBlock.Hash(), Number: nextBlock.Number().Uint64()} 579 newBlockHash := &NewBlockHashes{announcement} 580 if err := sendConn.Write(newBlockHash); err != nil { 581 return fmt.Errorf("failed to write to connection: %v", err) 582 } 583 584 // Announcement sent, now wait for a header request 585 msg := sendConn.Read() 586 blockHeaderReq, ok := msg.(*GetBlockHeaders) 587 if !ok { 588 return fmt.Errorf("unexpected %s", pretty.Sdump(msg)) 589 } 590 if blockHeaderReq.Amount != 1 { 591 return fmt.Errorf("unexpected number of block headers requested: %v", blockHeaderReq.Amount) 592 } 593 if blockHeaderReq.Origin.Hash != announcement.Hash { 594 return fmt.Errorf("unexpected block header requested. Announced:\n %v\n Remote request:\n%v", 595 pretty.Sdump(announcement), 596 pretty.Sdump(blockHeaderReq)) 597 } 598 err = sendConn.Write(&BlockHeaders{ 599 RequestId: blockHeaderReq.ReqID(), 600 BlockHeadersPacket: eth.BlockHeadersPacket{nextBlock.Header()}, 601 }) 602 if err != nil { 603 return fmt.Errorf("failed to write to connection: %v", err) 604 } 605 606 // wait for block announcement 607 msg = recvConn.readAndServe(s.chain, timeout) 608 switch msg := msg.(type) { 609 case *NewBlockHashes: 610 hashes := *msg 611 if len(hashes) != 1 { 612 return fmt.Errorf("unexpected new block hash announcement: wanted 1 announcement, got %d", len(hashes)) 613 } 614 if nextBlock.Hash() != hashes[0].Hash { 615 return fmt.Errorf("unexpected block hash announcement, wanted %v, got %v", nextBlock.Hash(), 616 hashes[0].Hash) 617 } 618 619 case *NewBlock: 620 // node should only propagate NewBlock without having requested the body if the body is empty 621 nextBlockBody := nextBlock.Body() 622 if len(nextBlockBody.Transactions) != 0 || len(nextBlockBody.Uncles) != 0 { 623 return fmt.Errorf("unexpected non-empty new block propagated: %s", pretty.Sdump(msg)) 624 } 625 if msg.Block.Hash() != nextBlock.Hash() { 626 return fmt.Errorf("mismatched hash of propagated new block: wanted %v, got %v", 627 nextBlock.Hash(), msg.Block.Hash()) 628 } 629 // check to make sure header matches header that was sent to the node 630 if !reflect.DeepEqual(nextBlock.Header(), msg.Block.Header()) { 631 return fmt.Errorf("incorrect header received: wanted %v, got %v", nextBlock.Header(), msg.Block.Header()) 632 } 633 default: 634 return fmt.Errorf("unexpected: %s", pretty.Sdump(msg)) 635 } 636 // confirm node imported block 637 if err := s.waitForBlockImport(recvConn, nextBlock); err != nil { 638 return fmt.Errorf("error waiting for node to import new block: %v", err) 639 } 640 // update the chain 641 s.chain.blocks = append(s.chain.blocks, nextBlock) 642 return nil 643 }