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