gitlab.com/flarenetwork/coreth@v0.1.1/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/ethereum/go-ethereum/common" 40 "github.com/ethereum/go-ethereum/event" 41 "github.com/ethereum/go-ethereum/log" 42 "gitlab.com/flarenetwork/coreth/consensus" 43 "gitlab.com/flarenetwork/coreth/consensus/dummy" 44 "gitlab.com/flarenetwork/coreth/consensus/misc" 45 "gitlab.com/flarenetwork/coreth/core" 46 "gitlab.com/flarenetwork/coreth/core/state" 47 "gitlab.com/flarenetwork/coreth/core/types" 48 "gitlab.com/flarenetwork/coreth/params" 49 ) 50 51 // environment is the worker's current environment and holds all of the current state information. 52 type environment struct { 53 signer types.Signer 54 55 state *state.StateDB // apply state changes here 56 tcount int // tx count in cycle 57 gasPool *core.GasPool // available gas used to pack transactions 58 59 header *types.Header 60 txs []*types.Transaction 61 receipts []*types.Receipt 62 63 start time.Time // Time that block building began 64 } 65 66 // worker is the main object which takes care of submitting new work to consensus engine 67 // and gathering the sealing result. 68 type worker struct { 69 config *Config 70 chainConfig *params.ChainConfig 71 engine consensus.Engine 72 eth Backend 73 chain *core.BlockChain 74 75 // Feeds 76 // TODO remove since this will never be written to 77 pendingLogsFeed event.Feed 78 79 // Subscriptions 80 mux *event.TypeMux // TODO replace 81 mu sync.RWMutex // The lock used to protect the coinbase and extra fields 82 coinbase common.Address 83 } 84 85 func newWorker(config *Config, chainConfig *params.ChainConfig, engine consensus.Engine, eth Backend, mux *event.TypeMux) *worker { 86 worker := &worker{ 87 config: config, 88 chainConfig: chainConfig, 89 engine: engine, 90 eth: eth, 91 mux: mux, 92 chain: eth.BlockChain(), 93 } 94 95 return worker 96 } 97 98 // setEtherbase sets the etherbase used to initialize the block coinbase field. 99 func (w *worker) setEtherbase(addr common.Address) { 100 w.mu.Lock() 101 defer w.mu.Unlock() 102 w.coinbase = addr 103 } 104 105 // commitNewWork generates several new sealing tasks based on the parent block. 106 func (w *worker) commitNewWork() (*types.Block, error) { 107 w.mu.RLock() 108 defer w.mu.RUnlock() 109 110 tstart := time.Now() 111 timestamp := tstart.Unix() 112 parent := w.chain.CurrentBlock() 113 // Note: in order to support asynchronous block production, blocks are allowed to have 114 // the same timestamp as their parent. This allows more than one block to be produced 115 // per second. 116 if parent.Time() >= uint64(timestamp) { 117 timestamp = int64(parent.Time()) 118 } 119 120 var gasLimit uint64 121 if w.chainConfig.IsApricotPhase1(big.NewInt(timestamp)) { 122 gasLimit = params.ApricotPhase1GasLimit 123 } else { 124 // The gas limit is set in phase1 to ApricotPhase1GasLimit because the ceiling and floor were set to the same value 125 // such that the gas limit converged to it. Since this is hardbaked now, we remove the ability to configure it. 126 gasLimit = core.CalcGasLimit(parent.GasUsed(), parent.GasLimit(), params.ApricotPhase1GasLimit, params.ApricotPhase1GasLimit) 127 } 128 num := parent.Number() 129 header := &types.Header{ 130 ParentHash: parent.Hash(), 131 Number: num.Add(num, common.Big1), 132 GasLimit: gasLimit, 133 Extra: nil, 134 Time: uint64(timestamp), 135 } 136 // Set BaseFee and Extra data field if we are post ApricotPhase3 137 bigTimestamp := big.NewInt(timestamp) 138 if w.chainConfig.IsApricotPhase3(bigTimestamp) { 139 var err error 140 header.Extra, header.BaseFee, err = dummy.CalcBaseFee(w.chainConfig, parent.Header(), uint64(timestamp)) 141 if err != nil { 142 return nil, fmt.Errorf("failed to calculate new base fee: %w", err) 143 } 144 } 145 if w.coinbase == (common.Address{}) { 146 return nil, errors.New("cannot mine without etherbase") 147 } 148 header.Coinbase = w.coinbase 149 if err := w.engine.Prepare(w.chain, header); err != nil { 150 return nil, fmt.Errorf("failed to prepare header for mining: %w", err) 151 } 152 153 env, err := w.createCurrentEnvironment(parent, header, tstart) 154 if err != nil { 155 return nil, fmt.Errorf("failed to create new current environment: %w", err) 156 } 157 if w.chainConfig.DAOForkSupport && w.chainConfig.DAOForkBlock != nil && w.chainConfig.DAOForkBlock.Cmp(header.Number) == 0 { 158 misc.ApplyDAOHardFork(env.state) 159 } 160 161 // Fill the block with all available pending transactions. 162 pending, err := w.eth.TxPool().Pending(true) 163 if err != nil { 164 return nil, fmt.Errorf("failed to fetch pending transactions: %w", err) 165 } 166 167 // Split the pending transactions into locals and remotes 168 localTxs := make(map[common.Address]types.Transactions) 169 remoteTxs := pending 170 for _, account := range w.eth.TxPool().Locals() { 171 if txs := remoteTxs[account]; len(txs) > 0 { 172 delete(remoteTxs, account) 173 localTxs[account] = txs 174 } 175 } 176 if len(localTxs) > 0 { 177 txs := types.NewTransactionsByPriceAndNonce(env.signer, localTxs, header.BaseFee) 178 w.commitTransactions(env, txs, w.coinbase) 179 } 180 if len(remoteTxs) > 0 { 181 txs := types.NewTransactionsByPriceAndNonce(env.signer, remoteTxs, header.BaseFee) 182 w.commitTransactions(env, txs, w.coinbase) 183 } 184 185 return w.commit(env) 186 } 187 188 func (w *worker) createCurrentEnvironment(parent *types.Block, header *types.Header, tstart time.Time) (*environment, error) { 189 state, err := w.chain.StateAt(parent.Root()) 190 if err != nil { 191 return nil, err 192 } 193 return &environment{ 194 signer: types.MakeSigner(w.chainConfig, header.Number, new(big.Int).SetUint64(header.Time)), 195 state: state, 196 header: header, 197 tcount: 0, 198 gasPool: new(core.GasPool).AddGas(header.GasLimit), 199 start: tstart, 200 }, nil 201 } 202 203 func (w *worker) commitTransaction(env *environment, tx *types.Transaction, coinbase common.Address) ([]*types.Log, error) { 204 snap := env.state.Snapshot() 205 206 receipt, err := core.ApplyTransaction(w.chainConfig, w.chain, &coinbase, env.gasPool, env.state, env.header, tx, &env.header.GasUsed, *w.chain.GetVMConfig()) 207 if err != nil { 208 env.state.RevertToSnapshot(snap) 209 return nil, err 210 } 211 env.txs = append(env.txs, tx) 212 env.receipts = append(env.receipts, receipt) 213 214 return receipt.Logs, nil 215 } 216 217 func (w *worker) commitTransactions(env *environment, txs *types.TransactionsByPriceAndNonce, coinbase common.Address) { 218 for { 219 // If we don't have enough gas for any further transactions then we're done 220 if env.gasPool.Gas() < params.TxGas { 221 log.Trace("Not enough gas for further transactions", "have", env.gasPool, "want", params.TxGas) 222 break 223 } 224 // Retrieve the next transaction and abort if all done 225 tx := txs.Peek() 226 if tx == nil { 227 break 228 } 229 // Error may be ignored here. The error has already been checked 230 // during transaction acceptance is the transaction pool. 231 // 232 // We use the eip155 signer regardless of the current hf. 233 from, _ := types.Sender(env.signer, tx) 234 // Check whether the tx is replay protected. If we're not in the EIP155 hf 235 // phase, start ignoring the sender until we do. 236 if tx.Protected() && !w.chainConfig.IsEIP155(env.header.Number) { 237 log.Trace("Ignoring reply protected transaction", "hash", tx.Hash(), "eip155", w.chainConfig.EIP155Block) 238 239 txs.Pop() 240 continue 241 } 242 // Start executing the transaction 243 env.state.Prepare(tx.Hash(), env.tcount) 244 245 _, err := w.commitTransaction(env, tx, coinbase) 246 switch { 247 case errors.Is(err, core.ErrGasLimitReached): 248 // Pop the current out-of-gas transaction without shifting in the next from the account 249 log.Trace("Gas limit exceeded for current block", "sender", from) 250 txs.Pop() 251 252 case errors.Is(err, core.ErrNonceTooLow): 253 // New head notification data race between the transaction pool and miner, shift 254 log.Trace("Skipping transaction with low nonce", "sender", from, "nonce", tx.Nonce()) 255 txs.Shift() 256 257 case errors.Is(err, core.ErrNonceTooHigh): 258 // Reorg notification data race between the transaction pool and miner, skip account = 259 log.Trace("Skipping account with high nonce", "sender", from, "nonce", tx.Nonce()) 260 txs.Pop() 261 262 case errors.Is(err, nil): 263 env.tcount++ 264 txs.Shift() 265 266 case errors.Is(err, core.ErrTxTypeNotSupported): 267 // Pop the unsupported transaction without shifting in the next from the account 268 log.Trace("Skipping unsupported transaction type", "sender", from, "type", tx.Type()) 269 txs.Pop() 270 271 default: 272 // Strange error, discard the transaction and get the next in line (note, the 273 // nonce-too-high clause will prevent us from executing in vain). 274 log.Debug("Transaction failed, account skipped", "hash", tx.Hash(), "err", err) 275 txs.Shift() 276 } 277 } 278 } 279 280 // commit runs any post-transaction state modifications, assembles the final block 281 // and commits new work if consensus engine is running. 282 func (w *worker) commit(env *environment) (*types.Block, error) { 283 // Deep copy receipts here to avoid interaction between different tasks. 284 receipts := copyReceipts(env.receipts) 285 block, err := w.engine.FinalizeAndAssemble(w.chain, env.header, env.state, env.txs, nil, receipts) 286 if err != nil { 287 return nil, err 288 } 289 290 return w.handleResult(env, block, time.Now(), receipts) 291 } 292 293 func (w *worker) handleResult(env *environment, block *types.Block, createdAt time.Time, unfinishedReceipts []*types.Receipt) (*types.Block, error) { 294 // Short circuit when receiving duplicate result caused by resubmitting. 295 if w.chain.HasBlock(block.Hash(), block.NumberU64()) { 296 return nil, fmt.Errorf("produced duplicate block (Hash: %s, Number %d)", block.Hash(), block.NumberU64()) 297 } 298 var ( 299 hash = block.Hash() 300 ) 301 // Different block could share same sealhash, deep copy here to prevent write-write conflict. 302 var ( 303 receipts = make([]*types.Receipt, len(unfinishedReceipts)) 304 logs []*types.Log 305 ) 306 for i, receipt := range unfinishedReceipts { 307 // add block location fields 308 receipt.BlockHash = hash 309 receipt.BlockNumber = block.Number() 310 receipt.TransactionIndex = uint(i) 311 312 receipts[i] = new(types.Receipt) 313 *receipts[i] = *receipt 314 // Update the block hash in all logs since it is now available and not when the 315 // receipt/log of individual transactions were created. 316 for _, log := range receipt.Logs { 317 log.BlockHash = hash 318 } 319 logs = append(logs, receipt.Logs...) 320 } 321 322 log.Info("Commit new mining work", "number", block.Number(), "hash", hash, "uncles", 0, "txs", env.tcount, 323 "gas", block.GasUsed(), "fees", totalFees(block, receipts), "elapsed", common.PrettyDuration(time.Since(env.start))) 324 325 // Note: the miner no longer emits a NewMinedBlock event. Instead the caller 326 // is responsible for running any additional verification and then inserting 327 // the block with InsertChain, which will also emit a new head event. 328 return block, nil 329 } 330 331 // copyReceipts makes a deep copy of the given receipts. 332 func copyReceipts(receipts []*types.Receipt) []*types.Receipt { 333 result := make([]*types.Receipt, len(receipts)) 334 for i, l := range receipts { 335 cpy := *l 336 result[i] = &cpy 337 } 338 return result 339 } 340 341 // totalFees computes total consumed fees in ETH. Block transactions and receipts have to have the same order. 342 func totalFees(block *types.Block, receipts []*types.Receipt) *big.Float { 343 feesWei := new(big.Int) 344 for i, tx := range block.Transactions() { 345 feesWei.Add(feesWei, new(big.Int).Mul(new(big.Int).SetUint64(receipts[i].GasUsed), tx.GasPrice())) 346 } 347 return new(big.Float).Quo(new(big.Float).SetInt(feesWei), new(big.Float).SetInt(big.NewInt(params.Ether))) 348 }