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