github.com/theQRL/go-zond@v0.1.1/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 "sync/atomic" 22 "testing" 23 "time" 24 25 "github.com/theQRL/go-zond/accounts" 26 "github.com/theQRL/go-zond/common" 27 "github.com/theQRL/go-zond/consensus" 28 "github.com/theQRL/go-zond/consensus/clique" 29 "github.com/theQRL/go-zond/consensus/ethash" 30 "github.com/theQRL/go-zond/core" 31 "github.com/theQRL/go-zond/core/rawdb" 32 "github.com/theQRL/go-zond/core/txpool" 33 "github.com/theQRL/go-zond/core/txpool/legacypool" 34 "github.com/theQRL/go-zond/core/types" 35 "github.com/theQRL/go-zond/core/vm" 36 "github.com/theQRL/go-zond/crypto" 37 "github.com/theQRL/go-zond/event" 38 "github.com/theQRL/go-zond/params" 39 "github.com/theQRL/go-zond/zonddb" 40 ) 41 42 const ( 43 // testCode is the testing contract binary code which will initialises some 44 // variables in constructor 45 testCode = "0x60806040527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0060005534801561003457600080fd5b5060fc806100436000396000f3fe6080604052348015600f57600080fd5b506004361060325760003560e01c80630c4dae8814603757806398a213cf146053575b600080fd5b603d607e565b6040518082815260200191505060405180910390f35b607c60048036036020811015606757600080fd5b81019080803590602001909291905050506084565b005b60005481565b806000819055507fe9e44f9f7da8c559de847a3232b57364adc0354f15a2cd8dc636d54396f9587a6000546040518082815260200191505060405180910390a15056fea265627a7a723058208ae31d9424f2d0bc2a3da1a5dd659db2d71ec322a17db8f87e19e209e3a1ff4a64736f6c634300050a0032" 46 47 // testGas is the gas required for contract deployment. 48 testGas = 144109 49 ) 50 51 var ( 52 // Test chain configurations 53 testTxPoolConfig legacypool.Config 54 ethashChainConfig *params.ChainConfig 55 cliqueChainConfig *params.ChainConfig 56 57 // Test accounts 58 testBankKey, _ = crypto.GenerateKey() 59 testBankAddress = crypto.PubkeyToAddress(testBankKey.PublicKey) 60 testBankFunds = big.NewInt(1000000000000000000) 61 62 testUserKey, _ = crypto.GenerateKey() 63 testUserAddress = crypto.PubkeyToAddress(testUserKey.PublicKey) 64 65 // Test transactions 66 pendingTxs []*types.Transaction 67 newTxs []*types.Transaction 68 69 testConfig = &Config{ 70 Recommit: time.Second, 71 GasCeil: params.GenesisGasLimit, 72 } 73 ) 74 75 func init() { 76 testTxPoolConfig = legacypool.DefaultConfig 77 testTxPoolConfig.Journal = "" 78 ethashChainConfig = new(params.ChainConfig) 79 *ethashChainConfig = *params.TestChainConfig 80 cliqueChainConfig = new(params.ChainConfig) 81 *cliqueChainConfig = *params.TestChainConfig 82 cliqueChainConfig.Clique = ¶ms.CliqueConfig{ 83 Period: 10, 84 Epoch: 30000, 85 } 86 87 signer := types.LatestSigner(params.TestChainConfig) 88 tx1 := types.MustSignNewTx(testBankKey, signer, &types.AccessListTx{ 89 ChainID: params.TestChainConfig.ChainID, 90 Nonce: 0, 91 To: &testUserAddress, 92 Value: big.NewInt(1000), 93 Gas: params.TxGas, 94 GasPrice: big.NewInt(params.InitialBaseFee), 95 }) 96 pendingTxs = append(pendingTxs, tx1) 97 98 tx2 := types.MustSignNewTx(testBankKey, signer, &types.LegacyTx{ 99 Nonce: 1, 100 To: &testUserAddress, 101 Value: big.NewInt(1000), 102 Gas: params.TxGas, 103 GasPrice: big.NewInt(params.InitialBaseFee), 104 }) 105 newTxs = append(newTxs, tx2) 106 } 107 108 // testWorkerBackend implements worker.Backend interfaces and wraps all information needed during the testing. 109 type testWorkerBackend struct { 110 db zonddb.Database 111 txPool *txpool.TxPool 112 chain *core.BlockChain 113 genesis *core.Genesis 114 } 115 116 func newTestWorkerBackend(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine, db zonddb.Database, n int) *testWorkerBackend { 117 var gspec = &core.Genesis{ 118 Config: chainConfig, 119 Alloc: core.GenesisAlloc{testBankAddress: {Balance: testBankFunds}}, 120 } 121 switch e := engine.(type) { 122 case *clique.Clique: 123 gspec.ExtraData = make([]byte, 32+common.AddressLength+crypto.SignatureLength) 124 copy(gspec.ExtraData[32:32+common.AddressLength], testBankAddress.Bytes()) 125 e.Authorize(testBankAddress, func(account accounts.Account, s string, data []byte) ([]byte, error) { 126 return crypto.Sign(crypto.Keccak256(data), testBankKey) 127 }) 128 case *ethash.Ethash: 129 default: 130 t.Fatalf("unexpected consensus engine type: %T", engine) 131 } 132 chain, err := core.NewBlockChain(db, &core.CacheConfig{TrieDirtyDisabled: true}, gspec, nil, engine, vm.Config{}, nil, nil) 133 if err != nil { 134 t.Fatalf("core.NewBlockChain failed: %v", err) 135 } 136 pool := legacypool.New(testTxPoolConfig, chain) 137 txpool, _ := txpool.New(new(big.Int).SetUint64(testTxPoolConfig.PriceLimit), chain, []txpool.SubPool{pool}) 138 139 return &testWorkerBackend{ 140 db: db, 141 chain: chain, 142 txPool: txpool, 143 genesis: gspec, 144 } 145 } 146 147 func (b *testWorkerBackend) BlockChain() *core.BlockChain { return b.chain } 148 func (b *testWorkerBackend) TxPool() *txpool.TxPool { return b.txPool } 149 150 func (b *testWorkerBackend) newRandomTx(creation bool) *types.Transaction { 151 var tx *types.Transaction 152 gasPrice := big.NewInt(10 * params.InitialBaseFee) 153 if creation { 154 tx, _ = types.SignTx(types.NewContractCreation(b.txPool.Nonce(testBankAddress), big.NewInt(0), testGas, gasPrice, common.FromHex(testCode)), types.HomesteadSigner{}, testBankKey) 155 } else { 156 tx, _ = types.SignTx(types.NewTransaction(b.txPool.Nonce(testBankAddress), testUserAddress, big.NewInt(1000), params.TxGas, gasPrice, nil), types.HomesteadSigner{}, testBankKey) 157 } 158 return tx 159 } 160 161 func newTestWorker(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine, db zonddb.Database, blocks int) (*worker, *testWorkerBackend) { 162 backend := newTestWorkerBackend(t, chainConfig, engine, db, blocks) 163 backend.txPool.Add(pendingTxs, true, false) 164 w := newWorker(testConfig, chainConfig, engine, backend, new(event.TypeMux), nil, false) 165 w.setEtherbase(testBankAddress) 166 return w, backend 167 } 168 169 func TestGenerateAndImportBlock(t *testing.T) { 170 var ( 171 db = rawdb.NewMemoryDatabase() 172 config = *params.AllCliqueProtocolChanges 173 ) 174 config.Clique = ¶ms.CliqueConfig{Period: 1, Epoch: 30000} 175 engine := clique.New(config.Clique, db) 176 177 w, b := newTestWorker(t, &config, engine, db, 0) 178 defer w.close() 179 180 // This test chain imports the mined blocks. 181 chain, _ := core.NewBlockChain(rawdb.NewMemoryDatabase(), nil, b.genesis, nil, engine, vm.Config{}, nil, nil) 182 defer chain.Stop() 183 184 // Ignore empty commit here for less noise. 185 w.skipSealHook = func(task *task) bool { 186 return len(task.receipts) == 0 187 } 188 189 // Wait for mined blocks. 190 sub := w.mux.Subscribe(core.NewMinedBlockEvent{}) 191 defer sub.Unsubscribe() 192 193 // Start mining! 194 w.start() 195 196 for i := 0; i < 5; i++ { 197 b.txPool.Add([]*types.Transaction{b.newRandomTx(true)}, true, false) 198 b.txPool.Add([]*types.Transaction{b.newRandomTx(false)}, true, false) 199 200 select { 201 case ev := <-sub.Chan(): 202 block := ev.Data.(core.NewMinedBlockEvent).Block 203 if _, err := chain.InsertChain([]*types.Block{block}); err != nil { 204 t.Fatalf("failed to insert new mined block %d: %v", block.NumberU64(), err) 205 } 206 case <-time.After(3 * time.Second): // Worker needs 1s to include new changes. 207 t.Fatalf("timeout") 208 } 209 } 210 } 211 212 func TestEmptyWorkEthash(t *testing.T) { 213 testEmptyWork(t, ethashChainConfig, ethash.NewFaker()) 214 } 215 func TestEmptyWorkClique(t *testing.T) { 216 testEmptyWork(t, cliqueChainConfig, clique.New(cliqueChainConfig.Clique, rawdb.NewMemoryDatabase())) 217 } 218 219 func testEmptyWork(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine) { 220 defer engine.Close() 221 222 w, _ := newTestWorker(t, chainConfig, engine, rawdb.NewMemoryDatabase(), 0) 223 defer w.close() 224 225 taskCh := make(chan struct{}, 2) 226 checkEqual := func(t *testing.T, task *task) { 227 // The work should contain 1 tx 228 receiptLen, balance := 1, big.NewInt(1000) 229 if len(task.receipts) != receiptLen { 230 t.Fatalf("receipt number mismatch: have %d, want %d", len(task.receipts), receiptLen) 231 } 232 if task.state.GetBalance(testUserAddress).Cmp(balance) != 0 { 233 t.Fatalf("account balance mismatch: have %d, want %d", task.state.GetBalance(testUserAddress), balance) 234 } 235 } 236 w.newTaskHook = func(task *task) { 237 if task.block.NumberU64() == 1 { 238 checkEqual(t, task) 239 taskCh <- struct{}{} 240 } 241 } 242 w.skipSealHook = func(task *task) bool { return true } 243 w.fullTaskHook = func() { 244 time.Sleep(100 * time.Millisecond) 245 } 246 w.start() // Start mining! 247 select { 248 case <-taskCh: 249 case <-time.NewTimer(3 * time.Second).C: 250 t.Error("new task timeout") 251 } 252 } 253 254 func TestAdjustIntervalEthash(t *testing.T) { 255 testAdjustInterval(t, ethashChainConfig, ethash.NewFaker()) 256 } 257 258 func TestAdjustIntervalClique(t *testing.T) { 259 testAdjustInterval(t, cliqueChainConfig, clique.New(cliqueChainConfig.Clique, rawdb.NewMemoryDatabase())) 260 } 261 262 func testAdjustInterval(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine) { 263 defer engine.Close() 264 265 w, _ := newTestWorker(t, chainConfig, engine, rawdb.NewMemoryDatabase(), 0) 266 defer w.close() 267 268 w.skipSealHook = func(task *task) bool { 269 return true 270 } 271 w.fullTaskHook = func() { 272 time.Sleep(100 * time.Millisecond) 273 } 274 var ( 275 progress = make(chan struct{}, 10) 276 result = make([]float64, 0, 10) 277 index = 0 278 start atomic.Bool 279 ) 280 w.resubmitHook = func(minInterval time.Duration, recommitInterval time.Duration) { 281 // Short circuit if interval checking hasn't started. 282 if !start.Load() { 283 return 284 } 285 var wantMinInterval, wantRecommitInterval time.Duration 286 287 switch index { 288 case 0: 289 wantMinInterval, wantRecommitInterval = 3*time.Second, 3*time.Second 290 case 1: 291 origin := float64(3 * time.Second.Nanoseconds()) 292 estimate := origin*(1-intervalAdjustRatio) + intervalAdjustRatio*(origin/0.8+intervalAdjustBias) 293 wantMinInterval, wantRecommitInterval = 3*time.Second, time.Duration(estimate)*time.Nanosecond 294 case 2: 295 estimate := result[index-1] 296 min := float64(3 * time.Second.Nanoseconds()) 297 estimate = estimate*(1-intervalAdjustRatio) + intervalAdjustRatio*(min-intervalAdjustBias) 298 wantMinInterval, wantRecommitInterval = 3*time.Second, time.Duration(estimate)*time.Nanosecond 299 case 3: 300 wantMinInterval, wantRecommitInterval = time.Second, time.Second 301 } 302 303 // Check interval 304 if minInterval != wantMinInterval { 305 t.Errorf("resubmit min interval mismatch: have %v, want %v ", minInterval, wantMinInterval) 306 } 307 if recommitInterval != wantRecommitInterval { 308 t.Errorf("resubmit interval mismatch: have %v, want %v", recommitInterval, wantRecommitInterval) 309 } 310 result = append(result, float64(recommitInterval.Nanoseconds())) 311 index += 1 312 progress <- struct{}{} 313 } 314 w.start() 315 316 time.Sleep(time.Second) // Ensure two tasks have been submitted due to start opt 317 start.Store(true) 318 319 w.setRecommitInterval(3 * time.Second) 320 select { 321 case <-progress: 322 case <-time.NewTimer(time.Second).C: 323 t.Error("interval reset timeout") 324 } 325 326 w.resubmitAdjustCh <- &intervalAdjust{inc: true, ratio: 0.8} 327 select { 328 case <-progress: 329 case <-time.NewTimer(time.Second).C: 330 t.Error("interval reset timeout") 331 } 332 333 w.resubmitAdjustCh <- &intervalAdjust{inc: false} 334 select { 335 case <-progress: 336 case <-time.NewTimer(time.Second).C: 337 t.Error("interval reset timeout") 338 } 339 340 w.setRecommitInterval(500 * time.Millisecond) 341 select { 342 case <-progress: 343 case <-time.NewTimer(time.Second).C: 344 t.Error("interval reset timeout") 345 } 346 } 347 348 func TestGetSealingWorkEthash(t *testing.T) { 349 testGetSealingWork(t, ethashChainConfig, ethash.NewFaker()) 350 } 351 352 func TestGetSealingWorkClique(t *testing.T) { 353 testGetSealingWork(t, cliqueChainConfig, clique.New(cliqueChainConfig.Clique, rawdb.NewMemoryDatabase())) 354 } 355 356 func TestGetSealingWorkPostMerge(t *testing.T) { 357 local := new(params.ChainConfig) 358 *local = *ethashChainConfig 359 local.TerminalTotalDifficulty = big.NewInt(0) 360 testGetSealingWork(t, local, ethash.NewFaker()) 361 } 362 363 func testGetSealingWork(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine) { 364 defer engine.Close() 365 366 w, b := newTestWorker(t, chainConfig, engine, rawdb.NewMemoryDatabase(), 0) 367 defer w.close() 368 369 w.setExtra([]byte{0x01, 0x02}) 370 371 w.skipSealHook = func(task *task) bool { 372 return true 373 } 374 w.fullTaskHook = func() { 375 time.Sleep(100 * time.Millisecond) 376 } 377 timestamp := uint64(time.Now().Unix()) 378 assertBlock := func(block *types.Block, number uint64, coinbase common.Address, random common.Hash) { 379 if block.Time() != timestamp { 380 // Sometime the timestamp will be mutated if the timestamp 381 // is even smaller than parent block's. It's OK. 382 t.Logf("Invalid timestamp, want %d, get %d", timestamp, block.Time()) 383 } 384 _, isClique := engine.(*clique.Clique) 385 if !isClique { 386 if len(block.Extra()) != 2 { 387 t.Error("Unexpected extra field") 388 } 389 if block.Coinbase() != coinbase { 390 t.Errorf("Unexpected coinbase got %x want %x", block.Coinbase(), coinbase) 391 } 392 } else { 393 if block.Coinbase() != (common.Address{}) { 394 t.Error("Unexpected coinbase") 395 } 396 } 397 if !isClique { 398 if block.MixDigest() != random { 399 t.Error("Unexpected mix digest") 400 } 401 } 402 if block.Nonce() != 0 { 403 t.Error("Unexpected block nonce") 404 } 405 if block.NumberU64() != number { 406 t.Errorf("Mismatched block number, want %d got %d", number, block.NumberU64()) 407 } 408 } 409 var cases = []struct { 410 parent common.Hash 411 coinbase common.Address 412 random common.Hash 413 expectNumber uint64 414 expectErr bool 415 }{ 416 { 417 b.chain.Genesis().Hash(), 418 common.HexToAddress("0xdeadbeef"), 419 common.HexToHash("0xcafebabe"), 420 uint64(1), 421 false, 422 }, 423 { 424 b.chain.CurrentBlock().Hash(), 425 common.HexToAddress("0xdeadbeef"), 426 common.HexToHash("0xcafebabe"), 427 b.chain.CurrentBlock().Number.Uint64() + 1, 428 false, 429 }, 430 { 431 b.chain.CurrentBlock().Hash(), 432 common.Address{}, 433 common.HexToHash("0xcafebabe"), 434 b.chain.CurrentBlock().Number.Uint64() + 1, 435 false, 436 }, 437 { 438 b.chain.CurrentBlock().Hash(), 439 common.Address{}, 440 common.Hash{}, 441 b.chain.CurrentBlock().Number.Uint64() + 1, 442 false, 443 }, 444 { 445 common.HexToHash("0xdeadbeef"), 446 common.HexToAddress("0xdeadbeef"), 447 common.HexToHash("0xcafebabe"), 448 0, 449 true, 450 }, 451 } 452 453 // This API should work even when the automatic sealing is not enabled 454 for _, c := range cases { 455 r := w.getSealingBlock(&generateParams{ 456 parentHash: c.parent, 457 timestamp: timestamp, 458 coinbase: c.coinbase, 459 random: c.random, 460 withdrawals: nil, 461 beaconRoot: nil, 462 noTxs: false, 463 forceTime: true, 464 }) 465 if c.expectErr { 466 if r.err == nil { 467 t.Error("Expect error but get nil") 468 } 469 } else { 470 if r.err != nil { 471 t.Errorf("Unexpected error %v", r.err) 472 } 473 assertBlock(r.block, c.expectNumber, c.coinbase, c.random) 474 } 475 } 476 477 // This API should work even when the automatic sealing is enabled 478 w.start() 479 for _, c := range cases { 480 r := w.getSealingBlock(&generateParams{ 481 parentHash: c.parent, 482 timestamp: timestamp, 483 coinbase: c.coinbase, 484 random: c.random, 485 withdrawals: nil, 486 beaconRoot: nil, 487 noTxs: false, 488 forceTime: true, 489 }) 490 if c.expectErr { 491 if r.err == nil { 492 t.Error("Expect error but get nil") 493 } 494 } else { 495 if r.err != nil { 496 t.Errorf("Unexpected error %v", r.err) 497 } 498 assertBlock(r.block, c.expectNumber, c.coinbase, c.random) 499 } 500 } 501 }