github.com/kisexp/xdchain@v0.0.0-20211206025815-490d6b732aa7/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  	"time"
    23  
    24  	"github.com/davecgh/go-spew/spew"
    25  	"github.com/kisexp/xdchain/core/types"
    26  	"github.com/kisexp/xdchain/crypto"
    27  	"github.com/kisexp/xdchain/internal/utesting"
    28  	"github.com/kisexp/xdchain/p2p"
    29  	"github.com/kisexp/xdchain/p2p/enode"
    30  	"github.com/kisexp/xdchain/p2p/rlpx"
    31  	"github.com/stretchr/testify/assert"
    32  )
    33  
    34  var pretty = spew.ConfigState{
    35  	Indent:                  "  ",
    36  	DisableCapacities:       true,
    37  	DisablePointerAddresses: true,
    38  	SortKeys:                true,
    39  }
    40  
    41  var timeout = 20 * time.Second
    42  
    43  // Suite represents a structure used to test the eth
    44  // protocol of a node(s).
    45  type Suite struct {
    46  	Dest *enode.Node
    47  
    48  	chain     *Chain
    49  	fullChain *Chain
    50  }
    51  
    52  // NewSuite creates and returns a new eth-test suite that can
    53  // be used to test the given node against the given blockchain
    54  // data.
    55  func NewSuite(dest *enode.Node, chainfile string, genesisfile string) *Suite {
    56  	chain, err := loadChain(chainfile, genesisfile)
    57  	if err != nil {
    58  		panic(err)
    59  	}
    60  	return &Suite{
    61  		Dest:      dest,
    62  		chain:     chain.Shorten(1000),
    63  		fullChain: chain,
    64  	}
    65  }
    66  
    67  func (s *Suite) AllTests() []utesting.Test {
    68  	return []utesting.Test{
    69  		{Name: "Status", Fn: s.TestStatus},
    70  		{Name: "GetBlockHeaders", Fn: s.TestGetBlockHeaders},
    71  		{Name: "Broadcast", Fn: s.TestBroadcast},
    72  		{Name: "GetBlockBodies", Fn: s.TestGetBlockBodies},
    73  		{Name: "TestLargeAnnounce", Fn: s.TestLargeAnnounce},
    74  		{Name: "TestMaliciousHandshake", Fn: s.TestMaliciousHandshake},
    75  		{Name: "TestMaliciousStatus", Fn: s.TestMaliciousStatus},
    76  		{Name: "TestTransactions", Fn: s.TestTransaction},
    77  		{Name: "TestMaliciousTransactions", Fn: s.TestMaliciousTx},
    78  	}
    79  }
    80  
    81  // TestStatus attempts to connect to the given node and exchange
    82  // a status message with it, and then check to make sure
    83  // the chain head is correct.
    84  func (s *Suite) TestStatus(t *utesting.T) {
    85  	conn, err := s.dial()
    86  	if err != nil {
    87  		t.Fatalf("could not dial: %v", err)
    88  	}
    89  	// get protoHandshake
    90  	conn.handshake(t)
    91  	// get status
    92  	switch msg := conn.statusExchange(t, s.chain, nil).(type) {
    93  	case *Status:
    94  		t.Logf("got status message: %s", pretty.Sdump(msg))
    95  	default:
    96  		t.Fatalf("unexpected: %s", pretty.Sdump(msg))
    97  	}
    98  }
    99  
   100  // TestMaliciousStatus sends a status package with a large total difficulty.
   101  func (s *Suite) TestMaliciousStatus(t *utesting.T) {
   102  	conn, err := s.dial()
   103  	if err != nil {
   104  		t.Fatalf("could not dial: %v", err)
   105  	}
   106  	// get protoHandshake
   107  	conn.handshake(t)
   108  	status := &Status{
   109  		ProtocolVersion: uint32(conn.ethProtocolVersion),
   110  		NetworkID:       s.chain.chainConfig.ChainID.Uint64(),
   111  		TD:              largeNumber(2),
   112  		Head:            s.chain.blocks[s.chain.Len()-1].Hash(),
   113  		Genesis:         s.chain.blocks[0].Hash(),
   114  		ForkID:          s.chain.ForkID(),
   115  	}
   116  	// get status
   117  	switch msg := conn.statusExchange(t, s.chain, status).(type) {
   118  	case *Status:
   119  		t.Logf("%+v\n", msg)
   120  	default:
   121  		t.Fatalf("expected status, got: %#v ", msg)
   122  	}
   123  	// wait for disconnect
   124  	switch msg := conn.ReadAndServe(s.chain, timeout).(type) {
   125  	case *Disconnect:
   126  	case *Error:
   127  		return
   128  	default:
   129  		t.Fatalf("expected disconnect, got: %s", pretty.Sdump(msg))
   130  	}
   131  }
   132  
   133  // TestGetBlockHeaders tests whether the given node can respond to
   134  // a `GetBlockHeaders` request and that the response is accurate.
   135  func (s *Suite) TestGetBlockHeaders(t *utesting.T) {
   136  	conn, err := s.dial()
   137  	if err != nil {
   138  		t.Fatalf("could not dial: %v", err)
   139  	}
   140  
   141  	conn.handshake(t)
   142  	conn.statusExchange(t, s.chain, nil)
   143  
   144  	// get block headers
   145  	req := &GetBlockHeaders{
   146  		Origin: hashOrNumber{
   147  			Hash: s.chain.blocks[1].Hash(),
   148  		},
   149  		Amount:  2,
   150  		Skip:    1,
   151  		Reverse: false,
   152  	}
   153  
   154  	if err := conn.Write(req); err != nil {
   155  		t.Fatalf("could not write to connection: %v", err)
   156  	}
   157  
   158  	switch msg := conn.ReadAndServe(s.chain, timeout).(type) {
   159  	case *BlockHeaders:
   160  		headers := msg
   161  		for _, header := range *headers {
   162  			num := header.Number.Uint64()
   163  			t.Logf("received header (%d): %s", num, pretty.Sdump(header))
   164  			assert.Equal(t, s.chain.blocks[int(num)].Header(), header)
   165  		}
   166  	default:
   167  		t.Fatalf("unexpected: %s", pretty.Sdump(msg))
   168  	}
   169  }
   170  
   171  // TestGetBlockBodies tests whether the given node can respond to
   172  // a `GetBlockBodies` request and that the response is accurate.
   173  func (s *Suite) TestGetBlockBodies(t *utesting.T) {
   174  	conn, err := s.dial()
   175  	if err != nil {
   176  		t.Fatalf("could not dial: %v", err)
   177  	}
   178  
   179  	conn.handshake(t)
   180  	conn.statusExchange(t, s.chain, nil)
   181  	// create block bodies request
   182  	req := &GetBlockBodies{s.chain.blocks[54].Hash(), s.chain.blocks[75].Hash()}
   183  	if err := conn.Write(req); err != nil {
   184  		t.Fatalf("could not write to connection: %v", err)
   185  	}
   186  
   187  	switch msg := conn.ReadAndServe(s.chain, timeout).(type) {
   188  	case *BlockBodies:
   189  		t.Logf("received %d block bodies", len(*msg))
   190  	default:
   191  		t.Fatalf("unexpected: %s", pretty.Sdump(msg))
   192  	}
   193  }
   194  
   195  // TestBroadcast tests whether a block announcement is correctly
   196  // propagated to the given node's peer(s).
   197  func (s *Suite) TestBroadcast(t *utesting.T) {
   198  	sendConn, receiveConn := s.setupConnection(t), s.setupConnection(t)
   199  	nextBlock := len(s.chain.blocks)
   200  	blockAnnouncement := &NewBlock{
   201  		Block: s.fullChain.blocks[nextBlock],
   202  		TD:    s.fullChain.TD(nextBlock + 1),
   203  	}
   204  	s.testAnnounce(t, sendConn, receiveConn, blockAnnouncement)
   205  	// update test suite chain
   206  	s.chain.blocks = append(s.chain.blocks, s.fullChain.blocks[nextBlock])
   207  	// wait for client to update its chain
   208  	if err := receiveConn.waitForBlock(s.chain.Head()); err != nil {
   209  		t.Fatal(err)
   210  	}
   211  }
   212  
   213  // TestMaliciousHandshake tries to send malicious data during the handshake.
   214  func (s *Suite) TestMaliciousHandshake(t *utesting.T) {
   215  	conn, err := s.dial()
   216  	if err != nil {
   217  		t.Fatalf("could not dial: %v", err)
   218  	}
   219  	// write hello to client
   220  	pub0 := crypto.FromECDSAPub(&conn.ourKey.PublicKey)[1:]
   221  	handshakes := []*Hello{
   222  		{
   223  			Version: 5,
   224  			Caps: []p2p.Cap{
   225  				{Name: largeString(2), Version: 64},
   226  			},
   227  			ID: pub0,
   228  		},
   229  		{
   230  			Version: 5,
   231  			Caps: []p2p.Cap{
   232  				{Name: "eth", Version: 64},
   233  				{Name: "eth", Version: 65},
   234  			},
   235  			ID: append(pub0, byte(0)),
   236  		},
   237  		{
   238  			Version: 5,
   239  			Caps: []p2p.Cap{
   240  				{Name: "eth", Version: 64},
   241  				{Name: "eth", Version: 65},
   242  			},
   243  			ID: append(pub0, pub0...),
   244  		},
   245  		{
   246  			Version: 5,
   247  			Caps: []p2p.Cap{
   248  				{Name: "eth", Version: 64},
   249  				{Name: "eth", Version: 65},
   250  			},
   251  			ID: largeBuffer(2),
   252  		},
   253  		{
   254  			Version: 5,
   255  			Caps: []p2p.Cap{
   256  				{Name: largeString(2), Version: 64},
   257  			},
   258  			ID: largeBuffer(2),
   259  		},
   260  	}
   261  	for i, handshake := range handshakes {
   262  		t.Logf("Testing malicious handshake %v\n", i)
   263  		// Init the handshake
   264  		if err := conn.Write(handshake); err != nil {
   265  			t.Fatalf("could not write to connection: %v", err)
   266  		}
   267  		// check that the peer disconnected
   268  		timeout := 20 * time.Second
   269  		// Discard one hello
   270  		for i := 0; i < 2; i++ {
   271  			switch msg := conn.ReadAndServe(s.chain, timeout).(type) {
   272  			case *Disconnect:
   273  			case *Error:
   274  			case *Hello:
   275  				// Hello's are send concurrently, so ignore them
   276  				continue
   277  			default:
   278  				t.Fatalf("unexpected: %s", pretty.Sdump(msg))
   279  			}
   280  		}
   281  		// Dial for the next round
   282  		conn, err = s.dial()
   283  		if err != nil {
   284  			t.Fatalf("could not dial: %v", err)
   285  		}
   286  	}
   287  }
   288  
   289  // TestLargeAnnounce tests the announcement mechanism with a large block.
   290  func (s *Suite) TestLargeAnnounce(t *utesting.T) {
   291  	nextBlock := len(s.chain.blocks)
   292  	blocks := []*NewBlock{
   293  		{
   294  			Block: largeBlock(),
   295  			TD:    s.fullChain.TD(nextBlock + 1),
   296  		},
   297  		{
   298  			Block: s.fullChain.blocks[nextBlock],
   299  			TD:    largeNumber(2),
   300  		},
   301  		{
   302  			Block: largeBlock(),
   303  			TD:    largeNumber(2),
   304  		},
   305  		{
   306  			Block: s.fullChain.blocks[nextBlock],
   307  			TD:    s.fullChain.TD(nextBlock + 1),
   308  		},
   309  	}
   310  
   311  	for i, blockAnnouncement := range blocks[0:3] {
   312  		t.Logf("Testing malicious announcement: %v\n", i)
   313  		sendConn := s.setupConnection(t)
   314  		if err := sendConn.Write(blockAnnouncement); err != nil {
   315  			t.Fatalf("could not write to connection: %v", err)
   316  		}
   317  		// Invalid announcement, check that peer disconnected
   318  		switch msg := sendConn.ReadAndServe(s.chain, timeout).(type) {
   319  		case *Disconnect:
   320  		case *Error:
   321  			break
   322  		default:
   323  			t.Fatalf("unexpected: %s wanted disconnect", pretty.Sdump(msg))
   324  		}
   325  	}
   326  	// Test the last block as a valid block
   327  	sendConn := s.setupConnection(t)
   328  	receiveConn := s.setupConnection(t)
   329  	s.testAnnounce(t, sendConn, receiveConn, blocks[3])
   330  	// update test suite chain
   331  	s.chain.blocks = append(s.chain.blocks, s.fullChain.blocks[nextBlock])
   332  	// wait for client to update its chain
   333  	if err := receiveConn.waitForBlock(s.fullChain.blocks[nextBlock]); err != nil {
   334  		t.Fatal(err)
   335  	}
   336  }
   337  
   338  func (s *Suite) testAnnounce(t *utesting.T, sendConn, receiveConn *Conn, blockAnnouncement *NewBlock) {
   339  	// Announce the block.
   340  	if err := sendConn.Write(blockAnnouncement); err != nil {
   341  		t.Fatalf("could not write to connection: %v", err)
   342  	}
   343  	s.waitAnnounce(t, receiveConn, blockAnnouncement)
   344  }
   345  
   346  func (s *Suite) waitAnnounce(t *utesting.T, conn *Conn, blockAnnouncement *NewBlock) {
   347  	timeout := 20 * time.Second
   348  	switch msg := conn.ReadAndServe(s.chain, timeout).(type) {
   349  	case *NewBlock:
   350  		t.Logf("received NewBlock message: %s", pretty.Sdump(msg.Block))
   351  		assert.Equal(t,
   352  			blockAnnouncement.Block.Header(), msg.Block.Header(),
   353  			"wrong block header in announcement",
   354  		)
   355  		assert.Equal(t,
   356  			blockAnnouncement.TD, msg.TD,
   357  			"wrong TD in announcement",
   358  		)
   359  	case *NewBlockHashes:
   360  		hashes := *msg
   361  		t.Logf("received NewBlockHashes message: %s", pretty.Sdump(hashes))
   362  		assert.Equal(t,
   363  			blockAnnouncement.Block.Hash(), hashes[0].Hash,
   364  			"wrong block hash in announcement",
   365  		)
   366  	default:
   367  		t.Fatalf("unexpected: %s", pretty.Sdump(msg))
   368  	}
   369  }
   370  
   371  func (s *Suite) setupConnection(t *utesting.T) *Conn {
   372  	// create conn
   373  	sendConn, err := s.dial()
   374  	if err != nil {
   375  		t.Fatalf("could not dial: %v", err)
   376  	}
   377  	sendConn.handshake(t)
   378  	sendConn.statusExchange(t, s.chain, nil)
   379  	return sendConn
   380  }
   381  
   382  // dial attempts to dial the given node and perform a handshake,
   383  // returning the created Conn if successful.
   384  func (s *Suite) dial() (*Conn, error) {
   385  	var conn Conn
   386  
   387  	fd, err := net.Dial("tcp", fmt.Sprintf("%v:%d", s.Dest.IP(), s.Dest.TCP()))
   388  	if err != nil {
   389  		return nil, err
   390  	}
   391  	conn.Conn = rlpx.NewConn(fd, s.Dest.Pubkey())
   392  
   393  	// do encHandshake
   394  	conn.ourKey, _ = crypto.GenerateKey()
   395  	_, err = conn.Handshake(conn.ourKey)
   396  	if err != nil {
   397  		return nil, err
   398  	}
   399  
   400  	return &conn, nil
   401  }
   402  
   403  func (s *Suite) TestTransaction(t *utesting.T) {
   404  	tests := []*types.Transaction{
   405  		getNextTxFromChain(t, s),
   406  		unknownTx(t, s),
   407  	}
   408  	for i, tx := range tests {
   409  		t.Logf("Testing tx propagation: %v\n", i)
   410  		sendSuccessfulTx(t, s, tx)
   411  	}
   412  }
   413  
   414  func (s *Suite) TestMaliciousTx(t *utesting.T) {
   415  	tests := []*types.Transaction{
   416  		getOldTxFromChain(t, s),
   417  		invalidNonceTx(t, s),
   418  		hugeAmount(t, s),
   419  		hugeGasPrice(t, s),
   420  		hugeData(t, s),
   421  	}
   422  	for i, tx := range tests {
   423  		t.Logf("Testing malicious tx propagation: %v\n", i)
   424  		sendFailingTx(t, s, tx)
   425  	}
   426  }