github.com/core-coin/go-core/v2@v2.1.9/miner/worker_test.go (about) 1 // Copyright 2018 by the Authors 2 // This file is part of the go-core library. 3 // 4 // The go-core 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-core 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-core library. If not, see <http://www.gnu.org/licenses/>. 16 17 package miner 18 19 import ( 20 crand "crypto/rand" 21 "math/big" 22 "math/rand" 23 "sync/atomic" 24 "testing" 25 "time" 26 27 "github.com/core-coin/go-core/v2/xcbdb" 28 29 "github.com/core-coin/go-core/v2/consensus/cryptore" 30 31 "github.com/core-coin/go-core/v2/accounts" 32 "github.com/core-coin/go-core/v2/common" 33 "github.com/core-coin/go-core/v2/consensus" 34 "github.com/core-coin/go-core/v2/consensus/clique" 35 "github.com/core-coin/go-core/v2/core" 36 "github.com/core-coin/go-core/v2/core/rawdb" 37 "github.com/core-coin/go-core/v2/core/types" 38 "github.com/core-coin/go-core/v2/core/vm" 39 "github.com/core-coin/go-core/v2/crypto" 40 "github.com/core-coin/go-core/v2/event" 41 "github.com/core-coin/go-core/v2/params" 42 ) 43 44 const ( 45 // testCode is the testing contract binary code which will initialises some 46 // variables in constructor 47 testCode = "0x60806040527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0060005534801561003457600080fd5b5060fc806100436000396000f3fe6080604052348015600f57600080fd5b506004361060325760003560e01c80630c4dae8814603757806398a213cf146053575b600080fd5b603d607e565b6040518082815260200191505060405180910390f35b607c60048036036020811015606757600080fd5b81019080803590602001909291905050506084565b005b60005481565b806000819055507fe9e44f9f7da8c559de847a3232b57364adc0354f15a2cd8dc636d54396f9587a6000546040518082815260200191505060405180910390a15056fea265627a7a723058208ae31d9424f2d0bc2a3da1a5dd659db2d71ec322a17db8f87e19e209e3a1ff4a64736f6c634300050a0032" 48 49 // testEnergy is the energy required for contract deployment. 50 testEnergy = 144109 51 ) 52 53 var ( 54 // Test chain configurations 55 testTxPoolConfig core.TxPoolConfig 56 cryptoreChainConfig *params.ChainConfig 57 cliqueChainConfig *params.ChainConfig 58 59 // Test accounts 60 testBankKey, _ = crypto.GenerateKey(crand.Reader) 61 testBankFunds = big.NewInt(1000000000000000000) 62 63 testUserKey, _ = crypto.GenerateKey(crand.Reader) 64 65 // Test transactions 66 pendingTxs []*types.Transaction 67 newTxs []*types.Transaction 68 69 testConfig = &Config{ 70 Recommit: time.Second, 71 EnergyFloor: params.GenesisEnergyLimit, 72 EnergyCeil: params.GenesisEnergyLimit, 73 } 74 ) 75 76 func init() { 77 testTxPoolConfig = core.DefaultTxPoolConfig 78 testTxPoolConfig.Journal = "" 79 cryptoreChainConfig = params.MainnetChainConfig 80 cliqueChainConfig = params.MainnetChainConfig 81 cliqueChainConfig.Clique = ¶ms.CliqueConfig{ 82 Period: 10, 83 Epoch: 30000, 84 } 85 tx1, _ := types.SignTx(types.NewTransaction(0, testUserKey.Address(), big.NewInt(1000), params.TxEnergy, nil, nil), types.NewNucleusSigner(params.MainnetChainConfig.NetworkID), testBankKey) 86 pendingTxs = append(pendingTxs, tx1) 87 tx2, _ := types.SignTx(types.NewTransaction(1, testUserKey.Address(), big.NewInt(1000), params.TxEnergy, nil, nil), types.NewNucleusSigner(params.MainnetChainConfig.NetworkID), testBankKey) 88 newTxs = append(newTxs, tx2) 89 rand.Seed(time.Now().UnixNano()) 90 } 91 92 // testWorkerBackend implements worker.Backend interfaces and wraps all information needed during the testing. 93 type testWorkerBackend struct { 94 db xcbdb.Database 95 txPool *core.TxPool 96 chain *core.BlockChain 97 testTxFeed event.Feed 98 genesis *core.Genesis 99 uncleBlock *types.Block 100 } 101 102 func newTestWorkerBackend(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine, db xcbdb.Database, n int) *testWorkerBackend { 103 var gspec = core.Genesis{ 104 Config: chainConfig, 105 Alloc: core.GenesisAlloc{testBankKey.Address(): {Balance: testBankFunds}}, 106 } 107 108 switch e := engine.(type) { 109 case *clique.Clique: 110 gspec.ExtraData = make([]byte, 32+common.AddressLength+crypto.ExtendedSignatureLength) 111 copy(gspec.ExtraData[32:32+common.AddressLength], testBankKey.Address().Bytes()) 112 e.Authorize(testBankKey.Address(), func(account accounts.Account, s string, data []byte) ([]byte, error) { 113 return crypto.Sign(crypto.SHA3(data), testBankKey) 114 }) 115 case *cryptore.Cryptore: 116 default: 117 t.Fatalf("unexpected consensus engine type: %T", engine) 118 } 119 genesis := gspec.MustCommit(db) 120 121 chain, _ := core.NewBlockChain(db, &core.CacheConfig{TrieDirtyDisabled: true}, gspec.Config, engine, vm.Config{}, nil, nil) 122 txpool := core.NewTxPool(testTxPoolConfig, chainConfig, chain) 123 124 // Generate a small n-block chain and an uncle block for it 125 if n > 0 { 126 blocks, _ := core.GenerateChain(chainConfig, genesis, engine, db, n, func(i int, gen *core.BlockGen) { 127 gen.SetCoinbase(testBankKey.Address()) 128 }) 129 if _, err := chain.InsertChain(blocks); err != nil { 130 t.Fatalf("failed to insert origin chain: %v", err) 131 } 132 } 133 parent := genesis 134 if n > 0 { 135 parent = chain.GetBlockByHash(chain.CurrentBlock().ParentHash()) 136 } 137 blocks, _ := core.GenerateChain(chainConfig, parent, engine, db, 1, func(i int, gen *core.BlockGen) { 138 gen.SetCoinbase(testUserKey.Address()) 139 }) 140 141 return &testWorkerBackend{ 142 db: db, 143 chain: chain, 144 txPool: txpool, 145 genesis: &gspec, 146 uncleBlock: blocks[0], 147 } 148 } 149 150 func (b *testWorkerBackend) BlockChain() *core.BlockChain { return b.chain } 151 func (b *testWorkerBackend) TxPool() *core.TxPool { return b.txPool } 152 153 func (b *testWorkerBackend) newRandomUncle() *types.Block { 154 var parent *types.Block 155 cur := b.chain.CurrentBlock() 156 if cur.NumberU64() == 0 { 157 parent = b.chain.Genesis() 158 } else { 159 parent = b.chain.GetBlockByHash(b.chain.CurrentBlock().ParentHash()) 160 } 161 blocks, _ := core.GenerateChain(b.chain.Config(), parent, b.chain.Engine(), b.db, 1, func(i int, gen *core.BlockGen) { 162 var addr = make([]byte, common.AddressLength) 163 rand.Read(addr) 164 gen.SetCoinbase(common.BytesToAddress(addr)) 165 }) 166 return blocks[0] 167 } 168 169 func (b *testWorkerBackend) newRandomTx(creation bool) *types.Transaction { 170 var tx *types.Transaction 171 if creation { 172 tx, _ = types.SignTx(types.NewContractCreation(b.txPool.Nonce(testBankKey.Address()), big.NewInt(0), testEnergy, nil, common.FromHex(testCode)), types.NewNucleusSigner(params.MainnetChainConfig.NetworkID), testBankKey) 173 } else { 174 tx, _ = types.SignTx(types.NewTransaction(b.txPool.Nonce(testBankKey.Address()), testUserKey.Address(), big.NewInt(1000), params.TxEnergy, nil, nil), types.NewNucleusSigner(params.MainnetChainConfig.NetworkID), testBankKey) 175 } 176 return tx 177 } 178 179 func newTestWorker(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine, db xcbdb.Database, blocks int) (*worker, *testWorkerBackend) { 180 backend := newTestWorkerBackend(t, chainConfig, engine, db, blocks) 181 backend.txPool.AddLocals(pendingTxs) 182 w := newWorker(testConfig, chainConfig, engine, backend, new(event.TypeMux), nil, false) 183 w.setCorebase(testBankKey.Address()) 184 return w, backend 185 } 186 187 func TestGenerateBlockAndImportCryptore(t *testing.T) { 188 testGenerateBlockAndImport(t, false) 189 } 190 191 func TestGenerateBlockAndImportClique(t *testing.T) { 192 testGenerateBlockAndImport(t, true) 193 } 194 195 func testGenerateBlockAndImport(t *testing.T, isClique bool) { 196 var ( 197 engine consensus.Engine 198 chainConfig *params.ChainConfig 199 db = rawdb.NewMemoryDatabase() 200 ) 201 if isClique { 202 chainConfig = params.MainnetChainConfig 203 chainConfig.Clique = ¶ms.CliqueConfig{Period: 1, Epoch: 30000} 204 engine = clique.New(chainConfig.Clique, db) 205 } else { 206 chainConfig = params.MainnetChainConfig 207 engine = cryptore.NewFaker() 208 } 209 210 w, b := newTestWorker(t, chainConfig, engine, db, 0) 211 defer w.close() 212 213 // This test chain imports the mined blocks. 214 db2 := rawdb.NewMemoryDatabase() 215 b.genesis.MustCommit(db2) 216 chain, _ := core.NewBlockChain(db2, nil, b.chain.Config(), engine, vm.Config{}, nil, nil) 217 defer chain.Stop() 218 219 // Ignore empty commit here for less noise. 220 w.skipSealHook = func(task *task) bool { 221 return len(task.receipts) == 0 222 } 223 224 // Wait for mined blocks. 225 sub := w.mux.Subscribe(core.NewMinedBlockEvent{}) 226 defer sub.Unsubscribe() 227 228 // Start mining! 229 w.start() 230 231 for i := 0; i < 5; i++ { 232 b.txPool.AddLocal(b.newRandomTx(true)) 233 b.txPool.AddLocal(b.newRandomTx(false)) 234 w.postSideBlock(core.ChainSideEvent{Block: b.newRandomUncle()}) 235 w.postSideBlock(core.ChainSideEvent{Block: b.newRandomUncle()}) 236 237 select { 238 case ev := <-sub.Chan(): 239 block := ev.Data.(core.NewMinedBlockEvent).Block 240 if _, err := chain.InsertChain([]*types.Block{block}); err != nil { 241 t.Fatalf("failed to insert new mined block %d: %v", block.NumberU64(), err) 242 } 243 case <-time.After(3 * time.Second): // Worker needs 1s to include new changes. 244 t.Fatalf("timeout") 245 } 246 } 247 } 248 249 func TestEmptyWorkCryptore(t *testing.T) { 250 testEmptyWork(t, cryptoreChainConfig, cryptore.NewFaker()) 251 } 252 func TestEmptyWorkClique(t *testing.T) { 253 testEmptyWork(t, cliqueChainConfig, clique.New(cliqueChainConfig.Clique, rawdb.NewMemoryDatabase())) 254 } 255 256 func testEmptyWork(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine) { 257 defer engine.Close() 258 259 w, _ := newTestWorker(t, chainConfig, engine, rawdb.NewMemoryDatabase(), 0) 260 defer w.close() 261 262 var ( 263 taskIndex int 264 taskCh = make(chan struct{}, 2) 265 ) 266 checkEqual := func(t *testing.T, task *task, index int) { 267 // The first empty work without any txs included 268 receiptLen, balance := 0, big.NewInt(0) 269 if index == 1 { 270 // The second full work with 1 tx included 271 receiptLen, balance = 1, big.NewInt(1000) 272 } 273 if len(task.receipts) != receiptLen { 274 t.Fatalf("receipt number mismatch: have %d, want %d", len(task.receipts), receiptLen) 275 } 276 if task.state.GetBalance(testUserKey.Address()).Cmp(balance) != 0 { 277 t.Fatalf("account balance mismatch: have %d, want %d", task.state.GetBalance(testUserKey.Address()), balance) 278 } 279 } 280 w.newTaskHook = func(task *task) { 281 if task.block.NumberU64() == 1 { 282 checkEqual(t, task, taskIndex) 283 taskIndex += 1 284 taskCh <- struct{}{} 285 } 286 } 287 w.skipSealHook = func(task *task) bool { return true } 288 w.fullTaskHook = func() { 289 time.Sleep(100 * time.Millisecond) 290 } 291 w.start() // Start mining! 292 for i := 0; i < 2; i += 1 { 293 select { 294 case <-taskCh: 295 case <-time.NewTimer(3 * time.Second).C: 296 t.Error("new task timeout") 297 } 298 } 299 } 300 301 func TestStreamUncleBlock(t *testing.T) { 302 cryptore := cryptore.NewFaker() 303 defer cryptore.Close() 304 305 w, b := newTestWorker(t, cryptoreChainConfig, cryptore, rawdb.NewMemoryDatabase(), 1) 306 defer w.close() 307 308 var taskCh = make(chan struct{}) 309 310 taskIndex := 0 311 w.newTaskHook = func(task *task) { 312 if task.block.NumberU64() == 2 { 313 // The first task is an empty task, the second 314 // one has 1 pending tx, the third one has 1 tx 315 // and 1 uncle. 316 if taskIndex == 2 { 317 have := task.block.Header().UncleHash 318 want := types.CalcUncleHash([]*types.Header{b.uncleBlock.Header()}) 319 if have != want { 320 t.Errorf("uncle hash mismatch: have %s, want %s", have.Hex(), want.Hex()) 321 } 322 } 323 taskCh <- struct{}{} 324 taskIndex += 1 325 } 326 } 327 w.skipSealHook = func(task *task) bool { 328 return true 329 } 330 w.fullTaskHook = func() { 331 time.Sleep(100 * time.Millisecond) 332 } 333 w.start() 334 335 for i := 0; i < 2; i += 1 { 336 select { 337 case <-taskCh: 338 case <-time.NewTimer(time.Second).C: 339 t.Error("new task timeout") 340 } 341 } 342 343 w.postSideBlock(core.ChainSideEvent{Block: b.uncleBlock}) 344 345 select { 346 case <-taskCh: 347 case <-time.NewTimer(time.Second).C: 348 t.Error("new task timeout") 349 } 350 } 351 352 func TestRegenerateMiningBlockCryptore(t *testing.T) { 353 testRegenerateMiningBlock(t, cryptoreChainConfig, cryptore.NewFaker()) 354 } 355 356 func TestRegenerateMiningBlockClique(t *testing.T) { 357 testRegenerateMiningBlock(t, cliqueChainConfig, clique.New(cliqueChainConfig.Clique, rawdb.NewMemoryDatabase())) 358 } 359 360 func testRegenerateMiningBlock(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine) { 361 defer engine.Close() 362 363 w, b := newTestWorker(t, chainConfig, engine, rawdb.NewMemoryDatabase(), 0) 364 defer w.close() 365 366 var taskCh = make(chan struct{}) 367 368 taskIndex := 0 369 w.newTaskHook = func(task *task) { 370 if task.block.NumberU64() == 1 { 371 // The first task is an empty task, the second 372 // one has 1 pending tx, the third one has 2 txs 373 if taskIndex == 2 { 374 receiptLen, balance := 2, big.NewInt(2000) 375 if len(task.receipts) != receiptLen { 376 t.Errorf("receipt number mismatch: have %d, want %d", len(task.receipts), receiptLen) 377 } 378 if task.state.GetBalance(testUserKey.Address()).Cmp(balance) != 0 { 379 t.Errorf("account balance mismatch: have %d, want %d", task.state.GetBalance(testUserKey.Address()), balance) 380 } 381 } 382 taskCh <- struct{}{} 383 taskIndex += 1 384 } 385 } 386 w.skipSealHook = func(task *task) bool { 387 return true 388 } 389 w.fullTaskHook = func() { 390 time.Sleep(100 * time.Millisecond) 391 } 392 393 w.start() 394 // Ignore the first two works 395 for i := 0; i < 2; i += 1 { 396 select { 397 case <-taskCh: 398 case <-time.NewTimer(time.Second).C: 399 t.Error("new task timeout") 400 } 401 } 402 b.txPool.AddLocals(newTxs) 403 time.Sleep(time.Second) 404 405 select { 406 case <-taskCh: 407 case <-time.NewTimer(time.Second).C: 408 t.Error("new task timeout") 409 } 410 } 411 412 func TestAdjustIntervalCryptore(t *testing.T) { 413 testAdjustInterval(t, cryptoreChainConfig, cryptore.NewFaker()) 414 } 415 416 func TestAdjustIntervalClique(t *testing.T) { 417 testAdjustInterval(t, cliqueChainConfig, clique.New(cliqueChainConfig.Clique, rawdb.NewMemoryDatabase())) 418 } 419 420 func testAdjustInterval(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine) { 421 defer engine.Close() 422 423 w, _ := newTestWorker(t, chainConfig, engine, rawdb.NewMemoryDatabase(), 0) 424 defer w.close() 425 426 w.skipSealHook = func(task *task) bool { 427 return true 428 } 429 w.fullTaskHook = func() { 430 time.Sleep(100 * time.Millisecond) 431 } 432 var ( 433 progress = make(chan struct{}, 10) 434 result = make([]float64, 0, 10) 435 index = 0 436 start uint32 437 ) 438 w.resubmitHook = func(minInterval time.Duration, recommitInterval time.Duration) { 439 // Short circuit if interval checking hasn't started. 440 if atomic.LoadUint32(&start) == 0 { 441 return 442 } 443 var wantMinInterval, wantRecommitInterval time.Duration 444 445 switch index { 446 case 0: 447 wantMinInterval, wantRecommitInterval = 3*time.Second, 3*time.Second 448 case 1: 449 origin := float64(3 * time.Second.Nanoseconds()) 450 estimate := origin*(1-intervalAdjustRatio) + intervalAdjustRatio*(origin/0.8+intervalAdjustBias) 451 wantMinInterval, wantRecommitInterval = 3*time.Second, time.Duration(estimate)*time.Nanosecond 452 case 2: 453 estimate := result[index-1] 454 min := float64(3 * time.Second.Nanoseconds()) 455 estimate = estimate*(1-intervalAdjustRatio) + intervalAdjustRatio*(min-intervalAdjustBias) 456 wantMinInterval, wantRecommitInterval = 3*time.Second, time.Duration(estimate)*time.Nanosecond 457 case 3: 458 wantMinInterval, wantRecommitInterval = time.Second, time.Second 459 } 460 461 // Check interval 462 if minInterval != wantMinInterval { 463 t.Errorf("resubmit min interval mismatch: have %v, want %v ", minInterval, wantMinInterval) 464 } 465 if recommitInterval != wantRecommitInterval { 466 t.Errorf("resubmit interval mismatch: have %v, want %v", recommitInterval, wantRecommitInterval) 467 } 468 result = append(result, float64(recommitInterval.Nanoseconds())) 469 index += 1 470 progress <- struct{}{} 471 } 472 w.start() 473 474 time.Sleep(time.Second) // Ensure two tasks have been summitted due to start opt 475 atomic.StoreUint32(&start, 1) 476 477 w.setRecommitInterval(3 * time.Second) 478 select { 479 case <-progress: 480 case <-time.NewTimer(time.Second).C: 481 t.Error("interval reset timeout") 482 } 483 484 w.resubmitAdjustCh <- &intervalAdjust{inc: true, ratio: 0.8} 485 select { 486 case <-progress: 487 case <-time.NewTimer(time.Second).C: 488 t.Error("interval reset timeout") 489 } 490 491 w.resubmitAdjustCh <- &intervalAdjust{inc: false} 492 select { 493 case <-progress: 494 case <-time.NewTimer(time.Second).C: 495 t.Error("interval reset timeout") 496 } 497 498 w.setRecommitInterval(500 * time.Millisecond) 499 select { 500 case <-progress: 501 case <-time.NewTimer(time.Second).C: 502 t.Error("interval reset timeout") 503 } 504 }