github.com/dim4egster/coreth@v0.10.2/miner/worker.go (about) 1 // (c) 2019-2020, Ava Labs, Inc. 2 // 3 // This file is a derived work, based on the go-ethereum library whose original 4 // notices appear below. 5 // 6 // It is distributed under a license compatible with the licensing terms of the 7 // original code from which it is derived. 8 // 9 // Much love to the original authors for their work. 10 // ********** 11 // Copyright 2015 The go-ethereum Authors 12 // This file is part of the go-ethereum library. 13 // 14 // The go-ethereum library is free software: you can redistribute it and/or modify 15 // it under the terms of the GNU Lesser General Public License as published by 16 // the Free Software Foundation, either version 3 of the License, or 17 // (at your option) any later version. 18 // 19 // The go-ethereum library is distributed in the hope that it will be useful, 20 // but WITHOUT ANY WARRANTY; without even the implied warranty of 21 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 // GNU Lesser General Public License for more details. 23 // 24 // You should have received a copy of the GNU Lesser General Public License 25 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 26 // 27 // NOTE: this piece of code is modified by Ted Yin. 28 // The modification is also licensed under the same LGPL. 29 30 package miner 31 32 import ( 33 "errors" 34 "fmt" 35 "math/big" 36 "sync" 37 "time" 38 39 "github.com/dim4egster/qmallgo/utils/timer/mockable" 40 "github.com/dim4egster/qmallgo/utils/units" 41 "github.com/dim4egster/coreth/consensus" 42 "github.com/dim4egster/coreth/consensus/dummy" 43 "github.com/dim4egster/coreth/consensus/misc" 44 "github.com/dim4egster/coreth/core" 45 "github.com/dim4egster/coreth/core/state" 46 "github.com/dim4egster/coreth/core/types" 47 "github.com/dim4egster/coreth/params" 48 "github.com/dim4egster/coreth/vmerrs" 49 "github.com/ethereum/go-ethereum/common" 50 "github.com/ethereum/go-ethereum/event" 51 "github.com/ethereum/go-ethereum/log" 52 ) 53 54 const ( 55 targetTxsSize = 192 * units.KiB 56 ) 57 58 // environment is the worker's current environment and holds all of the current state information. 59 type environment struct { 60 signer types.Signer 61 62 state *state.StateDB // apply state changes here 63 tcount int // tx count in cycle 64 gasPool *core.GasPool // available gas used to pack transactions 65 66 parent *types.Header 67 header *types.Header 68 txs []*types.Transaction 69 receipts []*types.Receipt 70 size common.StorageSize 71 72 start time.Time // Time that block building began 73 } 74 75 // worker is the main object which takes care of submitting new work to consensus engine 76 // and gathering the sealing result. 77 type worker struct { 78 config *Config 79 chainConfig *params.ChainConfig 80 engine consensus.Engine 81 eth Backend 82 chain *core.BlockChain 83 84 // Feeds 85 // TODO remove since this will never be written to 86 pendingLogsFeed event.Feed 87 88 // Subscriptions 89 mux *event.TypeMux // TODO replace 90 mu sync.RWMutex // The lock used to protect the coinbase and extra fields 91 coinbase common.Address 92 clock *mockable.Clock // Allows us mock the clock for testing 93 } 94 95 func newWorker(config *Config, chainConfig *params.ChainConfig, engine consensus.Engine, eth Backend, mux *event.TypeMux, clock *mockable.Clock) *worker { 96 worker := &worker{ 97 config: config, 98 chainConfig: chainConfig, 99 engine: engine, 100 eth: eth, 101 mux: mux, 102 chain: eth.BlockChain(), 103 clock: clock, 104 } 105 106 return worker 107 } 108 109 // setEtherbase sets the etherbase used to initialize the block coinbase field. 110 func (w *worker) setEtherbase(addr common.Address) { 111 w.mu.Lock() 112 defer w.mu.Unlock() 113 w.coinbase = addr 114 } 115 116 // commitNewWork generates several new sealing tasks based on the parent block. 117 func (w *worker) commitNewWork() (*types.Block, error) { 118 w.mu.RLock() 119 defer w.mu.RUnlock() 120 121 tstart := w.clock.Time() 122 timestamp := tstart.Unix() 123 parent := w.chain.CurrentBlock() 124 // Note: in order to support asynchronous block production, blocks are allowed to have 125 // the same timestamp as their parent. This allows more than one block to be produced 126 // per second. 127 if parent.Time() >= uint64(timestamp) { 128 timestamp = int64(parent.Time()) 129 } 130 131 var gasLimit uint64 132 if w.chainConfig.IsApricotPhase1(big.NewInt(timestamp)) { 133 gasLimit = params.ApricotPhase1GasLimit 134 } else { 135 // The gas limit is set in phase1 to ApricotPhase1GasLimit because the ceiling and floor were set to the same value 136 // such that the gas limit converged to it. Since this is hardbaked now, we remove the ability to configure it. 137 gasLimit = core.CalcGasLimit(parent.GasUsed(), parent.GasLimit(), params.ApricotPhase1GasLimit, params.ApricotPhase1GasLimit) 138 } 139 num := parent.Number() 140 header := &types.Header{ 141 ParentHash: parent.Hash(), 142 Number: num.Add(num, common.Big1), 143 GasLimit: gasLimit, 144 Extra: nil, 145 Time: uint64(timestamp), 146 } 147 // Set BaseFee and Extra data field if we are post ApricotPhase3 148 bigTimestamp := big.NewInt(timestamp) 149 if w.chainConfig.IsApricotPhase3(bigTimestamp) { 150 var err error 151 header.Extra, header.BaseFee, err = dummy.CalcBaseFee(w.chainConfig, parent.Header(), uint64(timestamp)) 152 if err != nil { 153 return nil, fmt.Errorf("failed to calculate new base fee: %w", err) 154 } 155 } 156 if w.coinbase == (common.Address{}) { 157 return nil, errors.New("cannot mine without etherbase") 158 } 159 header.Coinbase = w.coinbase 160 if err := w.engine.Prepare(w.chain, header); err != nil { 161 return nil, fmt.Errorf("failed to prepare header for mining: %w", err) 162 } 163 164 env, err := w.createCurrentEnvironment(parent, header, tstart) 165 if err != nil { 166 return nil, fmt.Errorf("failed to create new current environment: %w", err) 167 } 168 if w.chainConfig.DAOForkSupport && w.chainConfig.DAOForkBlock != nil && w.chainConfig.DAOForkBlock.Cmp(header.Number) == 0 { 169 misc.ApplyDAOHardFork(env.state) 170 } 171 // Configure any stateful precompiles that should go into effect during this block. 172 w.chainConfig.CheckConfigurePrecompiles(new(big.Int).SetUint64(parent.Time()), types.NewBlockWithHeader(header), env.state) 173 174 // Fill the block with all available pending transactions. 175 pending := w.eth.TxPool().Pending(true) 176 177 // Split the pending transactions into locals and remotes 178 localTxs := make(map[common.Address]types.Transactions) 179 remoteTxs := pending 180 for _, account := range w.eth.TxPool().Locals() { 181 if txs := remoteTxs[account]; len(txs) > 0 { 182 delete(remoteTxs, account) 183 localTxs[account] = txs 184 } 185 } 186 if len(localTxs) > 0 { 187 txs := types.NewTransactionsByPriceAndNonce(env.signer, localTxs, header.BaseFee) 188 w.commitTransactions(env, txs, w.coinbase) 189 } 190 if len(remoteTxs) > 0 { 191 txs := types.NewTransactionsByPriceAndNonce(env.signer, remoteTxs, header.BaseFee) 192 w.commitTransactions(env, txs, w.coinbase) 193 } 194 195 return w.commit(env) 196 } 197 198 func (w *worker) createCurrentEnvironment(parent *types.Block, header *types.Header, tstart time.Time) (*environment, error) { 199 state, err := w.chain.StateAt(parent.Root()) 200 if err != nil { 201 return nil, err 202 } 203 return &environment{ 204 signer: types.MakeSigner(w.chainConfig, header.Number, new(big.Int).SetUint64(header.Time)), 205 state: state, 206 parent: parent.Header(), 207 header: header, 208 tcount: 0, 209 gasPool: new(core.GasPool).AddGas(header.GasLimit), 210 start: tstart, 211 }, nil 212 } 213 214 func (w *worker) commitTransaction(env *environment, tx *types.Transaction, coinbase common.Address) ([]*types.Log, error) { 215 snap := env.state.Snapshot() 216 217 receipt, err := core.ApplyTransaction(w.chainConfig, w.chain, &coinbase, env.gasPool, env.state, env.header, tx, &env.header.GasUsed, *w.chain.GetVMConfig()) 218 if err != nil { 219 env.state.RevertToSnapshot(snap) 220 return nil, err 221 } 222 env.txs = append(env.txs, tx) 223 env.receipts = append(env.receipts, receipt) 224 env.size += tx.Size() 225 226 return receipt.Logs, nil 227 } 228 229 func (w *worker) commitTransactions(env *environment, txs *types.TransactionsByPriceAndNonce, coinbase common.Address) { 230 for { 231 // If we don't have enough gas for any further transactions then we're done 232 if env.gasPool.Gas() < params.TxGas { 233 log.Trace("Not enough gas for further transactions", "have", env.gasPool, "want", params.TxGas) 234 break 235 } 236 // Retrieve the next transaction and abort if all done 237 tx := txs.Peek() 238 if tx == nil { 239 break 240 } 241 // Abort transaction if it won't fit in the block and continue to search for a smaller 242 // transction that will fit. 243 if totalTxsSize := env.size + tx.Size(); totalTxsSize > targetTxsSize { 244 log.Trace("Skipping transaction that would exceed target size", "hash", tx.Hash(), "totalTxsSize", totalTxsSize, "txSize", tx.Size()) 245 246 txs.Pop() 247 continue 248 } 249 // Error may be ignored here. The error has already been checked 250 // during transaction acceptance is the transaction pool. 251 // 252 // We use the eip155 signer regardless of the current hf. 253 from, _ := types.Sender(env.signer, tx) 254 // Check whether the tx is replay protected. If we're not in the EIP155 hf 255 // phase, start ignoring the sender until we do. 256 if tx.Protected() && !w.chainConfig.IsEIP155(env.header.Number) { 257 log.Trace("Ignoring reply protected transaction", "hash", tx.Hash(), "eip155", w.chainConfig.EIP155Block) 258 259 txs.Pop() 260 continue 261 } 262 // Start executing the transaction 263 env.state.Prepare(tx.Hash(), env.tcount) 264 265 _, err := w.commitTransaction(env, tx, coinbase) 266 switch { 267 case errors.Is(err, core.ErrGasLimitReached): 268 // Pop the current out-of-gas transaction without shifting in the next from the account 269 log.Trace("Gas limit exceeded for current block", "sender", from) 270 txs.Pop() 271 272 case errors.Is(err, core.ErrNonceTooLow): 273 // New head notification data race between the transaction pool and miner, shift 274 log.Trace("Skipping transaction with low nonce", "sender", from, "nonce", tx.Nonce()) 275 txs.Shift() 276 277 case errors.Is(err, core.ErrNonceTooHigh): 278 // Reorg notification data race between the transaction pool and miner, skip account = 279 log.Trace("Skipping account with high nonce", "sender", from, "nonce", tx.Nonce()) 280 txs.Pop() 281 282 case errors.Is(err, nil): 283 env.tcount++ 284 txs.Shift() 285 286 case errors.Is(err, core.ErrTxTypeNotSupported): 287 // Pop the unsupported transaction without shifting in the next from the account 288 log.Trace("Skipping unsupported transaction type", "sender", from, "type", tx.Type()) 289 txs.Pop() 290 291 case errors.Is(err, vmerrs.ErrToAddrProhibitedSoft): 292 log.Warn("Tx dropped: failed verification", "tx", tx.Hash(), "sender", from, "data", tx.Data(), "err", err) 293 w.eth.TxPool().RemoveTx(tx.Hash()) 294 txs.Pop() 295 default: 296 // Strange error, discard the transaction and get the next in line (note, the 297 // nonce-too-high clause will prevent us from executing in vain). 298 log.Debug("Transaction failed, account skipped", "hash", tx.Hash(), "err", err) 299 txs.Shift() 300 } 301 } 302 } 303 304 // commit runs any post-transaction state modifications, assembles the final block 305 // and commits new work if consensus engine is running. 306 func (w *worker) commit(env *environment) (*types.Block, error) { 307 // Deep copy receipts here to avoid interaction between different tasks. 308 receipts := copyReceipts(env.receipts) 309 block, err := w.engine.FinalizeAndAssemble(w.chain, env.header, env.parent, env.state, env.txs, nil, receipts) 310 if err != nil { 311 return nil, err 312 } 313 314 return w.handleResult(env, block, time.Now(), receipts) 315 } 316 317 func (w *worker) handleResult(env *environment, block *types.Block, createdAt time.Time, unfinishedReceipts []*types.Receipt) (*types.Block, error) { 318 // Short circuit when receiving duplicate result caused by resubmitting. 319 if w.chain.HasBlock(block.Hash(), block.NumberU64()) { 320 return nil, fmt.Errorf("produced duplicate block (Hash: %s, Number %d)", block.Hash(), block.NumberU64()) 321 } 322 // Different block could share same sealhash, deep copy here to prevent write-write conflict. 323 var ( 324 hash = block.Hash() 325 receipts = make([]*types.Receipt, len(unfinishedReceipts)) 326 logs []*types.Log 327 ) 328 for i, unfinishedReceipt := range unfinishedReceipts { 329 receipt := new(types.Receipt) 330 receipts[i] = receipt 331 *receipt = *unfinishedReceipt 332 333 // add block location fields 334 receipt.BlockHash = hash 335 receipt.BlockNumber = block.Number() 336 receipt.TransactionIndex = uint(i) 337 338 // Update the block hash in all logs since it is now available and not when the 339 // receipt/log of individual transactions were created. 340 receipt.Logs = make([]*types.Log, len(unfinishedReceipt.Logs)) 341 for j, unfinishedLog := range unfinishedReceipt.Logs { 342 log := new(types.Log) 343 receipt.Logs[j] = log 344 *log = *unfinishedLog 345 log.BlockHash = hash 346 } 347 logs = append(logs, receipt.Logs...) 348 } 349 350 log.Info("Commit new mining work", "number", block.Number(), "hash", hash, "uncles", 0, "txs", env.tcount, 351 "gas", block.GasUsed(), "fees", totalFees(block, receipts), "elapsed", common.PrettyDuration(time.Since(env.start))) 352 353 // Note: the miner no longer emits a NewMinedBlock event. Instead the caller 354 // is responsible for running any additional verification and then inserting 355 // the block with InsertChain, which will also emit a new head event. 356 return block, nil 357 } 358 359 // copyReceipts makes a deep copy of the given receipts. 360 func copyReceipts(receipts []*types.Receipt) []*types.Receipt { 361 result := make([]*types.Receipt, len(receipts)) 362 for i, l := range receipts { 363 cpy := *l 364 result[i] = &cpy 365 } 366 return result 367 } 368 369 // totalFees computes total consumed fees in ETH. Block transactions and receipts have to have the same order. 370 func totalFees(block *types.Block, receipts []*types.Receipt) *big.Float { 371 feesWei := new(big.Int) 372 for i, tx := range block.Transactions() { 373 feesWei.Add(feesWei, new(big.Int).Mul(new(big.Int).SetUint64(receipts[i].GasUsed), tx.GasPrice())) 374 } 375 return new(big.Float).Quo(new(big.Float).SetInt(feesWei), new(big.Float).SetInt(big.NewInt(params.Ether))) 376 }