github.com/theQRL/go-zond@v0.2.1/miner/worker.go (about) 1 // Copyright 2015 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 18 19 import ( 20 "errors" 21 "fmt" 22 "math/big" 23 "sync/atomic" 24 "time" 25 26 "github.com/theQRL/go-zond/common" 27 "github.com/theQRL/go-zond/consensus/misc/eip1559" 28 "github.com/theQRL/go-zond/core" 29 "github.com/theQRL/go-zond/core/state" 30 "github.com/theQRL/go-zond/core/txpool" 31 "github.com/theQRL/go-zond/core/types" 32 "github.com/theQRL/go-zond/core/vm" 33 "github.com/theQRL/go-zond/log" 34 "github.com/theQRL/go-zond/params" 35 ) 36 37 var ( 38 errBlockInterruptedByNewHead = errors.New("new head arrived while building block") 39 errBlockInterruptedByRecommit = errors.New("recommit interrupt while building block") 40 errBlockInterruptedByTimeout = errors.New("timeout while building block") 41 ) 42 43 // environment is the worker's current environment and holds all 44 // information of the sealing block generation. 45 type environment struct { 46 signer types.Signer 47 state *state.StateDB // apply state changes here 48 tcount int // tx count in cycle 49 gasPool *core.GasPool // available gas used to pack transactions 50 coinbase common.Address 51 52 header *types.Header 53 txs []*types.Transaction 54 receipts []*types.Receipt 55 } 56 57 const ( 58 commitInterruptNone int32 = iota 59 commitInterruptNewHead 60 commitInterruptResubmit 61 commitInterruptTimeout 62 ) 63 64 // newPayloadResult is the result of payload generation. 65 type newPayloadResult struct { 66 err error 67 block *types.Block 68 fees *big.Int // total block fees 69 stateDB *state.StateDB // StateDB after executing the transactions 70 receipts []*types.Receipt // Receipts collected during construction 71 } 72 73 // generateParams wraps various settings for generating sealing task. 74 type generateParams struct { 75 timestamp uint64 // The timestamp for sealing task 76 forceTime bool // Flag whether the given timestamp is immutable or not 77 parentHash common.Hash // Parent block hash, empty means the latest chain head 78 coinbase common.Address // The fee recipient address for including transaction 79 random common.Hash // The randomness generated by beacon chain, empty before the merge 80 withdrawals types.Withdrawals // List of withdrawals to include in block. 81 noTxs bool // Flag whether an empty block without any transaction is expected 82 } 83 84 // generateWork generates a sealing block based on the given parameters. 85 func (miner *Miner) generateWork(params *generateParams) *newPayloadResult { 86 work, err := miner.prepareWork(params) 87 if err != nil { 88 return &newPayloadResult{err: err} 89 } 90 if !params.noTxs { 91 interrupt := new(atomic.Int32) 92 timer := time.AfterFunc(miner.config.Recommit, func() { 93 interrupt.Store(commitInterruptTimeout) 94 }) 95 defer timer.Stop() 96 97 err := miner.fillTransactions(interrupt, work) 98 if errors.Is(err, errBlockInterruptedByTimeout) { 99 log.Warn("Block building is interrupted", "allowance", common.PrettyDuration(miner.config.Recommit)) 100 } 101 } 102 body := types.Body{Transactions: work.txs, Withdrawals: params.withdrawals} 103 block, err := miner.engine.FinalizeAndAssemble(miner.chain, work.header, work.state, &body, work.receipts) 104 if err != nil { 105 return &newPayloadResult{err: err} 106 } 107 return &newPayloadResult{ 108 block: block, 109 fees: totalFees(block, work.receipts), 110 stateDB: work.state, 111 receipts: work.receipts, 112 } 113 } 114 115 // prepareWork constructs the sealing task according to the given parameters, 116 // either based on the last chain head or specified parent. In this function 117 // the pending transactions are not filled yet, only the empty task returned. 118 func (miner *Miner) prepareWork(genParams *generateParams) (*environment, error) { 119 miner.confMu.RLock() 120 defer miner.confMu.RUnlock() 121 122 // Find the parent block for sealing task 123 parent := miner.chain.CurrentBlock() 124 if genParams.parentHash != (common.Hash{}) { 125 block := miner.chain.GetBlockByHash(genParams.parentHash) 126 if block == nil { 127 return nil, errors.New("missing parent") 128 } 129 parent = block.Header() 130 } 131 // Sanity check the timestamp correctness, recap the timestamp 132 // to parent+1 if the mutation is allowed. 133 timestamp := genParams.timestamp 134 if parent.Time >= timestamp { 135 if genParams.forceTime { 136 return nil, fmt.Errorf("invalid timestamp, parent %d given %d", parent.Time, timestamp) 137 } 138 timestamp = parent.Time + 1 139 } 140 // Construct the sealing block header. 141 header := &types.Header{ 142 ParentHash: parent.Hash(), 143 Number: new(big.Int).Add(parent.Number, common.Big1), 144 GasLimit: params.MaxGasLimit, 145 Time: timestamp, 146 Coinbase: genParams.coinbase, 147 } 148 // Set the extra field. 149 if len(miner.config.ExtraData) != 0 { 150 header.Extra = miner.config.ExtraData 151 } 152 // Set the randomness field from the beacon chain if it's available. 153 if genParams.random != (common.Hash{}) { 154 header.Random = genParams.random 155 } 156 // Set baseFee and GasLimit if we are on an EIP-1559 chain 157 header.BaseFee = eip1559.CalcBaseFee(miner.chainConfig, parent) 158 159 // Could potentially happen if starting to mine in an odd state.. 160 env, err := miner.makeEnv(parent, header, genParams.coinbase) 161 if err != nil { 162 log.Error("Failed to create sealing context", "err", err) 163 return nil, err 164 } 165 return env, nil 166 } 167 168 // makeEnv creates a new environment for the sealing block. 169 func (miner *Miner) makeEnv(parent *types.Header, header *types.Header, coinbase common.Address) (*environment, error) { 170 // Retrieve the parent state to execute on top. 171 state, err := miner.chain.StateAt(parent.Root) 172 if err != nil { 173 return nil, err 174 } 175 // Note the passed coinbase may be different with header.Coinbase. 176 return &environment{ 177 signer: types.MakeSigner(miner.chainConfig), 178 state: state, 179 coinbase: coinbase, 180 header: header, 181 }, nil 182 } 183 184 func (miner *Miner) commitTransaction(env *environment, tx *types.Transaction) error { 185 receipt, err := miner.applyTransaction(env, tx) 186 if err != nil { 187 return err 188 } 189 env.txs = append(env.txs, tx) 190 env.receipts = append(env.receipts, receipt) 191 env.tcount++ 192 return nil 193 } 194 195 // applyTransaction runs the transaction. If execution fails, state and gas pool are reverted. 196 func (miner *Miner) applyTransaction(env *environment, tx *types.Transaction) (*types.Receipt, error) { 197 var ( 198 snap = env.state.Snapshot() 199 gp = env.gasPool.Gas() 200 ) 201 receipt, err := core.ApplyTransaction(miner.chainConfig, miner.chain, &env.coinbase, env.gasPool, env.state, env.header, tx, &env.header.GasUsed, vm.Config{}) 202 if err != nil { 203 env.state.RevertToSnapshot(snap) 204 env.gasPool.SetGas(gp) 205 } 206 return receipt, err 207 } 208 209 func (miner *Miner) commitTransactions(env *environment, txs *transactionsByPriceAndNonce, interrupt *atomic.Int32) error { 210 gasLimit := env.header.GasLimit 211 if env.gasPool == nil { 212 env.gasPool = new(core.GasPool).AddGas(gasLimit) 213 } 214 215 for { 216 // Check interruption signal and abort building if it's fired. 217 if interrupt != nil { 218 if signal := interrupt.Load(); signal != commitInterruptNone { 219 return signalToErr(signal) 220 } 221 } 222 // If we don't have enough gas for any further transactions then we're done. 223 if env.gasPool.Gas() < params.TxGas { 224 log.Trace("Not enough gas for further transactions", "have", env.gasPool, "want", params.TxGas) 225 break 226 } 227 // Retrieve the next transaction and abort if all done. 228 ltx, _ := txs.Peek() 229 if ltx == nil { 230 break 231 } 232 // If we don't have enough space for the next transaction, skip the account. 233 if env.gasPool.Gas() < ltx.Gas { 234 log.Trace("Not enough gas left for transaction", "hash", ltx.Hash, "left", env.gasPool.Gas(), "needed", ltx.Gas) 235 txs.Pop() 236 continue 237 } 238 239 // Transaction seems to fit, pull it up from the pool 240 tx := ltx.Resolve() 241 if tx == nil { 242 log.Trace("Ignoring evicted transaction", "hash", ltx.Hash) 243 txs.Pop() 244 continue 245 } 246 247 // Error may be ignored here. The error has already been checked 248 // during transaction acceptance in the transaction pool. 249 from, _ := types.Sender(env.signer, tx) 250 251 // Start executing the transaction 252 env.state.SetTxContext(tx.Hash(), env.tcount) 253 254 err := miner.commitTransaction(env, tx) 255 switch { 256 case errors.Is(err, core.ErrNonceTooLow): 257 // New head notification data race between the transaction pool and miner, shift 258 log.Trace("Skipping transaction with low nonce", "hash", ltx.Hash, "sender", from, "nonce", tx.Nonce()) 259 txs.Shift() 260 261 case errors.Is(err, nil): 262 // Everything ok, collect the logs and shift in the next transaction from the same account 263 txs.Shift() 264 265 default: 266 // Transaction is regarded as invalid, drop all consecutive transactions from 267 // the same sender because of `nonce-too-high` clause. 268 log.Debug("Transaction failed, account skipped", "hash", ltx.Hash, "err", err) 269 txs.Pop() 270 } 271 } 272 273 return nil 274 } 275 276 // fillTransactions retrieves the pending transactions from the txpool and fills them 277 // into the given sealing block. The transaction selection and ordering strategy can 278 // be customized with the plugin in the future. 279 func (miner *Miner) fillTransactions(interrupt *atomic.Int32, env *environment) error { 280 miner.confMu.RLock() 281 tip := miner.config.GasPrice 282 miner.confMu.RUnlock() 283 284 // Retrieve the pending transactions pre-filtered by the 1559 dynamic fees 285 filter := txpool.PendingFilter{ 286 MinTip: tip, 287 } 288 if env.header.BaseFee != nil { 289 filter.BaseFee = env.header.BaseFee 290 } 291 pendingTxs := miner.txpool.Pending(filter) 292 293 // Split the pending transactions into locals and remotes. 294 localTxs, remoteTxs := make(map[common.Address][]*txpool.LazyTransaction), pendingTxs 295 for _, account := range miner.txpool.Locals() { 296 if txs := remoteTxs[account]; len(txs) > 0 { 297 delete(remoteTxs, account) 298 localTxs[account] = txs 299 } 300 } 301 // Fill the block with all available pending transactions. 302 if len(localTxs) > 0 { 303 txs := newTransactionsByPriceAndNonce(env.signer, localTxs, env.header.BaseFee) 304 if err := miner.commitTransactions(env, txs, interrupt); err != nil { 305 return err 306 } 307 } 308 if len(remoteTxs) > 0 { 309 txs := newTransactionsByPriceAndNonce(env.signer, remoteTxs, env.header.BaseFee) 310 if err := miner.commitTransactions(env, txs, interrupt); err != nil { 311 return err 312 } 313 } 314 return nil 315 } 316 317 // totalFees computes total consumed miner fees in Wei. Block transactions and receipts have to have the same order. 318 func totalFees(block *types.Block, receipts []*types.Receipt) *big.Int { 319 feesWei := new(big.Int) 320 for i, tx := range block.Transactions() { 321 minerFee, _ := tx.EffectiveGasTip(block.BaseFee()) 322 feesWei.Add(feesWei, new(big.Int).Mul(new(big.Int).SetUint64(receipts[i].GasUsed), minerFee)) 323 } 324 return feesWei 325 } 326 327 // signalToErr converts the interruption signal to a concrete error type for return. 328 // The given signal must be a valid interruption signal. 329 func signalToErr(signal int32) error { 330 switch signal { 331 case commitInterruptNewHead: 332 return errBlockInterruptedByNewHead 333 case commitInterruptResubmit: 334 return errBlockInterruptedByRecommit 335 case commitInterruptTimeout: 336 return errBlockInterruptedByTimeout 337 default: 338 panic(fmt.Errorf("undefined signal %d", signal)) 339 } 340 }