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  }