github.com/yinchengtsinghua/golang-Eos-dpos-Ethereum@v0.0.0-20190121132951-92cc4225ed8e/miner/worker_test.go (about)

     1  
     2  //此源码被清华学神尹成大魔王专业翻译分析并修改
     3  //尹成QQ77025077
     4  //尹成微信18510341407
     5  //尹成所在QQ群721929980
     6  //尹成邮箱 yinc13@mails.tsinghua.edu.cn
     7  //尹成毕业于清华大学,微软区块链领域全球最有价值专家
     8  //https://mvp.microsoft.com/zh-cn/PublicProfile/4033620
     9  //版权所有2018 Go Ethereum作者
    10  //此文件是Go以太坊库的一部分。
    11  //
    12  //Go-Ethereum库是免费软件:您可以重新分发它和/或修改
    13  //根据GNU发布的较低通用公共许可证的条款
    14  //自由软件基金会,或者许可证的第3版,或者
    15  //(由您选择)任何更高版本。
    16  //
    17  //Go以太坊图书馆的发行目的是希望它会有用,
    18  //但没有任何保证;甚至没有
    19  //适销性或特定用途的适用性。见
    20  //GNU较低的通用公共许可证,了解更多详细信息。
    21  //
    22  //你应该收到一份GNU较低级别的公共许可证副本
    23  //以及Go以太坊图书馆。如果没有,请参见<http://www.gnu.org/licenses/>。
    24  
    25  package miner
    26  
    27  import (
    28  	"math/big"
    29  	"testing"
    30  	"time"
    31  
    32  	"github.com/ethereum/go-ethereum/common"
    33  	"github.com/ethereum/go-ethereum/consensus"
    34  	"github.com/ethereum/go-ethereum/consensus/clique"
    35  	"github.com/ethereum/go-ethereum/consensus/ethash"
    36  	"github.com/ethereum/go-ethereum/core"
    37  	"github.com/ethereum/go-ethereum/core/types"
    38  	"github.com/ethereum/go-ethereum/core/vm"
    39  	"github.com/ethereum/go-ethereum/crypto"
    40  	"github.com/ethereum/go-ethereum/ethdb"
    41  	"github.com/ethereum/go-ethereum/event"
    42  	"github.com/ethereum/go-ethereum/params"
    43  )
    44  
    45  var (
    46  //测试链配置
    47  	testTxPoolConfig  core.TxPoolConfig
    48  	ethashChainConfig *params.ChainConfig
    49  	cliqueChainConfig *params.ChainConfig
    50  
    51  //测试帐目
    52  	testBankKey, _  = crypto.GenerateKey()
    53  	testBankAddress = crypto.PubkeyToAddress(testBankKey.PublicKey)
    54  	testBankFunds   = big.NewInt(1000000000000000000)
    55  
    56  	acc1Key, _ = crypto.GenerateKey()
    57  	acc1Addr   = crypto.PubkeyToAddress(acc1Key.PublicKey)
    58  
    59  //测试事务
    60  	pendingTxs []*types.Transaction
    61  	newTxs     []*types.Transaction
    62  )
    63  
    64  func init() {
    65  	testTxPoolConfig = core.DefaultTxPoolConfig
    66  	testTxPoolConfig.Journal = ""
    67  	ethashChainConfig = params.TestChainConfig
    68  	cliqueChainConfig = params.TestChainConfig
    69  	cliqueChainConfig.Clique = &params.CliqueConfig{
    70  		Period: 10,
    71  		Epoch:  30000,
    72  	}
    73  	tx1, _ := types.SignTx(types.NewTransaction(0, acc1Addr, big.NewInt(1000), params.TxGas, nil, nil), types.HomesteadSigner{}, testBankKey)
    74  	pendingTxs = append(pendingTxs, tx1)
    75  	tx2, _ := types.SignTx(types.NewTransaction(1, acc1Addr, big.NewInt(1000), params.TxGas, nil, nil), types.HomesteadSigner{}, testBankKey)
    76  	newTxs = append(newTxs, tx2)
    77  }
    78  
    79  //TestWorkerBackend实现worker.backend接口,并包装测试期间所需的所有信息。
    80  type testWorkerBackend struct {
    81  	db         ethdb.Database
    82  	txPool     *core.TxPool
    83  	chain      *core.BlockChain
    84  	testTxFeed event.Feed
    85  	uncleBlock *types.Block
    86  }
    87  
    88  func newTestWorkerBackend(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine) *testWorkerBackend {
    89  	var (
    90  		db    = ethdb.NewMemDatabase()
    91  		gspec = core.Genesis{
    92  			Config: chainConfig,
    93  			Alloc:  core.GenesisAlloc{testBankAddress: {Balance: testBankFunds}},
    94  		}
    95  	)
    96  
    97  	switch engine.(type) {
    98  	case *clique.Clique:
    99  		gspec.ExtraData = make([]byte, 32+common.AddressLength+65)
   100  		copy(gspec.ExtraData[32:], testBankAddress[:])
   101  	case *ethash.Ethash:
   102  	default:
   103  		t.Fatal("unexpect consensus engine type")
   104  	}
   105  	genesis := gspec.MustCommit(db)
   106  
   107  	chain, _ := core.NewBlockChain(db, nil, gspec.Config, engine, vm.Config{})
   108  	txpool := core.NewTxPool(testTxPoolConfig, chainConfig, chain)
   109  	blocks, _ := core.GenerateChain(chainConfig, genesis, engine, db, 1, func(i int, gen *core.BlockGen) {
   110  		gen.SetCoinbase(acc1Addr)
   111  	})
   112  
   113  	return &testWorkerBackend{
   114  		db:         db,
   115  		chain:      chain,
   116  		txPool:     txpool,
   117  		uncleBlock: blocks[0],
   118  	}
   119  }
   120  
   121  func (b *testWorkerBackend) BlockChain() *core.BlockChain { return b.chain }
   122  func (b *testWorkerBackend) TxPool() *core.TxPool         { return b.txPool }
   123  func (b *testWorkerBackend) PostChainEvents(events []interface{}) {
   124  	b.chain.PostChainEvents(events, nil)
   125  }
   126  
   127  func newTestWorker(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine) (*worker, *testWorkerBackend) {
   128  	backend := newTestWorkerBackend(t, chainConfig, engine)
   129  	backend.txPool.AddLocals(pendingTxs)
   130  	w := newWorker(chainConfig, engine, backend, new(event.TypeMux), time.Second)
   131  	w.setEtherbase(testBankAddress)
   132  	return w, backend
   133  }
   134  
   135  func TestPendingStateAndBlockEthash(t *testing.T) {
   136  	testPendingStateAndBlock(t, ethashChainConfig, ethash.NewFaker())
   137  }
   138  func TestPendingStateAndBlockClique(t *testing.T) {
   139  	testPendingStateAndBlock(t, cliqueChainConfig, clique.New(cliqueChainConfig.Clique, ethdb.NewMemDatabase()))
   140  }
   141  
   142  func testPendingStateAndBlock(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine) {
   143  	defer engine.Close()
   144  
   145  	w, b := newTestWorker(t, chainConfig, engine)
   146  	defer w.close()
   147  
   148  //确保快照已更新。
   149  	time.Sleep(100 * time.Millisecond)
   150  	block, state := w.pending()
   151  	if block.NumberU64() != 1 {
   152  		t.Errorf("block number mismatch, has %d, want %d", block.NumberU64(), 1)
   153  	}
   154  	if balance := state.GetBalance(acc1Addr); balance.Cmp(big.NewInt(1000)) != 0 {
   155  		t.Errorf("account balance mismatch, has %d, want %d", balance, 1000)
   156  	}
   157  	b.txPool.AddLocals(newTxs)
   158  //确保已处理新的Tx事件
   159  	time.Sleep(100 * time.Millisecond)
   160  	block, state = w.pending()
   161  	if balance := state.GetBalance(acc1Addr); balance.Cmp(big.NewInt(2000)) != 0 {
   162  		t.Errorf("account balance mismatch, has %d, want %d", balance, 2000)
   163  	}
   164  }
   165  
   166  func TestEmptyWorkEthash(t *testing.T) {
   167  	testEmptyWork(t, ethashChainConfig, ethash.NewFaker())
   168  }
   169  func TestEmptyWorkClique(t *testing.T) {
   170  	testEmptyWork(t, cliqueChainConfig, clique.New(cliqueChainConfig.Clique, ethdb.NewMemDatabase()))
   171  }
   172  
   173  func testEmptyWork(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine) {
   174  	defer engine.Close()
   175  
   176  	w, _ := newTestWorker(t, chainConfig, engine)
   177  	defer w.close()
   178  
   179  	var (
   180  		taskCh    = make(chan struct{}, 2)
   181  		taskIndex int
   182  	)
   183  
   184  	checkEqual := func(t *testing.T, task *task, index int) {
   185  		receiptLen, balance := 0, big.NewInt(0)
   186  		if index == 1 {
   187  			receiptLen, balance = 1, big.NewInt(1000)
   188  		}
   189  		if len(task.receipts) != receiptLen {
   190  			t.Errorf("receipt number mismatch has %d, want %d", len(task.receipts), receiptLen)
   191  		}
   192  		if task.state.GetBalance(acc1Addr).Cmp(balance) != 0 {
   193  			t.Errorf("account balance mismatch has %d, want %d", task.state.GetBalance(acc1Addr), balance)
   194  		}
   195  	}
   196  
   197  	w.newTaskHook = func(task *task) {
   198  		if task.block.NumberU64() == 1 {
   199  			checkEqual(t, task, taskIndex)
   200  			taskIndex += 1
   201  			taskCh <- struct{}{}
   202  		}
   203  	}
   204  	w.fullTaskHook = func() {
   205  		time.Sleep(100 * time.Millisecond)
   206  	}
   207  
   208  //确保工作进程已完成初始化
   209  	for {
   210  		b := w.pendingBlock()
   211  		if b != nil && b.NumberU64() == 1 {
   212  			break
   213  		}
   214  	}
   215  
   216  	w.start()
   217  	for i := 0; i < 2; i += 1 {
   218  		select {
   219  		case <-taskCh:
   220  		case <-time.NewTimer(time.Second).C:
   221  			t.Error("new task timeout")
   222  		}
   223  	}
   224  }
   225  
   226  func TestStreamUncleBlock(t *testing.T) {
   227  	ethash := ethash.NewFaker()
   228  	defer ethash.Close()
   229  
   230  	w, b := newTestWorker(t, ethashChainConfig, ethash)
   231  	defer w.close()
   232  
   233  	var taskCh = make(chan struct{})
   234  
   235  	taskIndex := 0
   236  	w.newTaskHook = func(task *task) {
   237  		if task.block.NumberU64() == 1 {
   238  			if taskIndex == 2 {
   239  				has := task.block.Header().UncleHash
   240  				want := types.CalcUncleHash([]*types.Header{b.uncleBlock.Header()})
   241  				if has != want {
   242  					t.Errorf("uncle hash mismatch, has %s, want %s", has.Hex(), want.Hex())
   243  				}
   244  			}
   245  			taskCh <- struct{}{}
   246  			taskIndex += 1
   247  		}
   248  	}
   249  	w.skipSealHook = func(task *task) bool {
   250  		return true
   251  	}
   252  	w.fullTaskHook = func() {
   253  		time.Sleep(100 * time.Millisecond)
   254  	}
   255  
   256  //确保工作进程已完成初始化
   257  	for {
   258  		b := w.pendingBlock()
   259  		if b != nil && b.NumberU64() == 1 {
   260  			break
   261  		}
   262  	}
   263  
   264  	w.start()
   265  //忽略前两个作品
   266  	for i := 0; i < 2; i += 1 {
   267  		select {
   268  		case <-taskCh:
   269  		case <-time.NewTimer(time.Second).C:
   270  			t.Error("new task timeout")
   271  		}
   272  	}
   273  	b.PostChainEvents([]interface{}{core.ChainSideEvent{Block: b.uncleBlock}})
   274  
   275  	select {
   276  	case <-taskCh:
   277  	case <-time.NewTimer(time.Second).C:
   278  		t.Error("new task timeout")
   279  	}
   280  }
   281  
   282  func TestRegenerateMiningBlockEthash(t *testing.T) {
   283  	testRegenerateMiningBlock(t, ethashChainConfig, ethash.NewFaker())
   284  }
   285  
   286  func TestRegenerateMiningBlockClique(t *testing.T) {
   287  	testRegenerateMiningBlock(t, cliqueChainConfig, clique.New(cliqueChainConfig.Clique, ethdb.NewMemDatabase()))
   288  }
   289  
   290  func testRegenerateMiningBlock(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine) {
   291  	defer engine.Close()
   292  
   293  	w, b := newTestWorker(t, chainConfig, engine)
   294  	defer w.close()
   295  
   296  	var taskCh = make(chan struct{})
   297  
   298  	taskIndex := 0
   299  	w.newTaskHook = func(task *task) {
   300  		if task.block.NumberU64() == 1 {
   301  			if taskIndex == 2 {
   302  				receiptLen, balance := 2, big.NewInt(2000)
   303  				if len(task.receipts) != receiptLen {
   304  					t.Errorf("receipt number mismatch has %d, want %d", len(task.receipts), receiptLen)
   305  				}
   306  				if task.state.GetBalance(acc1Addr).Cmp(balance) != 0 {
   307  					t.Errorf("account balance mismatch has %d, want %d", task.state.GetBalance(acc1Addr), balance)
   308  				}
   309  			}
   310  			taskCh <- struct{}{}
   311  			taskIndex += 1
   312  		}
   313  	}
   314  	w.skipSealHook = func(task *task) bool {
   315  		return true
   316  	}
   317  	w.fullTaskHook = func() {
   318  		time.Sleep(100 * time.Millisecond)
   319  	}
   320  //确保工作进程已完成初始化
   321  	for {
   322  		b := w.pendingBlock()
   323  		if b != nil && b.NumberU64() == 1 {
   324  			break
   325  		}
   326  	}
   327  
   328  	w.start()
   329  //忽略前两个作品
   330  	for i := 0; i < 2; i += 1 {
   331  		select {
   332  		case <-taskCh:
   333  		case <-time.NewTimer(time.Second).C:
   334  			t.Error("new task timeout")
   335  		}
   336  	}
   337  	b.txPool.AddLocals(newTxs)
   338  	time.Sleep(time.Second)
   339  
   340  	select {
   341  	case <-taskCh:
   342  	case <-time.NewTimer(time.Second).C:
   343  		t.Error("new task timeout")
   344  	}
   345  }
   346  
   347  func TestAdjustIntervalEthash(t *testing.T) {
   348  	testAdjustInterval(t, ethashChainConfig, ethash.NewFaker())
   349  }
   350  
   351  func TestAdjustIntervalClique(t *testing.T) {
   352  	testAdjustInterval(t, cliqueChainConfig, clique.New(cliqueChainConfig.Clique, ethdb.NewMemDatabase()))
   353  }
   354  
   355  func testAdjustInterval(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine) {
   356  	defer engine.Close()
   357  
   358  	w, _ := newTestWorker(t, chainConfig, engine)
   359  	defer w.close()
   360  
   361  	w.skipSealHook = func(task *task) bool {
   362  		return true
   363  	}
   364  	w.fullTaskHook = func() {
   365  		time.Sleep(100 * time.Millisecond)
   366  	}
   367  	var (
   368  		progress = make(chan struct{}, 10)
   369  		result   = make([]float64, 0, 10)
   370  		index    = 0
   371  		start    = false
   372  	)
   373  	w.resubmitHook = func(minInterval time.Duration, recommitInterval time.Duration) {
   374  //如果间隔检查未开始,则短路。
   375  		if !start {
   376  			return
   377  		}
   378  		var wantMinInterval, wantRecommitInterval time.Duration
   379  
   380  		switch index {
   381  		case 0:
   382  			wantMinInterval, wantRecommitInterval = 3*time.Second, 3*time.Second
   383  		case 1:
   384  			origin := float64(3 * time.Second.Nanoseconds())
   385  			estimate := origin*(1-intervalAdjustRatio) + intervalAdjustRatio*(origin/0.8+intervalAdjustBias)
   386  			wantMinInterval, wantRecommitInterval = 3*time.Second, time.Duration(int(estimate))*time.Nanosecond
   387  		case 2:
   388  			estimate := result[index-1]
   389  			min := float64(3 * time.Second.Nanoseconds())
   390  			estimate = estimate*(1-intervalAdjustRatio) + intervalAdjustRatio*(min-intervalAdjustBias)
   391  			wantMinInterval, wantRecommitInterval = 3*time.Second, time.Duration(int(estimate))*time.Nanosecond
   392  		case 3:
   393  			wantMinInterval, wantRecommitInterval = time.Second, time.Second
   394  		}
   395  
   396  //检查间隔
   397  		if minInterval != wantMinInterval {
   398  			t.Errorf("resubmit min interval mismatch want %s has %s", wantMinInterval, minInterval)
   399  		}
   400  		if recommitInterval != wantRecommitInterval {
   401  			t.Errorf("resubmit interval mismatch want %s has %s", wantRecommitInterval, recommitInterval)
   402  		}
   403  		result = append(result, float64(recommitInterval.Nanoseconds()))
   404  		index += 1
   405  		progress <- struct{}{}
   406  	}
   407  //确保工作进程已完成初始化
   408  	for {
   409  		b := w.pendingBlock()
   410  		if b != nil && b.NumberU64() == 1 {
   411  			break
   412  		}
   413  	}
   414  
   415  	w.start()
   416  
   417  	time.Sleep(time.Second)
   418  
   419  	start = true
   420  	w.setRecommitInterval(3 * time.Second)
   421  	select {
   422  	case <-progress:
   423  	case <-time.NewTimer(time.Second).C:
   424  		t.Error("interval reset timeout")
   425  	}
   426  
   427  	w.resubmitAdjustCh <- &intervalAdjust{inc: true, ratio: 0.8}
   428  	select {
   429  	case <-progress:
   430  	case <-time.NewTimer(time.Second).C:
   431  		t.Error("interval reset timeout")
   432  	}
   433  
   434  	w.resubmitAdjustCh <- &intervalAdjust{inc: false}
   435  	select {
   436  	case <-progress:
   437  	case <-time.NewTimer(time.Second).C:
   438  		t.Error("interval reset timeout")
   439  	}
   440  
   441  	w.setRecommitInterval(500 * time.Millisecond)
   442  	select {
   443  	case <-progress:
   444  	case <-time.NewTimer(time.Second).C:
   445  		t.Error("interval reset timeout")
   446  	}
   447  }