github.com/bcskill/bcschain/v3@v3.4.9-beta2/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/bcskill/bcschain/v3/common" 26 "github.com/bcskill/bcschain/v3/consensus" 27 "github.com/bcskill/bcschain/v3/consensus/clique" 28 "github.com/bcskill/bcschain/v3/core" 29 "github.com/bcskill/bcschain/v3/core/types" 30 "github.com/bcskill/bcschain/v3/core/vm" 31 "github.com/bcskill/bcschain/v3/crypto" 32 "github.com/bcskill/bcschain/v3/ethdb" 33 "github.com/bcskill/bcschain/v3/params" 34 ) 35 36 var ( 37 // Test chain configurations 38 testTxPoolConfig core.TxPoolConfig 39 cliqueChainConfig *params.ChainConfig 40 41 // Test accounts 42 testBankKey, _ = crypto.GenerateKey() 43 testBankAddress = crypto.PubkeyToAddress(testBankKey.PublicKey) 44 testBankFunds = big.NewInt(1000000000000000000) 45 46 testUserKey, _ = crypto.GenerateKey() 47 testUserAddress = crypto.PubkeyToAddress(testUserKey.PublicKey) 48 49 // Test transactions 50 pendingTxs []*types.Transaction 51 newTxs []*types.Transaction 52 ) 53 54 func init() { 55 testTxPoolConfig = core.DefaultTxPoolConfig 56 testTxPoolConfig.Journal = "" 57 cliqueChainConfig = params.TestChainConfig 58 cliqueChainConfig.Clique = ¶ms.CliqueConfig{ 59 Period: 10, 60 Epoch: 30000, 61 } 62 tx1, _ := types.SignTx(types.NewTransaction(0, testUserAddress, big.NewInt(1000), params.TxGas, nil, nil), types.HomesteadSigner{}, testBankKey) 63 pendingTxs = append(pendingTxs, tx1) 64 tx2, _ := types.SignTx(types.NewTransaction(1, testUserAddress, big.NewInt(1000), params.TxGas, nil, nil), types.HomesteadSigner{}, testBankKey) 65 newTxs = append(newTxs, tx2) 66 } 67 68 // testWorkerBackend implements worker.Backend interfaces and wraps all information needed during the testing. 69 type testWorkerBackend struct { 70 db common.Database 71 txPool *core.TxPool 72 chain *core.BlockChain 73 uncleBlock *types.Block 74 } 75 76 func newTestWorkerBackend(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine, n int) *testWorkerBackend { 77 var ( 78 db = ethdb.NewMemDatabase() 79 gspec = core.Genesis{ 80 Config: chainConfig, 81 Alloc: core.GenesisAlloc{testBankAddress: {Balance: testBankFunds}}, 82 Signers: []common.Address{testBankAddress}, 83 Voters: []common.Address{testBankAddress}, 84 Signer: make([]byte, 65), 85 ExtraData: make([]byte, 32), 86 } 87 ) 88 89 genesis := gspec.MustCommit(db) 90 91 chain, _ := core.NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}) 92 txpool := core.NewTxPool(testTxPoolConfig, chainConfig, chain) 93 94 // Generate a small n-block chain and an uncle block for it 95 if n > 0 { 96 blocks, _ := core.GenerateChain(chainConfig, genesis, engine, db, n, func(_ int, gen *core.BlockGen) { 97 gen.SetCoinbase(testBankAddress) 98 }) 99 if _, err := chain.InsertChain(blocks); err != nil { 100 t.Fatalf("failed to insert origin chain: %v", err) 101 } 102 } 103 parent := genesis 104 if n > 0 { 105 parent = chain.GetBlockByHash(chain.CurrentBlock().ParentHash()) 106 } 107 blocks, _ := core.GenerateChain(chainConfig, parent, engine, db, 1, func(_ int, gen *core.BlockGen) { 108 gen.SetCoinbase(testUserAddress) 109 }) 110 111 return &testWorkerBackend{ 112 db: db, 113 chain: chain, 114 txPool: txpool, 115 uncleBlock: blocks[0], 116 } 117 } 118 119 func (b *testWorkerBackend) BlockChain() *core.BlockChain { return b.chain } 120 func (b *testWorkerBackend) TxPool() *core.TxPool { return b.txPool } 121 func (b *testWorkerBackend) PostChainEvents(events []interface{}) { 122 b.chain.PostChainEvents(events, nil) 123 } 124 125 func newTestWorker(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine, blocks int) (*worker, *testWorkerBackend) { 126 backend := newTestWorkerBackend(t, chainConfig, engine, blocks) 127 backend.txPool.AddLocals(pendingTxs) 128 w := newWorker(chainConfig, engine, backend, new(core.InterfaceFeed), time.Second, params.GenesisGasLimit, params.GenesisGasLimit, nil) 129 w.setEtherbase(testBankAddress) 130 return w, backend 131 } 132 133 func TestPendingStateAndBlockClique(t *testing.T) { 134 testPendingStateAndBlock(t, cliqueChainConfig, clique.NewFaker()) 135 } 136 137 func testPendingStateAndBlock(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine) { 138 w, b := newTestWorker(t, chainConfig, engine, 0) 139 defer w.close() 140 141 // Ensure snapshot has been updated. 142 time.Sleep(100 * time.Millisecond) 143 block, state := w.pending() 144 for block == nil { 145 time.Sleep(100 * time.Millisecond) 146 block, state = w.pending() 147 } 148 if block.NumberU64() != 1 { 149 t.Errorf("block number mismatch: have %d, want %d", block.NumberU64(), 1) 150 } 151 if balance := state.GetBalance(testUserAddress); balance.Cmp(big.NewInt(1000)) != 0 { 152 t.Errorf("account balance mismatch: have %d, want %d", balance, 1000) 153 } 154 b.txPool.AddLocals(newTxs) 155 156 // Ensure the new tx events has been processed 157 time.Sleep(100 * time.Millisecond) 158 block, state = w.pending() 159 if balance := state.GetBalance(testUserAddress); balance.Cmp(big.NewInt(2000)) != 0 { 160 t.Errorf("account balance mismatch: have %d, want %d", balance, 2000) 161 } 162 } 163 164 func TestEmptyWorkClique(t *testing.T) { 165 testEmptyWork(t, cliqueChainConfig, clique.NewFaker()) 166 } 167 168 func testEmptyWork(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine) { 169 w, _ := newTestWorker(t, chainConfig, engine, 0) 170 defer w.close() 171 172 var ( 173 taskCh = make(chan struct{}, 2) 174 taskIndex int 175 ) 176 177 checkEqual := func(t *testing.T, task *task, index int) { 178 receiptLen, balance := 0, big.NewInt(0) 179 if index == 1 { 180 receiptLen, balance = 1, big.NewInt(1000) 181 } 182 if len(task.receipts) != receiptLen { 183 t.Errorf("receipt number mismatch: have %d, want %d", len(task.receipts), receiptLen) 184 } 185 if task.state.GetBalance(testUserAddress).Cmp(balance) != 0 { 186 t.Errorf("account balance mismatch: have %d, want %d", task.state.GetBalance(testUserAddress), balance) 187 } 188 } 189 190 w.newTaskHook = func(task *task) { 191 if task.block.NumberU64() == 1 { 192 checkEqual(t, task, taskIndex) 193 taskIndex += 1 194 taskCh <- struct{}{} 195 } 196 } 197 w.setFullTaskDelay(100 * time.Millisecond) 198 199 // Ensure worker has finished initialization 200 for { 201 b := w.pendingBlock() 202 if b != nil && b.NumberU64() == 1 { 203 break 204 } 205 } 206 207 w.start() 208 for i := 0; i < 2; i += 1 { 209 select { 210 case <-taskCh: 211 case <-time.After(10*time.Second): 212 t.Error("new task timeout") 213 } 214 } 215 } 216 217 func TestRegenerateMiningBlockClique(t *testing.T) { 218 testRegenerateMiningBlock(t, cliqueChainConfig, clique.NewFaker()) 219 } 220 221 func testRegenerateMiningBlock(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine) { 222 w, b := newTestWorker(t, chainConfig, engine, 0) 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 receiptLen, balance := 2, big.NewInt(2000) 232 if len(task.receipts) != receiptLen { 233 t.Errorf("receipt number mismatch: have %d, want %d", len(task.receipts), receiptLen) 234 } 235 if task.state.GetBalance(testUserAddress).Cmp(balance) != 0 { 236 t.Errorf("account balance mismatch: have %d, want %d", task.state.GetBalance(testUserAddress), balance) 237 } 238 } 239 taskCh <- struct{}{} 240 taskIndex += 1 241 } 242 } 243 w.skipSealHook = func(task *task) bool { 244 return true 245 } 246 w.setFullTaskDelay(100 * time.Millisecond) 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.txPool.AddLocals(newTxs) 266 time.Sleep(time.Second) 267 268 select { 269 case <-taskCh: 270 case <-time.NewTimer(time.Second).C: 271 t.Error("new task timeout") 272 } 273 } 274 275 func TestAdjustIntervalClique(t *testing.T) { 276 testAdjustInterval(t, cliqueChainConfig, clique.NewFaker()) 277 } 278 279 func testAdjustInterval(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine) { 280 w, _ := newTestWorker(t, chainConfig, engine, 0) 281 defer w.close() 282 283 w.skipSealHook = func(task *task) bool { 284 return true 285 } 286 w.setFullTaskDelay(100 * time.Millisecond) 287 var ( 288 progress = make(chan struct{}, 10) 289 result = make([]float64, 0, 10) 290 index = 0 291 start int32 292 ) 293 w.setResubmitHook(func(minInterval time.Duration, recommitInterval time.Duration) { 294 // Short circuit if interval checking hasn't started. 295 if atomic.LoadInt32(&start) == 0 { 296 return 297 } 298 var wantMinInterval, wantRecommitInterval time.Duration 299 300 switch index { 301 case 0: 302 wantMinInterval, wantRecommitInterval = 3*time.Second, 3*time.Second 303 case 1: 304 origin := float64(3 * time.Second.Nanoseconds()) 305 estimate := origin*(1-intervalAdjustRatio) + intervalAdjustRatio*(origin/0.8+intervalAdjustBias) 306 wantMinInterval, wantRecommitInterval = 3*time.Second, time.Duration(estimate)*time.Nanosecond 307 case 2: 308 estimate := result[index-1] 309 min := float64(3 * time.Second.Nanoseconds()) 310 estimate = estimate*(1-intervalAdjustRatio) + intervalAdjustRatio*(min-intervalAdjustBias) 311 wantMinInterval, wantRecommitInterval = 3*time.Second, time.Duration(estimate)*time.Nanosecond 312 case 3: 313 wantMinInterval, wantRecommitInterval = time.Second, time.Second 314 } 315 316 // Check interval 317 if minInterval != wantMinInterval { 318 t.Errorf("resubmit min interval mismatch: have %v, want %v ", minInterval, wantMinInterval) 319 } 320 if recommitInterval != wantRecommitInterval { 321 t.Errorf("resubmit interval mismatch: have %v, want %v", recommitInterval, wantRecommitInterval) 322 } 323 result = append(result, float64(recommitInterval.Nanoseconds())) 324 index += 1 325 progress <- struct{}{} 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 337 time.Sleep(time.Second) 338 339 atomic.StoreInt32(&start, 1) 340 w.setRecommitInterval(3 * time.Second) 341 select { 342 case <-progress: 343 case <-time.NewTimer(time.Second).C: 344 t.Error("interval reset timeout") 345 } 346 347 w.resubmitAdjustCh <- &intervalAdjust{inc: true, ratio: 0.8} 348 select { 349 case <-progress: 350 case <-time.NewTimer(time.Second).C: 351 t.Error("interval reset timeout") 352 } 353 354 w.resubmitAdjustCh <- &intervalAdjust{inc: false} 355 select { 356 case <-progress: 357 case <-time.NewTimer(time.Second).C: 358 t.Error("interval reset timeout") 359 } 360 361 w.setRecommitInterval(500 * time.Millisecond) 362 select { 363 case <-progress: 364 case <-time.NewTimer(time.Second).C: 365 t.Error("interval reset timeout") 366 } 367 }