github.com/fff-chain/go-fff@v0.0.0-20220726032732-1c84420b8a99/cmd/devp2p/internal/ethtest/suite.go (about)

     1  // Copyright 2020 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser 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  // The go-ethereum library 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 Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package ethtest
    18  
    19  import (
    20  	"fmt"
    21  	"net"
    22  	"strings"
    23  	"time"
    24  
    25  	"github.com/davecgh/go-spew/spew"
    26  	"github.com/fff-chain/go-fff/core/types"
    27  	"github.com/fff-chain/go-fff/crypto"
    28  	"github.com/fff-chain/go-fff/eth/protocols/eth"
    29  	"github.com/fff-chain/go-fff/internal/utesting"
    30  	"github.com/fff-chain/go-fff/p2p"
    31  	"github.com/fff-chain/go-fff/p2p/enode"
    32  	"github.com/fff-chain/go-fff/p2p/rlpx"
    33  	"github.com/stretchr/testify/assert"
    34  )
    35  
    36  var pretty = spew.ConfigState{
    37  	Indent:                  "  ",
    38  	DisableCapacities:       true,
    39  	DisablePointerAddresses: true,
    40  	SortKeys:                true,
    41  }
    42  
    43  var timeout = 20 * time.Second
    44  
    45  // Suite represents a structure used to test the eth
    46  // protocol of a node(s).
    47  type Suite struct {
    48  	Dest *enode.Node
    49  
    50  	chain     *Chain
    51  	fullChain *Chain
    52  }
    53  
    54  // NewSuite creates and returns a new eth-test suite that can
    55  // be used to test the given node against the given blockchain
    56  // data.
    57  func NewSuite(dest *enode.Node, chainfile string, genesisfile string) (*Suite, error) {
    58  	chain, err := loadChain(chainfile, genesisfile)
    59  	if err != nil {
    60  		return nil, err
    61  	}
    62  	return &Suite{
    63  		Dest:      dest,
    64  		chain:     chain.Shorten(1000),
    65  		fullChain: chain,
    66  	}, nil
    67  }
    68  
    69  func (s *Suite) AllEthTests() []utesting.Test {
    70  	return []utesting.Test{
    71  		// status
    72  		{Name: "TestStatus", Fn: s.TestStatus},
    73  		{Name: "TestStatus_66", Fn: s.TestStatus_66},
    74  		// get block headers
    75  		{Name: "TestGetBlockHeaders", Fn: s.TestGetBlockHeaders},
    76  		{Name: "TestGetBlockHeaders_66", Fn: s.TestGetBlockHeaders_66},
    77  		{Name: "TestSimultaneousRequests_66", Fn: s.TestSimultaneousRequests_66},
    78  		{Name: "TestSameRequestID_66", Fn: s.TestSameRequestID_66},
    79  		{Name: "TestZeroRequestID_66", Fn: s.TestZeroRequestID_66},
    80  		// get block bodies
    81  		{Name: "TestGetBlockBodies", Fn: s.TestGetBlockBodies},
    82  		{Name: "TestGetBlockBodies_66", Fn: s.TestGetBlockBodies_66},
    83  		// broadcast
    84  		{Name: "TestBroadcast", Fn: s.TestBroadcast},
    85  		{Name: "TestBroadcast_66", Fn: s.TestBroadcast_66},
    86  		{Name: "TestLargeAnnounce", Fn: s.TestLargeAnnounce},
    87  		{Name: "TestLargeAnnounce_66", Fn: s.TestLargeAnnounce_66},
    88  		{Name: "TestOldAnnounce", Fn: s.TestOldAnnounce},
    89  		{Name: "TestOldAnnounce_66", Fn: s.TestOldAnnounce_66},
    90  		// malicious handshakes + status
    91  		{Name: "TestMaliciousHandshake", Fn: s.TestMaliciousHandshake},
    92  		{Name: "TestMaliciousStatus", Fn: s.TestMaliciousStatus},
    93  		{Name: "TestMaliciousHandshake_66", Fn: s.TestMaliciousHandshake_66},
    94  		{Name: "TestMaliciousStatus_66", Fn: s.TestMaliciousStatus_66},
    95  		// test transactions
    96  		{Name: "TestTransaction", Fn: s.TestTransaction},
    97  		{Name: "TestTransaction_66", Fn: s.TestTransaction_66},
    98  		{Name: "TestMaliciousTx", Fn: s.TestMaliciousTx},
    99  		{Name: "TestMaliciousTx_66", Fn: s.TestMaliciousTx_66},
   100  		{Name: "TestLargeTxRequest_66", Fn: s.TestLargeTxRequest_66},
   101  		{Name: "TestNewPooledTxs_66", Fn: s.TestNewPooledTxs_66},
   102  	}
   103  }
   104  
   105  func (s *Suite) EthTests() []utesting.Test {
   106  	return []utesting.Test{
   107  		{Name: "TestStatus", Fn: s.TestStatus},
   108  		{Name: "TestGetBlockHeaders", Fn: s.TestGetBlockHeaders},
   109  		{Name: "TestGetBlockBodies", Fn: s.TestGetBlockBodies},
   110  		{Name: "TestBroadcast", Fn: s.TestBroadcast},
   111  		{Name: "TestLargeAnnounce", Fn: s.TestLargeAnnounce},
   112  		{Name: "TestMaliciousHandshake", Fn: s.TestMaliciousHandshake},
   113  		{Name: "TestMaliciousStatus", Fn: s.TestMaliciousStatus},
   114  		{Name: "TestTransaction", Fn: s.TestTransaction},
   115  		{Name: "TestMaliciousTx", Fn: s.TestMaliciousTx},
   116  	}
   117  }
   118  
   119  func (s *Suite) Eth66Tests() []utesting.Test {
   120  	return []utesting.Test{
   121  		// only proceed with eth66 test suite if node supports eth 66 protocol
   122  		{Name: "TestStatus_66", Fn: s.TestStatus_66},
   123  		{Name: "TestGetBlockHeaders_66", Fn: s.TestGetBlockHeaders_66},
   124  		{Name: "TestSimultaneousRequests_66", Fn: s.TestSimultaneousRequests_66},
   125  		{Name: "TestSameRequestID_66", Fn: s.TestSameRequestID_66},
   126  		{Name: "TestZeroRequestID_66", Fn: s.TestZeroRequestID_66},
   127  		{Name: "TestGetBlockBodies_66", Fn: s.TestGetBlockBodies_66},
   128  		{Name: "TestBroadcast_66", Fn: s.TestBroadcast_66},
   129  		{Name: "TestLargeAnnounce_66", Fn: s.TestLargeAnnounce_66},
   130  		{Name: "TestMaliciousHandshake_66", Fn: s.TestMaliciousHandshake_66},
   131  		{Name: "TestMaliciousStatus_66", Fn: s.TestMaliciousStatus_66},
   132  		{Name: "TestTransaction_66", Fn: s.TestTransaction_66},
   133  		{Name: "TestMaliciousTx_66", Fn: s.TestMaliciousTx_66},
   134  		{Name: "TestLargeTxRequest_66", Fn: s.TestLargeTxRequest_66},
   135  		{Name: "TestNewPooledTxs_66", Fn: s.TestNewPooledTxs_66},
   136  	}
   137  }
   138  
   139  // TestStatus attempts to connect to the given node and exchange
   140  // a status message with it, and then check to make sure
   141  // the chain head is correct.
   142  func (s *Suite) TestStatus(t *utesting.T) {
   143  	conn, err := s.dial()
   144  	if err != nil {
   145  		t.Fatalf("could not dial: %v", err)
   146  	}
   147  	defer conn.Close()
   148  	// get protoHandshake
   149  	conn.handshake(t)
   150  	// get status
   151  	switch msg := conn.statusExchange(t, s.chain, nil).(type) {
   152  	case *Status:
   153  		t.Logf("got status message: %s", pretty.Sdump(msg))
   154  	default:
   155  		t.Fatalf("unexpected: %s", pretty.Sdump(msg))
   156  	}
   157  }
   158  
   159  // TestMaliciousStatus sends a status package with a large total difficulty.
   160  func (s *Suite) TestMaliciousStatus(t *utesting.T) {
   161  	conn, err := s.dial()
   162  	if err != nil {
   163  		t.Fatalf("could not dial: %v", err)
   164  	}
   165  	defer conn.Close()
   166  	// get protoHandshake
   167  	conn.handshake(t)
   168  	status := &Status{
   169  		ProtocolVersion: uint32(conn.negotiatedProtoVersion),
   170  		NetworkID:       s.chain.chainConfig.ChainID.Uint64(),
   171  		TD:              largeNumber(2),
   172  		Head:            s.chain.blocks[s.chain.Len()-1].Hash(),
   173  		Genesis:         s.chain.blocks[0].Hash(),
   174  		ForkID:          s.chain.ForkID(),
   175  	}
   176  	// get status
   177  	switch msg := conn.statusExchange(t, s.chain, status).(type) {
   178  	case *Status:
   179  		t.Logf("%+v\n", msg)
   180  	default:
   181  		t.Fatalf("expected status, got: %#v ", msg)
   182  	}
   183  	// wait for disconnect
   184  	switch msg := conn.ReadAndServe(s.chain, timeout).(type) {
   185  	case *Disconnect:
   186  	case *Error:
   187  		return
   188  	default:
   189  		t.Fatalf("expected disconnect, got: %s", pretty.Sdump(msg))
   190  	}
   191  }
   192  
   193  // TestGetBlockHeaders tests whether the given node can respond to
   194  // a `GetBlockHeaders` request and that the response is accurate.
   195  func (s *Suite) TestGetBlockHeaders(t *utesting.T) {
   196  	conn, err := s.dial()
   197  	if err != nil {
   198  		t.Fatalf("could not dial: %v", err)
   199  	}
   200  	defer conn.Close()
   201  
   202  	conn.handshake(t)
   203  	conn.statusExchange(t, s.chain, nil)
   204  
   205  	// get block headers
   206  	req := &GetBlockHeaders{
   207  		Origin: eth.HashOrNumber{
   208  			Hash: s.chain.blocks[1].Hash(),
   209  		},
   210  		Amount:  2,
   211  		Skip:    1,
   212  		Reverse: false,
   213  	}
   214  
   215  	if err := conn.Write(req); err != nil {
   216  		t.Fatalf("could not write to connection: %v", err)
   217  	}
   218  
   219  	switch msg := conn.ReadAndServe(s.chain, timeout).(type) {
   220  	case *BlockHeaders:
   221  		headers := *msg
   222  		for _, header := range headers {
   223  			num := header.Number.Uint64()
   224  			t.Logf("received header (%d): %s", num, pretty.Sdump(header.Hash()))
   225  			assert.Equal(t, s.chain.blocks[int(num)].Header(), header)
   226  		}
   227  	default:
   228  		t.Fatalf("unexpected: %s", pretty.Sdump(msg))
   229  	}
   230  }
   231  
   232  // TestGetBlockBodies tests whether the given node can respond to
   233  // a `GetBlockBodies` request and that the response is accurate.
   234  func (s *Suite) TestGetBlockBodies(t *utesting.T) {
   235  	conn, err := s.dial()
   236  	if err != nil {
   237  		t.Fatalf("could not dial: %v", err)
   238  	}
   239  	defer conn.Close()
   240  
   241  	conn.handshake(t)
   242  	conn.statusExchange(t, s.chain, nil)
   243  	// create block bodies request
   244  	req := &GetBlockBodies{
   245  		s.chain.blocks[54].Hash(),
   246  		s.chain.blocks[75].Hash(),
   247  	}
   248  	if err := conn.Write(req); err != nil {
   249  		t.Fatalf("could not write to connection: %v", err)
   250  	}
   251  
   252  	switch msg := conn.ReadAndServe(s.chain, timeout).(type) {
   253  	case *BlockBodies:
   254  		t.Logf("received %d block bodies", len(*msg))
   255  	default:
   256  		t.Fatalf("unexpected: %s", pretty.Sdump(msg))
   257  	}
   258  }
   259  
   260  // TestBroadcast tests whether a block announcement is correctly
   261  // propagated to the given node's peer(s).
   262  func (s *Suite) TestBroadcast(t *utesting.T) {
   263  	s.sendNextBlock(t)
   264  }
   265  
   266  func (s *Suite) sendNextBlock(t *utesting.T) {
   267  	sendConn, receiveConn := s.setupConnection(t), s.setupConnection(t)
   268  	defer sendConn.Close()
   269  	defer receiveConn.Close()
   270  
   271  	// create new block announcement
   272  	nextBlock := len(s.chain.blocks)
   273  	blockAnnouncement := &NewBlock{
   274  		Block: s.fullChain.blocks[nextBlock],
   275  		TD:    s.fullChain.TD(nextBlock + 1),
   276  	}
   277  	// send announcement and wait for node to request the header
   278  	s.testAnnounce(t, sendConn, receiveConn, blockAnnouncement)
   279  	// wait for client to update its chain
   280  	if err := receiveConn.waitForBlock(s.fullChain.blocks[nextBlock]); err != nil {
   281  		t.Fatal(err)
   282  	}
   283  	// update test suite chain
   284  	s.chain.blocks = append(s.chain.blocks, s.fullChain.blocks[nextBlock])
   285  }
   286  
   287  // TestMaliciousHandshake tries to send malicious data during the handshake.
   288  func (s *Suite) TestMaliciousHandshake(t *utesting.T) {
   289  	conn, err := s.dial()
   290  	if err != nil {
   291  		t.Fatalf("could not dial: %v", err)
   292  	}
   293  	defer conn.Close()
   294  	// write hello to client
   295  	pub0 := crypto.FromECDSAPub(&conn.ourKey.PublicKey)[1:]
   296  	handshakes := []*Hello{
   297  		{
   298  			Version: 5,
   299  			Caps: []p2p.Cap{
   300  				{Name: largeString(2), Version: 64},
   301  			},
   302  			ID: pub0,
   303  		},
   304  		{
   305  			Version: 5,
   306  			Caps: []p2p.Cap{
   307  				{Name: "eth", Version: 64},
   308  				{Name: "eth", Version: 65},
   309  			},
   310  			ID: append(pub0, byte(0)),
   311  		},
   312  		{
   313  			Version: 5,
   314  			Caps: []p2p.Cap{
   315  				{Name: "eth", Version: 64},
   316  				{Name: "eth", Version: 65},
   317  			},
   318  			ID: append(pub0, pub0...),
   319  		},
   320  		{
   321  			Version: 5,
   322  			Caps: []p2p.Cap{
   323  				{Name: "eth", Version: 64},
   324  				{Name: "eth", Version: 65},
   325  			},
   326  			ID: largeBuffer(2),
   327  		},
   328  		{
   329  			Version: 5,
   330  			Caps: []p2p.Cap{
   331  				{Name: largeString(2), Version: 64},
   332  			},
   333  			ID: largeBuffer(2),
   334  		},
   335  	}
   336  	for i, handshake := range handshakes {
   337  		t.Logf("Testing malicious handshake %v\n", i)
   338  		// Init the handshake
   339  		if err := conn.Write(handshake); err != nil {
   340  			t.Fatalf("could not write to connection: %v", err)
   341  		}
   342  		// check that the peer disconnected
   343  		timeout := 20 * time.Second
   344  		// Discard one hello
   345  		for i := 0; i < 2; i++ {
   346  			switch msg := conn.ReadAndServe(s.chain, timeout).(type) {
   347  			case *Disconnect:
   348  			case *Error:
   349  			case *Hello:
   350  				// Hello's are send concurrently, so ignore them
   351  				continue
   352  			default:
   353  				t.Fatalf("unexpected: %s", pretty.Sdump(msg))
   354  			}
   355  		}
   356  		// Dial for the next round
   357  		conn, err = s.dial()
   358  		if err != nil {
   359  			t.Fatalf("could not dial: %v", err)
   360  		}
   361  	}
   362  }
   363  
   364  // TestLargeAnnounce tests the announcement mechanism with a large block.
   365  func (s *Suite) TestLargeAnnounce(t *utesting.T) {
   366  	nextBlock := len(s.chain.blocks)
   367  	blocks := []*NewBlock{
   368  		{
   369  			Block: largeBlock(),
   370  			TD:    s.fullChain.TD(nextBlock + 1),
   371  		},
   372  		{
   373  			Block: s.fullChain.blocks[nextBlock],
   374  			TD:    largeNumber(2),
   375  		},
   376  		{
   377  			Block: largeBlock(),
   378  			TD:    largeNumber(2),
   379  		},
   380  		{
   381  			Block: s.fullChain.blocks[nextBlock],
   382  			TD:    s.fullChain.TD(nextBlock + 1),
   383  		},
   384  	}
   385  
   386  	for i, blockAnnouncement := range blocks[0:3] {
   387  		t.Logf("Testing malicious announcement: %v\n", i)
   388  		sendConn := s.setupConnection(t)
   389  		if err := sendConn.Write(blockAnnouncement); err != nil {
   390  			t.Fatalf("could not write to connection: %v", err)
   391  		}
   392  		// Invalid announcement, check that peer disconnected
   393  		switch msg := sendConn.ReadAndServe(s.chain, time.Second*8).(type) {
   394  		case *Disconnect:
   395  		case *Error:
   396  			break
   397  		default:
   398  			t.Fatalf("unexpected: %s wanted disconnect", pretty.Sdump(msg))
   399  		}
   400  		sendConn.Close()
   401  	}
   402  	// Test the last block as a valid block
   403  	s.sendNextBlock(t)
   404  }
   405  
   406  func (s *Suite) TestOldAnnounce(t *utesting.T) {
   407  	sendConn, recvConn := s.setupConnection(t), s.setupConnection(t)
   408  	defer sendConn.Close()
   409  	defer recvConn.Close()
   410  
   411  	s.oldAnnounce(t, sendConn, recvConn)
   412  }
   413  
   414  func (s *Suite) oldAnnounce(t *utesting.T, sendConn, receiveConn *Conn) {
   415  	oldBlockAnnounce := &NewBlock{
   416  		Block: s.chain.blocks[len(s.chain.blocks)/2],
   417  		TD:    s.chain.blocks[len(s.chain.blocks)/2].Difficulty(),
   418  	}
   419  
   420  	if err := sendConn.Write(oldBlockAnnounce); err != nil {
   421  		t.Fatalf("could not write to connection: %v", err)
   422  	}
   423  
   424  	switch msg := receiveConn.ReadAndServe(s.chain, time.Second*8).(type) {
   425  	case *NewBlock:
   426  		block := *msg
   427  		if block.Block.Hash() == oldBlockAnnounce.Block.Hash() {
   428  			t.Fatalf("unexpected: block propagated: %s", pretty.Sdump(msg))
   429  		}
   430  	case *NewBlockHashes:
   431  		hashes := *msg
   432  		for _, hash := range hashes {
   433  			if hash.Hash == oldBlockAnnounce.Block.Hash() {
   434  				t.Fatalf("unexpected: block announced: %s", pretty.Sdump(msg))
   435  			}
   436  		}
   437  	case *Error:
   438  		errMsg := *msg
   439  		// check to make sure error is timeout (propagation didn't come through == test successful)
   440  		if !strings.Contains(errMsg.String(), "timeout") {
   441  			t.Fatalf("unexpected error: %v", pretty.Sdump(msg))
   442  		}
   443  	default:
   444  		t.Fatalf("unexpected: %s", pretty.Sdump(msg))
   445  	}
   446  }
   447  
   448  func (s *Suite) testAnnounce(t *utesting.T, sendConn, receiveConn *Conn, blockAnnouncement *NewBlock) {
   449  	// Announce the block.
   450  	if err := sendConn.Write(blockAnnouncement); err != nil {
   451  		t.Fatalf("could not write to connection: %v", err)
   452  	}
   453  	s.waitAnnounce(t, receiveConn, blockAnnouncement)
   454  }
   455  
   456  func (s *Suite) waitAnnounce(t *utesting.T, conn *Conn, blockAnnouncement *NewBlock) {
   457  	switch msg := conn.ReadAndServe(s.chain, timeout).(type) {
   458  	case *NewBlock:
   459  		t.Logf("received NewBlock message: %s", pretty.Sdump(msg.Block))
   460  		assert.Equal(t,
   461  			blockAnnouncement.Block.Header(), msg.Block.Header(),
   462  			"wrong block header in announcement",
   463  		)
   464  		assert.Equal(t,
   465  			blockAnnouncement.TD, msg.TD,
   466  			"wrong TD in announcement",
   467  		)
   468  	case *NewBlockHashes:
   469  		message := *msg
   470  		t.Logf("received NewBlockHashes message: %s", pretty.Sdump(message))
   471  		assert.Equal(t, blockAnnouncement.Block.Hash(), message[0].Hash,
   472  			"wrong block hash in announcement",
   473  		)
   474  	default:
   475  		t.Fatalf("unexpected: %s", pretty.Sdump(msg))
   476  	}
   477  }
   478  
   479  func (s *Suite) setupConnection(t *utesting.T) *Conn {
   480  	// create conn
   481  	sendConn, err := s.dial()
   482  	if err != nil {
   483  		t.Fatalf("could not dial: %v", err)
   484  	}
   485  	sendConn.handshake(t)
   486  	sendConn.statusExchange(t, s.chain, nil)
   487  	return sendConn
   488  }
   489  
   490  // dial attempts to dial the given node and perform a handshake,
   491  // returning the created Conn if successful.
   492  func (s *Suite) dial() (*Conn, error) {
   493  	var conn Conn
   494  	// dial
   495  	fd, err := net.Dial("tcp", fmt.Sprintf("%v:%d", s.Dest.IP(), s.Dest.TCP()))
   496  	if err != nil {
   497  		return nil, err
   498  	}
   499  	conn.Conn = rlpx.NewConn(fd, s.Dest.Pubkey())
   500  	// do encHandshake
   501  	conn.ourKey, _ = crypto.GenerateKey()
   502  	_, err = conn.Handshake(conn.ourKey)
   503  	if err != nil {
   504  		return nil, err
   505  	}
   506  	// set default p2p capabilities
   507  	conn.caps = []p2p.Cap{
   508  		{Name: "eth", Version: 64},
   509  		{Name: "eth", Version: 65},
   510  	}
   511  	conn.ourHighestProtoVersion = 65
   512  	return &conn, nil
   513  }
   514  
   515  func (s *Suite) TestTransaction(t *utesting.T) {
   516  	tests := []*types.Transaction{
   517  		getNextTxFromChain(t, s),
   518  		unknownTx(t, s),
   519  	}
   520  	for i, tx := range tests {
   521  		t.Logf("Testing tx propagation: %v\n", i)
   522  		sendSuccessfulTx(t, s, tx)
   523  	}
   524  }
   525  
   526  func (s *Suite) TestMaliciousTx(t *utesting.T) {
   527  	badTxs := []*types.Transaction{
   528  		getOldTxFromChain(t, s),
   529  		invalidNonceTx(t, s),
   530  		hugeAmount(t, s),
   531  		hugeGasPrice(t, s),
   532  		hugeData(t, s),
   533  	}
   534  	sendConn := s.setupConnection(t)
   535  	defer sendConn.Close()
   536  	// set up receiving connection before sending txs to make sure
   537  	// no announcements are missed
   538  	recvConn := s.setupConnection(t)
   539  	defer recvConn.Close()
   540  
   541  	for i, tx := range badTxs {
   542  		t.Logf("Testing malicious tx propagation: %v\n", i)
   543  		if err := sendConn.Write(&Transactions{tx}); err != nil {
   544  			t.Fatalf("could not write to connection: %v", err)
   545  		}
   546  
   547  	}
   548  	// check to make sure bad txs aren't propagated
   549  	waitForTxPropagation(t, s, badTxs, recvConn)
   550  }