github.com/theQRL/go-zond@v0.2.1/zond/handler_zond_test.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 zond
    18  
    19  import (
    20  	"fmt"
    21  	"math/big"
    22  	"testing"
    23  	"time"
    24  
    25  	"github.com/theQRL/go-zond/common"
    26  	"github.com/theQRL/go-zond/consensus/beacon"
    27  	"github.com/theQRL/go-zond/core"
    28  	"github.com/theQRL/go-zond/core/forkid"
    29  	"github.com/theQRL/go-zond/core/rawdb"
    30  	"github.com/theQRL/go-zond/core/types"
    31  	"github.com/theQRL/go-zond/core/vm"
    32  	"github.com/theQRL/go-zond/event"
    33  	"github.com/theQRL/go-zond/p2p"
    34  	"github.com/theQRL/go-zond/p2p/enode"
    35  	"github.com/theQRL/go-zond/params"
    36  	"github.com/theQRL/go-zond/zond/downloader"
    37  	"github.com/theQRL/go-zond/zond/protocols/zond"
    38  )
    39  
    40  // testZondHandler is a mock event handler to listen for inbound network requests
    41  // on the `zond` protocol and convert them into a more easily testable form.
    42  type testZondHandler struct {
    43  	txAnnounces  event.Feed
    44  	txBroadcasts event.Feed
    45  }
    46  
    47  func (h *testZondHandler) Chain() *core.BlockChain                { panic("no backing chain") }
    48  func (h *testZondHandler) TxPool() zond.TxPool                    { panic("no backing tx pool") }
    49  func (h *testZondHandler) AcceptTxs() bool                        { return true }
    50  func (h *testZondHandler) RunPeer(*zond.Peer, zond.Handler) error { panic("not used in tests") }
    51  func (h *testZondHandler) PeerInfo(enode.ID) interface{}          { panic("not used in tests") }
    52  
    53  func (h *testZondHandler) Handle(peer *zond.Peer, packet zond.Packet) error {
    54  	switch packet := packet.(type) {
    55  	case *zond.NewPooledTransactionHashesPacket:
    56  		h.txAnnounces.Send(packet.Hashes)
    57  		return nil
    58  
    59  	case *zond.TransactionsPacket:
    60  		h.txBroadcasts.Send(([]*types.Transaction)(*packet))
    61  		return nil
    62  
    63  	case *zond.PooledTransactionsResponse:
    64  		h.txBroadcasts.Send(([]*types.Transaction)(*packet))
    65  		return nil
    66  
    67  	default:
    68  		panic(fmt.Sprintf("unexpected zond packet type in tests: %T", packet))
    69  	}
    70  }
    71  
    72  // Tests that peers are correctly accepted (or rejected) based on the advertised
    73  // fork IDs in the protocol handshake.
    74  func TestForkIDSplit68(t *testing.T) { testForkIDSplit(t, zond.ETH68) }
    75  
    76  func testForkIDSplit(t *testing.T, protocol uint) {
    77  	t.Parallel()
    78  
    79  	var (
    80  		engine = beacon.NewFaker()
    81  
    82  		configNoFork  = &params.ChainConfig{}
    83  		configProFork = &params.ChainConfig{}
    84  		dbNoFork      = rawdb.NewMemoryDatabase()
    85  		dbProFork     = rawdb.NewMemoryDatabase()
    86  
    87  		gspecNoFork  = &core.Genesis{Config: configNoFork}
    88  		gspecProFork = &core.Genesis{Config: configProFork}
    89  
    90  		chainNoFork, _  = core.NewBlockChain(dbNoFork, nil, gspecNoFork, engine, vm.Config{}, nil)
    91  		chainProFork, _ = core.NewBlockChain(dbProFork, nil, gspecProFork, engine, vm.Config{}, nil)
    92  
    93  		_, blocksNoFork, _  = core.GenerateChainWithGenesis(gspecNoFork, engine, 2, nil)
    94  		_, blocksProFork, _ = core.GenerateChainWithGenesis(gspecProFork, engine, 2, nil)
    95  
    96  		zondNoFork, _ = newHandler(&handlerConfig{
    97  			Database:   dbNoFork,
    98  			Chain:      chainNoFork,
    99  			TxPool:     newTestTxPool(),
   100  			Network:    1,
   101  			Sync:       downloader.FullSync,
   102  			BloomCache: 1,
   103  		})
   104  		zondProFork, _ = newHandler(&handlerConfig{
   105  			Database:   dbProFork,
   106  			Chain:      chainProFork,
   107  			TxPool:     newTestTxPool(),
   108  			Network:    1,
   109  			Sync:       downloader.FullSync,
   110  			BloomCache: 1,
   111  		})
   112  	)
   113  	zondNoFork.Start(1000)
   114  	zondProFork.Start(1000)
   115  
   116  	// Clean up everything after ourselves
   117  	defer chainNoFork.Stop()
   118  	defer chainProFork.Stop()
   119  
   120  	defer zondNoFork.Stop()
   121  	defer zondProFork.Stop()
   122  
   123  	// Both nodes should allow the other to connect (same genesis, next fork is the same)
   124  	p2pNoFork, p2pProFork := p2p.MsgPipe()
   125  	defer p2pNoFork.Close()
   126  	defer p2pProFork.Close()
   127  
   128  	peerNoFork := zond.NewPeer(protocol, p2p.NewPeerPipe(enode.ID{1}, "", nil, p2pNoFork), p2pNoFork, nil)
   129  	peerProFork := zond.NewPeer(protocol, p2p.NewPeerPipe(enode.ID{2}, "", nil, p2pProFork), p2pProFork, nil)
   130  	defer peerNoFork.Close()
   131  	defer peerProFork.Close()
   132  
   133  	errc := make(chan error, 2)
   134  	go func(errc chan error) {
   135  		errc <- zondNoFork.runZondPeer(peerProFork, func(peer *zond.Peer) error { return nil })
   136  	}(errc)
   137  	go func(errc chan error) {
   138  		errc <- zondProFork.runZondPeer(peerNoFork, func(peer *zond.Peer) error { return nil })
   139  	}(errc)
   140  
   141  	for i := 0; i < 2; i++ {
   142  		select {
   143  		case err := <-errc:
   144  			if err != nil {
   145  				t.Fatalf("frontier nofork <-> profork failed: %v", err)
   146  			}
   147  		case <-time.After(250 * time.Millisecond):
   148  			t.Fatalf("frontier nofork <-> profork handler timeout")
   149  		}
   150  	}
   151  	// Progress into Homestead. Fork's match, so we don't care what the future holds
   152  	chainNoFork.InsertChain(blocksNoFork[:1])
   153  	chainProFork.InsertChain(blocksProFork[:1])
   154  
   155  	p2pNoFork, p2pProFork = p2p.MsgPipe()
   156  	defer p2pNoFork.Close()
   157  	defer p2pProFork.Close()
   158  
   159  	peerNoFork = zond.NewPeer(protocol, p2p.NewPeer(enode.ID{1}, "", nil), p2pNoFork, nil)
   160  	peerProFork = zond.NewPeer(protocol, p2p.NewPeer(enode.ID{2}, "", nil), p2pProFork, nil)
   161  	defer peerNoFork.Close()
   162  	defer peerProFork.Close()
   163  
   164  	errc = make(chan error, 2)
   165  	go func(errc chan error) {
   166  		errc <- zondNoFork.runZondPeer(peerProFork, func(peer *zond.Peer) error { return nil })
   167  	}(errc)
   168  	go func(errc chan error) {
   169  		errc <- zondProFork.runZondPeer(peerNoFork, func(peer *zond.Peer) error { return nil })
   170  	}(errc)
   171  
   172  	for i := 0; i < 2; i++ {
   173  		select {
   174  		case err := <-errc:
   175  			if err != nil {
   176  				t.Fatalf("homestead nofork <-> profork failed: %v", err)
   177  			}
   178  		case <-time.After(250 * time.Millisecond):
   179  			t.Fatalf("homestead nofork <-> profork handler timeout")
   180  		}
   181  	}
   182  	// NOTE(rgeraldes24): revisit upon new fork
   183  	/*
   184  		// Progress into Spurious. Forks mismatch, signalling differing chains, reject
   185  		chainNoFork.InsertChain(blocksNoFork[1:2])
   186  		chainProFork.InsertChain(blocksProFork[1:2])
   187  
   188  		p2pNoFork, p2pProFork = p2p.MsgPipe()
   189  		defer p2pNoFork.Close()
   190  		defer p2pProFork.Close()
   191  
   192  		peerNoFork = zond.NewPeer(protocol, p2p.NewPeerPipe(enode.ID{1}, "", nil, p2pNoFork), p2pNoFork, nil)
   193  		peerProFork = zond.NewPeer(protocol, p2p.NewPeerPipe(enode.ID{2}, "", nil, p2pProFork), p2pProFork, nil)
   194  		defer peerNoFork.Close()
   195  		defer peerProFork.Close()
   196  
   197  		errc = make(chan error, 2)
   198  		go func(errc chan error) {
   199  			errc <- zondNoFork.runZondPeer(peerProFork, func(peer *zond.Peer) error { return nil })
   200  		}(errc)
   201  		go func(errc chan error) {
   202  			errc <- zondProFork.runZondPeer(peerNoFork, func(peer *zond.Peer) error { return nil })
   203  		}(errc)
   204  
   205  		var successes int
   206  		for i := 0; i < 2; i++ {
   207  			select {
   208  			case err := <-errc:
   209  				if err == nil {
   210  					successes++
   211  					if successes == 2 { // Only one side disconnects
   212  						t.Fatalf("fork ID rejection didn't happen")
   213  					}
   214  				}
   215  			case <-time.After(250 * time.Millisecond):
   216  				t.Fatalf("split peers not rejected")
   217  			}
   218  		}
   219  	*/
   220  }
   221  
   222  // Tests that received transactions are added to the local pool.
   223  func TestRecvTransactions68(t *testing.T) { testRecvTransactions(t, zond.ETH68) }
   224  
   225  func testRecvTransactions(t *testing.T, protocol uint) {
   226  	t.Parallel()
   227  
   228  	// Create a message handler, configure it to accept transactions and watch them
   229  	handler := newTestHandler()
   230  	defer handler.close()
   231  
   232  	handler.handler.synced.Store(true) // mark synced to accept transactions
   233  
   234  	txs := make(chan core.NewTxsEvent)
   235  	sub := handler.txpool.SubscribeTransactions(txs)
   236  	defer sub.Unsubscribe()
   237  
   238  	// Create a source peer to send messages through and a sink handler to receive them
   239  	p2pSrc, p2pSink := p2p.MsgPipe()
   240  	defer p2pSrc.Close()
   241  	defer p2pSink.Close()
   242  
   243  	src := zond.NewPeer(protocol, p2p.NewPeerPipe(enode.ID{1}, "", nil, p2pSrc), p2pSrc, handler.txpool)
   244  	sink := zond.NewPeer(protocol, p2p.NewPeerPipe(enode.ID{2}, "", nil, p2pSink), p2pSink, handler.txpool)
   245  	defer src.Close()
   246  	defer sink.Close()
   247  
   248  	go handler.handler.runZondPeer(sink, func(peer *zond.Peer) error {
   249  		return zond.Handle((*zondHandler)(handler.handler), peer)
   250  	})
   251  	// Run the handshake locally to avoid spinning up a source handler
   252  	var (
   253  		genesis = handler.chain.Genesis()
   254  		head    = handler.chain.CurrentBlock()
   255  	)
   256  	if err := src.Handshake(1, head.Hash(), genesis.Hash(), forkid.NewIDWithChain(handler.chain), forkid.NewFilter(handler.chain)); err != nil {
   257  		t.Fatalf("failed to run protocol handshake")
   258  	}
   259  	// Send the transaction to the sink and verify that it's added to the tx pool
   260  	tx := types.NewTx(&types.DynamicFeeTx{
   261  		Nonce:     0,
   262  		To:        &common.Address{},
   263  		Value:     big.NewInt(0),
   264  		Gas:       100000,
   265  		GasFeeCap: big.NewInt(0),
   266  		Data:      nil,
   267  	})
   268  	tx, _ = types.SignTx(tx, types.ShanghaiSigner{ChainId: big.NewInt(0)}, testKey)
   269  
   270  	if err := src.SendTransactions([]*types.Transaction{tx}); err != nil {
   271  		t.Fatalf("failed to send transaction: %v", err)
   272  	}
   273  	select {
   274  	case event := <-txs:
   275  		if len(event.Txs) != 1 {
   276  			t.Errorf("wrong number of added transactions: got %d, want 1", len(event.Txs))
   277  		} else if event.Txs[0].Hash() != tx.Hash() {
   278  			t.Errorf("added wrong tx hash: got %v, want %v", event.Txs[0].Hash(), tx.Hash())
   279  		}
   280  	case <-time.After(2 * time.Second):
   281  		t.Errorf("no NewTxsEvent received within 2 seconds")
   282  	}
   283  }
   284  
   285  // This test checks that pending transactions are sent.
   286  func TestSendTransactions68(t *testing.T) { testSendTransactions(t, zond.ETH68) }
   287  
   288  func testSendTransactions(t *testing.T, protocol uint) {
   289  	t.Parallel()
   290  
   291  	// Create a message handler and fill the pool with big transactions
   292  	handler := newTestHandler()
   293  	defer handler.close()
   294  
   295  	insert := make([]*types.Transaction, 100)
   296  	for nonce := range insert {
   297  		tx := types.NewTx(&types.DynamicFeeTx{
   298  			Nonce:     uint64(nonce),
   299  			To:        &common.Address{},
   300  			Value:     big.NewInt(0),
   301  			Gas:       100000,
   302  			GasFeeCap: big.NewInt(0),
   303  			Data:      make([]byte, 10240),
   304  		})
   305  		tx, _ = types.SignTx(tx, types.ShanghaiSigner{ChainId: big.NewInt(0)}, testKey)
   306  		insert[nonce] = tx
   307  	}
   308  	go handler.txpool.Add(insert, false, false) // Need goroutine to not block on feed
   309  	time.Sleep(250 * time.Millisecond)          // Wait until tx events get out of the system (can't use events, tx broadcaster races with peer join)
   310  
   311  	// Create a source handler to send messages through and a sink peer to receive them
   312  	p2pSrc, p2pSink := p2p.MsgPipe()
   313  	defer p2pSrc.Close()
   314  	defer p2pSink.Close()
   315  
   316  	src := zond.NewPeer(protocol, p2p.NewPeerPipe(enode.ID{1}, "", nil, p2pSrc), p2pSrc, handler.txpool)
   317  	sink := zond.NewPeer(protocol, p2p.NewPeerPipe(enode.ID{2}, "", nil, p2pSink), p2pSink, handler.txpool)
   318  	defer src.Close()
   319  	defer sink.Close()
   320  
   321  	go handler.handler.runZondPeer(src, func(peer *zond.Peer) error {
   322  		return zond.Handle((*zondHandler)(handler.handler), peer)
   323  	})
   324  	// Run the handshake locally to avoid spinning up a source handler
   325  	var (
   326  		genesis = handler.chain.Genesis()
   327  		head    = handler.chain.CurrentBlock()
   328  	)
   329  	if err := sink.Handshake(1, head.Hash(), genesis.Hash(), forkid.NewIDWithChain(handler.chain), forkid.NewFilter(handler.chain)); err != nil {
   330  		t.Fatalf("failed to run protocol handshake")
   331  	}
   332  	// After the handshake completes, the source handler should stream the sink
   333  	// the transactions, subscribe to all inbound network events
   334  	backend := new(testZondHandler)
   335  
   336  	anns := make(chan []common.Hash)
   337  	annSub := backend.txAnnounces.Subscribe(anns)
   338  	defer annSub.Unsubscribe()
   339  
   340  	bcasts := make(chan []*types.Transaction)
   341  	bcastSub := backend.txBroadcasts.Subscribe(bcasts)
   342  	defer bcastSub.Unsubscribe()
   343  
   344  	go zond.Handle(backend, sink)
   345  
   346  	// Make sure we get all the transactions on the correct channels
   347  	seen := make(map[common.Hash]struct{})
   348  	for len(seen) < len(insert) {
   349  		switch protocol {
   350  		case 68:
   351  			select {
   352  			case hashes := <-anns:
   353  				for _, hash := range hashes {
   354  					if _, ok := seen[hash]; ok {
   355  						t.Errorf("duplicate transaction announced: %x", hash)
   356  					}
   357  					seen[hash] = struct{}{}
   358  				}
   359  			case <-bcasts:
   360  				t.Errorf("initial tx broadcast received on post zond/66")
   361  			}
   362  
   363  		default:
   364  			panic("unsupported protocol, please extend test")
   365  		}
   366  	}
   367  	for _, tx := range insert {
   368  		if _, ok := seen[tx.Hash()]; !ok {
   369  			t.Errorf("missing transaction: %x", tx.Hash())
   370  		}
   371  	}
   372  }
   373  
   374  // Tests that transactions get propagated to all attached peers, either via direct
   375  // broadcasts or via announcements/retrievals.
   376  func TestTransactionPropagation68(t *testing.T) { testTransactionPropagation(t, zond.ETH68) }
   377  
   378  func testTransactionPropagation(t *testing.T, protocol uint) {
   379  	t.Parallel()
   380  
   381  	// Create a source handler to send transactions from and a number of sinks
   382  	// to receive them. We need multiple sinks since a one-to-one peering would
   383  	// broadcast all transactions without announcement.
   384  	source := newTestHandler()
   385  	source.handler.snapSync.Store(false) // Avoid requiring snap, otherwise some will be dropped below
   386  	defer source.close()
   387  
   388  	sinks := make([]*testHandler, 10)
   389  	for i := 0; i < len(sinks); i++ {
   390  		sinks[i] = newTestHandler()
   391  		defer sinks[i].close()
   392  
   393  		sinks[i].handler.synced.Store(true) // mark synced to accept transactions
   394  	}
   395  	// Interconnect all the sink handlers with the source handler
   396  	for i, sink := range sinks {
   397  		sink := sink // Closure for goroutine below
   398  
   399  		sourcePipe, sinkPipe := p2p.MsgPipe()
   400  		defer sourcePipe.Close()
   401  		defer sinkPipe.Close()
   402  
   403  		sourcePeer := zond.NewPeer(protocol, p2p.NewPeerPipe(enode.ID{byte(i + 1)}, "", nil, sourcePipe), sourcePipe, source.txpool)
   404  		sinkPeer := zond.NewPeer(protocol, p2p.NewPeerPipe(enode.ID{0}, "", nil, sinkPipe), sinkPipe, sink.txpool)
   405  		defer sourcePeer.Close()
   406  		defer sinkPeer.Close()
   407  
   408  		go source.handler.runZondPeer(sourcePeer, func(peer *zond.Peer) error {
   409  			return zond.Handle((*zondHandler)(source.handler), peer)
   410  		})
   411  		go sink.handler.runZondPeer(sinkPeer, func(peer *zond.Peer) error {
   412  			return zond.Handle((*zondHandler)(sink.handler), peer)
   413  		})
   414  	}
   415  	// Subscribe to all the transaction pools
   416  	txChs := make([]chan core.NewTxsEvent, len(sinks))
   417  	for i := 0; i < len(sinks); i++ {
   418  		txChs[i] = make(chan core.NewTxsEvent, 1024)
   419  
   420  		sub := sinks[i].txpool.SubscribeTransactions(txChs[i])
   421  		defer sub.Unsubscribe()
   422  	}
   423  	// Fill the source pool with transactions and wait for them at the sinks
   424  	txs := make([]*types.Transaction, 1024)
   425  	for nonce := range txs {
   426  		tx := types.NewTx(&types.DynamicFeeTx{
   427  			Nonce:     uint64(nonce),
   428  			To:        &common.Address{},
   429  			Value:     big.NewInt(0),
   430  			Gas:       100000,
   431  			GasFeeCap: big.NewInt(0),
   432  			Data:      nil,
   433  		})
   434  		tx, _ = types.SignTx(tx, types.ShanghaiSigner{ChainId: big.NewInt(0)}, testKey)
   435  
   436  		txs[nonce] = tx
   437  	}
   438  	source.txpool.Add(txs, false, false)
   439  
   440  	// Iterate through all the sinks and ensure they all got the transactions
   441  	for i := range sinks {
   442  		for arrived, timeout := 0, false; arrived < len(txs) && !timeout; {
   443  			select {
   444  			case event := <-txChs[i]:
   445  				arrived += len(event.Txs)
   446  			case <-time.After(2 * time.Second):
   447  				t.Errorf("sink %d: transaction propagation timed out: have %d, want %d", i, arrived, len(txs))
   448  				timeout = true
   449  			}
   450  		}
   451  	}
   452  }