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