github.com/calmw/ethereum@v0.1.1/miner/miner_test.go (about) 1 // Copyright 2020 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 implements Ethereum block creation and mining. 18 package miner 19 20 import ( 21 "errors" 22 "math/big" 23 "testing" 24 "time" 25 26 "github.com/calmw/ethereum/common" 27 "github.com/calmw/ethereum/consensus/clique" 28 "github.com/calmw/ethereum/core" 29 "github.com/calmw/ethereum/core/rawdb" 30 "github.com/calmw/ethereum/core/state" 31 "github.com/calmw/ethereum/core/txpool" 32 "github.com/calmw/ethereum/core/types" 33 "github.com/calmw/ethereum/core/vm" 34 "github.com/calmw/ethereum/eth/downloader" 35 "github.com/calmw/ethereum/event" 36 "github.com/calmw/ethereum/trie" 37 ) 38 39 type mockBackend struct { 40 bc *core.BlockChain 41 txPool *txpool.TxPool 42 } 43 44 func NewMockBackend(bc *core.BlockChain, txPool *txpool.TxPool) *mockBackend { 45 return &mockBackend{ 46 bc: bc, 47 txPool: txPool, 48 } 49 } 50 51 func (m *mockBackend) BlockChain() *core.BlockChain { 52 return m.bc 53 } 54 55 func (m *mockBackend) TxPool() *txpool.TxPool { 56 return m.txPool 57 } 58 59 func (m *mockBackend) StateAtBlock(block *types.Block, reexec uint64, base *state.StateDB, checkLive bool, preferDisk bool) (statedb *state.StateDB, err error) { 60 return nil, errors.New("not supported") 61 } 62 63 type testBlockChain struct { 64 statedb *state.StateDB 65 gasLimit uint64 66 chainHeadFeed *event.Feed 67 } 68 69 func (bc *testBlockChain) CurrentBlock() *types.Header { 70 return &types.Header{ 71 Number: new(big.Int), 72 GasLimit: bc.gasLimit, 73 } 74 } 75 76 func (bc *testBlockChain) GetBlock(hash common.Hash, number uint64) *types.Block { 77 return types.NewBlock(bc.CurrentBlock(), nil, nil, nil, trie.NewStackTrie(nil)) 78 } 79 80 func (bc *testBlockChain) StateAt(common.Hash) (*state.StateDB, error) { 81 return bc.statedb, nil 82 } 83 84 func (bc *testBlockChain) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription { 85 return bc.chainHeadFeed.Subscribe(ch) 86 } 87 88 func TestMiner(t *testing.T) { 89 miner, mux, cleanup := createMiner(t) 90 defer cleanup(false) 91 92 miner.Start() 93 waitForMiningState(t, miner, true) 94 // Start the downloader 95 mux.Post(downloader.StartEvent{}) 96 waitForMiningState(t, miner, false) 97 // Stop the downloader and wait for the update loop to run 98 mux.Post(downloader.DoneEvent{}) 99 waitForMiningState(t, miner, true) 100 101 // Subsequent downloader events after a successful DoneEvent should not cause the 102 // miner to start or stop. This prevents a security vulnerability 103 // that would allow entities to present fake high blocks that would 104 // stop mining operations by causing a downloader sync 105 // until it was discovered they were invalid, whereon mining would resume. 106 mux.Post(downloader.StartEvent{}) 107 waitForMiningState(t, miner, true) 108 109 mux.Post(downloader.FailedEvent{}) 110 waitForMiningState(t, miner, true) 111 } 112 113 // TestMinerDownloaderFirstFails tests that mining is only 114 // permitted to run indefinitely once the downloader sees a DoneEvent (success). 115 // An initial FailedEvent should allow mining to stop on a subsequent 116 // downloader StartEvent. 117 func TestMinerDownloaderFirstFails(t *testing.T) { 118 miner, mux, cleanup := createMiner(t) 119 defer cleanup(false) 120 121 miner.Start() 122 waitForMiningState(t, miner, true) 123 // Start the downloader 124 mux.Post(downloader.StartEvent{}) 125 waitForMiningState(t, miner, false) 126 127 // Stop the downloader and wait for the update loop to run 128 mux.Post(downloader.FailedEvent{}) 129 waitForMiningState(t, miner, true) 130 131 // Since the downloader hasn't yet emitted a successful DoneEvent, 132 // we expect the miner to stop on next StartEvent. 133 mux.Post(downloader.StartEvent{}) 134 waitForMiningState(t, miner, false) 135 136 // Downloader finally succeeds. 137 mux.Post(downloader.DoneEvent{}) 138 waitForMiningState(t, miner, true) 139 140 // Downloader starts again. 141 // Since it has achieved a DoneEvent once, we expect miner 142 // state to be unchanged. 143 mux.Post(downloader.StartEvent{}) 144 waitForMiningState(t, miner, true) 145 146 mux.Post(downloader.FailedEvent{}) 147 waitForMiningState(t, miner, true) 148 } 149 150 func TestMinerStartStopAfterDownloaderEvents(t *testing.T) { 151 miner, mux, cleanup := createMiner(t) 152 defer cleanup(false) 153 154 miner.Start() 155 waitForMiningState(t, miner, true) 156 // Start the downloader 157 mux.Post(downloader.StartEvent{}) 158 waitForMiningState(t, miner, false) 159 160 // Downloader finally succeeds. 161 mux.Post(downloader.DoneEvent{}) 162 waitForMiningState(t, miner, true) 163 164 miner.Stop() 165 waitForMiningState(t, miner, false) 166 167 miner.Start() 168 waitForMiningState(t, miner, true) 169 170 miner.Stop() 171 waitForMiningState(t, miner, false) 172 } 173 174 func TestStartWhileDownload(t *testing.T) { 175 miner, mux, cleanup := createMiner(t) 176 defer cleanup(false) 177 waitForMiningState(t, miner, false) 178 miner.Start() 179 waitForMiningState(t, miner, true) 180 // Stop the downloader and wait for the update loop to run 181 mux.Post(downloader.StartEvent{}) 182 waitForMiningState(t, miner, false) 183 // Starting the miner after the downloader should not work 184 miner.Start() 185 waitForMiningState(t, miner, false) 186 } 187 188 func TestStartStopMiner(t *testing.T) { 189 miner, _, cleanup := createMiner(t) 190 defer cleanup(false) 191 waitForMiningState(t, miner, false) 192 miner.Start() 193 waitForMiningState(t, miner, true) 194 miner.Stop() 195 waitForMiningState(t, miner, false) 196 } 197 198 func TestCloseMiner(t *testing.T) { 199 miner, _, cleanup := createMiner(t) 200 defer cleanup(true) 201 waitForMiningState(t, miner, false) 202 miner.Start() 203 waitForMiningState(t, miner, true) 204 // Terminate the miner and wait for the update loop to run 205 miner.Close() 206 waitForMiningState(t, miner, false) 207 } 208 209 // TestMinerSetEtherbase checks that etherbase becomes set even if mining isn't 210 // possible at the moment 211 func TestMinerSetEtherbase(t *testing.T) { 212 miner, mux, cleanup := createMiner(t) 213 defer cleanup(false) 214 miner.Start() 215 waitForMiningState(t, miner, true) 216 // Start the downloader 217 mux.Post(downloader.StartEvent{}) 218 waitForMiningState(t, miner, false) 219 // Now user tries to configure proper mining address 220 miner.Start() 221 // Stop the downloader and wait for the update loop to run 222 mux.Post(downloader.DoneEvent{}) 223 waitForMiningState(t, miner, true) 224 225 coinbase := common.HexToAddress("0xdeedbeef") 226 miner.SetEtherbase(coinbase) 227 if addr := miner.worker.etherbase(); addr != coinbase { 228 t.Fatalf("Unexpected etherbase want %x got %x", coinbase, addr) 229 } 230 } 231 232 // waitForMiningState waits until either 233 // * the desired mining state was reached 234 // * a timeout was reached which fails the test 235 func waitForMiningState(t *testing.T, m *Miner, mining bool) { 236 t.Helper() 237 238 var state bool 239 for i := 0; i < 100; i++ { 240 time.Sleep(10 * time.Millisecond) 241 if state = m.Mining(); state == mining { 242 return 243 } 244 } 245 t.Fatalf("Mining() == %t, want %t", state, mining) 246 } 247 248 func createMiner(t *testing.T) (*Miner, *event.TypeMux, func(skipMiner bool)) { 249 // Create Ethash config 250 config := Config{ 251 Etherbase: common.HexToAddress("123456789"), 252 } 253 // Create chainConfig 254 chainDB := rawdb.NewMemoryDatabase() 255 genesis := core.DeveloperGenesisBlock(15, 11_500_000, common.HexToAddress("12345")) 256 chainConfig, _, err := core.SetupGenesisBlock(chainDB, trie.NewDatabase(chainDB), genesis) 257 if err != nil { 258 t.Fatalf("can't create new chain config: %v", err) 259 } 260 // Create consensus engine 261 engine := clique.New(chainConfig.Clique, chainDB) 262 // Create Ethereum backend 263 bc, err := core.NewBlockChain(chainDB, nil, genesis, nil, engine, vm.Config{}, nil, nil) 264 if err != nil { 265 t.Fatalf("can't create new chain %v", err) 266 } 267 statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(chainDB), nil) 268 blockchain := &testBlockChain{statedb, 10000000, new(event.Feed)} 269 270 pool := txpool.NewTxPool(testTxPoolConfig, chainConfig, blockchain) 271 backend := NewMockBackend(bc, pool) 272 // Create event Mux 273 mux := new(event.TypeMux) 274 // Create Miner 275 miner := New(backend, &config, chainConfig, mux, engine, nil) 276 cleanup := func(skipMiner bool) { 277 bc.Stop() 278 engine.Close() 279 pool.Stop() 280 if !skipMiner { 281 miner.Close() 282 } 283 } 284 return miner, mux, cleanup 285 }