github.com/theQRL/go-zond@v0.1.1/miner/miner_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 miner implements Ethereum block creation and mining.
    18  package miner
    19  
    20  import (
    21  	"errors"
    22  	"math/big"
    23  	"testing"
    24  	"time"
    25  
    26  	"github.com/theQRL/go-zond/common"
    27  	"github.com/theQRL/go-zond/consensus/clique"
    28  	"github.com/theQRL/go-zond/core"
    29  	"github.com/theQRL/go-zond/core/rawdb"
    30  	"github.com/theQRL/go-zond/core/state"
    31  	"github.com/theQRL/go-zond/core/txpool"
    32  	"github.com/theQRL/go-zond/core/txpool/legacypool"
    33  	"github.com/theQRL/go-zond/core/types"
    34  	"github.com/theQRL/go-zond/core/vm"
    35  	"github.com/theQRL/go-zond/crypto"
    36  	"github.com/theQRL/go-zond/event"
    37  	"github.com/theQRL/go-zond/params"
    38  	"github.com/theQRL/go-zond/trie"
    39  	"github.com/theQRL/go-zond/zond/downloader"
    40  )
    41  
    42  type mockBackend struct {
    43  	bc     *core.BlockChain
    44  	txPool *txpool.TxPool
    45  }
    46  
    47  func NewMockBackend(bc *core.BlockChain, txPool *txpool.TxPool) *mockBackend {
    48  	return &mockBackend{
    49  		bc:     bc,
    50  		txPool: txPool,
    51  	}
    52  }
    53  
    54  func (m *mockBackend) BlockChain() *core.BlockChain {
    55  	return m.bc
    56  }
    57  
    58  func (m *mockBackend) TxPool() *txpool.TxPool {
    59  	return m.txPool
    60  }
    61  
    62  func (m *mockBackend) StateAtBlock(block *types.Block, reexec uint64, base *state.StateDB, checkLive bool, preferDisk bool) (statedb *state.StateDB, err error) {
    63  	return nil, errors.New("not supported")
    64  }
    65  
    66  type testBlockChain struct {
    67  	config        *params.ChainConfig
    68  	statedb       *state.StateDB
    69  	gasLimit      uint64
    70  	chainHeadFeed *event.Feed
    71  }
    72  
    73  func (bc *testBlockChain) Config() *params.ChainConfig {
    74  	return bc.config
    75  }
    76  
    77  func (bc *testBlockChain) CurrentBlock() *types.Header {
    78  	return &types.Header{
    79  		Number:   new(big.Int),
    80  		GasLimit: bc.gasLimit,
    81  	}
    82  }
    83  
    84  func (bc *testBlockChain) GetBlock(hash common.Hash, number uint64) *types.Block {
    85  	return types.NewBlock(bc.CurrentBlock(), nil, nil, nil, trie.NewStackTrie(nil))
    86  }
    87  
    88  func (bc *testBlockChain) StateAt(common.Hash) (*state.StateDB, error) {
    89  	return bc.statedb, nil
    90  }
    91  
    92  func (bc *testBlockChain) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription {
    93  	return bc.chainHeadFeed.Subscribe(ch)
    94  }
    95  
    96  func TestMiner(t *testing.T) {
    97  	miner, mux, cleanup := createMiner(t)
    98  	defer cleanup(false)
    99  
   100  	miner.Start()
   101  	waitForMiningState(t, miner, true)
   102  	// Start the downloader
   103  	mux.Post(downloader.StartEvent{})
   104  	waitForMiningState(t, miner, false)
   105  	// Stop the downloader and wait for the update loop to run
   106  	mux.Post(downloader.DoneEvent{})
   107  	waitForMiningState(t, miner, true)
   108  
   109  	// Subsequent downloader events after a successful DoneEvent should not cause the
   110  	// miner to start or stop. This prevents a security vulnerability
   111  	// that would allow entities to present fake high blocks that would
   112  	// stop mining operations by causing a downloader sync
   113  	// until it was discovered they were invalid, whereon mining would resume.
   114  	mux.Post(downloader.StartEvent{})
   115  	waitForMiningState(t, miner, true)
   116  
   117  	mux.Post(downloader.FailedEvent{})
   118  	waitForMiningState(t, miner, true)
   119  }
   120  
   121  // TestMinerDownloaderFirstFails tests that mining is only
   122  // permitted to run indefinitely once the downloader sees a DoneEvent (success).
   123  // An initial FailedEvent should allow mining to stop on a subsequent
   124  // downloader StartEvent.
   125  func TestMinerDownloaderFirstFails(t *testing.T) {
   126  	miner, mux, cleanup := createMiner(t)
   127  	defer cleanup(false)
   128  
   129  	miner.Start()
   130  	waitForMiningState(t, miner, true)
   131  	// Start the downloader
   132  	mux.Post(downloader.StartEvent{})
   133  	waitForMiningState(t, miner, false)
   134  
   135  	// Stop the downloader and wait for the update loop to run
   136  	mux.Post(downloader.FailedEvent{})
   137  	waitForMiningState(t, miner, true)
   138  
   139  	// Since the downloader hasn't yet emitted a successful DoneEvent,
   140  	// we expect the miner to stop on next StartEvent.
   141  	mux.Post(downloader.StartEvent{})
   142  	waitForMiningState(t, miner, false)
   143  
   144  	// Downloader finally succeeds.
   145  	mux.Post(downloader.DoneEvent{})
   146  	waitForMiningState(t, miner, true)
   147  
   148  	// Downloader starts again.
   149  	// Since it has achieved a DoneEvent once, we expect miner
   150  	// state to be unchanged.
   151  	mux.Post(downloader.StartEvent{})
   152  	waitForMiningState(t, miner, true)
   153  
   154  	mux.Post(downloader.FailedEvent{})
   155  	waitForMiningState(t, miner, true)
   156  }
   157  
   158  func TestMinerStartStopAfterDownloaderEvents(t *testing.T) {
   159  	miner, mux, cleanup := createMiner(t)
   160  	defer cleanup(false)
   161  
   162  	miner.Start()
   163  	waitForMiningState(t, miner, true)
   164  	// Start the downloader
   165  	mux.Post(downloader.StartEvent{})
   166  	waitForMiningState(t, miner, false)
   167  
   168  	// Downloader finally succeeds.
   169  	mux.Post(downloader.DoneEvent{})
   170  	waitForMiningState(t, miner, true)
   171  
   172  	miner.Stop()
   173  	waitForMiningState(t, miner, false)
   174  
   175  	miner.Start()
   176  	waitForMiningState(t, miner, true)
   177  
   178  	miner.Stop()
   179  	waitForMiningState(t, miner, false)
   180  }
   181  
   182  func TestStartWhileDownload(t *testing.T) {
   183  	miner, mux, cleanup := createMiner(t)
   184  	defer cleanup(false)
   185  	waitForMiningState(t, miner, false)
   186  	miner.Start()
   187  	waitForMiningState(t, miner, true)
   188  	// Stop the downloader and wait for the update loop to run
   189  	mux.Post(downloader.StartEvent{})
   190  	waitForMiningState(t, miner, false)
   191  	// Starting the miner after the downloader should not work
   192  	miner.Start()
   193  	waitForMiningState(t, miner, false)
   194  }
   195  
   196  func TestStartStopMiner(t *testing.T) {
   197  	miner, _, cleanup := createMiner(t)
   198  	defer cleanup(false)
   199  	waitForMiningState(t, miner, false)
   200  	miner.Start()
   201  	waitForMiningState(t, miner, true)
   202  	miner.Stop()
   203  	waitForMiningState(t, miner, false)
   204  }
   205  
   206  func TestCloseMiner(t *testing.T) {
   207  	miner, _, cleanup := createMiner(t)
   208  	defer cleanup(true)
   209  	waitForMiningState(t, miner, false)
   210  	miner.Start()
   211  	waitForMiningState(t, miner, true)
   212  	// Terminate the miner and wait for the update loop to run
   213  	miner.Close()
   214  	waitForMiningState(t, miner, false)
   215  }
   216  
   217  // TestMinerSetEtherbase checks that etherbase becomes set even if mining isn't
   218  // possible at the moment
   219  func TestMinerSetEtherbase(t *testing.T) {
   220  	miner, mux, cleanup := createMiner(t)
   221  	defer cleanup(false)
   222  	miner.Start()
   223  	waitForMiningState(t, miner, true)
   224  	// Start the downloader
   225  	mux.Post(downloader.StartEvent{})
   226  	waitForMiningState(t, miner, false)
   227  	// Now user tries to configure proper mining address
   228  	miner.Start()
   229  	// Stop the downloader and wait for the update loop to run
   230  	mux.Post(downloader.DoneEvent{})
   231  	waitForMiningState(t, miner, true)
   232  
   233  	coinbase := common.HexToAddress("0xdeedbeef")
   234  	miner.SetEtherbase(coinbase)
   235  	if addr := miner.worker.etherbase(); addr != coinbase {
   236  		t.Fatalf("Unexpected etherbase want %x got %x", coinbase, addr)
   237  	}
   238  }
   239  
   240  // waitForMiningState waits until either
   241  // * the desired mining state was reached
   242  // * a timeout was reached which fails the test
   243  func waitForMiningState(t *testing.T, m *Miner, mining bool) {
   244  	t.Helper()
   245  
   246  	var state bool
   247  	for i := 0; i < 100; i++ {
   248  		time.Sleep(10 * time.Millisecond)
   249  		if state = m.Mining(); state == mining {
   250  			return
   251  		}
   252  	}
   253  	t.Fatalf("Mining() == %t, want %t", state, mining)
   254  }
   255  
   256  func minerTestGenesisBlock(period uint64, gasLimit uint64, faucet common.Address) *core.Genesis {
   257  	config := *params.AllCliqueProtocolChanges
   258  	config.Clique = &params.CliqueConfig{
   259  		Period: period,
   260  		Epoch:  config.Clique.Epoch,
   261  	}
   262  
   263  	// Assemble and return the genesis with the precompiles and faucet pre-funded
   264  	return &core.Genesis{
   265  		Config:     &config,
   266  		ExtraData:  append(append(make([]byte, 32), faucet[:]...), make([]byte, crypto.SignatureLength)...),
   267  		GasLimit:   gasLimit,
   268  		BaseFee:    big.NewInt(params.InitialBaseFee),
   269  		Difficulty: big.NewInt(1),
   270  		Alloc: map[common.Address]core.GenesisAccount{
   271  			common.BytesToAddress([]byte{1}): {Balance: big.NewInt(1)}, // ECRecover
   272  			common.BytesToAddress([]byte{2}): {Balance: big.NewInt(1)}, // SHA256
   273  			common.BytesToAddress([]byte{3}): {Balance: big.NewInt(1)}, // RIPEMD
   274  			common.BytesToAddress([]byte{4}): {Balance: big.NewInt(1)}, // Identity
   275  			common.BytesToAddress([]byte{5}): {Balance: big.NewInt(1)}, // ModExp
   276  			common.BytesToAddress([]byte{6}): {Balance: big.NewInt(1)}, // ECAdd
   277  			common.BytesToAddress([]byte{7}): {Balance: big.NewInt(1)}, // ECScalarMul
   278  			common.BytesToAddress([]byte{8}): {Balance: big.NewInt(1)}, // ECPairing
   279  			common.BytesToAddress([]byte{9}): {Balance: big.NewInt(1)}, // BLAKE2b
   280  			faucet:                           {Balance: new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 256), big.NewInt(9))},
   281  		},
   282  	}
   283  }
   284  func createMiner(t *testing.T) (*Miner, *event.TypeMux, func(skipMiner bool)) {
   285  	// Create Ethash config
   286  	config := Config{
   287  		Etherbase: common.HexToAddress("123456789"),
   288  	}
   289  	// Create chainConfig
   290  	chainDB := rawdb.NewMemoryDatabase()
   291  	triedb := trie.NewDatabase(chainDB, nil)
   292  	genesis := minerTestGenesisBlock(15, 11_500_000, common.HexToAddress("12345"))
   293  	chainConfig, _, err := core.SetupGenesisBlock(chainDB, triedb, genesis)
   294  	if err != nil {
   295  		t.Fatalf("can't create new chain config: %v", err)
   296  	}
   297  	// Create consensus engine
   298  	engine := clique.New(chainConfig.Clique, chainDB)
   299  	// Create Ethereum backend
   300  	bc, err := core.NewBlockChain(chainDB, nil, genesis, nil, engine, vm.Config{}, nil, nil)
   301  	if err != nil {
   302  		t.Fatalf("can't create new chain %v", err)
   303  	}
   304  	statedb, _ := state.New(bc.Genesis().Root(), bc.StateCache(), nil)
   305  	blockchain := &testBlockChain{chainConfig, statedb, 10000000, new(event.Feed)}
   306  
   307  	pool := legacypool.New(testTxPoolConfig, blockchain)
   308  	txpool, _ := txpool.New(new(big.Int).SetUint64(testTxPoolConfig.PriceLimit), blockchain, []txpool.SubPool{pool})
   309  
   310  	backend := NewMockBackend(bc, txpool)
   311  	// Create event Mux
   312  	mux := new(event.TypeMux)
   313  	// Create Miner
   314  	miner := New(backend, &config, chainConfig, mux, engine, nil)
   315  	cleanup := func(skipMiner bool) {
   316  		bc.Stop()
   317  		engine.Close()
   318  		txpool.Close()
   319  		if !skipMiner {
   320  			miner.Close()
   321  		}
   322  	}
   323  	return miner, mux, cleanup
   324  }