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