github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/miner/worker_test.go (about) 1 2 //<developer> 3 // <name>linapex 曹一峰</name> 4 // <email>linapex@163.com</email> 5 // <wx>superexc</wx> 6 // <qqgroup>128148617</qqgroup> 7 // <url>https://jsq.ink</url> 8 // <role>pku engineer</role> 9 // <date>2019-03-16 19:16:40</date> 10 //</624450100743114752> 11 12 13 package miner 14 15 import ( 16 "math/big" 17 "testing" 18 "time" 19 20 "github.com/ethereum/go-ethereum/common" 21 "github.com/ethereum/go-ethereum/consensus" 22 "github.com/ethereum/go-ethereum/consensus/clique" 23 "github.com/ethereum/go-ethereum/consensus/ethash" 24 "github.com/ethereum/go-ethereum/core" 25 "github.com/ethereum/go-ethereum/core/types" 26 "github.com/ethereum/go-ethereum/core/vm" 27 "github.com/ethereum/go-ethereum/crypto" 28 "github.com/ethereum/go-ethereum/ethdb" 29 "github.com/ethereum/go-ethereum/event" 30 "github.com/ethereum/go-ethereum/params" 31 ) 32 33 var ( 34 //测试链配置 35 testTxPoolConfig core.TxPoolConfig 36 ethashChainConfig *params.ChainConfig 37 cliqueChainConfig *params.ChainConfig 38 39 //测试帐目 40 testBankKey, _ = crypto.GenerateKey() 41 testBankAddress = crypto.PubkeyToAddress(testBankKey.PublicKey) 42 testBankFunds = big.NewInt(1000000000000000000) 43 44 testUserKey, _ = crypto.GenerateKey() 45 testUserAddress = crypto.PubkeyToAddress(testUserKey.PublicKey) 46 47 //测试事务 48 pendingTxs []*types.Transaction 49 newTxs []*types.Transaction 50 ) 51 52 func init() { 53 testTxPoolConfig = core.DefaultTxPoolConfig 54 testTxPoolConfig.Journal = "" 55 ethashChainConfig = params.TestChainConfig 56 cliqueChainConfig = params.TestChainConfig 57 cliqueChainConfig.Clique = ¶ms.CliqueConfig{ 58 Period: 10, 59 Epoch: 30000, 60 } 61 tx1, _ := types.SignTx(types.NewTransaction(0, testUserAddress, big.NewInt(1000), params.TxGas, nil, nil), types.HomesteadSigner{}, testBankKey) 62 pendingTxs = append(pendingTxs, tx1) 63 tx2, _ := types.SignTx(types.NewTransaction(1, testUserAddress, big.NewInt(1000), params.TxGas, nil, nil), types.HomesteadSigner{}, testBankKey) 64 newTxs = append(newTxs, tx2) 65 } 66 67 //TestWorkerBackend实现worker.backend接口,并包装测试期间所需的所有信息。 68 type testWorkerBackend struct { 69 db ethdb.Database 70 txPool *core.TxPool 71 chain *core.BlockChain 72 testTxFeed event.Feed 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 } 83 ) 84 85 switch engine.(type) { 86 case *clique.Clique: 87 gspec.ExtraData = make([]byte, 32+common.AddressLength+65) 88 copy(gspec.ExtraData[32:], testBankAddress[:]) 89 case *ethash.Ethash: 90 default: 91 t.Fatalf("unexpected consensus engine type: %T", engine) 92 } 93 genesis := gspec.MustCommit(db) 94 95 chain, _ := core.NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil) 96 txpool := core.NewTxPool(testTxPoolConfig, chainConfig, chain) 97 98 //为它生成一个小的n块链和一个叔叔块 99 if n > 0 { 100 blocks, _ := core.GenerateChain(chainConfig, genesis, engine, db, n, func(i int, gen *core.BlockGen) { 101 gen.SetCoinbase(testBankAddress) 102 }) 103 if _, err := chain.InsertChain(blocks); err != nil { 104 t.Fatalf("failed to insert origin chain: %v", err) 105 } 106 } 107 parent := genesis 108 if n > 0 { 109 parent = chain.GetBlockByHash(chain.CurrentBlock().ParentHash()) 110 } 111 blocks, _ := core.GenerateChain(chainConfig, parent, engine, db, 1, func(i int, gen *core.BlockGen) { 112 gen.SetCoinbase(testUserAddress) 113 }) 114 115 return &testWorkerBackend{ 116 db: db, 117 chain: chain, 118 txPool: txpool, 119 uncleBlock: blocks[0], 120 } 121 } 122 123 func (b *testWorkerBackend) BlockChain() *core.BlockChain { return b.chain } 124 func (b *testWorkerBackend) TxPool() *core.TxPool { return b.txPool } 125 func (b *testWorkerBackend) PostChainEvents(events []interface{}) { 126 b.chain.PostChainEvents(events, nil) 127 } 128 129 func newTestWorker(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine, blocks int) (*worker, *testWorkerBackend) { 130 backend := newTestWorkerBackend(t, chainConfig, engine, blocks) 131 backend.txPool.AddLocals(pendingTxs) 132 w := newWorker(chainConfig, engine, backend, new(event.TypeMux), time.Second, params.GenesisGasLimit, params.GenesisGasLimit, nil) 133 w.setEtherbase(testBankAddress) 134 return w, backend 135 } 136 137 func TestPendingStateAndBlockEthash(t *testing.T) { 138 testPendingStateAndBlock(t, ethashChainConfig, ethash.NewFaker()) 139 } 140 func TestPendingStateAndBlockClique(t *testing.T) { 141 testPendingStateAndBlock(t, cliqueChainConfig, clique.New(cliqueChainConfig.Clique, ethdb.NewMemDatabase())) 142 } 143 144 func testPendingStateAndBlock(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine) { 145 defer engine.Close() 146 147 w, b := newTestWorker(t, chainConfig, engine, 0) 148 defer w.close() 149 150 //确保快照已更新。 151 time.Sleep(100 * time.Millisecond) 152 block, state := w.pending() 153 if block.NumberU64() != 1 { 154 t.Errorf("block number mismatch: have %d, want %d", block.NumberU64(), 1) 155 } 156 if balance := state.GetBalance(testUserAddress); balance.Cmp(big.NewInt(1000)) != 0 { 157 t.Errorf("account balance mismatch: have %d, want %d", balance, 1000) 158 } 159 b.txPool.AddLocals(newTxs) 160 161 //确保已处理新的Tx事件 162 time.Sleep(100 * time.Millisecond) 163 block, state = w.pending() 164 if balance := state.GetBalance(testUserAddress); balance.Cmp(big.NewInt(2000)) != 0 { 165 t.Errorf("account balance mismatch: have %d, want %d", balance, 2000) 166 } 167 } 168 169 func TestEmptyWorkEthash(t *testing.T) { 170 testEmptyWork(t, ethashChainConfig, ethash.NewFaker()) 171 } 172 func TestEmptyWorkClique(t *testing.T) { 173 testEmptyWork(t, cliqueChainConfig, clique.New(cliqueChainConfig.Clique, ethdb.NewMemDatabase())) 174 } 175 176 func testEmptyWork(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine) { 177 defer engine.Close() 178 179 w, _ := newTestWorker(t, chainConfig, engine, 0) 180 defer w.close() 181 182 var ( 183 taskCh = make(chan struct{}, 2) 184 taskIndex int 185 ) 186 187 checkEqual := func(t *testing.T, task *task, index int) { 188 receiptLen, balance := 0, big.NewInt(0) 189 if index == 1 { 190 receiptLen, balance = 1, big.NewInt(1000) 191 } 192 if len(task.receipts) != receiptLen { 193 t.Errorf("receipt number mismatch: have %d, want %d", len(task.receipts), receiptLen) 194 } 195 if task.state.GetBalance(testUserAddress).Cmp(balance) != 0 { 196 t.Errorf("account balance mismatch: have %d, want %d", task.state.GetBalance(testUserAddress), balance) 197 } 198 } 199 200 w.newTaskHook = func(task *task) { 201 if task.block.NumberU64() == 1 { 202 checkEqual(t, task, taskIndex) 203 taskIndex += 1 204 taskCh <- struct{}{} 205 } 206 } 207 w.fullTaskHook = func() { 208 time.Sleep(100 * time.Millisecond) 209 } 210 211 //确保工作进程已完成初始化 212 for { 213 b := w.pendingBlock() 214 if b != nil && b.NumberU64() == 1 { 215 break 216 } 217 } 218 219 w.start() 220 for i := 0; i < 2; i += 1 { 221 select { 222 case <-taskCh: 223 case <-time.NewTimer(time.Second).C: 224 t.Error("new task timeout") 225 } 226 } 227 } 228 229 func TestStreamUncleBlock(t *testing.T) { 230 ethash := ethash.NewFaker() 231 defer ethash.Close() 232 233 w, b := newTestWorker(t, ethashChainConfig, ethash, 1) 234 defer w.close() 235 236 var taskCh = make(chan struct{}) 237 238 taskIndex := 0 239 w.newTaskHook = func(task *task) { 240 if task.block.NumberU64() == 2 { 241 if taskIndex == 2 { 242 have := task.block.Header().UncleHash 243 want := types.CalcUncleHash([]*types.Header{b.uncleBlock.Header()}) 244 if have != want { 245 t.Errorf("uncle hash mismatch: have %s, want %s", have.Hex(), want.Hex()) 246 } 247 } 248 taskCh <- struct{}{} 249 taskIndex += 1 250 } 251 } 252 w.skipSealHook = func(task *task) bool { 253 return true 254 } 255 w.fullTaskHook = func() { 256 time.Sleep(100 * time.Millisecond) 257 } 258 259 //确保工作进程已完成初始化 260 for { 261 b := w.pendingBlock() 262 if b != nil && b.NumberU64() == 2 { 263 break 264 } 265 } 266 w.start() 267 268 //忽略前两个作品 269 for i := 0; i < 2; i += 1 { 270 select { 271 case <-taskCh: 272 case <-time.NewTimer(time.Second).C: 273 t.Error("new task timeout") 274 } 275 } 276 b.PostChainEvents([]interface{}{core.ChainSideEvent{Block: b.uncleBlock}}) 277 278 select { 279 case <-taskCh: 280 case <-time.NewTimer(time.Second).C: 281 t.Error("new task timeout") 282 } 283 } 284 285 func TestRegenerateMiningBlockEthash(t *testing.T) { 286 testRegenerateMiningBlock(t, ethashChainConfig, ethash.NewFaker()) 287 } 288 289 func TestRegenerateMiningBlockClique(t *testing.T) { 290 testRegenerateMiningBlock(t, cliqueChainConfig, clique.New(cliqueChainConfig.Clique, ethdb.NewMemDatabase())) 291 } 292 293 func testRegenerateMiningBlock(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine) { 294 defer engine.Close() 295 296 w, b := newTestWorker(t, chainConfig, engine, 0) 297 defer w.close() 298 299 var taskCh = make(chan struct{}) 300 301 taskIndex := 0 302 w.newTaskHook = func(task *task) { 303 if task.block.NumberU64() == 1 { 304 if taskIndex == 2 { 305 receiptLen, balance := 2, big.NewInt(2000) 306 if len(task.receipts) != receiptLen { 307 t.Errorf("receipt number mismatch: have %d, want %d", len(task.receipts), receiptLen) 308 } 309 if task.state.GetBalance(testUserAddress).Cmp(balance) != 0 { 310 t.Errorf("account balance mismatch: have %d, want %d", task.state.GetBalance(testUserAddress), balance) 311 } 312 } 313 taskCh <- struct{}{} 314 taskIndex += 1 315 } 316 } 317 w.skipSealHook = func(task *task) bool { 318 return true 319 } 320 w.fullTaskHook = func() { 321 time.Sleep(100 * time.Millisecond) 322 } 323 //确保工作进程已完成初始化 324 for { 325 b := w.pendingBlock() 326 if b != nil && b.NumberU64() == 1 { 327 break 328 } 329 } 330 331 w.start() 332 //忽略前两个作品 333 for i := 0; i < 2; i += 1 { 334 select { 335 case <-taskCh: 336 case <-time.NewTimer(time.Second).C: 337 t.Error("new task timeout") 338 } 339 } 340 b.txPool.AddLocals(newTxs) 341 time.Sleep(time.Second) 342 343 select { 344 case <-taskCh: 345 case <-time.NewTimer(time.Second).C: 346 t.Error("new task timeout") 347 } 348 } 349 350 func TestAdjustIntervalEthash(t *testing.T) { 351 testAdjustInterval(t, ethashChainConfig, ethash.NewFaker()) 352 } 353 354 func TestAdjustIntervalClique(t *testing.T) { 355 testAdjustInterval(t, cliqueChainConfig, clique.New(cliqueChainConfig.Clique, ethdb.NewMemDatabase())) 356 } 357 358 func testAdjustInterval(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine) { 359 defer engine.Close() 360 361 w, _ := newTestWorker(t, chainConfig, engine, 0) 362 defer w.close() 363 364 w.skipSealHook = func(task *task) bool { 365 return true 366 } 367 w.fullTaskHook = func() { 368 time.Sleep(100 * time.Millisecond) 369 } 370 var ( 371 progress = make(chan struct{}, 10) 372 result = make([]float64, 0, 10) 373 index = 0 374 start = false 375 ) 376 w.resubmitHook = func(minInterval time.Duration, recommitInterval time.Duration) { 377 //如果间隔检查未开始,则短路。 378 if !start { 379 return 380 } 381 var wantMinInterval, wantRecommitInterval time.Duration 382 383 switch index { 384 case 0: 385 wantMinInterval, wantRecommitInterval = 3*time.Second, 3*time.Second 386 case 1: 387 origin := float64(3 * time.Second.Nanoseconds()) 388 estimate := origin*(1-intervalAdjustRatio) + intervalAdjustRatio*(origin/0.8+intervalAdjustBias) 389 wantMinInterval, wantRecommitInterval = 3*time.Second, time.Duration(estimate)*time.Nanosecond 390 case 2: 391 estimate := result[index-1] 392 min := float64(3 * time.Second.Nanoseconds()) 393 estimate = estimate*(1-intervalAdjustRatio) + intervalAdjustRatio*(min-intervalAdjustBias) 394 wantMinInterval, wantRecommitInterval = 3*time.Second, time.Duration(estimate)*time.Nanosecond 395 case 3: 396 wantMinInterval, wantRecommitInterval = time.Second, time.Second 397 } 398 399 //检查间隔 400 if minInterval != wantMinInterval { 401 t.Errorf("resubmit min interval mismatch: have %v, want %v ", minInterval, wantMinInterval) 402 } 403 if recommitInterval != wantRecommitInterval { 404 t.Errorf("resubmit interval mismatch: have %v, want %v", recommitInterval, wantRecommitInterval) 405 } 406 result = append(result, float64(recommitInterval.Nanoseconds())) 407 index += 1 408 progress <- struct{}{} 409 } 410 //确保工作进程已完成初始化 411 for { 412 b := w.pendingBlock() 413 if b != nil && b.NumberU64() == 1 { 414 break 415 } 416 } 417 418 w.start() 419 420 time.Sleep(time.Second) 421 422 start = true 423 w.setRecommitInterval(3 * time.Second) 424 select { 425 case <-progress: 426 case <-time.NewTimer(time.Second).C: 427 t.Error("interval reset timeout") 428 } 429 430 w.resubmitAdjustCh <- &intervalAdjust{inc: true, ratio: 0.8} 431 select { 432 case <-progress: 433 case <-time.NewTimer(time.Second).C: 434 t.Error("interval reset timeout") 435 } 436 437 w.resubmitAdjustCh <- &intervalAdjust{inc: false} 438 select { 439 case <-progress: 440 case <-time.NewTimer(time.Second).C: 441 t.Error("interval reset timeout") 442 } 443 444 w.setRecommitInterval(500 * time.Millisecond) 445 select { 446 case <-progress: 447 case <-time.NewTimer(time.Second).C: 448 t.Error("interval reset timeout") 449 } 450 } 451