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