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