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