github.com/m3shine/gochain@v2.2.26+incompatible/eth/helper_test.go (about)

     1  // Copyright 2015 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  // This file contains some shares testing functionality, common to  multiple
    18  // different files and modules being tested.
    19  
    20  package eth
    21  
    22  import (
    23  	"context"
    24  	"crypto/ecdsa"
    25  	"crypto/rand"
    26  	"math/big"
    27  	"sort"
    28  	"sync"
    29  	"testing"
    30  
    31  	"github.com/gochain-io/gochain/common"
    32  	"github.com/gochain-io/gochain/common/hexutil"
    33  	"github.com/gochain-io/gochain/consensus/clique"
    34  	"github.com/gochain-io/gochain/core"
    35  	"github.com/gochain-io/gochain/core/types"
    36  	"github.com/gochain-io/gochain/core/vm"
    37  	"github.com/gochain-io/gochain/crypto"
    38  	"github.com/gochain-io/gochain/eth/downloader"
    39  	"github.com/gochain-io/gochain/ethdb"
    40  	"github.com/gochain-io/gochain/event"
    41  	"github.com/gochain-io/gochain/p2p"
    42  	"github.com/gochain-io/gochain/p2p/discover"
    43  	"github.com/gochain-io/gochain/params"
    44  )
    45  
    46  var (
    47  	testBankKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
    48  	testBank       = crypto.PubkeyToAddress(testBankKey.PublicKey)
    49  )
    50  
    51  // newTestProtocolManager creates a new protocol manager for testing purposes,
    52  // with the given number of blocks already known, and potential notification
    53  // channels for different events.
    54  func newTestProtocolManager(ctx context.Context, mode downloader.SyncMode, blocks int, generator func(context.Context, int, *core.BlockGen), newtx chan<- []*types.Transaction) (*ProtocolManager, *ethdb.MemDatabase, error) {
    55  	var (
    56  		evmux  = new(event.TypeMux)
    57  		db     = ethdb.NewMemDatabase()
    58  		engine = clique.NewFaker()
    59  		gspec  = &core.Genesis{
    60  			Config: params.TestChainConfig,
    61  			Alloc:  core.GenesisAlloc{testBank: {Balance: big.NewInt(1000000)}},
    62  			Signer: hexutil.MustDecode("0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
    63  		}
    64  		genesis       = gspec.MustCommit(db)
    65  		blockchain, _ = core.NewBlockChain(ctx, db, nil, gspec.Config, engine, vm.Config{})
    66  	)
    67  	chain, _ := core.GenerateChain(ctx, gspec.Config, genesis, engine, db, blocks, generator)
    68  	if _, err := blockchain.InsertChain(ctx, chain); err != nil {
    69  		panic(err)
    70  	}
    71  
    72  	pm, err := NewProtocolManager(ctx, gspec.Config, mode, DefaultConfig.NetworkId, evmux, &testTxPool{added: newtx}, engine, blockchain, db)
    73  	if err != nil {
    74  		return nil, nil, err
    75  	}
    76  	pm.Start(1000)
    77  	return pm, db, nil
    78  }
    79  
    80  // newTestProtocolManagerMust creates a new protocol manager for testing purposes,
    81  // with the given number of blocks already known, and potential notification
    82  // channels for different events. In case of an error, the constructor force-
    83  // fails the test.
    84  func newTestProtocolManagerMust(ctx context.Context, t *testing.T, mode downloader.SyncMode, blocks int, generator func(context.Context, int, *core.BlockGen), newtx chan<- []*types.Transaction) (*ProtocolManager, *ethdb.MemDatabase) {
    85  	pm, db, err := newTestProtocolManager(ctx, mode, blocks, generator, newtx)
    86  	if err != nil {
    87  		t.Fatalf("Failed to create protocol manager: %v", err)
    88  	}
    89  	return pm, db
    90  }
    91  
    92  // testTxPool is a fake, helper transaction pool for testing purposes
    93  type testTxPool struct {
    94  	txFeed core.NewTxsFeed
    95  	pool   []*types.Transaction        // Collection of all transactions
    96  	added  chan<- []*types.Transaction // Notification channel for new transactions
    97  
    98  	lock sync.RWMutex // Protects the transaction pool
    99  }
   100  
   101  // AddRemotes appends a batch of transactions to the pool, and notifies any
   102  // listeners if the addition channel is non nil
   103  func (p *testTxPool) AddRemotes(ctx context.Context, txs []*types.Transaction) []error {
   104  	p.lock.Lock()
   105  	defer p.lock.Unlock()
   106  
   107  	p.pool = append(p.pool, txs...)
   108  	if p.added != nil {
   109  		p.added <- txs
   110  	}
   111  	return make([]error, len(txs))
   112  }
   113  
   114  // Pending returns all the transactions known to the pool
   115  func (p *testTxPool) Pending(ctx context.Context) map[common.Address]types.Transactions {
   116  	p.lock.RLock()
   117  	defer p.lock.RUnlock()
   118  
   119  	batches := make(map[common.Address]types.Transactions)
   120  	for _, tx := range p.pool {
   121  		from, _ := types.Sender(types.HomesteadSigner{}, tx)
   122  		batches[from] = append(batches[from], tx)
   123  	}
   124  	for _, batch := range batches {
   125  		sort.Sort(types.TxByNonce(batch))
   126  	}
   127  	return batches
   128  }
   129  
   130  func (p *testTxPool) PendingList(ctx context.Context) types.Transactions {
   131  	var pending types.Transactions
   132  	for _, txs := range p.Pending(ctx) {
   133  		pending = append(pending, txs...)
   134  	}
   135  	return pending
   136  }
   137  
   138  func (p *testTxPool) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent, name string) {
   139  	p.txFeed.Subscribe(ch, name)
   140  }
   141  
   142  func (p *testTxPool) UnsubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) {
   143  	p.txFeed.Unsubscribe(ch)
   144  }
   145  
   146  // newTestTransaction create a new dummy transaction.
   147  func newTestTransaction(from *ecdsa.PrivateKey, nonce uint64, datasize int) *types.Transaction {
   148  	tx := types.NewTransaction(nonce, common.Address{}, big.NewInt(0), 100000, big.NewInt(0), make([]byte, datasize))
   149  	tx, _ = types.SignTx(tx, types.HomesteadSigner{}, from)
   150  	return tx
   151  }
   152  
   153  // testPeer is a simulated peer to allow testing direct network calls.
   154  type testPeer struct {
   155  	net p2p.MsgReadWriter // Network layer reader/writer to simulate remote messaging
   156  	app *p2p.MsgPipeRW    // Application layer reader/writer to simulate the local side
   157  	*peer
   158  }
   159  
   160  // newTestPeer creates a new peer registered at the given protocol manager.
   161  func newTestPeer(ctx context.Context, name string, version int, pm *ProtocolManager, shake bool) (*testPeer, <-chan error) {
   162  	// Create a message pipe to communicate through
   163  	app, net := p2p.MsgPipe()
   164  
   165  	// Generate a random id and create the peer
   166  	var id discover.NodeID
   167  	rand.Read(id[:])
   168  
   169  	peer := pm.newPeer(version, p2p.NewPeer(id, name, nil), net)
   170  
   171  	// Start the peer on a new thread
   172  	errc := make(chan error, 1)
   173  	go func() {
   174  		select {
   175  		case pm.newPeerCh <- peer:
   176  			errc <- pm.handle(peer)
   177  		case <-pm.quitSync:
   178  			errc <- p2p.DiscQuitting
   179  		}
   180  	}()
   181  	tp := &testPeer{app: app, net: net, peer: peer}
   182  	// Execute any implicitly requested handshakes and return
   183  	if shake {
   184  		var (
   185  			genesis = pm.blockchain.Genesis()
   186  			head    = pm.blockchain.CurrentHeader()
   187  			td      = pm.blockchain.GetTd(head.Hash(), head.Number.Uint64())
   188  		)
   189  		tp.handshake(nil, td, head.Hash(), genesis.Hash())
   190  	}
   191  	return tp, errc
   192  }
   193  
   194  // handshake simulates a trivial handshake that expects the same state from the
   195  // remote side as we are simulating locally.
   196  func (p *testPeer) handshake(t *testing.T, td *big.Int, head common.Hash, genesis common.Hash) {
   197  	msg := &statusData{
   198  		ProtocolVersion: uint32(p.version),
   199  		NetworkId:       DefaultConfig.NetworkId,
   200  		TD:              td,
   201  		CurrentBlock:    head,
   202  		GenesisBlock:    genesis,
   203  	}
   204  	if err := p2p.ExpectMsg(p.app, StatusMsg, msg); err != nil {
   205  		t.Fatalf("status recv: %v", err)
   206  	}
   207  	if err := p2p.Send(p.app, StatusMsg, msg); err != nil {
   208  		t.Fatalf("status send: %v", err)
   209  	}
   210  }
   211  
   212  // close terminates the local side of the peer, notifying the remote protocol
   213  // manager of termination.
   214  func (p *testPeer) close() {
   215  	p.app.Close()
   216  }