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