github.com/etclabscore/ethereum.go-ethereum@v1.8.15/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/ethereum/go-ethereum/common" 25 "github.com/ethereum/go-ethereum/consensus" 26 "github.com/ethereum/go-ethereum/consensus/clique" 27 "github.com/ethereum/go-ethereum/consensus/ethash" 28 "github.com/ethereum/go-ethereum/core" 29 "github.com/ethereum/go-ethereum/core/types" 30 "github.com/ethereum/go-ethereum/core/vm" 31 "github.com/ethereum/go-ethereum/crypto" 32 "github.com/ethereum/go-ethereum/ethdb" 33 "github.com/ethereum/go-ethereum/event" 34 "github.com/ethereum/go-ethereum/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 testUserKey, _ = crypto.GenerateKey() 49 testUserAddress = crypto.PubkeyToAddress(testUserKey.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 = ¶ms.CliqueConfig{ 62 Period: 10, 63 Epoch: 30000, 64 } 65 tx1, _ := types.SignTx(types.NewTransaction(0, testUserAddress, big.NewInt(1000), params.TxGas, nil, nil), types.HomesteadSigner{}, testBankKey) 66 pendingTxs = append(pendingTxs, tx1) 67 tx2, _ := types.SignTx(types.NewTransaction(1, testUserAddress, 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, n int) *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.Fatalf("unexpected consensus engine type: %T", engine) 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 102 // Generate a small n-block chain and an uncle block for it 103 if n > 0 { 104 blocks, _ := core.GenerateChain(chainConfig, genesis, engine, db, n, func(i int, gen *core.BlockGen) { 105 gen.SetCoinbase(testBankAddress) 106 }) 107 if _, err := chain.InsertChain(blocks); err != nil { 108 t.Fatalf("failed to insert origin chain: %v", err) 109 } 110 } 111 parent := genesis 112 if n > 0 { 113 parent = chain.GetBlockByHash(chain.CurrentBlock().ParentHash()) 114 } 115 blocks, _ := core.GenerateChain(chainConfig, parent, engine, db, 1, func(i int, gen *core.BlockGen) { 116 gen.SetCoinbase(testUserAddress) 117 }) 118 119 return &testWorkerBackend{ 120 db: db, 121 chain: chain, 122 txPool: txpool, 123 uncleBlock: blocks[0], 124 } 125 } 126 127 func (b *testWorkerBackend) BlockChain() *core.BlockChain { return b.chain } 128 func (b *testWorkerBackend) TxPool() *core.TxPool { return b.txPool } 129 func (b *testWorkerBackend) PostChainEvents(events []interface{}) { 130 b.chain.PostChainEvents(events, nil) 131 } 132 133 func newTestWorker(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine, blocks int) (*worker, *testWorkerBackend) { 134 backend := newTestWorkerBackend(t, chainConfig, engine, blocks) 135 backend.txPool.AddLocals(pendingTxs) 136 w := newWorker(chainConfig, engine, backend, new(event.TypeMux), time.Second, params.GenesisGasLimit, params.GenesisGasLimit) 137 w.setEtherbase(testBankAddress) 138 return w, backend 139 } 140 141 func TestPendingStateAndBlockEthash(t *testing.T) { 142 testPendingStateAndBlock(t, ethashChainConfig, ethash.NewFaker()) 143 } 144 func TestPendingStateAndBlockClique(t *testing.T) { 145 testPendingStateAndBlock(t, cliqueChainConfig, clique.New(cliqueChainConfig.Clique, ethdb.NewMemDatabase())) 146 } 147 148 func testPendingStateAndBlock(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine) { 149 defer engine.Close() 150 151 w, b := newTestWorker(t, chainConfig, engine, 0) 152 defer w.close() 153 154 // Ensure snapshot has been updated. 155 time.Sleep(100 * time.Millisecond) 156 block, state := w.pending() 157 if block.NumberU64() != 1 { 158 t.Errorf("block number mismatch: have %d, want %d", block.NumberU64(), 1) 159 } 160 if balance := state.GetBalance(testUserAddress); balance.Cmp(big.NewInt(1000)) != 0 { 161 t.Errorf("account balance mismatch: have %d, want %d", balance, 1000) 162 } 163 b.txPool.AddLocals(newTxs) 164 // Ensure the new tx events has been processed 165 time.Sleep(100 * time.Millisecond) 166 block, state = w.pending() 167 if balance := state.GetBalance(testUserAddress); balance.Cmp(big.NewInt(2000)) != 0 { 168 t.Errorf("account balance mismatch: have %d, want %d", balance, 2000) 169 } 170 } 171 172 func TestEmptyWorkEthash(t *testing.T) { 173 testEmptyWork(t, ethashChainConfig, ethash.NewFaker()) 174 } 175 func TestEmptyWorkClique(t *testing.T) { 176 testEmptyWork(t, cliqueChainConfig, clique.New(cliqueChainConfig.Clique, ethdb.NewMemDatabase())) 177 } 178 179 func testEmptyWork(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine) { 180 defer engine.Close() 181 182 w, _ := newTestWorker(t, chainConfig, engine, 0) 183 defer w.close() 184 185 var ( 186 taskCh = make(chan struct{}, 2) 187 taskIndex int 188 ) 189 190 checkEqual := func(t *testing.T, task *task, index int) { 191 receiptLen, balance := 0, big.NewInt(0) 192 if index == 1 { 193 receiptLen, balance = 1, big.NewInt(1000) 194 } 195 if len(task.receipts) != receiptLen { 196 t.Errorf("receipt number mismatch: have %d, want %d", len(task.receipts), receiptLen) 197 } 198 if task.state.GetBalance(testUserAddress).Cmp(balance) != 0 { 199 t.Errorf("account balance mismatch: have %d, want %d", task.state.GetBalance(testUserAddress), balance) 200 } 201 } 202 203 w.newTaskHook = func(task *task) { 204 if task.block.NumberU64() == 1 { 205 checkEqual(t, task, taskIndex) 206 taskIndex += 1 207 taskCh <- struct{}{} 208 } 209 } 210 w.fullTaskHook = func() { 211 time.Sleep(100 * time.Millisecond) 212 } 213 214 // Ensure worker has finished initialization 215 for { 216 b := w.pendingBlock() 217 if b != nil && b.NumberU64() == 1 { 218 break 219 } 220 } 221 222 w.start() 223 for i := 0; i < 2; i += 1 { 224 select { 225 case <-taskCh: 226 case <-time.NewTimer(time.Second).C: 227 t.Error("new task timeout") 228 } 229 } 230 } 231 232 func TestStreamUncleBlock(t *testing.T) { 233 ethash := ethash.NewFaker() 234 defer ethash.Close() 235 236 w, b := newTestWorker(t, ethashChainConfig, ethash, 1) 237 defer w.close() 238 239 var taskCh = make(chan struct{}) 240 241 taskIndex := 0 242 w.newTaskHook = func(task *task) { 243 if task.block.NumberU64() == 2 { 244 if taskIndex == 2 { 245 have := task.block.Header().UncleHash 246 want := types.CalcUncleHash([]*types.Header{b.uncleBlock.Header()}) 247 if have != want { 248 t.Errorf("uncle hash mismatch: have %s, want %s", have.Hex(), want.Hex()) 249 } 250 } 251 taskCh <- struct{}{} 252 taskIndex += 1 253 } 254 } 255 w.skipSealHook = func(task *task) bool { 256 return true 257 } 258 w.fullTaskHook = func() { 259 time.Sleep(100 * time.Millisecond) 260 } 261 262 // Ensure worker has finished initialization 263 for { 264 b := w.pendingBlock() 265 if b != nil && b.NumberU64() == 2 { 266 break 267 } 268 } 269 w.start() 270 271 // Ignore the first two works 272 for i := 0; i < 2; i += 1 { 273 select { 274 case <-taskCh: 275 case <-time.NewTimer(time.Second).C: 276 t.Error("new task timeout") 277 } 278 } 279 b.PostChainEvents([]interface{}{core.ChainSideEvent{Block: b.uncleBlock}}) 280 281 select { 282 case <-taskCh: 283 case <-time.NewTimer(time.Second).C: 284 t.Error("new task timeout") 285 } 286 } 287 288 func TestRegenerateMiningBlockEthash(t *testing.T) { 289 testRegenerateMiningBlock(t, ethashChainConfig, ethash.NewFaker()) 290 } 291 292 func TestRegenerateMiningBlockClique(t *testing.T) { 293 testRegenerateMiningBlock(t, cliqueChainConfig, clique.New(cliqueChainConfig.Clique, ethdb.NewMemDatabase())) 294 } 295 296 func testRegenerateMiningBlock(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine) { 297 defer engine.Close() 298 299 w, b := newTestWorker(t, chainConfig, engine, 0) 300 defer w.close() 301 302 var taskCh = make(chan struct{}) 303 304 taskIndex := 0 305 w.newTaskHook = func(task *task) { 306 if task.block.NumberU64() == 1 { 307 if taskIndex == 2 { 308 receiptLen, balance := 2, big.NewInt(2000) 309 if len(task.receipts) != receiptLen { 310 t.Errorf("receipt number mismatch: have %d, want %d", len(task.receipts), receiptLen) 311 } 312 if task.state.GetBalance(testUserAddress).Cmp(balance) != 0 { 313 t.Errorf("account balance mismatch: have %d, want %d", task.state.GetBalance(testUserAddress), balance) 314 } 315 } 316 taskCh <- struct{}{} 317 taskIndex += 1 318 } 319 } 320 w.skipSealHook = func(task *task) bool { 321 return true 322 } 323 w.fullTaskHook = func() { 324 time.Sleep(100 * time.Millisecond) 325 } 326 // Ensure worker has finished initialization 327 for { 328 b := w.pendingBlock() 329 if b != nil && b.NumberU64() == 1 { 330 break 331 } 332 } 333 334 w.start() 335 // Ignore the first two works 336 for i := 0; i < 2; i += 1 { 337 select { 338 case <-taskCh: 339 case <-time.NewTimer(time.Second).C: 340 t.Error("new task timeout") 341 } 342 } 343 b.txPool.AddLocals(newTxs) 344 time.Sleep(time.Second) 345 346 select { 347 case <-taskCh: 348 case <-time.NewTimer(time.Second).C: 349 t.Error("new task timeout") 350 } 351 } 352 353 func TestAdjustIntervalEthash(t *testing.T) { 354 testAdjustInterval(t, ethashChainConfig, ethash.NewFaker()) 355 } 356 357 func TestAdjustIntervalClique(t *testing.T) { 358 testAdjustInterval(t, cliqueChainConfig, clique.New(cliqueChainConfig.Clique, ethdb.NewMemDatabase())) 359 } 360 361 func testAdjustInterval(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine) { 362 defer engine.Close() 363 364 w, _ := newTestWorker(t, chainConfig, engine, 0) 365 defer w.close() 366 367 w.skipSealHook = func(task *task) bool { 368 return true 369 } 370 w.fullTaskHook = func() { 371 time.Sleep(100 * time.Millisecond) 372 } 373 var ( 374 progress = make(chan struct{}, 10) 375 result = make([]float64, 0, 10) 376 index = 0 377 start = false 378 ) 379 w.resubmitHook = func(minInterval time.Duration, recommitInterval time.Duration) { 380 // Short circuit if interval checking hasn't started. 381 if !start { 382 return 383 } 384 var wantMinInterval, wantRecommitInterval time.Duration 385 386 switch index { 387 case 0: 388 wantMinInterval, wantRecommitInterval = 3*time.Second, 3*time.Second 389 case 1: 390 origin := float64(3 * time.Second.Nanoseconds()) 391 estimate := origin*(1-intervalAdjustRatio) + intervalAdjustRatio*(origin/0.8+intervalAdjustBias) 392 wantMinInterval, wantRecommitInterval = 3*time.Second, time.Duration(int(estimate))*time.Nanosecond 393 case 2: 394 estimate := result[index-1] 395 min := float64(3 * time.Second.Nanoseconds()) 396 estimate = estimate*(1-intervalAdjustRatio) + intervalAdjustRatio*(min-intervalAdjustBias) 397 wantMinInterval, wantRecommitInterval = 3*time.Second, time.Duration(int(estimate))*time.Nanosecond 398 case 3: 399 wantMinInterval, wantRecommitInterval = time.Second, time.Second 400 } 401 402 // Check interval 403 if minInterval != wantMinInterval { 404 t.Errorf("resubmit min interval mismatch: have %v, want %v ", minInterval, wantMinInterval) 405 } 406 if recommitInterval != wantRecommitInterval { 407 t.Errorf("resubmit interval mismatch: have %v, want %v", recommitInterval, wantRecommitInterval) 408 } 409 result = append(result, float64(recommitInterval.Nanoseconds())) 410 index += 1 411 progress <- struct{}{} 412 } 413 // Ensure worker has finished initialization 414 for { 415 b := w.pendingBlock() 416 if b != nil && b.NumberU64() == 1 { 417 break 418 } 419 } 420 421 w.start() 422 423 time.Sleep(time.Second) 424 425 start = true 426 w.setRecommitInterval(3 * time.Second) 427 select { 428 case <-progress: 429 case <-time.NewTimer(time.Second).C: 430 t.Error("interval reset timeout") 431 } 432 433 w.resubmitAdjustCh <- &intervalAdjust{inc: true, ratio: 0.8} 434 select { 435 case <-progress: 436 case <-time.NewTimer(time.Second).C: 437 t.Error("interval reset timeout") 438 } 439 440 w.resubmitAdjustCh <- &intervalAdjust{inc: false} 441 select { 442 case <-progress: 443 case <-time.NewTimer(time.Second).C: 444 t.Error("interval reset timeout") 445 } 446 447 w.setRecommitInterval(500 * time.Millisecond) 448 select { 449 case <-progress: 450 case <-time.NewTimer(time.Second).C: 451 t.Error("interval reset timeout") 452 } 453 }