github.1485827954.workers.dev/ethereum/go-ethereum@v1.14.3/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/ethereum/go-ethereum/common" 27 "github.com/ethereum/go-ethereum/consensus/misc/eip1559" 28 "github.com/ethereum/go-ethereum/consensus/misc/eip4844" 29 "github.com/ethereum/go-ethereum/core" 30 "github.com/ethereum/go-ethereum/core/state" 31 "github.com/ethereum/go-ethereum/core/txpool" 32 "github.com/ethereum/go-ethereum/core/types" 33 "github.com/ethereum/go-ethereum/core/vm" 34 "github.com/ethereum/go-ethereum/log" 35 "github.com/ethereum/go-ethereum/params" 36 "github.com/holiman/uint256" 37 ) 38 39 var ( 40 errBlockInterruptedByNewHead = errors.New("new head arrived while building block") 41 errBlockInterruptedByRecommit = errors.New("recommit interrupt while building block") 42 errBlockInterruptedByTimeout = errors.New("timeout while building block") 43 ) 44 45 // environment is the worker's current environment and holds all 46 // information of the sealing block generation. 47 type environment struct { 48 signer types.Signer 49 state *state.StateDB // apply state changes here 50 tcount int // tx count in cycle 51 gasPool *core.GasPool // available gas used to pack transactions 52 coinbase common.Address 53 54 header *types.Header 55 txs []*types.Transaction 56 receipts []*types.Receipt 57 sidecars []*types.BlobTxSidecar 58 blobs int 59 } 60 61 const ( 62 commitInterruptNone int32 = iota 63 commitInterruptNewHead 64 commitInterruptResubmit 65 commitInterruptTimeout 66 ) 67 68 // newPayloadResult is the result of payload generation. 69 type newPayloadResult struct { 70 err error 71 block *types.Block 72 fees *big.Int // total block fees 73 sidecars []*types.BlobTxSidecar // collected blobs of blob transactions 74 stateDB *state.StateDB // StateDB after executing the transactions 75 receipts []*types.Receipt // Receipts collected during construction 76 } 77 78 // generateParams wraps various settings for generating sealing task. 79 type generateParams struct { 80 timestamp uint64 // The timestamp for sealing task 81 forceTime bool // Flag whether the given timestamp is immutable or not 82 parentHash common.Hash // Parent block hash, empty means the latest chain head 83 coinbase common.Address // The fee recipient address for including transaction 84 random common.Hash // The randomness generated by beacon chain, empty before the merge 85 withdrawals types.Withdrawals // List of withdrawals to include in block (shanghai field) 86 beaconRoot *common.Hash // The beacon root (cancun field). 87 noTxs bool // Flag whether an empty block without any transaction is expected 88 } 89 90 // generateWork generates a sealing block based on the given parameters. 91 func (miner *Miner) generateWork(params *generateParams) *newPayloadResult { 92 work, err := miner.prepareWork(params) 93 if err != nil { 94 return &newPayloadResult{err: err} 95 } 96 if !params.noTxs { 97 interrupt := new(atomic.Int32) 98 timer := time.AfterFunc(miner.config.Recommit, func() { 99 interrupt.Store(commitInterruptTimeout) 100 }) 101 defer timer.Stop() 102 103 err := miner.fillTransactions(interrupt, work) 104 if errors.Is(err, errBlockInterruptedByTimeout) { 105 log.Warn("Block building is interrupted", "allowance", common.PrettyDuration(miner.config.Recommit)) 106 } 107 } 108 body := types.Body{Transactions: work.txs, Withdrawals: params.withdrawals} 109 block, err := miner.engine.FinalizeAndAssemble(miner.chain, work.header, work.state, &body, work.receipts) 110 if err != nil { 111 return &newPayloadResult{err: err} 112 } 113 return &newPayloadResult{ 114 block: block, 115 fees: totalFees(block, work.receipts), 116 sidecars: work.sidecars, 117 stateDB: work.state, 118 receipts: work.receipts, 119 } 120 } 121 122 // prepareWork constructs the sealing task according to the given parameters, 123 // either based on the last chain head or specified parent. In this function 124 // the pending transactions are not filled yet, only the empty task returned. 125 func (miner *Miner) prepareWork(genParams *generateParams) (*environment, error) { 126 miner.confMu.RLock() 127 defer miner.confMu.RUnlock() 128 129 // Find the parent block for sealing task 130 parent := miner.chain.CurrentBlock() 131 if genParams.parentHash != (common.Hash{}) { 132 block := miner.chain.GetBlockByHash(genParams.parentHash) 133 if block == nil { 134 return nil, errors.New("missing parent") 135 } 136 parent = block.Header() 137 } 138 // Sanity check the timestamp correctness, recap the timestamp 139 // to parent+1 if the mutation is allowed. 140 timestamp := genParams.timestamp 141 if parent.Time >= timestamp { 142 if genParams.forceTime { 143 return nil, fmt.Errorf("invalid timestamp, parent %d given %d", parent.Time, timestamp) 144 } 145 timestamp = parent.Time + 1 146 } 147 // Construct the sealing block header. 148 header := &types.Header{ 149 ParentHash: parent.Hash(), 150 Number: new(big.Int).Add(parent.Number, common.Big1), 151 GasLimit: core.CalcGasLimit(parent.GasLimit, miner.config.GasCeil), 152 Time: timestamp, 153 Coinbase: genParams.coinbase, 154 } 155 // Set the extra field. 156 if len(miner.config.ExtraData) != 0 { 157 header.Extra = miner.config.ExtraData 158 } 159 // Set the randomness field from the beacon chain if it's available. 160 if genParams.random != (common.Hash{}) { 161 header.MixDigest = genParams.random 162 } 163 // Set baseFee and GasLimit if we are on an EIP-1559 chain 164 if miner.chainConfig.IsLondon(header.Number) { 165 header.BaseFee = eip1559.CalcBaseFee(miner.chainConfig, parent) 166 if !miner.chainConfig.IsLondon(parent.Number) { 167 parentGasLimit := parent.GasLimit * miner.chainConfig.ElasticityMultiplier() 168 header.GasLimit = core.CalcGasLimit(parentGasLimit, miner.config.GasCeil) 169 } 170 } 171 // Run the consensus preparation with the default or customized consensus engine. 172 // Note that the `header.Time` may be changed. 173 if err := miner.engine.Prepare(miner.chain, header); err != nil { 174 log.Error("Failed to prepare header for sealing", "err", err) 175 return nil, err 176 } 177 // Apply EIP-4844, EIP-4788. 178 if miner.chainConfig.IsCancun(header.Number, header.Time) { 179 var excessBlobGas uint64 180 if miner.chainConfig.IsCancun(parent.Number, parent.Time) { 181 excessBlobGas = eip4844.CalcExcessBlobGas(*parent.ExcessBlobGas, *parent.BlobGasUsed) 182 } else { 183 // For the first post-fork block, both parent.data_gas_used and parent.excess_data_gas are evaluated as 0 184 excessBlobGas = eip4844.CalcExcessBlobGas(0, 0) 185 } 186 header.BlobGasUsed = new(uint64) 187 header.ExcessBlobGas = &excessBlobGas 188 header.ParentBeaconRoot = genParams.beaconRoot 189 } 190 // Could potentially happen if starting to mine in an odd state. 191 // Note genParams.coinbase can be different with header.Coinbase 192 // since clique algorithm can modify the coinbase field in header. 193 env, err := miner.makeEnv(parent, header, genParams.coinbase) 194 if err != nil { 195 log.Error("Failed to create sealing context", "err", err) 196 return nil, err 197 } 198 if header.ParentBeaconRoot != nil { 199 context := core.NewEVMBlockContext(header, miner.chain, nil) 200 vmenv := vm.NewEVM(context, vm.TxContext{}, env.state, miner.chainConfig, vm.Config{}) 201 core.ProcessBeaconBlockRoot(*header.ParentBeaconRoot, vmenv, env.state) 202 } 203 return env, nil 204 } 205 206 // makeEnv creates a new environment for the sealing block. 207 func (miner *Miner) makeEnv(parent *types.Header, header *types.Header, coinbase common.Address) (*environment, error) { 208 // Retrieve the parent state to execute on top and start a prefetcher for 209 // the miner to speed block sealing up a bit. 210 state, err := miner.chain.StateAt(parent.Root) 211 if err != nil { 212 return nil, err 213 } 214 // Note the passed coinbase may be different with header.Coinbase. 215 return &environment{ 216 signer: types.MakeSigner(miner.chainConfig, header.Number, header.Time), 217 state: state, 218 coinbase: coinbase, 219 header: header, 220 }, nil 221 } 222 223 func (miner *Miner) commitTransaction(env *environment, tx *types.Transaction) error { 224 if tx.Type() == types.BlobTxType { 225 return miner.commitBlobTransaction(env, tx) 226 } 227 receipt, err := miner.applyTransaction(env, tx) 228 if err != nil { 229 return err 230 } 231 env.txs = append(env.txs, tx) 232 env.receipts = append(env.receipts, receipt) 233 env.tcount++ 234 return nil 235 } 236 237 func (miner *Miner) commitBlobTransaction(env *environment, tx *types.Transaction) error { 238 sc := tx.BlobTxSidecar() 239 if sc == nil { 240 panic("blob transaction without blobs in miner") 241 } 242 // Checking against blob gas limit: It's kind of ugly to perform this check here, but there 243 // isn't really a better place right now. The blob gas limit is checked at block validation time 244 // and not during execution. This means core.ApplyTransaction will not return an error if the 245 // tx has too many blobs. So we have to explicitly check it here. 246 if (env.blobs+len(sc.Blobs))*params.BlobTxBlobGasPerBlob > params.MaxBlobGasPerBlock { 247 return errors.New("max data blobs reached") 248 } 249 receipt, err := miner.applyTransaction(env, tx) 250 if err != nil { 251 return err 252 } 253 env.txs = append(env.txs, tx.WithoutBlobTxSidecar()) 254 env.receipts = append(env.receipts, receipt) 255 env.sidecars = append(env.sidecars, sc) 256 env.blobs += len(sc.Blobs) 257 *env.header.BlobGasUsed += receipt.BlobGasUsed 258 env.tcount++ 259 return nil 260 } 261 262 // applyTransaction runs the transaction. If execution fails, state and gas pool are reverted. 263 func (miner *Miner) applyTransaction(env *environment, tx *types.Transaction) (*types.Receipt, error) { 264 var ( 265 snap = env.state.Snapshot() 266 gp = env.gasPool.Gas() 267 ) 268 receipt, err := core.ApplyTransaction(miner.chainConfig, miner.chain, &env.coinbase, env.gasPool, env.state, env.header, tx, &env.header.GasUsed, vm.Config{}) 269 if err != nil { 270 env.state.RevertToSnapshot(snap) 271 env.gasPool.SetGas(gp) 272 } 273 return receipt, err 274 } 275 276 func (miner *Miner) commitTransactions(env *environment, plainTxs, blobTxs *transactionsByPriceAndNonce, interrupt *atomic.Int32) error { 277 gasLimit := env.header.GasLimit 278 if env.gasPool == nil { 279 env.gasPool = new(core.GasPool).AddGas(gasLimit) 280 } 281 for { 282 // Check interruption signal and abort building if it's fired. 283 if interrupt != nil { 284 if signal := interrupt.Load(); signal != commitInterruptNone { 285 return signalToErr(signal) 286 } 287 } 288 // If we don't have enough gas for any further transactions then we're done. 289 if env.gasPool.Gas() < params.TxGas { 290 log.Trace("Not enough gas for further transactions", "have", env.gasPool, "want", params.TxGas) 291 break 292 } 293 // If we don't have enough blob space for any further blob transactions, 294 // skip that list altogether 295 if !blobTxs.Empty() && env.blobs*params.BlobTxBlobGasPerBlob >= params.MaxBlobGasPerBlock { 296 log.Trace("Not enough blob space for further blob transactions") 297 blobTxs.Clear() 298 // Fall though to pick up any plain txs 299 } 300 // Retrieve the next transaction and abort if all done. 301 var ( 302 ltx *txpool.LazyTransaction 303 txs *transactionsByPriceAndNonce 304 ) 305 pltx, ptip := plainTxs.Peek() 306 bltx, btip := blobTxs.Peek() 307 308 switch { 309 case pltx == nil: 310 txs, ltx = blobTxs, bltx 311 case bltx == nil: 312 txs, ltx = plainTxs, pltx 313 default: 314 if ptip.Lt(btip) { 315 txs, ltx = blobTxs, bltx 316 } else { 317 txs, ltx = plainTxs, pltx 318 } 319 } 320 if ltx == nil { 321 break 322 } 323 // If we don't have enough space for the next transaction, skip the account. 324 if env.gasPool.Gas() < ltx.Gas { 325 log.Trace("Not enough gas left for transaction", "hash", ltx.Hash, "left", env.gasPool.Gas(), "needed", ltx.Gas) 326 txs.Pop() 327 continue 328 } 329 if left := uint64(params.MaxBlobGasPerBlock - env.blobs*params.BlobTxBlobGasPerBlob); left < ltx.BlobGas { 330 log.Trace("Not enough blob gas left for transaction", "hash", ltx.Hash, "left", left, "needed", ltx.BlobGas) 331 txs.Pop() 332 continue 333 } 334 // Transaction seems to fit, pull it up from the pool 335 tx := ltx.Resolve() 336 if tx == nil { 337 log.Trace("Ignoring evicted transaction", "hash", ltx.Hash) 338 txs.Pop() 339 continue 340 } 341 // Error may be ignored here. The error has already been checked 342 // during transaction acceptance in the transaction pool. 343 from, _ := types.Sender(env.signer, tx) 344 345 // Check whether the tx is replay protected. If we're not in the EIP155 hf 346 // phase, start ignoring the sender until we do. 347 if tx.Protected() && !miner.chainConfig.IsEIP155(env.header.Number) { 348 log.Trace("Ignoring replay protected transaction", "hash", ltx.Hash, "eip155", miner.chainConfig.EIP155Block) 349 txs.Pop() 350 continue 351 } 352 // Start executing the transaction 353 env.state.SetTxContext(tx.Hash(), env.tcount) 354 355 err := miner.commitTransaction(env, tx) 356 switch { 357 case errors.Is(err, core.ErrNonceTooLow): 358 // New head notification data race between the transaction pool and miner, shift 359 log.Trace("Skipping transaction with low nonce", "hash", ltx.Hash, "sender", from, "nonce", tx.Nonce()) 360 txs.Shift() 361 362 case errors.Is(err, nil): 363 // Everything ok, collect the logs and shift in the next transaction from the same account 364 txs.Shift() 365 366 default: 367 // Transaction is regarded as invalid, drop all consecutive transactions from 368 // the same sender because of `nonce-too-high` clause. 369 log.Debug("Transaction failed, account skipped", "hash", ltx.Hash, "err", err) 370 txs.Pop() 371 } 372 } 373 return nil 374 } 375 376 // fillTransactions retrieves the pending transactions from the txpool and fills them 377 // into the given sealing block. The transaction selection and ordering strategy can 378 // be customized with the plugin in the future. 379 func (miner *Miner) fillTransactions(interrupt *atomic.Int32, env *environment) error { 380 miner.confMu.RLock() 381 tip := miner.config.GasPrice 382 miner.confMu.RUnlock() 383 384 // Retrieve the pending transactions pre-filtered by the 1559/4844 dynamic fees 385 filter := txpool.PendingFilter{ 386 MinTip: uint256.MustFromBig(tip), 387 } 388 if env.header.BaseFee != nil { 389 filter.BaseFee = uint256.MustFromBig(env.header.BaseFee) 390 } 391 if env.header.ExcessBlobGas != nil { 392 filter.BlobFee = uint256.MustFromBig(eip4844.CalcBlobFee(*env.header.ExcessBlobGas)) 393 } 394 filter.OnlyPlainTxs, filter.OnlyBlobTxs = true, false 395 pendingPlainTxs := miner.txpool.Pending(filter) 396 397 filter.OnlyPlainTxs, filter.OnlyBlobTxs = false, true 398 pendingBlobTxs := miner.txpool.Pending(filter) 399 400 // Split the pending transactions into locals and remotes. 401 localPlainTxs, remotePlainTxs := make(map[common.Address][]*txpool.LazyTransaction), pendingPlainTxs 402 localBlobTxs, remoteBlobTxs := make(map[common.Address][]*txpool.LazyTransaction), pendingBlobTxs 403 404 for _, account := range miner.txpool.Locals() { 405 if txs := remotePlainTxs[account]; len(txs) > 0 { 406 delete(remotePlainTxs, account) 407 localPlainTxs[account] = txs 408 } 409 if txs := remoteBlobTxs[account]; len(txs) > 0 { 410 delete(remoteBlobTxs, account) 411 localBlobTxs[account] = txs 412 } 413 } 414 // Fill the block with all available pending transactions. 415 if len(localPlainTxs) > 0 || len(localBlobTxs) > 0 { 416 plainTxs := newTransactionsByPriceAndNonce(env.signer, localPlainTxs, env.header.BaseFee) 417 blobTxs := newTransactionsByPriceAndNonce(env.signer, localBlobTxs, env.header.BaseFee) 418 419 if err := miner.commitTransactions(env, plainTxs, blobTxs, interrupt); err != nil { 420 return err 421 } 422 } 423 if len(remotePlainTxs) > 0 || len(remoteBlobTxs) > 0 { 424 plainTxs := newTransactionsByPriceAndNonce(env.signer, remotePlainTxs, env.header.BaseFee) 425 blobTxs := newTransactionsByPriceAndNonce(env.signer, remoteBlobTxs, env.header.BaseFee) 426 427 if err := miner.commitTransactions(env, plainTxs, blobTxs, interrupt); err != nil { 428 return err 429 } 430 } 431 return nil 432 } 433 434 // totalFees computes total consumed miner fees in Wei. Block transactions and receipts have to have the same order. 435 func totalFees(block *types.Block, receipts []*types.Receipt) *big.Int { 436 feesWei := new(big.Int) 437 for i, tx := range block.Transactions() { 438 minerFee, _ := tx.EffectiveGasTip(block.BaseFee()) 439 feesWei.Add(feesWei, new(big.Int).Mul(new(big.Int).SetUint64(receipts[i].GasUsed), minerFee)) 440 } 441 return feesWei 442 } 443 444 // signalToErr converts the interruption signal to a concrete error type for return. 445 // The given signal must be a valid interruption signal. 446 func signalToErr(signal int32) error { 447 switch signal { 448 case commitInterruptNewHead: 449 return errBlockInterruptedByNewHead 450 case commitInterruptResubmit: 451 return errBlockInterruptedByRecommit 452 case commitInterruptTimeout: 453 return errBlockInterruptedByTimeout 454 default: 455 panic(fmt.Errorf("undefined signal %d", signal)) 456 } 457 }