github.com/ebceco/ebc@v1.8.19-0.20190309150932-8cb0b9e06484/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/ebceco/ebc/common" 25 "github.com/ebceco/ebc/consensus" 26 "github.com/ebceco/ebc/consensus/clique" 27 "github.com/ebceco/ebc/consensus/ethash" 28 "github.com/ebceco/ebc/core" 29 "github.com/ebceco/ebc/core/types" 30 "github.com/ebceco/ebc/core/vm" 31 "github.com/ebceco/ebc/crypto" 32 "github.com/ebceco/ebc/ethdb" 33 "github.com/ebceco/ebc/event" 34 "github.com/ebceco/ebc/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{}, nil) 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, nil) 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 165 // Ensure the new tx events has been processed 166 time.Sleep(100 * time.Millisecond) 167 block, state = w.pending() 168 if balance := state.GetBalance(testUserAddress); balance.Cmp(big.NewInt(2000)) != 0 { 169 t.Errorf("account balance mismatch: have %d, want %d", balance, 2000) 170 } 171 } 172 173 func TestEmptyWorkEthash(t *testing.T) { 174 testEmptyWork(t, ethashChainConfig, ethash.NewFaker()) 175 } 176 func TestEmptyWorkClique(t *testing.T) { 177 testEmptyWork(t, cliqueChainConfig, clique.New(cliqueChainConfig.Clique, ethdb.NewMemDatabase())) 178 } 179 180 func testEmptyWork(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine) { 181 defer engine.Close() 182 183 w, _ := newTestWorker(t, chainConfig, engine, 0) 184 defer w.close() 185 186 var ( 187 taskCh = make(chan struct{}, 2) 188 taskIndex int 189 ) 190 191 checkEqual := func(t *testing.T, task *task, index int) { 192 receiptLen, balance := 0, big.NewInt(0) 193 if index == 1 { 194 receiptLen, balance = 1, big.NewInt(1000) 195 } 196 if len(task.receipts) != receiptLen { 197 t.Errorf("receipt number mismatch: have %d, want %d", len(task.receipts), receiptLen) 198 } 199 if task.state.GetBalance(testUserAddress).Cmp(balance) != 0 { 200 t.Errorf("account balance mismatch: have %d, want %d", task.state.GetBalance(testUserAddress), balance) 201 } 202 } 203 204 w.newTaskHook = func(task *task) { 205 if task.block.NumberU64() == 1 { 206 checkEqual(t, task, taskIndex) 207 taskIndex += 1 208 taskCh <- struct{}{} 209 } 210 } 211 w.fullTaskHook = func() { 212 time.Sleep(100 * time.Millisecond) 213 } 214 215 // Ensure worker has finished initialization 216 for { 217 b := w.pendingBlock() 218 if b != nil && b.NumberU64() == 1 { 219 break 220 } 221 } 222 223 w.start() 224 for i := 0; i < 2; i += 1 { 225 select { 226 case <-taskCh: 227 case <-time.NewTimer(time.Second).C: 228 t.Error("new task timeout") 229 } 230 } 231 } 232 233 func TestStreamUncleBlock(t *testing.T) { 234 ethash := ethash.NewFaker() 235 defer ethash.Close() 236 237 w, b := newTestWorker(t, ethashChainConfig, ethash, 1) 238 defer w.close() 239 240 var taskCh = make(chan struct{}) 241 242 taskIndex := 0 243 w.newTaskHook = func(task *task) { 244 if task.block.NumberU64() == 2 { 245 if taskIndex == 2 { 246 have := task.block.Header().UncleHash 247 want := types.CalcUncleHash([]*types.Header{b.uncleBlock.Header()}) 248 if have != want { 249 t.Errorf("uncle hash mismatch: have %s, want %s", have.Hex(), want.Hex()) 250 } 251 } 252 taskCh <- struct{}{} 253 taskIndex += 1 254 } 255 } 256 w.skipSealHook = func(task *task) bool { 257 return true 258 } 259 w.fullTaskHook = func() { 260 time.Sleep(100 * time.Millisecond) 261 } 262 263 // Ensure worker has finished initialization 264 for { 265 b := w.pendingBlock() 266 if b != nil && b.NumberU64() == 2 { 267 break 268 } 269 } 270 w.start() 271 272 // Ignore the first two works 273 for i := 0; i < 2; i += 1 { 274 select { 275 case <-taskCh: 276 case <-time.NewTimer(time.Second).C: 277 t.Error("new task timeout") 278 } 279 } 280 b.PostChainEvents([]interface{}{core.ChainSideEvent{Block: b.uncleBlock}}) 281 282 select { 283 case <-taskCh: 284 case <-time.NewTimer(time.Second).C: 285 t.Error("new task timeout") 286 } 287 } 288 289 func TestRegenerateMiningBlockEthash(t *testing.T) { 290 testRegenerateMiningBlock(t, ethashChainConfig, ethash.NewFaker()) 291 } 292 293 func TestRegenerateMiningBlockClique(t *testing.T) { 294 testRegenerateMiningBlock(t, cliqueChainConfig, clique.New(cliqueChainConfig.Clique, ethdb.NewMemDatabase())) 295 } 296 297 func testRegenerateMiningBlock(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine) { 298 defer engine.Close() 299 300 w, b := newTestWorker(t, chainConfig, engine, 0) 301 defer w.close() 302 303 var taskCh = make(chan struct{}) 304 305 taskIndex := 0 306 w.newTaskHook = func(task *task) { 307 if task.block.NumberU64() == 1 { 308 if taskIndex == 2 { 309 receiptLen, balance := 2, big.NewInt(2000) 310 if len(task.receipts) != receiptLen { 311 t.Errorf("receipt number mismatch: have %d, want %d", len(task.receipts), receiptLen) 312 } 313 if task.state.GetBalance(testUserAddress).Cmp(balance) != 0 { 314 t.Errorf("account balance mismatch: have %d, want %d", task.state.GetBalance(testUserAddress), balance) 315 } 316 } 317 taskCh <- struct{}{} 318 taskIndex += 1 319 } 320 } 321 w.skipSealHook = func(task *task) bool { 322 return true 323 } 324 w.fullTaskHook = func() { 325 time.Sleep(100 * time.Millisecond) 326 } 327 // Ensure worker has finished initialization 328 for { 329 b := w.pendingBlock() 330 if b != nil && b.NumberU64() == 1 { 331 break 332 } 333 } 334 335 w.start() 336 // Ignore the first two works 337 for i := 0; i < 2; i += 1 { 338 select { 339 case <-taskCh: 340 case <-time.NewTimer(time.Second).C: 341 t.Error("new task timeout") 342 } 343 } 344 b.txPool.AddLocals(newTxs) 345 time.Sleep(time.Second) 346 347 select { 348 case <-taskCh: 349 case <-time.NewTimer(time.Second).C: 350 t.Error("new task timeout") 351 } 352 } 353 354 func TestAdjustIntervalEthash(t *testing.T) { 355 testAdjustInterval(t, ethashChainConfig, ethash.NewFaker()) 356 } 357 358 func TestAdjustIntervalClique(t *testing.T) { 359 testAdjustInterval(t, cliqueChainConfig, clique.New(cliqueChainConfig.Clique, ethdb.NewMemDatabase())) 360 } 361 362 func testAdjustInterval(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine) { 363 defer engine.Close() 364 365 w, _ := newTestWorker(t, chainConfig, engine, 0) 366 defer w.close() 367 368 w.skipSealHook = func(task *task) bool { 369 return true 370 } 371 w.fullTaskHook = func() { 372 time.Sleep(100 * time.Millisecond) 373 } 374 var ( 375 progress = make(chan struct{}, 10) 376 result = make([]float64, 0, 10) 377 index = 0 378 start = false 379 ) 380 w.resubmitHook = func(minInterval time.Duration, recommitInterval time.Duration) { 381 // Short circuit if interval checking hasn't started. 382 if !start { 383 return 384 } 385 var wantMinInterval, wantRecommitInterval time.Duration 386 387 switch index { 388 case 0: 389 wantMinInterval, wantRecommitInterval = 3*time.Second, 3*time.Second 390 case 1: 391 origin := float64(3 * time.Second.Nanoseconds()) 392 estimate := origin*(1-intervalAdjustRatio) + intervalAdjustRatio*(origin/0.8+intervalAdjustBias) 393 wantMinInterval, wantRecommitInterval = 3*time.Second, time.Duration(estimate)*time.Nanosecond 394 case 2: 395 estimate := result[index-1] 396 min := float64(3 * time.Second.Nanoseconds()) 397 estimate = estimate*(1-intervalAdjustRatio) + intervalAdjustRatio*(min-intervalAdjustBias) 398 wantMinInterval, wantRecommitInterval = 3*time.Second, time.Duration(estimate)*time.Nanosecond 399 case 3: 400 wantMinInterval, wantRecommitInterval = time.Second, time.Second 401 } 402 403 // Check interval 404 if minInterval != wantMinInterval { 405 t.Errorf("resubmit min interval mismatch: have %v, want %v ", minInterval, wantMinInterval) 406 } 407 if recommitInterval != wantRecommitInterval { 408 t.Errorf("resubmit interval mismatch: have %v, want %v", recommitInterval, wantRecommitInterval) 409 } 410 result = append(result, float64(recommitInterval.Nanoseconds())) 411 index += 1 412 progress <- struct{}{} 413 } 414 // Ensure worker has finished initialization 415 for { 416 b := w.pendingBlock() 417 if b != nil && b.NumberU64() == 1 { 418 break 419 } 420 } 421 422 w.start() 423 424 time.Sleep(time.Second) 425 426 start = true 427 w.setRecommitInterval(3 * time.Second) 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: true, ratio: 0.8} 435 select { 436 case <-progress: 437 case <-time.NewTimer(time.Second).C: 438 t.Error("interval reset timeout") 439 } 440 441 w.resubmitAdjustCh <- &intervalAdjust{inc: false} 442 select { 443 case <-progress: 444 case <-time.NewTimer(time.Second).C: 445 t.Error("interval reset timeout") 446 } 447 448 w.setRecommitInterval(500 * time.Millisecond) 449 select { 450 case <-progress: 451 case <-time.NewTimer(time.Second).C: 452 t.Error("interval reset timeout") 453 } 454 }