github.com/luckypickle/go-ethereum-vet@v1.14.2/miner/worker_test.go (about)

     1  // Copyright 2018 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
    18  
    19  import (
    20  	"math/big"
    21  	"testing"
    22  	"time"
    23  
    24  	"github.com/luckypickle/go-ethereum-vet/common"
    25  	"github.com/luckypickle/go-ethereum-vet/consensus"
    26  	"github.com/luckypickle/go-ethereum-vet/consensus/clique"
    27  	"github.com/luckypickle/go-ethereum-vet/consensus/ethash"
    28  	"github.com/luckypickle/go-ethereum-vet/core"
    29  	"github.com/luckypickle/go-ethereum-vet/core/types"
    30  	"github.com/luckypickle/go-ethereum-vet/core/vm"
    31  	"github.com/luckypickle/go-ethereum-vet/crypto"
    32  	"github.com/luckypickle/go-ethereum-vet/ethdb"
    33  	"github.com/luckypickle/go-ethereum-vet/event"
    34  	"github.com/luckypickle/go-ethereum-vet/params"
    35  )
    36  
    37  var (
    38  	// Test chain configurations
    39  	testTxPoolConfig  core.TxPoolConfig
    40  	ethashChainConfig *params.ChainConfig
    41  	cliqueChainConfig *params.ChainConfig
    42  
    43  	// Test accounts
    44  	testBankKey, _  = crypto.GenerateKey()
    45  	testBankAddress = crypto.PubkeyToAddress(testBankKey.PublicKey)
    46  	testBankFunds   = big.NewInt(1000000000000000000)
    47  
    48  	acc1Key, _ = crypto.GenerateKey()
    49  	acc1Addr   = crypto.PubkeyToAddress(acc1Key.PublicKey)
    50  
    51  	// Test transactions
    52  	pendingTxs []*types.Transaction
    53  	newTxs     []*types.Transaction
    54  )
    55  
    56  func init() {
    57  	testTxPoolConfig = core.DefaultTxPoolConfig
    58  	testTxPoolConfig.Journal = ""
    59  	ethashChainConfig = params.TestChainConfig
    60  	cliqueChainConfig = params.TestChainConfig
    61  	cliqueChainConfig.Clique = &params.CliqueConfig{
    62  		Period: 10,
    63  		Epoch:  30000,
    64  	}
    65  	tx1, _ := types.SignTx(types.NewTransaction(0, acc1Addr, big.NewInt(1000), params.TxGas, nil, nil), types.HomesteadSigner{}, testBankKey)
    66  	pendingTxs = append(pendingTxs, tx1)
    67  	tx2, _ := types.SignTx(types.NewTransaction(1, acc1Addr, big.NewInt(1000), params.TxGas, nil, nil), types.HomesteadSigner{}, testBankKey)
    68  	newTxs = append(newTxs, tx2)
    69  }
    70  
    71  // testWorkerBackend implements worker.Backend interfaces and wraps all information needed during the testing.
    72  type testWorkerBackend struct {
    73  	db         ethdb.Database
    74  	txPool     *core.TxPool
    75  	chain      *core.BlockChain
    76  	testTxFeed event.Feed
    77  	uncleBlock *types.Block
    78  }
    79  
    80  func newTestWorkerBackend(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine) *testWorkerBackend {
    81  	var (
    82  		db    = ethdb.NewMemDatabase()
    83  		gspec = core.Genesis{
    84  			Config: chainConfig,
    85  			Alloc:  core.GenesisAlloc{testBankAddress: {Balance: testBankFunds}},
    86  		}
    87  	)
    88  
    89  	switch engine.(type) {
    90  	case *clique.Clique:
    91  		gspec.ExtraData = make([]byte, 32+common.AddressLength+65)
    92  		copy(gspec.ExtraData[32:], testBankAddress[:])
    93  	case *ethash.Ethash:
    94  	default:
    95  		t.Fatal("unexpect consensus engine type")
    96  	}
    97  	genesis := gspec.MustCommit(db)
    98  
    99  	chain, _ := core.NewBlockChain(db, nil, gspec.Config, engine, vm.Config{})
   100  	txpool := core.NewTxPool(testTxPoolConfig, chainConfig, chain)
   101  	blocks, _ := core.GenerateChain(chainConfig, genesis, engine, db, 1, func(i int, gen *core.BlockGen) {
   102  		gen.SetCoinbase(acc1Addr)
   103  	})
   104  
   105  	return &testWorkerBackend{
   106  		db:         db,
   107  		chain:      chain,
   108  		txPool:     txpool,
   109  		uncleBlock: blocks[0],
   110  	}
   111  }
   112  
   113  func (b *testWorkerBackend) BlockChain() *core.BlockChain { return b.chain }
   114  func (b *testWorkerBackend) TxPool() *core.TxPool         { return b.txPool }
   115  func (b *testWorkerBackend) PostChainEvents(events []interface{}) {
   116  	b.chain.PostChainEvents(events, nil)
   117  }
   118  
   119  func newTestWorker(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine) (*worker, *testWorkerBackend) {
   120  	backend := newTestWorkerBackend(t, chainConfig, engine)
   121  	backend.txPool.AddLocals(pendingTxs)
   122  	w := newWorker(chainConfig, engine, backend, new(event.TypeMux), time.Second)
   123  	w.setEtherbase(testBankAddress)
   124  	return w, backend
   125  }
   126  
   127  func TestPendingStateAndBlockEthash(t *testing.T) {
   128  	testPendingStateAndBlock(t, ethashChainConfig, ethash.NewFaker())
   129  }
   130  func TestPendingStateAndBlockClique(t *testing.T) {
   131  	testPendingStateAndBlock(t, cliqueChainConfig, clique.New(cliqueChainConfig.Clique, ethdb.NewMemDatabase()))
   132  }
   133  
   134  func testPendingStateAndBlock(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine) {
   135  	defer engine.Close()
   136  
   137  	w, b := newTestWorker(t, chainConfig, engine)
   138  	defer w.close()
   139  
   140  	// Ensure snapshot has been updated.
   141  	time.Sleep(100 * time.Millisecond)
   142  	block, state := w.pending()
   143  	if block.NumberU64() != 1 {
   144  		t.Errorf("block number mismatch, has %d, want %d", block.NumberU64(), 1)
   145  	}
   146  	if balance := state.GetBalance(acc1Addr); balance.Cmp(big.NewInt(1000)) != 0 {
   147  		t.Errorf("account balance mismatch, has %d, want %d", balance, 1000)
   148  	}
   149  	b.txPool.AddLocals(newTxs)
   150  	// Ensure the new tx events has been processed
   151  	time.Sleep(100 * time.Millisecond)
   152  	block, state = w.pending()
   153  	if balance := state.GetBalance(acc1Addr); balance.Cmp(big.NewInt(2000)) != 0 {
   154  		t.Errorf("account balance mismatch, has %d, want %d", balance, 2000)
   155  	}
   156  }
   157  
   158  func TestEmptyWorkEthash(t *testing.T) {
   159  	testEmptyWork(t, ethashChainConfig, ethash.NewFaker())
   160  }
   161  func TestEmptyWorkClique(t *testing.T) {
   162  	testEmptyWork(t, cliqueChainConfig, clique.New(cliqueChainConfig.Clique, ethdb.NewMemDatabase()))
   163  }
   164  
   165  func testEmptyWork(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine) {
   166  	defer engine.Close()
   167  
   168  	w, _ := newTestWorker(t, chainConfig, engine)
   169  	defer w.close()
   170  
   171  	var (
   172  		taskCh    = make(chan struct{}, 2)
   173  		taskIndex int
   174  	)
   175  
   176  	checkEqual := func(t *testing.T, task *task, index int) {
   177  		receiptLen, balance := 0, big.NewInt(0)
   178  		if index == 1 {
   179  			receiptLen, balance = 1, big.NewInt(1000)
   180  		}
   181  		if len(task.receipts) != receiptLen {
   182  			t.Errorf("receipt number mismatch has %d, want %d", len(task.receipts), receiptLen)
   183  		}
   184  		if task.state.GetBalance(acc1Addr).Cmp(balance) != 0 {
   185  			t.Errorf("account balance mismatch has %d, want %d", task.state.GetBalance(acc1Addr), balance)
   186  		}
   187  	}
   188  
   189  	w.newTaskHook = func(task *task) {
   190  		if task.block.NumberU64() == 1 {
   191  			checkEqual(t, task, taskIndex)
   192  			taskIndex += 1
   193  			taskCh <- struct{}{}
   194  		}
   195  	}
   196  	w.fullTaskHook = func() {
   197  		time.Sleep(100 * time.Millisecond)
   198  	}
   199  
   200  	// Ensure worker has finished initialization
   201  	for {
   202  		b := w.pendingBlock()
   203  		if b != nil && b.NumberU64() == 1 {
   204  			break
   205  		}
   206  	}
   207  
   208  	w.start()
   209  	for i := 0; i < 2; i += 1 {
   210  		select {
   211  		case <-taskCh:
   212  		case <-time.NewTimer(time.Second).C:
   213  			t.Error("new task timeout")
   214  		}
   215  	}
   216  }
   217  
   218  func TestStreamUncleBlock(t *testing.T) {
   219  	ethash := ethash.NewFaker()
   220  	defer ethash.Close()
   221  
   222  	w, b := newTestWorker(t, ethashChainConfig, ethash)
   223  	defer w.close()
   224  
   225  	var taskCh = make(chan struct{})
   226  
   227  	taskIndex := 0
   228  	w.newTaskHook = func(task *task) {
   229  		if task.block.NumberU64() == 1 {
   230  			if taskIndex == 2 {
   231  				has := task.block.Header().UncleHash
   232  				want := types.CalcUncleHash([]*types.Header{b.uncleBlock.Header()})
   233  				if has != want {
   234  					t.Errorf("uncle hash mismatch, has %s, want %s", has.Hex(), want.Hex())
   235  				}
   236  			}
   237  			taskCh <- struct{}{}
   238  			taskIndex += 1
   239  		}
   240  	}
   241  	w.skipSealHook = func(task *task) bool {
   242  		return true
   243  	}
   244  	w.fullTaskHook = func() {
   245  		time.Sleep(100 * time.Millisecond)
   246  	}
   247  
   248  	// Ensure worker has finished initialization
   249  	for {
   250  		b := w.pendingBlock()
   251  		if b != nil && b.NumberU64() == 1 {
   252  			break
   253  		}
   254  	}
   255  
   256  	w.start()
   257  	// Ignore the first two works
   258  	for i := 0; i < 2; i += 1 {
   259  		select {
   260  		case <-taskCh:
   261  		case <-time.NewTimer(time.Second).C:
   262  			t.Error("new task timeout")
   263  		}
   264  	}
   265  	b.PostChainEvents([]interface{}{core.ChainSideEvent{Block: b.uncleBlock}})
   266  
   267  	select {
   268  	case <-taskCh:
   269  	case <-time.NewTimer(time.Second).C:
   270  		t.Error("new task timeout")
   271  	}
   272  }
   273  
   274  func TestRegenerateMiningBlockEthash(t *testing.T) {
   275  	testRegenerateMiningBlock(t, ethashChainConfig, ethash.NewFaker())
   276  }
   277  
   278  func TestRegenerateMiningBlockClique(t *testing.T) {
   279  	testRegenerateMiningBlock(t, cliqueChainConfig, clique.New(cliqueChainConfig.Clique, ethdb.NewMemDatabase()))
   280  }
   281  
   282  func testRegenerateMiningBlock(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine) {
   283  	defer engine.Close()
   284  
   285  	w, b := newTestWorker(t, chainConfig, engine)
   286  	defer w.close()
   287  
   288  	var taskCh = make(chan struct{})
   289  
   290  	taskIndex := 0
   291  	w.newTaskHook = func(task *task) {
   292  		if task.block.NumberU64() == 1 {
   293  			if taskIndex == 2 {
   294  				receiptLen, balance := 2, big.NewInt(2000)
   295  				if len(task.receipts) != receiptLen {
   296  					t.Errorf("receipt number mismatch has %d, want %d", len(task.receipts), receiptLen)
   297  				}
   298  				if task.state.GetBalance(acc1Addr).Cmp(balance) != 0 {
   299  					t.Errorf("account balance mismatch has %d, want %d", task.state.GetBalance(acc1Addr), balance)
   300  				}
   301  			}
   302  			taskCh <- struct{}{}
   303  			taskIndex += 1
   304  		}
   305  	}
   306  	w.skipSealHook = func(task *task) bool {
   307  		return true
   308  	}
   309  	w.fullTaskHook = func() {
   310  		time.Sleep(100 * time.Millisecond)
   311  	}
   312  	// Ensure worker has finished initialization
   313  	for {
   314  		b := w.pendingBlock()
   315  		if b != nil && b.NumberU64() == 1 {
   316  			break
   317  		}
   318  	}
   319  
   320  	w.start()
   321  	// Ignore the first two works
   322  	for i := 0; i < 2; i += 1 {
   323  		select {
   324  		case <-taskCh:
   325  		case <-time.NewTimer(time.Second).C:
   326  			t.Error("new task timeout")
   327  		}
   328  	}
   329  	b.txPool.AddLocals(newTxs)
   330  	time.Sleep(time.Second)
   331  
   332  	select {
   333  	case <-taskCh:
   334  	case <-time.NewTimer(time.Second).C:
   335  		t.Error("new task timeout")
   336  	}
   337  }
   338  
   339  func TestAdjustIntervalEthash(t *testing.T) {
   340  	testAdjustInterval(t, ethashChainConfig, ethash.NewFaker())
   341  }
   342  
   343  func TestAdjustIntervalClique(t *testing.T) {
   344  	testAdjustInterval(t, cliqueChainConfig, clique.New(cliqueChainConfig.Clique, ethdb.NewMemDatabase()))
   345  }
   346  
   347  func testAdjustInterval(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine) {
   348  	defer engine.Close()
   349  
   350  	w, _ := newTestWorker(t, chainConfig, engine)
   351  	defer w.close()
   352  
   353  	w.skipSealHook = func(task *task) bool {
   354  		return true
   355  	}
   356  	w.fullTaskHook = func() {
   357  		time.Sleep(100 * time.Millisecond)
   358  	}
   359  	var (
   360  		progress = make(chan struct{}, 10)
   361  		result   = make([]float64, 0, 10)
   362  		index    = 0
   363  		start    = false
   364  	)
   365  	w.resubmitHook = func(minInterval time.Duration, recommitInterval time.Duration) {
   366  		// Short circuit if interval checking hasn't started.
   367  		if !start {
   368  			return
   369  		}
   370  		var wantMinInterval, wantRecommitInterval time.Duration
   371  
   372  		switch index {
   373  		case 0:
   374  			wantMinInterval, wantRecommitInterval = 3*time.Second, 3*time.Second
   375  		case 1:
   376  			origin := float64(3 * time.Second.Nanoseconds())
   377  			estimate := origin*(1-intervalAdjustRatio) + intervalAdjustRatio*(origin/0.8+intervalAdjustBias)
   378  			wantMinInterval, wantRecommitInterval = 3*time.Second, time.Duration(int(estimate))*time.Nanosecond
   379  		case 2:
   380  			estimate := result[index-1]
   381  			min := float64(3 * time.Second.Nanoseconds())
   382  			estimate = estimate*(1-intervalAdjustRatio) + intervalAdjustRatio*(min-intervalAdjustBias)
   383  			wantMinInterval, wantRecommitInterval = 3*time.Second, time.Duration(int(estimate))*time.Nanosecond
   384  		case 3:
   385  			wantMinInterval, wantRecommitInterval = time.Second, time.Second
   386  		}
   387  
   388  		// Check interval
   389  		if minInterval != wantMinInterval {
   390  			t.Errorf("resubmit min interval mismatch want %s has %s", wantMinInterval, minInterval)
   391  		}
   392  		if recommitInterval != wantRecommitInterval {
   393  			t.Errorf("resubmit interval mismatch want %s has %s", wantRecommitInterval, recommitInterval)
   394  		}
   395  		result = append(result, float64(recommitInterval.Nanoseconds()))
   396  		index += 1
   397  		progress <- struct{}{}
   398  	}
   399  	// Ensure worker has finished initialization
   400  	for {
   401  		b := w.pendingBlock()
   402  		if b != nil && b.NumberU64() == 1 {
   403  			break
   404  		}
   405  	}
   406  
   407  	w.start()
   408  
   409  	time.Sleep(time.Second)
   410  
   411  	start = true
   412  	w.setRecommitInterval(3 * time.Second)
   413  	select {
   414  	case <-progress:
   415  	case <-time.NewTimer(time.Second).C:
   416  		t.Error("interval reset timeout")
   417  	}
   418  
   419  	w.resubmitAdjustCh <- &intervalAdjust{inc: true, ratio: 0.8}
   420  	select {
   421  	case <-progress:
   422  	case <-time.NewTimer(time.Second).C:
   423  		t.Error("interval reset timeout")
   424  	}
   425  
   426  	w.resubmitAdjustCh <- &intervalAdjust{inc: false}
   427  	select {
   428  	case <-progress:
   429  	case <-time.NewTimer(time.Second).C:
   430  		t.Error("interval reset timeout")
   431  	}
   432  
   433  	w.setRecommitInterval(500 * time.Millisecond)
   434  	select {
   435  	case <-progress:
   436  	case <-time.NewTimer(time.Second).C:
   437  		t.Error("interval reset timeout")
   438  	}
   439  }