mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-19 10:22:23 +00:00
feat: add ccc into miner (#674)
* add core/rawdb/accessors_row_consumption.go * update core/rawdb/schema.go * update eth/ethconfig/config.go * update eth/ethconfig/gen_config.go * update miner/miner.go * update eth/backend.go * update core/types.go * update cmd/geth/main.go * update cmd/utils/flags.go * update core/types/block.go * update core/block_validator.go * WIP: update miner/worker.go * WIP: update miner/worker.go * WIP: update miner/worker.go * WIP: update miner/worker.go * WIP: update miner/worker.go * WIP: update miner/worker.go add withTimer * WIP: update miner/worker.go * WIP: update miner/worker.go add some func * WIP: update miner/worker.go `makeEnv` * fix * fix * minor * init traces return * init accRows * update `applyTransaction` * fix `TestPriorityClient` * miner: update metrics * minor * add `l2CommitTxTraceStateRevertTimer` * update `calcAndSetAccRowsForEnv` * start to work on `commitTransactions` * seal block early if we're over time * If we have collected enough transactions then we're done * check L1 message queue index * A single L1 message leads to out-of-gas * more on nil err * more error * skip RemoveTx for now * minor * l1TxStrangeErr * fix interface * start to work on `commitWork` * minor * draft `fillTransactions` * fix some types * refactor * fix * add `circuitCapacityReached` * minor * fix `txToLazyTx` * fix `(m *mockBackend) ChainDb()` * fix `removeTx` * update comments
This commit is contained in:
parent
bdcede5221
commit
ca1ba1da87
21 changed files with 812 additions and 87 deletions
|
|
@ -123,6 +123,7 @@ var (
|
||||||
utils.MinerExtraDataFlag,
|
utils.MinerExtraDataFlag,
|
||||||
utils.MinerRecommitIntervalFlag,
|
utils.MinerRecommitIntervalFlag,
|
||||||
utils.MinerNewPayloadTimeout,
|
utils.MinerNewPayloadTimeout,
|
||||||
|
utils.MinerStoreSkippedTxTracesFlag,
|
||||||
utils.NATFlag,
|
utils.NATFlag,
|
||||||
utils.NoDiscoverFlag,
|
utils.NoDiscoverFlag,
|
||||||
utils.DiscoveryV4Flag,
|
utils.DiscoveryV4Flag,
|
||||||
|
|
@ -147,6 +148,7 @@ var (
|
||||||
utils.L1EndpointFlag,
|
utils.L1EndpointFlag,
|
||||||
utils.L1ConfirmationsFlag,
|
utils.L1ConfirmationsFlag,
|
||||||
utils.L1DeploymentBlockFlag,
|
utils.L1DeploymentBlockFlag,
|
||||||
|
utils.CircuitCapacityCheckEnabledFlag,
|
||||||
utils.RollupVerifyEnabledFlag,
|
utils.RollupVerifyEnabledFlag,
|
||||||
}, utils.NetworkFlags, utils.DatabaseFlags)
|
}, utils.NetworkFlags, utils.DatabaseFlags)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -503,6 +503,10 @@ var (
|
||||||
Value: ethconfig.Defaults.Miner.NewPayloadTimeout,
|
Value: ethconfig.Defaults.Miner.NewPayloadTimeout,
|
||||||
Category: flags.MinerCategory,
|
Category: flags.MinerCategory,
|
||||||
}
|
}
|
||||||
|
MinerStoreSkippedTxTracesFlag = &cli.BoolFlag{
|
||||||
|
Name: "miner.storeskippedtxtraces",
|
||||||
|
Usage: "Store the wrapped traces when storing a skipped tx",
|
||||||
|
}
|
||||||
|
|
||||||
// Account settings
|
// Account settings
|
||||||
UnlockedAccountFlag = &cli.StringFlag{
|
UnlockedAccountFlag = &cli.StringFlag{
|
||||||
|
|
@ -962,6 +966,12 @@ Please note that --` + MetricsHTTPFlag.Name + ` must be set to start the server.
|
||||||
Usage: "L1 block height to start syncing from. Should be set to the L1 message queue deployment block number.",
|
Usage: "L1 block height to start syncing from. Should be set to the L1 message queue deployment block number.",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Circuit capacity check settings
|
||||||
|
CircuitCapacityCheckEnabledFlag = &cli.BoolFlag{
|
||||||
|
Name: "ccc",
|
||||||
|
Usage: "Enable circuit capacity check during block validation",
|
||||||
|
}
|
||||||
|
|
||||||
// Rollup verify service settings
|
// Rollup verify service settings
|
||||||
RollupVerifyEnabledFlag = &cli.BoolFlag{
|
RollupVerifyEnabledFlag = &cli.BoolFlag{
|
||||||
Name: "rollup.verify",
|
Name: "rollup.verify",
|
||||||
|
|
@ -1641,6 +1651,9 @@ func setMiner(ctx *cli.Context, cfg *miner.Config) {
|
||||||
if ctx.IsSet(MinerNewPayloadTimeout.Name) {
|
if ctx.IsSet(MinerNewPayloadTimeout.Name) {
|
||||||
cfg.NewPayloadTimeout = ctx.Duration(MinerNewPayloadTimeout.Name)
|
cfg.NewPayloadTimeout = ctx.Duration(MinerNewPayloadTimeout.Name)
|
||||||
}
|
}
|
||||||
|
if ctx.IsSet(MinerStoreSkippedTxTracesFlag.Name) {
|
||||||
|
cfg.StoreSkippedTxTraces = ctx.Bool(MinerStoreSkippedTxTracesFlag.Name)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func setRequiredBlocks(ctx *cli.Context, cfg *ethconfig.Config) {
|
func setRequiredBlocks(ctx *cli.Context, cfg *ethconfig.Config) {
|
||||||
|
|
@ -1671,6 +1684,12 @@ func setRequiredBlocks(ctx *cli.Context, cfg *ethconfig.Config) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setCircuitCapacityCheck(ctx *cli.Context, cfg *ethconfig.Config) {
|
||||||
|
if ctx.IsSet(CircuitCapacityCheckEnabledFlag.Name) {
|
||||||
|
cfg.CheckCircuitCapacity = ctx.Bool(CircuitCapacityCheckEnabledFlag.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func setEnableRollupVerify(ctx *cli.Context, cfg *ethconfig.Config) {
|
func setEnableRollupVerify(ctx *cli.Context, cfg *ethconfig.Config) {
|
||||||
if ctx.IsSet(RollupVerifyEnabledFlag.Name) {
|
if ctx.IsSet(RollupVerifyEnabledFlag.Name) {
|
||||||
cfg.EnableRollupVerify = ctx.Bool(RollupVerifyEnabledFlag.Name)
|
cfg.EnableRollupVerify = ctx.Bool(RollupVerifyEnabledFlag.Name)
|
||||||
|
|
@ -1740,6 +1759,7 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
|
||||||
setMiner(ctx, &cfg.Miner)
|
setMiner(ctx, &cfg.Miner)
|
||||||
setRequiredBlocks(ctx, cfg)
|
setRequiredBlocks(ctx, cfg)
|
||||||
setLes(ctx, cfg)
|
setLes(ctx, cfg)
|
||||||
|
setCircuitCapacityCheck(ctx, cfg)
|
||||||
setEnableRollupVerify(ctx, cfg)
|
setEnableRollupVerify(ctx, cfg)
|
||||||
setMaxBlockRange(ctx, cfg)
|
setMaxBlockRange(ctx, cfg)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -155,9 +155,9 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error {
|
||||||
// if a block's RowConsumption has been stored, which means it has been processed before,
|
// if a block's RowConsumption has been stored, which means it has been processed before,
|
||||||
// (e.g., in miner/worker.go or in insertChain),
|
// (e.g., in miner/worker.go or in insertChain),
|
||||||
// we simply skip its calculation and validation
|
// we simply skip its calculation and validation
|
||||||
// if rawdb.ReadBlockRowConsumption(v.bc.db, block.Hash()) != nil {
|
if rawdb.ReadBlockRowConsumption(v.bc.db, block.Hash()) != nil {
|
||||||
// return nil
|
return nil
|
||||||
// }
|
}
|
||||||
rowConsumption, err := v.validateCircuitRowConsumption(block)
|
rowConsumption, err := v.validateCircuitRowConsumption(block)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -169,7 +169,7 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error {
|
||||||
"hash", block.Hash().String(),
|
"hash", block.Hash().String(),
|
||||||
"rowConsumption", rowConsumption,
|
"rowConsumption", rowConsumption,
|
||||||
)
|
)
|
||||||
// rawdb.WriteBlockRowConsumption(v.bc.db, block.Hash(), rowConsumption)
|
rawdb.WriteBlockRowConsumption(v.bc.db, block.Hash(), rowConsumption)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
51
core/rawdb/accessors_row_consumption.go
Normal file
51
core/rawdb/accessors_row_consumption.go
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
package rawdb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
|
)
|
||||||
|
|
||||||
|
// WriteBlockRowConsumption writes a RowConsumption of the block to the database.
|
||||||
|
func WriteBlockRowConsumption(db ethdb.KeyValueWriter, l2BlockHash common.Hash, rc *types.RowConsumption) {
|
||||||
|
if rc == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes, err := rlp.EncodeToBytes(&rc)
|
||||||
|
if err != nil {
|
||||||
|
log.Crit("Failed to RLP encode RowConsumption ", "err", err)
|
||||||
|
}
|
||||||
|
if err := db.Put(rowConsumptionKey(l2BlockHash), bytes); err != nil {
|
||||||
|
log.Crit("Failed to store RowConsumption ", "err", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadBlockRowConsumption retrieves the RowConsumption corresponding to the block hash.
|
||||||
|
func ReadBlockRowConsumption(db ethdb.Reader, l2BlockHash common.Hash) *types.RowConsumption {
|
||||||
|
data := ReadBlockRowConsumptionRLP(db, l2BlockHash)
|
||||||
|
if len(data) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
rc := new(types.RowConsumption)
|
||||||
|
if err := rlp.Decode(bytes.NewReader(data), rc); err != nil {
|
||||||
|
log.Crit("Invalid RowConsumption message RLP", "l2BlockHash", l2BlockHash.String(), "data", data, "err", err)
|
||||||
|
}
|
||||||
|
return rc
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadBlockRowConsumption retrieves the RowConsumption in its raw RLP database encoding.
|
||||||
|
func ReadBlockRowConsumptionRLP(db ethdb.Reader, l2BlockHash common.Hash) rlp.RawValue {
|
||||||
|
data, err := db.Get(rowConsumptionKey(l2BlockHash))
|
||||||
|
if err != nil && isNotFoundErr(err) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
log.Crit("Failed to load RowConsumption", "l2BlockHash", l2BlockHash.String(), "err", err)
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
@ -153,6 +153,9 @@ var (
|
||||||
batchMetaPrefix = []byte("R-bm")
|
batchMetaPrefix = []byte("R-bm")
|
||||||
finalizedL2BlockNumberKey = []byte("R-finalized")
|
finalizedL2BlockNumberKey = []byte("R-finalized")
|
||||||
|
|
||||||
|
// Row consumption
|
||||||
|
rowConsumptionPrefix = []byte("rc") // rowConsumptionPrefix + hash -> row consumption by block
|
||||||
|
|
||||||
// Skipped transactions
|
// Skipped transactions
|
||||||
numSkippedTransactionsKey = []byte("NumberOfSkippedTransactions")
|
numSkippedTransactionsKey = []byte("NumberOfSkippedTransactions")
|
||||||
skippedTransactionPrefix = []byte("skip") // skippedTransactionPrefix + tx hash -> skipped transaction
|
skippedTransactionPrefix = []byte("skip") // skippedTransactionPrefix + tx hash -> skipped transaction
|
||||||
|
|
@ -387,6 +390,11 @@ func FirstQueueIndexNotInL2BlockKey(l2BlockHash common.Hash) []byte {
|
||||||
return append(firstQueueIndexNotInL2BlockPrefix, l2BlockHash.Bytes()...)
|
return append(firstQueueIndexNotInL2BlockPrefix, l2BlockHash.Bytes()...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// rowConsumptionKey = rowConsumptionPrefix + hash
|
||||||
|
func rowConsumptionKey(hash common.Hash) []byte {
|
||||||
|
return append(rowConsumptionPrefix, hash.Bytes()...)
|
||||||
|
}
|
||||||
|
|
||||||
// SkippedTransactionKey = skippedTransactionPrefix + tx hash
|
// SkippedTransactionKey = skippedTransactionPrefix + tx hash
|
||||||
func SkippedTransactionKey(txHash common.Hash) []byte {
|
func SkippedTransactionKey(txHash common.Hash) []byte {
|
||||||
return append(skippedTransactionPrefix, txHash.Bytes()...)
|
return append(skippedTransactionPrefix, txHash.Bytes()...)
|
||||||
|
|
|
||||||
|
|
@ -1557,3 +1557,8 @@ func (p *BlobPool) Status(hash common.Hash) txpool.TxStatus {
|
||||||
}
|
}
|
||||||
return txpool.TxStatusUnknown
|
return txpool.TxStatusUnknown
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// added for interface compatibility, do nothing
|
||||||
|
func (p *BlobPool) RemoveTx(hash common.Hash, outofbound bool, unreserve bool) int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1063,6 +1063,15 @@ func (pool *LegacyPool) Has(hash common.Hash) bool {
|
||||||
return pool.all.Get(hash) != nil
|
return pool.all.Get(hash) != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RemoveTx is similar to removeTx, but with locking to prevent concurrency.
|
||||||
|
// Note: currently should only be called by miner/worker.go.
|
||||||
|
func (pool *LegacyPool) RemoveTx(hash common.Hash, outofbound bool, unreserve bool) int {
|
||||||
|
pool.mu.Lock()
|
||||||
|
defer pool.mu.Unlock()
|
||||||
|
|
||||||
|
return pool.removeTx(hash, outofbound, unreserve)
|
||||||
|
}
|
||||||
|
|
||||||
// removeTx removes a single transaction from the queue, moving all subsequent
|
// removeTx removes a single transaction from the queue, moving all subsequent
|
||||||
// transactions back to the future queue.
|
// transactions back to the future queue.
|
||||||
//
|
//
|
||||||
|
|
|
||||||
|
|
@ -137,4 +137,7 @@ type SubPool interface {
|
||||||
// Status returns the known status (unknown/pending/queued) of a transaction
|
// Status returns the known status (unknown/pending/queued) of a transaction
|
||||||
// identified by their hashes.
|
// identified by their hashes.
|
||||||
Status(hash common.Hash) TxStatus
|
Status(hash common.Hash) TxStatus
|
||||||
|
|
||||||
|
// RemoveTx removes a transaction from the pool, returning the number of transactions removed.
|
||||||
|
RemoveTx(hash common.Hash, outofbound bool, unreserve bool) int
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -415,3 +415,12 @@ func (p *TxPool) Status(hash common.Hash) TxStatus {
|
||||||
}
|
}
|
||||||
return TxStatusUnknown
|
return TxStatusUnknown
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RemoveTx removes a transaction from the pool, returning the number of transactions removed.
|
||||||
|
func (p *TxPool) RemoveTx(hash common.Hash, outofbound bool, unreserve bool) int {
|
||||||
|
var ret int
|
||||||
|
for _, subpool := range p.subpools {
|
||||||
|
ret += subpool.RemoveTx(hash, outofbound, unreserve)
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,10 @@ type Validator interface {
|
||||||
// ValidateState validates the given statedb and optionally the receipts and
|
// ValidateState validates the given statedb and optionally the receipts and
|
||||||
// gas used.
|
// gas used.
|
||||||
ValidateState(block *types.Block, state *state.StateDB, receipts types.Receipts, usedGas uint64) error
|
ValidateState(block *types.Block, state *state.StateDB, receipts types.Receipts, usedGas uint64) error
|
||||||
|
|
||||||
|
// SetupTracerAndCircuitCapacityChecker sets up ScrollTracerWrapper and CircuitCapacityChecker for validator,
|
||||||
|
// to get scroll-related traces and to validate the circuit row consumption
|
||||||
|
SetupTracerAndCircuitCapacityChecker(tracer tracerWrapper)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prefetcher is an interface for pre-caching transaction signatures and state.
|
// Prefetcher is an interface for pre-caching transaction signatures and state.
|
||||||
|
|
|
||||||
|
|
@ -557,6 +557,11 @@ func (b *Block) CountL2Tx() int {
|
||||||
|
|
||||||
type Blocks []*Block
|
type Blocks []*Block
|
||||||
|
|
||||||
|
type BlockWithRowConsumption struct {
|
||||||
|
*Block
|
||||||
|
*RowConsumption
|
||||||
|
}
|
||||||
|
|
||||||
// HeaderParentHashFromRLP returns the parentHash of an RLP-encoded
|
// HeaderParentHashFromRLP returns the parentHash of an RLP-encoded
|
||||||
// header. If 'header' is invalid, the zero hash is returned.
|
// header. If 'header' is invalid, the zero hash is returned.
|
||||||
func HeaderParentHashFromRLP(header []byte) common.Hash {
|
func HeaderParentHashFromRLP(header []byte) common.Hash {
|
||||||
|
|
|
||||||
|
|
@ -19,17 +19,14 @@ package types
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"math/big"
|
"math/big"
|
||||||
"sort"
|
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/math"
|
"github.com/ethereum/go-ethereum/common/math"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -613,45 +610,3 @@ func copyAddressPtr(a *common.Address) *common.Address {
|
||||||
cpy := *a
|
cpy := *a
|
||||||
return &cpy
|
return &cpy
|
||||||
}
|
}
|
||||||
|
|
||||||
// L1MessagesByQueueIndex represents a set of L1 messages ordered by their queue indices.
|
|
||||||
type L1MessagesByQueueIndex struct {
|
|
||||||
msgs []L1MessageTx
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewL1MessagesByQueueIndex(msgs []L1MessageTx) (*L1MessagesByQueueIndex, error) {
|
|
||||||
// sort by queue index
|
|
||||||
sort.Slice(msgs, func(i, j int) bool {
|
|
||||||
return msgs[i].QueueIndex < msgs[j].QueueIndex
|
|
||||||
})
|
|
||||||
|
|
||||||
// check for duplicates/gaps
|
|
||||||
for ii := 0; ii < len(msgs)-1; ii++ {
|
|
||||||
current := msgs[ii].QueueIndex
|
|
||||||
next := msgs[ii+1].QueueIndex
|
|
||||||
if next != current+1 {
|
|
||||||
return nil, fmt.Errorf("invalid L1 message set, current index: %d, next index: %d", current, next)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &L1MessagesByQueueIndex{msgs: msgs}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *L1MessagesByQueueIndex) Peek() *Transaction {
|
|
||||||
if len(t.msgs) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return NewTx(&t.msgs[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *L1MessagesByQueueIndex) Shift() {
|
|
||||||
t.msgs = t.msgs[1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *L1MessagesByQueueIndex) Pop() {
|
|
||||||
log.Error("Pop() is called on L1MessagesByQueueIndex")
|
|
||||||
|
|
||||||
// this is a logic error, the intention should be "Shift()",
|
|
||||||
// so we will follow the same behavior in Pop
|
|
||||||
t.Shift()
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
"github.com/ethereum/go-ethereum/rollup/rollup_sync_service"
|
"github.com/ethereum/go-ethereum/rollup/rollup_sync_service"
|
||||||
"github.com/ethereum/go-ethereum/rollup/sync_service"
|
"github.com/ethereum/go-ethereum/rollup/sync_service"
|
||||||
|
"github.com/ethereum/go-ethereum/rollup/tracing"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -218,6 +219,10 @@ func New(stack *node.Node, config *ethconfig.Config, l1Client sync_service.EthCl
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if config.CheckCircuitCapacity {
|
||||||
|
tracer := tracing.NewTracerWrapper()
|
||||||
|
eth.blockchain.Validator().SetupTracerAndCircuitCapacityChecker(tracer)
|
||||||
|
}
|
||||||
eth.bloomIndexer.Start(eth.blockchain)
|
eth.bloomIndexer.Start(eth.blockchain)
|
||||||
|
|
||||||
if config.BlobPool.Datadir != "" {
|
if config.BlobPool.Datadir != "" {
|
||||||
|
|
|
||||||
|
|
@ -170,6 +170,9 @@ type Config struct {
|
||||||
// OverrideVerkle (TODO: remove after the fork)
|
// OverrideVerkle (TODO: remove after the fork)
|
||||||
OverrideVerkle *uint64 `toml:",omitempty"`
|
OverrideVerkle *uint64 `toml:",omitempty"`
|
||||||
|
|
||||||
|
// Check circuit capacity in block validator
|
||||||
|
CheckCircuitCapacity bool
|
||||||
|
|
||||||
// Enable verification of batch consistency between L1 and L2 in rollup
|
// Enable verification of batch consistency between L1 and L2 in rollup
|
||||||
EnableRollupVerify bool
|
EnableRollupVerify bool
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,7 @@ func (c Config) MarshalTOML() (interface{}, error) {
|
||||||
RPCTxFeeCap float64
|
RPCTxFeeCap float64
|
||||||
OverrideCancun *uint64 `toml:",omitempty"`
|
OverrideCancun *uint64 `toml:",omitempty"`
|
||||||
OverrideVerkle *uint64 `toml:",omitempty"`
|
OverrideVerkle *uint64 `toml:",omitempty"`
|
||||||
|
CheckCircuitCapacity bool
|
||||||
EnableRollupVerify bool
|
EnableRollupVerify bool
|
||||||
MaxBlockRange int64
|
MaxBlockRange int64
|
||||||
}
|
}
|
||||||
|
|
@ -99,6 +100,7 @@ func (c Config) MarshalTOML() (interface{}, error) {
|
||||||
enc.RPCTxFeeCap = c.RPCTxFeeCap
|
enc.RPCTxFeeCap = c.RPCTxFeeCap
|
||||||
enc.OverrideCancun = c.OverrideCancun
|
enc.OverrideCancun = c.OverrideCancun
|
||||||
enc.OverrideVerkle = c.OverrideVerkle
|
enc.OverrideVerkle = c.OverrideVerkle
|
||||||
|
enc.CheckCircuitCapacity = c.CheckCircuitCapacity
|
||||||
enc.EnableRollupVerify = c.EnableRollupVerify
|
enc.EnableRollupVerify = c.EnableRollupVerify
|
||||||
enc.MaxBlockRange = c.MaxBlockRange
|
enc.MaxBlockRange = c.MaxBlockRange
|
||||||
return &enc, nil
|
return &enc, nil
|
||||||
|
|
@ -146,6 +148,7 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error {
|
||||||
RPCTxFeeCap *float64
|
RPCTxFeeCap *float64
|
||||||
OverrideCancun *uint64 `toml:",omitempty"`
|
OverrideCancun *uint64 `toml:",omitempty"`
|
||||||
OverrideVerkle *uint64 `toml:",omitempty"`
|
OverrideVerkle *uint64 `toml:",omitempty"`
|
||||||
|
CheckCircuitCapacity *bool
|
||||||
EnableRollupVerify *bool
|
EnableRollupVerify *bool
|
||||||
MaxBlockRange *int64
|
MaxBlockRange *int64
|
||||||
}
|
}
|
||||||
|
|
@ -270,6 +273,9 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error {
|
||||||
if dec.OverrideVerkle != nil {
|
if dec.OverrideVerkle != nil {
|
||||||
c.OverrideVerkle = dec.OverrideVerkle
|
c.OverrideVerkle = dec.OverrideVerkle
|
||||||
}
|
}
|
||||||
|
if dec.CheckCircuitCapacity != nil {
|
||||||
|
c.CheckCircuitCapacity = *dec.CheckCircuitCapacity
|
||||||
|
}
|
||||||
if dec.EnableRollupVerify != nil {
|
if dec.EnableRollupVerify != nil {
|
||||||
c.EnableRollupVerify = *dec.EnableRollupVerify
|
c.EnableRollupVerify = *dec.EnableRollupVerify
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,9 +31,11 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/txpool"
|
"github.com/ethereum/go-ethereum/core/txpool"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/eth/downloader"
|
"github.com/ethereum/go-ethereum/eth/downloader"
|
||||||
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
"github.com/ethereum/go-ethereum/rollup/sync_service"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Backend wraps all methods required for mining. Only full node is capable
|
// Backend wraps all methods required for mining. Only full node is capable
|
||||||
|
|
@ -41,6 +43,8 @@ import (
|
||||||
type Backend interface {
|
type Backend interface {
|
||||||
BlockChain() *core.BlockChain
|
BlockChain() *core.BlockChain
|
||||||
TxPool() *txpool.TxPool
|
TxPool() *txpool.TxPool
|
||||||
|
ChainDb() ethdb.Database
|
||||||
|
SyncService() *sync_service.SyncService
|
||||||
}
|
}
|
||||||
|
|
||||||
// Config is the configuration parameters of mining.
|
// Config is the configuration parameters of mining.
|
||||||
|
|
@ -53,6 +57,8 @@ type Config struct {
|
||||||
Recommit time.Duration // The time interval for miner to re-create mining work.
|
Recommit time.Duration // The time interval for miner to re-create mining work.
|
||||||
|
|
||||||
NewPayloadTimeout time.Duration // The maximum time allowance for creating a new payload
|
NewPayloadTimeout time.Duration // The maximum time allowance for creating a new payload
|
||||||
|
|
||||||
|
StoreSkippedTxTraces bool // Whether store the wrapped traces when storing a skipped tx
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultConfig contains default settings for miner.
|
// DefaultConfig contains default settings for miner.
|
||||||
|
|
|
||||||
|
|
@ -34,20 +34,24 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/eth/downloader"
|
"github.com/ethereum/go-ethereum/eth/downloader"
|
||||||
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
"github.com/ethereum/go-ethereum/rollup/sync_service"
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
)
|
)
|
||||||
|
|
||||||
type mockBackend struct {
|
type mockBackend struct {
|
||||||
bc *core.BlockChain
|
bc *core.BlockChain
|
||||||
txPool *txpool.TxPool
|
txPool *txpool.TxPool
|
||||||
|
chainDb ethdb.Database
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMockBackend(bc *core.BlockChain, txPool *txpool.TxPool) *mockBackend {
|
func NewMockBackend(bc *core.BlockChain, txPool *txpool.TxPool, chainDb ethdb.Database) *mockBackend {
|
||||||
return &mockBackend{
|
return &mockBackend{
|
||||||
bc: bc,
|
bc: bc,
|
||||||
txPool: txPool,
|
txPool: txPool,
|
||||||
|
chainDb: chainDb,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -59,6 +63,14 @@ func (m *mockBackend) TxPool() *txpool.TxPool {
|
||||||
return m.txPool
|
return m.txPool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *mockBackend) ChainDb() ethdb.Database {
|
||||||
|
return m.chainDb
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockBackend) SyncService() *sync_service.SyncService {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (m *mockBackend) StateAtBlock(block *types.Block, reexec uint64, base *state.StateDB, checkLive bool, preferDisk bool) (statedb *state.StateDB, err error) {
|
func (m *mockBackend) StateAtBlock(block *types.Block, reexec uint64, base *state.StateDB, checkLive bool, preferDisk bool) (statedb *state.StateDB, err error) {
|
||||||
return nil, errors.New("not supported")
|
return nil, errors.New("not supported")
|
||||||
}
|
}
|
||||||
|
|
@ -312,7 +324,7 @@ func createMiner(t *testing.T) (*Miner, *event.TypeMux, func(skipMiner bool)) {
|
||||||
pool := legacypool.New(testTxPoolConfig, blockchain)
|
pool := legacypool.New(testTxPoolConfig, blockchain)
|
||||||
txpool, _ := txpool.New(new(big.Int).SetUint64(testTxPoolConfig.PriceLimit), blockchain, []txpool.SubPool{pool})
|
txpool, _ := txpool.New(new(big.Int).SetUint64(testTxPoolConfig.PriceLimit), blockchain, []txpool.SubPool{pool})
|
||||||
|
|
||||||
backend := NewMockBackend(bc, txpool)
|
backend := NewMockBackend(bc, txpool, chainDB)
|
||||||
// Create event Mux
|
// Create event Mux
|
||||||
mux := new(event.TypeMux)
|
mux := new(event.TypeMux)
|
||||||
// Create Miner
|
// Create Miner
|
||||||
|
|
|
||||||
|
|
@ -18,12 +18,15 @@ package miner
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"container/heap"
|
"container/heap"
|
||||||
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
"sort"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/math"
|
"github.com/ethereum/go-ethereum/common/math"
|
||||||
"github.com/ethereum/go-ethereum/core/txpool"
|
"github.com/ethereum/go-ethereum/core/txpool"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
// txWithMinerFee wraps a transaction with its gas price or effective miner gasTipCap
|
// txWithMinerFee wraps a transaction with its gas price or effective miner gasTipCap
|
||||||
|
|
@ -145,3 +148,58 @@ func (t *transactionsByPriceAndNonce) Shift() {
|
||||||
func (t *transactionsByPriceAndNonce) Pop() {
|
func (t *transactionsByPriceAndNonce) Pop() {
|
||||||
heap.Pop(&t.heads)
|
heap.Pop(&t.heads)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// l1MessagesByQueueIndex represents a set of L1 messages ordered by their queue indices.
|
||||||
|
type l1MessagesByQueueIndex struct {
|
||||||
|
txPool *txpool.TxPool
|
||||||
|
msgs []types.L1MessageTx
|
||||||
|
}
|
||||||
|
|
||||||
|
func newL1MessagesByQueueIndex(txPool *txpool.TxPool, msgs []types.L1MessageTx) (*l1MessagesByQueueIndex, error) {
|
||||||
|
// sort by queue index
|
||||||
|
sort.Slice(msgs, func(i, j int) bool {
|
||||||
|
return msgs[i].QueueIndex < msgs[j].QueueIndex
|
||||||
|
})
|
||||||
|
|
||||||
|
// check for duplicates/gaps
|
||||||
|
for ii := 0; ii < len(msgs)-1; ii++ {
|
||||||
|
current := msgs[ii].QueueIndex
|
||||||
|
next := msgs[ii+1].QueueIndex
|
||||||
|
if next != current+1 {
|
||||||
|
return nil, fmt.Errorf("invalid L1 message set, current index: %d, next index: %d", current, next)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &l1MessagesByQueueIndex{txPool: txPool, msgs: msgs}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *l1MessagesByQueueIndex) Peek() *txpool.LazyTransaction {
|
||||||
|
if len(t.msgs) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return txToLazyTx(t.txPool, types.NewTx(&t.msgs[0]))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *l1MessagesByQueueIndex) Shift() {
|
||||||
|
t.msgs = t.msgs[1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *l1MessagesByQueueIndex) Pop() {
|
||||||
|
log.Error("Pop() is called on l1MessagesByQueueIndex")
|
||||||
|
|
||||||
|
// this is a logic error, the intention should be "Shift()",
|
||||||
|
// so we will follow the same behavior in Pop
|
||||||
|
t.Shift()
|
||||||
|
}
|
||||||
|
|
||||||
|
// orderedTransactionSet represents a set of transactions and some ordering on top of this set.
|
||||||
|
type orderedTransactionSet interface {
|
||||||
|
// Peek returns the next transaction.
|
||||||
|
Peek() *txpool.LazyTransaction
|
||||||
|
|
||||||
|
// Shift removes the next transaction.
|
||||||
|
Shift()
|
||||||
|
|
||||||
|
// Pop removes all transactions from the current account.
|
||||||
|
Pop()
|
||||||
|
}
|
||||||
|
|
|
||||||
613
miner/worker.go
613
miner/worker.go
|
|
@ -29,13 +29,17 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/consensus/misc/eip1559"
|
"github.com/ethereum/go-ethereum/consensus/misc/eip1559"
|
||||||
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
|
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/txpool"
|
"github.com/ethereum/go-ethereum/core/txpool"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
"github.com/ethereum/go-ethereum/metrics"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
"github.com/ethereum/go-ethereum/rollup/circuitcapacitychecker"
|
||||||
|
"github.com/ethereum/go-ethereum/rollup/tracing"
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -79,6 +83,40 @@ var (
|
||||||
errBlockInterruptedByTimeout = errors.New("timeout while building block")
|
errBlockInterruptedByTimeout = errors.New("timeout while building block")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Metrics for the skipped txs
|
||||||
|
l1TxGasLimitExceededCounter = metrics.NewRegisteredCounter("miner/skipped_txs/l1/gas_limit_exceeded", nil)
|
||||||
|
l1TxRowConsumptionOverflowCounter = metrics.NewRegisteredCounter("miner/skipped_txs/l1/row_consumption_overflow", nil)
|
||||||
|
l2TxRowConsumptionOverflowCounter = metrics.NewRegisteredCounter("miner/skipped_txs/l2/row_consumption_overflow", nil)
|
||||||
|
l1TxCccUnknownErrCounter = metrics.NewRegisteredCounter("miner/skipped_txs/l1/ccc_unknown_err", nil)
|
||||||
|
l2TxCccUnknownErrCounter = metrics.NewRegisteredCounter("miner/skipped_txs/l2/ccc_unknown_err", nil)
|
||||||
|
l1TxStrangeErrCounter = metrics.NewRegisteredCounter("miner/skipped_txs/l1/strange_err", nil)
|
||||||
|
|
||||||
|
l2CommitTxsTimer = metrics.NewRegisteredTimer("miner/commit/txs_all", nil)
|
||||||
|
l2CommitTxTimer = metrics.NewRegisteredTimer("miner/commit/tx_all", nil)
|
||||||
|
l2CommitTxFailedTimer = metrics.NewRegisteredTimer("miner/commit/tx_all_failed", nil)
|
||||||
|
l2CommitTxTraceTimer = metrics.NewRegisteredTimer("miner/commit/tx_trace", nil)
|
||||||
|
l2CommitTxTraceStateRevertTimer = metrics.NewRegisteredTimer("miner/commit/tx_trace_state_revert", nil)
|
||||||
|
l2CommitTxCCCTimer = metrics.NewRegisteredTimer("miner/commit/tx_ccc", nil)
|
||||||
|
l2CommitTxApplyTimer = metrics.NewRegisteredTimer("miner/commit/tx_apply", nil)
|
||||||
|
|
||||||
|
l2CommitNewWorkTimer = metrics.NewRegisteredTimer("miner/commit/new_work_all", nil)
|
||||||
|
l2CommitNewWorkL1CollectTimer = metrics.NewRegisteredTimer("miner/commit/new_work_collect_l1", nil)
|
||||||
|
l2CommitNewWorkPrepareTimer = metrics.NewRegisteredTimer("miner/commit/new_work_prepare", nil)
|
||||||
|
l2CommitNewWorkCommitUncleTimer = metrics.NewRegisteredTimer("miner/commit/new_work_uncle", nil)
|
||||||
|
l2CommitNewWorkTidyPendingTxTimer = metrics.NewRegisteredTimer("miner/commit/new_work_tidy_pending", nil)
|
||||||
|
l2CommitNewWorkCommitL1MsgTimer = metrics.NewRegisteredTimer("miner/commit/new_work_commit_l1_msg", nil)
|
||||||
|
l2CommitNewWorkPrioritizedTxCommitTimer = metrics.NewRegisteredTimer("miner/commit/new_work_prioritized", nil)
|
||||||
|
l2CommitNewWorkRemoteLocalCommitTimer = metrics.NewRegisteredTimer("miner/commit/new_work_remote_local", nil)
|
||||||
|
l2CommitNewWorkLocalPriceAndNonceTimer = metrics.NewRegisteredTimer("miner/commit/new_work_local_price_and_nonce", nil)
|
||||||
|
l2CommitNewWorkRemotePriceAndNonceTimer = metrics.NewRegisteredTimer("miner/commit/new_work_remote_price_and_nonce", nil)
|
||||||
|
|
||||||
|
l2CommitTimer = metrics.NewRegisteredTimer("miner/commit/all", nil)
|
||||||
|
l2CommitTraceTimer = metrics.NewRegisteredTimer("miner/commit/trace", nil)
|
||||||
|
l2CommitCCCTimer = metrics.NewRegisteredTimer("miner/commit/ccc", nil)
|
||||||
|
l2ResultTimer = metrics.NewRegisteredTimer("miner/result/all", nil)
|
||||||
|
)
|
||||||
|
|
||||||
// environment is the worker's current environment and holds all
|
// environment is the worker's current environment and holds all
|
||||||
// information of the sealing block generation.
|
// information of the sealing block generation.
|
||||||
type environment struct {
|
type environment struct {
|
||||||
|
|
@ -94,6 +132,13 @@ type environment struct {
|
||||||
receipts []*types.Receipt
|
receipts []*types.Receipt
|
||||||
sidecars []*types.BlobTxSidecar
|
sidecars []*types.BlobTxSidecar
|
||||||
blobs int
|
blobs int
|
||||||
|
|
||||||
|
l1TxCount int // l1 msg count in cycle
|
||||||
|
|
||||||
|
// circuit capacity check related fields
|
||||||
|
traceEnv *tracing.TraceEnv // env for tracing
|
||||||
|
accRows *types.RowConsumption // accumulated row consumption for a block
|
||||||
|
nextL1MsgIndex uint64 // next L1 queue index to be processed
|
||||||
}
|
}
|
||||||
|
|
||||||
// copy creates a deep copy of environment.
|
// copy creates a deep copy of environment.
|
||||||
|
|
@ -135,6 +180,9 @@ type task struct {
|
||||||
state *state.StateDB
|
state *state.StateDB
|
||||||
block *types.Block
|
block *types.Block
|
||||||
createdAt time.Time
|
createdAt time.Time
|
||||||
|
|
||||||
|
accRows *types.RowConsumption // accumulated row consumption in the circuit side
|
||||||
|
nextL1MsgIndex uint64 // next L1 queue index to be processed
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
@ -170,6 +218,13 @@ type intervalAdjust struct {
|
||||||
inc bool
|
inc bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// prioritizedTransaction represents a single transaction that
|
||||||
|
// should be processed as the first transaction in the next block.
|
||||||
|
type prioritizedTransaction struct {
|
||||||
|
blockNumber uint64
|
||||||
|
tx *types.Transaction
|
||||||
|
}
|
||||||
|
|
||||||
// worker is the main object which takes care of submitting new work to consensus engine
|
// worker is the main object which takes care of submitting new work to consensus engine
|
||||||
// and gathering the sealing result.
|
// and gathering the sealing result.
|
||||||
type worker struct {
|
type worker struct {
|
||||||
|
|
@ -188,6 +243,8 @@ type worker struct {
|
||||||
txsSub event.Subscription
|
txsSub event.Subscription
|
||||||
chainHeadCh chan core.ChainHeadEvent
|
chainHeadCh chan core.ChainHeadEvent
|
||||||
chainHeadSub event.Subscription
|
chainHeadSub event.Subscription
|
||||||
|
l1MsgsCh chan core.NewL1MsgsEvent
|
||||||
|
l1MsgsSub event.Subscription
|
||||||
|
|
||||||
// Channels
|
// Channels
|
||||||
newWorkCh chan *newWorkReq
|
newWorkCh chan *newWorkReq
|
||||||
|
|
@ -219,6 +276,7 @@ type worker struct {
|
||||||
running atomic.Bool // The indicator whether the consensus engine is running or not.
|
running atomic.Bool // The indicator whether the consensus engine is running or not.
|
||||||
newTxs atomic.Int32 // New arrival transaction count since last sealing work submitting.
|
newTxs atomic.Int32 // New arrival transaction count since last sealing work submitting.
|
||||||
syncing atomic.Bool // The indicator whether the node is still syncing.
|
syncing atomic.Bool // The indicator whether the node is still syncing.
|
||||||
|
newL1Msgs atomic.Int32 // New arrival L1 message count since last sealing work submitting.
|
||||||
|
|
||||||
// newpayloadTimeout is the maximum timeout allowance for creating payload.
|
// newpayloadTimeout is the maximum timeout allowance for creating payload.
|
||||||
// The default value is 2 seconds but node operator can set it to arbitrary
|
// The default value is 2 seconds but node operator can set it to arbitrary
|
||||||
|
|
@ -234,11 +292,15 @@ type worker struct {
|
||||||
// External functions
|
// External functions
|
||||||
isLocalBlock func(header *types.Header) bool // Function used to determine whether the specified block is mined by local miner.
|
isLocalBlock func(header *types.Header) bool // Function used to determine whether the specified block is mined by local miner.
|
||||||
|
|
||||||
|
circuitCapacityChecker *circuitcapacitychecker.CircuitCapacityChecker
|
||||||
|
prioritizedTx *prioritizedTransaction
|
||||||
|
|
||||||
// Test hooks
|
// Test hooks
|
||||||
newTaskHook func(*task) // Method to call upon receiving a new sealing task.
|
newTaskHook func(*task) // Method to call upon receiving a new sealing task.
|
||||||
skipSealHook func(*task) bool // Method to decide whether skipping the sealing.
|
skipSealHook func(*task) bool // Method to decide whether skipping the sealing.
|
||||||
fullTaskHook func() // Method to call before pushing the full sealing task.
|
fullTaskHook func() // Method to call before pushing the full sealing task.
|
||||||
resubmitHook func(time.Duration, time.Duration) // Method to call upon updating resubmitting interval.
|
resubmitHook func(time.Duration, time.Duration) // Method to call upon updating resubmitting interval.
|
||||||
|
beforeTxHook func() // Method to call before processing a transaction.
|
||||||
}
|
}
|
||||||
|
|
||||||
func newWorker(config *Config, chainConfig *params.ChainConfig, engine consensus.Engine, eth Backend, mux *event.TypeMux, isLocalBlock func(header *types.Header) bool, init bool) *worker {
|
func newWorker(config *Config, chainConfig *params.ChainConfig, engine consensus.Engine, eth Backend, mux *event.TypeMux, isLocalBlock func(header *types.Header) bool, init bool) *worker {
|
||||||
|
|
@ -263,12 +325,28 @@ func newWorker(config *Config, chainConfig *params.ChainConfig, engine consensus
|
||||||
exitCh: make(chan struct{}),
|
exitCh: make(chan struct{}),
|
||||||
resubmitIntervalCh: make(chan time.Duration),
|
resubmitIntervalCh: make(chan time.Duration),
|
||||||
resubmitAdjustCh: make(chan *intervalAdjust, resubmitAdjustChanSize),
|
resubmitAdjustCh: make(chan *intervalAdjust, resubmitAdjustChanSize),
|
||||||
|
|
||||||
|
l1MsgsCh: make(chan core.NewL1MsgsEvent, txChanSize),
|
||||||
|
circuitCapacityChecker: circuitcapacitychecker.NewCircuitCapacityChecker(true),
|
||||||
}
|
}
|
||||||
|
log.Info("created new worker", "CircuitCapacityChecker ID", worker.circuitCapacityChecker.ID)
|
||||||
|
|
||||||
// Subscribe for transaction insertion events (whether from network or resurrects)
|
// Subscribe for transaction insertion events (whether from network or resurrects)
|
||||||
worker.txsSub = eth.TxPool().SubscribeTransactions(worker.txsCh, true)
|
worker.txsSub = eth.TxPool().SubscribeTransactions(worker.txsCh, true)
|
||||||
// Subscribe events for blockchain
|
// Subscribe events for blockchain
|
||||||
worker.chainHeadSub = eth.BlockChain().SubscribeChainHeadEvent(worker.chainHeadCh)
|
worker.chainHeadSub = eth.BlockChain().SubscribeChainHeadEvent(worker.chainHeadCh)
|
||||||
|
|
||||||
|
// Subscribe NewL1MsgsEvent for sync service
|
||||||
|
if s := eth.SyncService(); s != nil {
|
||||||
|
worker.l1MsgsSub = s.SubscribeNewL1MsgsEvent(worker.l1MsgsCh)
|
||||||
|
} else {
|
||||||
|
// create an empty subscription so that the tests won't fail
|
||||||
|
worker.l1MsgsSub = event.NewSubscription(func(quit <-chan struct{}) error {
|
||||||
|
<-quit
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Sanitize recommit interval if the user-specified one is too short.
|
// Sanitize recommit interval if the user-specified one is too short.
|
||||||
recommit := worker.config.Recommit
|
recommit := worker.config.Recommit
|
||||||
if recommit < minRecommitInterval {
|
if recommit < minRecommitInterval {
|
||||||
|
|
@ -301,6 +379,12 @@ func newWorker(config *Config, chainConfig *params.ChainConfig, engine consensus
|
||||||
return worker
|
return worker
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getCCC returns a pointer to this worker's CCC instance.
|
||||||
|
// Only used in tests.
|
||||||
|
func (w *worker) getCCC() *circuitcapacitychecker.CircuitCapacityChecker {
|
||||||
|
return w.circuitCapacityChecker
|
||||||
|
}
|
||||||
|
|
||||||
// setEtherbase sets the etherbase used to initialize the block coinbase field.
|
// setEtherbase sets the etherbase used to initialize the block coinbase field.
|
||||||
func (w *worker) setEtherbase(addr common.Address) {
|
func (w *worker) setEtherbase(addr common.Address) {
|
||||||
w.mu.Lock()
|
w.mu.Lock()
|
||||||
|
|
@ -435,6 +519,7 @@ func (w *worker) newWorkLoop(recommit time.Duration) {
|
||||||
}
|
}
|
||||||
timer.Reset(recommit)
|
timer.Reset(recommit)
|
||||||
w.newTxs.Store(0)
|
w.newTxs.Store(0)
|
||||||
|
w.newL1Msgs.Store(0)
|
||||||
}
|
}
|
||||||
// clearPending cleans the stale pending tasks.
|
// clearPending cleans the stale pending tasks.
|
||||||
clearPending := func(number uint64) {
|
clearPending := func(number uint64) {
|
||||||
|
|
@ -464,7 +549,7 @@ func (w *worker) newWorkLoop(recommit time.Duration) {
|
||||||
// higher priced transactions. Disable this overhead for pending blocks.
|
// higher priced transactions. Disable this overhead for pending blocks.
|
||||||
if w.isRunning() && (w.chainConfig.Clique == nil || w.chainConfig.Clique.Period > 0) {
|
if w.isRunning() && (w.chainConfig.Clique == nil || w.chainConfig.Clique.Period > 0) {
|
||||||
// Short circuit if no new transaction arrives.
|
// Short circuit if no new transaction arrives.
|
||||||
if w.newTxs.Load() == 0 {
|
if w.newTxs.Load() == 0 && w.newL1Msgs.Load() == 0 {
|
||||||
timer.Reset(recommit)
|
timer.Reset(recommit)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
@ -513,6 +598,7 @@ func (w *worker) newWorkLoop(recommit time.Duration) {
|
||||||
func (w *worker) mainLoop() {
|
func (w *worker) mainLoop() {
|
||||||
defer w.wg.Done()
|
defer w.wg.Done()
|
||||||
defer w.txsSub.Unsubscribe()
|
defer w.txsSub.Unsubscribe()
|
||||||
|
defer w.l1MsgsSub.Unsubscribe()
|
||||||
defer w.chainHeadSub.Unsubscribe()
|
defer w.chainHeadSub.Unsubscribe()
|
||||||
defer func() {
|
defer func() {
|
||||||
if w.current != nil {
|
if w.current != nil {
|
||||||
|
|
@ -524,6 +610,7 @@ func (w *worker) mainLoop() {
|
||||||
select {
|
select {
|
||||||
case req := <-w.newWorkCh:
|
case req := <-w.newWorkCh:
|
||||||
w.commitWork(req.interrupt, req.timestamp)
|
w.commitWork(req.interrupt, req.timestamp)
|
||||||
|
// new block created.
|
||||||
|
|
||||||
case req := <-w.getWorkCh:
|
case req := <-w.getWorkCh:
|
||||||
req.result <- w.generateWork(req.params)
|
req.result <- w.generateWork(req.params)
|
||||||
|
|
@ -572,11 +659,16 @@ func (w *worker) mainLoop() {
|
||||||
}
|
}
|
||||||
w.newTxs.Add(int32(len(ev.Txs)))
|
w.newTxs.Add(int32(len(ev.Txs)))
|
||||||
|
|
||||||
|
case ev := <-w.l1MsgsCh:
|
||||||
|
w.newL1Msgs.Add(int32(ev.Count))
|
||||||
|
|
||||||
// System stopped
|
// System stopped
|
||||||
case <-w.exitCh:
|
case <-w.exitCh:
|
||||||
return
|
return
|
||||||
case <-w.txsSub.Err():
|
case <-w.txsSub.Err():
|
||||||
return
|
return
|
||||||
|
case <-w.l1MsgsSub.Err():
|
||||||
|
return
|
||||||
case <-w.chainHeadSub.Err():
|
case <-w.chainHeadSub.Err():
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -660,6 +752,7 @@ func (w *worker) resultLoop() {
|
||||||
log.Error("Block found but no relative pending task", "number", block.Number(), "sealhash", sealhash, "hash", hash)
|
log.Error("Block found but no relative pending task", "number", block.Number(), "sealhash", sealhash, "hash", hash)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
startTime := time.Now()
|
||||||
// Different block could share same sealhash, deep copy here to prevent write-write conflict.
|
// Different block could share same sealhash, deep copy here to prevent write-write conflict.
|
||||||
var (
|
var (
|
||||||
receipts = make([]*types.Receipt, len(task.receipts))
|
receipts = make([]*types.Receipt, len(task.receipts))
|
||||||
|
|
@ -686,9 +779,42 @@ func (w *worker) resultLoop() {
|
||||||
}
|
}
|
||||||
logs = append(logs, receipt.Logs...)
|
logs = append(logs, receipt.Logs...)
|
||||||
}
|
}
|
||||||
|
// It's possible that we've stored L1 queue index for this block previously,
|
||||||
|
// in this case do not overwrite it.
|
||||||
|
if index := rawdb.ReadFirstQueueIndexNotInL2Block(w.eth.ChainDb(), hash); index == nil {
|
||||||
|
// Store first L1 queue index not processed by this block.
|
||||||
|
// Note: This accounts for both included and skipped messages. This
|
||||||
|
// way, if a block only skips messages, we won't reprocess the same
|
||||||
|
// messages from the next block.
|
||||||
|
log.Trace(
|
||||||
|
"Worker WriteFirstQueueIndexNotInL2Block",
|
||||||
|
"number", block.Number(),
|
||||||
|
"hash", hash.String(),
|
||||||
|
"task.nextL1MsgIndex", task.nextL1MsgIndex,
|
||||||
|
)
|
||||||
|
rawdb.WriteFirstQueueIndexNotInL2Block(w.eth.ChainDb(), hash, task.nextL1MsgIndex)
|
||||||
|
} else {
|
||||||
|
log.Trace(
|
||||||
|
"Worker WriteFirstQueueIndexNotInL2Block: not overwriting existing index",
|
||||||
|
"number", block.Number(),
|
||||||
|
"hash", hash.String(),
|
||||||
|
"index", *index,
|
||||||
|
"task.nextL1MsgIndex", task.nextL1MsgIndex,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// Store circuit row consumption.
|
||||||
|
log.Trace(
|
||||||
|
"Worker write block row consumption",
|
||||||
|
"id", w.circuitCapacityChecker.ID,
|
||||||
|
"number", block.Number(),
|
||||||
|
"hash", hash.String(),
|
||||||
|
"accRows", task.accRows,
|
||||||
|
)
|
||||||
|
rawdb.WriteBlockRowConsumption(w.eth.ChainDb(), hash, task.accRows)
|
||||||
// Commit block and state to database.
|
// Commit block and state to database.
|
||||||
_, err := w.chain.WriteBlockAndSetHead(block, receipts, logs, task.state, true)
|
_, err := w.chain.WriteBlockAndSetHead(block, receipts, logs, task.state, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
l2ResultTimer.Update(time.Since(startTime))
|
||||||
log.Error("Failed writing block to chain", "err", err)
|
log.Error("Failed writing block to chain", "err", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
@ -698,6 +824,8 @@ func (w *worker) resultLoop() {
|
||||||
// Broadcast the block and announce chain insertion event
|
// Broadcast the block and announce chain insertion event
|
||||||
w.mux.Post(core.NewMinedBlockEvent{Block: block})
|
w.mux.Post(core.NewMinedBlockEvent{Block: block})
|
||||||
|
|
||||||
|
l2ResultTimer.Update(time.Since(startTime))
|
||||||
|
|
||||||
case <-w.exitCh:
|
case <-w.exitCh:
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -712,6 +840,18 @@ func (w *worker) makeEnv(parent *types.Header, header *types.Header, coinbase co
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// don't finalize the state during tracing for circuit capacity checker, otherwise we cannot revert.
|
||||||
|
// and even if we don't finalize the state, the `refund` value will still be correct, as explained in `CommitTransaction`
|
||||||
|
finaliseStateAfterApply := false
|
||||||
|
traceEnv, err := tracing.CreateTraceEnv(w.chainConfig, w.chain, w.engine, w.eth.ChainDb(), state, parent,
|
||||||
|
// new block with a placeholder tx, for traceEnv's ExecutionResults length & TxStorageTraces length
|
||||||
|
types.NewBlockWithHeader(header).WithBody([]*types.Transaction{types.NewTx(&types.LegacyTx{})}, nil),
|
||||||
|
finaliseStateAfterApply)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
state.StartPrefetcher("miner")
|
state.StartPrefetcher("miner")
|
||||||
|
|
||||||
// Note the passed coinbase may be different with header.Coinbase.
|
// Note the passed coinbase may be different with header.Coinbase.
|
||||||
|
|
@ -720,10 +860,15 @@ func (w *worker) makeEnv(parent *types.Header, header *types.Header, coinbase co
|
||||||
state: state,
|
state: state,
|
||||||
coinbase: coinbase,
|
coinbase: coinbase,
|
||||||
header: header,
|
header: header,
|
||||||
|
traceEnv: traceEnv,
|
||||||
|
accRows: nil,
|
||||||
}
|
}
|
||||||
// Keep track of transactions which return errors so they can be removed
|
// Keep track of transactions which return errors so they can be removed
|
||||||
env.tcount = 0
|
env.tcount = 0
|
||||||
env.blockSize = 0
|
env.blockSize = 0
|
||||||
|
env.blockSize = 0
|
||||||
|
env.l1TxCount = 0
|
||||||
|
env.nextL1MsgIndex = traceEnv.StartL1QueueIndex
|
||||||
return env, nil
|
return env, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -743,20 +888,21 @@ func (w *worker) updateSnapshot(env *environment) {
|
||||||
w.snapshotState = env.state.Copy()
|
w.snapshotState = env.state.Copy()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *worker) commitTransaction(env *environment, tx *types.Transaction) ([]*types.Log, error) {
|
func (w *worker) commitTransaction(env *environment, tx *types.Transaction) ([]*types.Log, *types.BlockTrace, error) {
|
||||||
if tx.Type() == types.BlobTxType {
|
if tx.Type() == types.BlobTxType {
|
||||||
return w.commitBlobTransaction(env, tx)
|
return w.commitBlobTransaction(env, tx)
|
||||||
}
|
}
|
||||||
receipt, err := w.applyTransaction(env, tx)
|
receipt, traces, accRows, err := w.applyTransaction(env, tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
env.txs = append(env.txs, tx)
|
env.txs = append(env.txs, tx)
|
||||||
env.receipts = append(env.receipts, receipt)
|
env.receipts = append(env.receipts, receipt)
|
||||||
return receipt.Logs, nil
|
env.accRows = accRows
|
||||||
|
return receipt.Logs, traces, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *worker) commitBlobTransaction(env *environment, tx *types.Transaction) ([]*types.Log, error) {
|
func (w *worker) commitBlobTransaction(env *environment, tx *types.Transaction) ([]*types.Log, *types.BlockTrace, error) {
|
||||||
sc := tx.BlobTxSidecar()
|
sc := tx.BlobTxSidecar()
|
||||||
if sc == nil {
|
if sc == nil {
|
||||||
panic("blob transaction without blobs in miner")
|
panic("blob transaction without blobs in miner")
|
||||||
|
|
@ -766,48 +912,146 @@ func (w *worker) commitBlobTransaction(env *environment, tx *types.Transaction)
|
||||||
// and not during execution. This means core.ApplyTransaction will not return an error if the
|
// and not during execution. This means core.ApplyTransaction will not return an error if the
|
||||||
// tx has too many blobs. So we have to explicitly check it here.
|
// tx has too many blobs. So we have to explicitly check it here.
|
||||||
if (env.blobs+len(sc.Blobs))*params.BlobTxBlobGasPerBlob > params.MaxBlobGasPerBlock {
|
if (env.blobs+len(sc.Blobs))*params.BlobTxBlobGasPerBlob > params.MaxBlobGasPerBlock {
|
||||||
return nil, errors.New("max data blobs reached")
|
return nil, nil, errors.New("max data blobs reached")
|
||||||
}
|
}
|
||||||
receipt, err := w.applyTransaction(env, tx)
|
receipt, traces, accRows, err := w.applyTransaction(env, tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
env.txs = append(env.txs, tx.WithoutBlobTxSidecar())
|
env.txs = append(env.txs, tx.WithoutBlobTxSidecar())
|
||||||
env.receipts = append(env.receipts, receipt)
|
env.receipts = append(env.receipts, receipt)
|
||||||
|
env.accRows = accRows
|
||||||
env.sidecars = append(env.sidecars, sc)
|
env.sidecars = append(env.sidecars, sc)
|
||||||
env.blobs += len(sc.Blobs)
|
env.blobs += len(sc.Blobs)
|
||||||
*env.header.BlobGasUsed += receipt.BlobGasUsed
|
*env.header.BlobGasUsed += receipt.BlobGasUsed
|
||||||
return receipt.Logs, nil
|
return receipt.Logs, traces, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// applyTransaction runs the transaction. If execution fails, state and gas pool are reverted.
|
// applyTransaction runs the transaction. If execution fails, state and gas pool are reverted.
|
||||||
func (w *worker) applyTransaction(env *environment, tx *types.Transaction) (*types.Receipt, error) {
|
func (w *worker) applyTransaction(env *environment, tx *types.Transaction) (*types.Receipt, *types.BlockTrace, *types.RowConsumption, error) {
|
||||||
var (
|
var (
|
||||||
snap = env.state.Snapshot()
|
traces *types.BlockTrace
|
||||||
|
accRows *types.RowConsumption
|
||||||
|
receipt *types.Receipt
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
// do not do CCC checks on follower nodes
|
||||||
|
if w.isRunning() {
|
||||||
|
defer func(t0 time.Time) {
|
||||||
|
l2CommitTxTimer.Update(time.Since(t0))
|
||||||
|
if err != nil {
|
||||||
|
l2CommitTxFailedTimer.Update(time.Since(t0))
|
||||||
|
}
|
||||||
|
}(time.Now())
|
||||||
|
|
||||||
|
// do gas limit check up-front and do not run CCC if it fails
|
||||||
|
if env.gasPool.Gas() < tx.Gas() {
|
||||||
|
return nil, nil, nil, core.ErrGasLimitReached
|
||||||
|
}
|
||||||
|
|
||||||
|
snap := env.state.Snapshot()
|
||||||
|
|
||||||
|
log.Trace(
|
||||||
|
"Worker apply ccc for tx",
|
||||||
|
"id", w.circuitCapacityChecker.ID,
|
||||||
|
"txHash", tx.Hash().Hex(),
|
||||||
|
)
|
||||||
|
|
||||||
|
// 1. we have to check circuit capacity before `core.ApplyTransaction`,
|
||||||
|
// because if the tx can be successfully executed but circuit capacity overflows, it will be inconvenient to revert.
|
||||||
|
// 2. even if we don't commit to the state during the tracing (which means `clearJournalAndRefund` is not called during the tracing),
|
||||||
|
// the `refund` value will still be correct, because:
|
||||||
|
// 2.1 when starting handling the first tx, `state.refund` is 0 by default,
|
||||||
|
// 2.2 after tracing, the state is either committed in `core.ApplyTransaction`, or reverted, so the `state.refund` can be cleared,
|
||||||
|
// 2.3 when starting handling the following txs, `state.refund` comes as 0
|
||||||
|
withTimer(l2CommitTxTraceTimer, func() {
|
||||||
|
traces, err = env.traceEnv.GetBlockTrace(
|
||||||
|
types.NewBlockWithHeader(env.header).WithBody([]*types.Transaction{tx}, nil),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
withTimer(l2CommitTxTraceStateRevertTimer, func() {
|
||||||
|
// `env.traceEnv.State` & `env.state` share a same pointer to the state, so only need to revert `env.state`
|
||||||
|
// revert to snapshot for calling `core.ApplyMessage` again, (both `traceEnv.GetBlockTrace` & `core.ApplyTransaction` will call `core.ApplyMessage`)
|
||||||
|
env.state.RevertToSnapshot(snap)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, err
|
||||||
|
}
|
||||||
|
withTimer(l2CommitTxCCCTimer, func() {
|
||||||
|
accRows, err = w.circuitCapacityChecker.ApplyTransaction(traces)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, traces, accRows, err
|
||||||
|
}
|
||||||
|
log.Trace(
|
||||||
|
"Worker apply ccc for tx result",
|
||||||
|
"id", w.circuitCapacityChecker.ID,
|
||||||
|
"txHash", tx.Hash().Hex(),
|
||||||
|
"accRows", accRows,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
snap = env.state.Snapshot() // create new snapshot for `core.ApplyTransaction`
|
||||||
gp = env.gasPool.Gas()
|
gp = env.gasPool.Gas()
|
||||||
)
|
)
|
||||||
receipt, err := core.ApplyTransaction(w.chainConfig, w.chain, &env.coinbase, env.gasPool, env.state, env.header, tx, &env.header.GasUsed, *w.chain.GetVMConfig())
|
withTimer(l2CommitTxApplyTimer, func() {
|
||||||
|
receipt, err = core.ApplyTransaction(w.chainConfig, w.chain, &env.coinbase, env.gasPool, env.state, env.header, tx, &env.header.GasUsed, *w.chain.GetVMConfig())
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
env.state.RevertToSnapshot(snap)
|
env.state.RevertToSnapshot(snap)
|
||||||
env.gasPool.SetGas(gp)
|
env.gasPool.SetGas(gp)
|
||||||
|
if accRows != nil {
|
||||||
|
// At this point, we have called CCC but the transaction failed in `ApplyTransaction`.
|
||||||
|
// If we skip this tx and continue to pack more, the next tx will likely fail with
|
||||||
|
// `circuitcapacitychecker.ErrUnknown`. However, at this point we cannot decide whether
|
||||||
|
// we should seal the block or skip the tx and continue, so we simply return the error.
|
||||||
|
log.Error(
|
||||||
|
"GetBlockTrace passed but ApplyTransaction failed, ccc is left in inconsistent state",
|
||||||
|
"blockNumber", env.header.Number,
|
||||||
|
"txHash", tx.Hash().Hex(),
|
||||||
|
"err", err,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
return receipt, err
|
}
|
||||||
|
return receipt, traces, accRows, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *worker) commitTransactions(env *environment, txs *transactionsByPriceAndNonce, interrupt *atomic.Int32) error {
|
func (w *worker) commitTransactions(env *environment, txs orderedTransactionSet, interrupt *atomic.Int32) (bool, error) {
|
||||||
|
defer func(t0 time.Time) {
|
||||||
|
l2CommitTxsTimer.Update(time.Since(t0))
|
||||||
|
}(time.Now())
|
||||||
|
|
||||||
|
var circuitCapacityReached bool
|
||||||
|
|
||||||
gasLimit := env.header.GasLimit
|
gasLimit := env.header.GasLimit
|
||||||
if env.gasPool == nil {
|
if env.gasPool == nil {
|
||||||
env.gasPool = new(core.GasPool).AddGas(gasLimit)
|
env.gasPool = new(core.GasPool).AddGas(gasLimit)
|
||||||
}
|
}
|
||||||
var coalescedLogs []*types.Log
|
var coalescedLogs []*types.Log
|
||||||
|
|
||||||
|
var loops int64
|
||||||
|
loop:
|
||||||
for {
|
for {
|
||||||
|
if w.beforeTxHook != nil {
|
||||||
|
w.beforeTxHook()
|
||||||
|
}
|
||||||
|
|
||||||
|
loops++
|
||||||
|
|
||||||
// Check interruption signal and abort building if it's fired.
|
// Check interruption signal and abort building if it's fired.
|
||||||
if interrupt != nil {
|
if interrupt != nil {
|
||||||
if signal := interrupt.Load(); signal != commitInterruptNone {
|
if signal := interrupt.Load(); signal != commitInterruptNone {
|
||||||
return signalToErr(signal)
|
return circuitCapacityReached, signalToErr(signal)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// seal block early if we're over time
|
||||||
|
// note: current.header.Time = max(parent.Time + cliquePeriod, now())
|
||||||
|
if env.tcount > 0 && w.chainConfig.Clique != nil && uint64(time.Now().Unix()) > env.header.Time {
|
||||||
|
circuitCapacityReached = true // skip subsequent invocations of commitTransactions
|
||||||
|
break
|
||||||
|
}
|
||||||
// If we don't have enough gas for any further transactions then we're done.
|
// If we don't have enough gas for any further transactions then we're done.
|
||||||
if env.gasPool.Gas() < params.TxGas {
|
if env.gasPool.Gas() < params.TxGas {
|
||||||
log.Trace("Not enough gas for further transactions", "have", env.gasPool, "want", params.TxGas)
|
log.Trace("Not enough gas for further transactions", "have", env.gasPool, "want", params.TxGas)
|
||||||
|
|
@ -818,6 +1062,12 @@ func (w *worker) commitTransactions(env *environment, txs *transactionsByPriceAn
|
||||||
if ltx == nil {
|
if ltx == nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
// If we have collected enough transactions then we're done
|
||||||
|
// Originally we only limit l2txs count, but now strictly limit total txs number.
|
||||||
|
if !w.chainConfig.Scroll.IsValidTxCount(env.tcount + 1) {
|
||||||
|
log.Trace("Transaction count limit reached", "have", env.tcount, "want", w.chainConfig.Scroll.MaxTxPerBlock)
|
||||||
|
break
|
||||||
|
}
|
||||||
// If we don't have enough space for the next transaction, skip the account.
|
// If we don't have enough space for the next transaction, skip the account.
|
||||||
if env.gasPool.Gas() < ltx.Gas {
|
if env.gasPool.Gas() < ltx.Gas {
|
||||||
log.Trace("Not enough gas left for transaction", "hash", ltx.Hash, "left", env.gasPool.Gas(), "needed", ltx.Gas)
|
log.Trace("Not enough gas left for transaction", "hash", ltx.Hash, "left", env.gasPool.Gas(), "needed", ltx.Gas)
|
||||||
|
|
@ -836,11 +1086,12 @@ func (w *worker) commitTransactions(env *environment, txs *transactionsByPriceAn
|
||||||
txs.Pop()
|
txs.Pop()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// If we have collected enough transactions then we're done
|
if tx.IsL1MessageTx() && tx.AsL1MessageTx().QueueIndex != env.nextL1MsgIndex {
|
||||||
// Originally we only limit l2txs count, but now strictly limit total txs number.
|
log.Error(
|
||||||
// log.Info("w.chainConfig", "w.chainConfig.Scroll", w.chainConfig.Scroll)
|
"Unexpected L1 message queue index in worker",
|
||||||
if !w.chainConfig.Scroll.IsValidTxCount(env.tcount + 1) {
|
"expected", env.nextL1MsgIndex,
|
||||||
log.Trace("Transaction count limit reached", "have", env.tcount, "want", w.chainConfig.Scroll.MaxTxPerBlock)
|
"got", tx.AsL1MessageTx().QueueIndex,
|
||||||
|
)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if !tx.IsL1MessageTx() && !w.chainConfig.Scroll.IsValidBlockSize(env.blockSize+tx.Size()) {
|
if !tx.IsL1MessageTx() && !w.chainConfig.Scroll.IsValidBlockSize(env.blockSize+tx.Size()) {
|
||||||
|
|
@ -862,7 +1113,7 @@ func (w *worker) commitTransactions(env *environment, txs *transactionsByPriceAn
|
||||||
// Start executing the transaction
|
// Start executing the transaction
|
||||||
env.state.SetTxContext(tx.Hash(), env.tcount)
|
env.state.SetTxContext(tx.Hash(), env.tcount)
|
||||||
|
|
||||||
logs, err := w.commitTransaction(env, tx)
|
logs, traces, err := w.commitTransaction(env, tx)
|
||||||
switch {
|
switch {
|
||||||
case errors.Is(err, core.ErrNonceTooLow):
|
case errors.Is(err, core.ErrNonceTooLow):
|
||||||
// New head notification data race between the transaction pool and miner, shift
|
// New head notification data race between the transaction pool and miner, shift
|
||||||
|
|
@ -876,15 +1127,151 @@ func (w *worker) commitTransactions(env *environment, txs *transactionsByPriceAn
|
||||||
txs.Shift()
|
txs.Shift()
|
||||||
|
|
||||||
if tx.IsL1MessageTx() {
|
if tx.IsL1MessageTx() {
|
||||||
|
queueIndex := tx.AsL1MessageTx().QueueIndex
|
||||||
|
log.Debug("Including L1 message", "queueIndex", queueIndex, "tx", tx.Hash().String())
|
||||||
|
env.l1TxCount++
|
||||||
|
env.nextL1MsgIndex = queueIndex + 1
|
||||||
} else {
|
} else {
|
||||||
// only consider block size limit for L2 transactions
|
// only consider block size limit for L2 transactions
|
||||||
env.blockSize += tx.Size()
|
env.blockSize += tx.Size()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case errors.Is(err, core.ErrGasLimitReached) && tx.IsL1MessageTx():
|
||||||
|
// If this block already contains some L1 messages,
|
||||||
|
// terminate here and try again in the next block.
|
||||||
|
if env.l1TxCount > 0 {
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
// A single L1 message leads to out-of-gas. Skip it.
|
||||||
|
queueIndex := tx.AsL1MessageTx().QueueIndex
|
||||||
|
log.Info("Skipping L1 message", "queueIndex", queueIndex, "tx", tx.Hash().String(), "block", env.header.Number, "reason", "gas limit exceeded")
|
||||||
|
env.nextL1MsgIndex = queueIndex + 1
|
||||||
|
txs.Shift()
|
||||||
|
if w.config.StoreSkippedTxTraces {
|
||||||
|
rawdb.WriteSkippedTransaction(w.eth.ChainDb(), tx, traces, "gas limit exceeded", env.header.Number.Uint64(), nil)
|
||||||
|
} else {
|
||||||
|
rawdb.WriteSkippedTransaction(w.eth.ChainDb(), tx, nil, "gas limit exceeded", env.header.Number.Uint64(), nil)
|
||||||
|
}
|
||||||
|
l1TxGasLimitExceededCounter.Inc(1)
|
||||||
|
|
||||||
|
// Circuit capacity check
|
||||||
|
case errors.Is(err, circuitcapacitychecker.ErrBlockRowConsumptionOverflow):
|
||||||
|
if env.tcount >= 1 {
|
||||||
|
// 1. Circuit capacity limit reached in a block, and it's not the first tx:
|
||||||
|
// don't pop or shift, just quit the loop immediately;
|
||||||
|
// though it might still be possible to add some "smaller" txs,
|
||||||
|
// but it's a trade-off between tracing overhead & block usage rate
|
||||||
|
log.Trace("Circuit capacity limit reached in a block", "acc_rows", env.accRows, "tx", tx.Hash().String())
|
||||||
|
log.Info("Skipping message", "tx", tx.Hash().String(), "block", env.header.Number, "reason", "accumulated row consumption overflow")
|
||||||
|
|
||||||
|
// Prioritize transaction for the next block.
|
||||||
|
// If there are no new L1 messages, this transaction will be the 1st transaction in the next block,
|
||||||
|
// at which point we can definitively decide if we should skip it or not.
|
||||||
|
log.Debug("Prioritizing transaction for next block", "blockNumber", env.header.Number.Uint64()+1, "tx", tx.Hash().String())
|
||||||
|
w.prioritizedTx = &prioritizedTransaction{
|
||||||
|
blockNumber: env.header.Number.Uint64() + 1,
|
||||||
|
tx: tx,
|
||||||
|
}
|
||||||
|
w.newTxs.Add(int32(1))
|
||||||
|
|
||||||
|
circuitCapacityReached = true
|
||||||
|
break loop
|
||||||
|
} else {
|
||||||
|
// 2. Circuit capacity limit reached in a block, and it's the first tx: skip the tx
|
||||||
|
log.Trace("Circuit capacity limit reached for a single tx", "tx", tx.Hash().String())
|
||||||
|
|
||||||
|
if tx.IsL1MessageTx() {
|
||||||
|
// Skip L1 message transaction,
|
||||||
|
// shift to the next from the account because we shouldn't skip the entire txs from the same account
|
||||||
|
txs.Shift()
|
||||||
|
|
||||||
|
queueIndex := tx.AsL1MessageTx().QueueIndex
|
||||||
|
log.Info("Skipping L1 message", "queueIndex", queueIndex, "tx", tx.Hash().String(), "block", env.header.Number, "reason", "first tx row consumption overflow")
|
||||||
|
env.nextL1MsgIndex = queueIndex + 1
|
||||||
|
l1TxRowConsumptionOverflowCounter.Inc(1)
|
||||||
|
} else {
|
||||||
|
// Skip L2 transaction and all other transactions from the same sender account
|
||||||
|
log.Info("Skipping L2 message", "tx", tx.Hash().String(), "block", env.header.Number, "reason", "first tx row consumption overflow")
|
||||||
|
txs.Pop()
|
||||||
|
w.eth.TxPool().RemoveTx(tx.Hash(), true, true)
|
||||||
|
l2TxRowConsumptionOverflowCounter.Inc(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset ccc so that we can process other transactions for this block
|
||||||
|
w.circuitCapacityChecker.Reset()
|
||||||
|
log.Trace("Worker reset ccc", "id", w.circuitCapacityChecker.ID)
|
||||||
|
circuitCapacityReached = false
|
||||||
|
|
||||||
|
// Store skipped transaction in local db
|
||||||
|
if w.config.StoreSkippedTxTraces {
|
||||||
|
rawdb.WriteSkippedTransaction(w.eth.ChainDb(), tx, traces, "row consumption overflow", env.header.Number.Uint64(), nil)
|
||||||
|
} else {
|
||||||
|
rawdb.WriteSkippedTransaction(w.eth.ChainDb(), tx, nil, "row consumption overflow", env.header.Number.Uint64(), nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case (errors.Is(err, circuitcapacitychecker.ErrUnknown) && tx.IsL1MessageTx()):
|
||||||
|
// Circuit capacity check: unknown circuit capacity checker error for L1MessageTx,
|
||||||
|
// shift to the next from the account because we shouldn't skip the entire txs from the same account
|
||||||
|
queueIndex := tx.AsL1MessageTx().QueueIndex
|
||||||
|
log.Trace("Unknown circuit capacity checker error for L1MessageTx", "tx", tx.Hash().String(), "queueIndex", queueIndex)
|
||||||
|
log.Info("Skipping L1 message", "queueIndex", queueIndex, "tx", tx.Hash().String(), "block", env.header.Number, "reason", "unknown row consumption error")
|
||||||
|
env.nextL1MsgIndex = queueIndex + 1
|
||||||
|
// TODO: propagate more info about the error from CCC
|
||||||
|
if w.config.StoreSkippedTxTraces {
|
||||||
|
rawdb.WriteSkippedTransaction(w.eth.ChainDb(), tx, traces, "unknown circuit capacity checker error", env.header.Number.Uint64(), nil)
|
||||||
|
} else {
|
||||||
|
rawdb.WriteSkippedTransaction(w.eth.ChainDb(), tx, nil, "unknown circuit capacity checker error", env.header.Number.Uint64(), nil)
|
||||||
|
}
|
||||||
|
l1TxCccUnknownErrCounter.Inc(1)
|
||||||
|
|
||||||
|
// Normally we would do `txs.Shift()` here.
|
||||||
|
// However, after `ErrUnknown`, ccc might remain in an
|
||||||
|
// inconsistent state, so we cannot pack more transactions.
|
||||||
|
circuitCapacityReached = true
|
||||||
|
w.checkCurrentTxNumWithCCC(env.tcount)
|
||||||
|
break loop
|
||||||
|
|
||||||
|
case (errors.Is(err, circuitcapacitychecker.ErrUnknown) && !tx.IsL1MessageTx()):
|
||||||
|
// Circuit capacity check: unknown circuit capacity checker error for L2MessageTx, skip the account
|
||||||
|
log.Trace("Unknown circuit capacity checker error for L2MessageTx", "tx", tx.Hash().String())
|
||||||
|
log.Info("Skipping L2 message", "tx", tx.Hash().String(), "block", env.header.Number, "reason", "unknown row consumption error")
|
||||||
|
// TODO: propagate more info about the error from CCC
|
||||||
|
if w.config.StoreSkippedTxTraces {
|
||||||
|
rawdb.WriteSkippedTransaction(w.eth.ChainDb(), tx, traces, "unknown circuit capacity checker error", env.header.Number.Uint64(), nil)
|
||||||
|
} else {
|
||||||
|
rawdb.WriteSkippedTransaction(w.eth.ChainDb(), tx, nil, "unknown circuit capacity checker error", env.header.Number.Uint64(), nil)
|
||||||
|
}
|
||||||
|
l2TxCccUnknownErrCounter.Inc(1)
|
||||||
|
|
||||||
|
// Normally we would do `txs.Pop()` here.
|
||||||
|
// However, after `ErrUnknown`, ccc might remain in an
|
||||||
|
// inconsistent state, so we cannot pack more transactions.
|
||||||
|
w.eth.TxPool().RemoveTx(tx.Hash(), true, true)
|
||||||
|
circuitCapacityReached = true
|
||||||
|
w.checkCurrentTxNumWithCCC(env.tcount)
|
||||||
|
break loop
|
||||||
|
|
||||||
|
case (errors.Is(err, core.ErrInsufficientFunds) || errors.Is(errors.Unwrap(err), core.ErrInsufficientFunds)):
|
||||||
|
log.Trace("Skipping tx with insufficient funds", "sender", from, "tx", tx.Hash().String())
|
||||||
|
txs.Pop()
|
||||||
|
w.eth.TxPool().RemoveTx(tx.Hash(), true, true)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// Transaction is regarded as invalid, drop all consecutive transactions from
|
// Transaction is regarded as invalid, drop all consecutive transactions from
|
||||||
// the same sender because of `nonce-too-high` clause.
|
// the same sender because of `nonce-too-high` clause.
|
||||||
log.Debug("Transaction failed, account skipped", "hash", ltx.Hash, "err", err)
|
log.Debug("Transaction failed, account skipped", "hash", ltx.Hash.String(), "err", err)
|
||||||
|
if tx.IsL1MessageTx() {
|
||||||
|
queueIndex := tx.AsL1MessageTx().QueueIndex
|
||||||
|
log.Info("Skipping L1 message", "queueIndex", queueIndex, "tx", tx.Hash().String(), "block", env.header.Number, "reason", "strange error", "err", err)
|
||||||
|
env.nextL1MsgIndex = queueIndex + 1
|
||||||
|
if w.config.StoreSkippedTxTraces {
|
||||||
|
rawdb.WriteSkippedTransaction(w.eth.ChainDb(), tx, traces, fmt.Sprintf("strange error: %v", err), env.header.Number.Uint64(), nil)
|
||||||
|
} else {
|
||||||
|
rawdb.WriteSkippedTransaction(w.eth.ChainDb(), tx, nil, fmt.Sprintf("strange error: %v", err), env.header.Number.Uint64(), nil)
|
||||||
|
}
|
||||||
|
l1TxStrangeErrCounter.Inc(1)
|
||||||
|
}
|
||||||
txs.Pop()
|
txs.Pop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -903,7 +1290,7 @@ func (w *worker) commitTransactions(env *environment, txs *transactionsByPriceAn
|
||||||
}
|
}
|
||||||
w.pendingLogsFeed.Send(cpy)
|
w.pendingLogsFeed.Send(cpy)
|
||||||
}
|
}
|
||||||
return nil
|
return circuitCapacityReached, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// generateParams wraps various of settings for generating sealing task.
|
// generateParams wraps various of settings for generating sealing task.
|
||||||
|
|
@ -1001,10 +1388,45 @@ func (w *worker) prepareWork(genParams *generateParams) (*environment, error) {
|
||||||
return env, nil
|
return env, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func txToLazyTx(txPool *txpool.TxPool, tx *types.Transaction) *txpool.LazyTransaction {
|
||||||
|
if tx.IsL1MessageTx() {
|
||||||
|
return &txpool.LazyTransaction{
|
||||||
|
Pool: nil, // we should never resolve a L1MessageTx from the txpool and we never need to
|
||||||
|
Hash: tx.Hash(),
|
||||||
|
Tx: tx, // set the tx directly, we don't need to resolve it
|
||||||
|
Time: tx.Time(),
|
||||||
|
GasFeeCap: tx.GasFeeCap(),
|
||||||
|
GasTipCap: tx.GasTipCap(),
|
||||||
|
Gas: tx.Gas(),
|
||||||
|
BlobGas: tx.BlobGas(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &txpool.LazyTransaction{
|
||||||
|
Pool: txPool,
|
||||||
|
Hash: tx.Hash(),
|
||||||
|
Tx: nil, // Do *not* set this! We need to resolve it later to pull blobs in
|
||||||
|
Time: tx.Time(),
|
||||||
|
GasFeeCap: tx.GasFeeCap(),
|
||||||
|
GasTipCap: tx.GasTipCap(),
|
||||||
|
Gas: tx.Gas(),
|
||||||
|
BlobGas: tx.BlobGas(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// fillTransactions retrieves the pending transactions from the txpool and fills them
|
// fillTransactions retrieves the pending transactions from the txpool and fills them
|
||||||
// into the given sealing block. The transaction selection and ordering strategy can
|
// into the given sealing block. The transaction selection and ordering strategy can
|
||||||
// be customized with the plugin in the future.
|
// be customized with the plugin in the future.
|
||||||
func (w *worker) fillTransactions(interrupt *atomic.Int32, env *environment) error {
|
func (w *worker) fillTransactions(interrupt *atomic.Int32, env *environment) error {
|
||||||
|
// fetch l1Txs
|
||||||
|
var l1Messages []types.L1MessageTx
|
||||||
|
if w.chainConfig.Scroll.ShouldIncludeL1Messages() {
|
||||||
|
withTimer(l2CommitNewWorkL1CollectTimer, func() {
|
||||||
|
l1Messages = w.collectPendingL1Messages(env.nextL1MsgIndex)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
tidyPendingStart := time.Now()
|
||||||
pending := w.eth.TxPool().Pending(true)
|
pending := w.eth.TxPool().Pending(true)
|
||||||
|
|
||||||
// Split the pending transactions into locals and remotes.
|
// Split the pending transactions into locals and remotes.
|
||||||
|
|
@ -1015,20 +1437,67 @@ func (w *worker) fillTransactions(interrupt *atomic.Int32, env *environment) err
|
||||||
localTxs[account] = txs
|
localTxs[account] = txs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
l2CommitNewWorkTidyPendingTxTimer.UpdateSince(tidyPendingStart)
|
||||||
|
|
||||||
// Fill the block with all available pending transactions.
|
// Fill the block with all available pending transactions.
|
||||||
if len(localTxs) > 0 {
|
var circuitCapacityReached bool
|
||||||
|
var err error
|
||||||
|
commitL1MsgStart := time.Now()
|
||||||
|
if w.chainConfig.Scroll.ShouldIncludeL1Messages() && len(l1Messages) > 0 {
|
||||||
|
log.Trace("Processing L1 messages for inclusion", "count", len(l1Messages))
|
||||||
|
txs, err := newL1MessagesByQueueIndex(w.eth.TxPool(), l1Messages)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Failed to create L1 message set", "l1Messages", l1Messages, "err", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
circuitCapacityReached, err = w.commitTransactions(env, txs, interrupt)
|
||||||
|
if err != nil {
|
||||||
|
l2CommitNewWorkCommitL1MsgTimer.UpdateSince(commitL1MsgStart)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
l2CommitNewWorkCommitL1MsgTimer.UpdateSince(commitL1MsgStart)
|
||||||
|
prioritizedTxStart := time.Now()
|
||||||
|
if w.prioritizedTx != nil && w.current.header.Number.Uint64() > w.prioritizedTx.blockNumber {
|
||||||
|
w.prioritizedTx = nil
|
||||||
|
}
|
||||||
|
if !circuitCapacityReached && w.prioritizedTx != nil && w.current.header.Number.Uint64() == w.prioritizedTx.blockNumber {
|
||||||
|
tx := w.prioritizedTx.tx
|
||||||
|
from, _ := types.Sender(w.current.signer, tx) // error already checked before
|
||||||
|
// we don't know where this came from, yolo resolve from everywhere (w.eth.TxPool())
|
||||||
|
txList := map[common.Address][]*txpool.LazyTransaction{from: {txToLazyTx(w.eth.TxPool(), tx)}}
|
||||||
|
// usually we should distinguish l1txs and l2txs:
|
||||||
|
// use `newL1MessagesByQueueIndex` for l1txs and `newTransactionsByPriceAndNonce` is for l2txs;
|
||||||
|
// but here there's only 1 tx, and hence no need for sorting, we could just simply use `newTransactionsByPriceAndNonce`
|
||||||
|
// (but we fill the LazyTransaction's tx first, in case it's a l1tx and cannot be resolved from the mempool).
|
||||||
|
txs := newTransactionsByPriceAndNonce(w.current.signer, txList, env.header.BaseFee)
|
||||||
|
circuitCapacityReached, err = w.commitTransactions(env, txs, interrupt)
|
||||||
|
if err != nil {
|
||||||
|
l2CommitNewWorkPrioritizedTxCommitTimer.UpdateSince(prioritizedTxStart)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
l2CommitNewWorkPrioritizedTxCommitTimer.UpdateSince(prioritizedTxStart)
|
||||||
|
remoteLocalStart := time.Now()
|
||||||
|
if !circuitCapacityReached && len(localTxs) > 0 {
|
||||||
|
localTxPriceAndNonceStart := time.Now()
|
||||||
txs := newTransactionsByPriceAndNonce(env.signer, localTxs, env.header.BaseFee)
|
txs := newTransactionsByPriceAndNonce(env.signer, localTxs, env.header.BaseFee)
|
||||||
if err := w.commitTransactions(env, txs, interrupt); err != nil {
|
l2CommitNewWorkLocalPriceAndNonceTimer.UpdateSince(localTxPriceAndNonceStart)
|
||||||
|
if circuitCapacityReached, err = w.commitTransactions(env, txs, interrupt); err != nil {
|
||||||
|
l2CommitNewWorkRemoteLocalCommitTimer.UpdateSince(remoteLocalStart)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(remoteTxs) > 0 {
|
if !circuitCapacityReached && len(remoteTxs) > 0 {
|
||||||
|
remoteTxPriceAndNonceStart := time.Now()
|
||||||
txs := newTransactionsByPriceAndNonce(env.signer, remoteTxs, env.header.BaseFee)
|
txs := newTransactionsByPriceAndNonce(env.signer, remoteTxs, env.header.BaseFee)
|
||||||
if err := w.commitTransactions(env, txs, interrupt); err != nil {
|
l2CommitNewWorkRemotePriceAndNonceTimer.UpdateSince(remoteTxPriceAndNonceStart)
|
||||||
|
if _, err = w.commitTransactions(env, txs, interrupt); err != nil {
|
||||||
|
l2CommitNewWorkRemoteLocalCommitTimer.UpdateSince(remoteLocalStart)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
l2CommitNewWorkRemoteLocalCommitTimer.UpdateSince(remoteLocalStart)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1070,7 +1539,14 @@ func (w *worker) commitWork(interrupt *atomic.Int32, timestamp int64) {
|
||||||
if w.syncing.Load() {
|
if w.syncing.Load() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
defer func(t0 time.Time) {
|
||||||
|
l2CommitNewWorkTimer.Update(time.Since(t0))
|
||||||
|
}(time.Now())
|
||||||
|
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
w.circuitCapacityChecker.Reset()
|
||||||
|
log.Trace("Worker reset ccc", "id", w.circuitCapacityChecker.ID)
|
||||||
|
|
||||||
// Set the coinbase if the worker is running or it's required
|
// Set the coinbase if the worker is running or it's required
|
||||||
var coinbase common.Address
|
var coinbase common.Address
|
||||||
|
|
@ -1081,6 +1557,9 @@ func (w *worker) commitWork(interrupt *atomic.Int32, timestamp int64) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// TODO:
|
||||||
|
// 1. l2CommitNewWorkPrepareTimer
|
||||||
|
// 2. no need for l2CommitNewWorkCommitUncleTimer any more?
|
||||||
work, err := w.prepareWork(&generateParams{
|
work, err := w.prepareWork(&generateParams{
|
||||||
timestamp: uint64(timestamp),
|
timestamp: uint64(timestamp),
|
||||||
coinbase: coinbase,
|
coinbase: coinbase,
|
||||||
|
|
@ -1128,11 +1607,57 @@ func (w *worker) commitWork(interrupt *atomic.Int32, timestamp int64) {
|
||||||
w.current = work
|
w.current = work
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *worker) calcAndSetAccRowsForEnv(env *environment) error {
|
||||||
|
log.Trace(
|
||||||
|
"Worker apply ccc for empty block",
|
||||||
|
"id", w.circuitCapacityChecker.ID,
|
||||||
|
"number", env.header.Number,
|
||||||
|
"hash", env.header.Hash().String(),
|
||||||
|
)
|
||||||
|
var traces *types.BlockTrace
|
||||||
|
var err error
|
||||||
|
withTimer(l2CommitTraceTimer, func() {
|
||||||
|
traces, err = env.traceEnv.GetBlockTrace(types.NewBlockWithHeader(env.header))
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if traces == nil {
|
||||||
|
log.Warn("running in light mode and traces is nil, don't update `env.accRows`")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// truncate ExecutionResults&TxStorageTraces, because we declare their lengths with a dummy tx before;
|
||||||
|
// however, we need to clean it up for an empty block
|
||||||
|
traces.ExecutionResults = traces.ExecutionResults[:0]
|
||||||
|
traces.TxStorageTraces = traces.TxStorageTraces[:0]
|
||||||
|
var accRows *types.RowConsumption
|
||||||
|
withTimer(l2CommitCCCTimer, func() {
|
||||||
|
accRows, err = w.circuitCapacityChecker.ApplyBlock(traces)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Trace(
|
||||||
|
"Worker apply ccc for empty block result",
|
||||||
|
"id", w.circuitCapacityChecker.ID,
|
||||||
|
"number", env.header.Number,
|
||||||
|
"hash", env.header.Hash().String(),
|
||||||
|
"accRows", accRows,
|
||||||
|
)
|
||||||
|
env.accRows = accRows
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// commit runs any post-transaction state modifications, assembles the final block
|
// commit runs any post-transaction state modifications, assembles the final block
|
||||||
// and commits new work if consensus engine is running.
|
// and commits new work if consensus engine is running.
|
||||||
// Note the assumption is held that the mutation is allowed to the passed env, do
|
// Note the assumption is held that the mutation is allowed to the passed env, do
|
||||||
// the deep copy first.
|
// the deep copy first.
|
||||||
func (w *worker) commit(env *environment, interval func(), update bool, start time.Time) error {
|
func (w *worker) commit(env *environment, interval func(), update bool, start time.Time) error {
|
||||||
|
defer func(t0 time.Time) {
|
||||||
|
l2CommitTimer.Update(time.Since(t0))
|
||||||
|
}(time.Now())
|
||||||
|
|
||||||
if w.isRunning() {
|
if w.isRunning() {
|
||||||
if interval != nil {
|
if interval != nil {
|
||||||
interval()
|
interval()
|
||||||
|
|
@ -1140,6 +1665,12 @@ func (w *worker) commit(env *environment, interval func(), update bool, start ti
|
||||||
// Create a local environment copy, avoid the data race with snapshot state.
|
// Create a local environment copy, avoid the data race with snapshot state.
|
||||||
// https://github.com/ethereum/go-ethereum/issues/24299
|
// https://github.com/ethereum/go-ethereum/issues/24299
|
||||||
env := env.copy()
|
env := env.copy()
|
||||||
|
// set env.accRows for empty-but-not-genesis block
|
||||||
|
if (env.header.Number.Uint64() != 0) && (env.accRows == nil || len(*env.accRows) == 0) {
|
||||||
|
if err := w.calcAndSetAccRowsForEnv(env); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
// Withdrawals are set to nil here, because this is only called in PoW.
|
// Withdrawals are set to nil here, because this is only called in PoW.
|
||||||
block, err := w.engine.FinalizeAndAssemble(w.chain, env.header, env.state, env.txs, nil, env.receipts, nil)
|
block, err := w.engine.FinalizeAndAssemble(w.chain, env.header, env.state, env.txs, nil, env.receipts, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -1148,7 +1679,7 @@ func (w *worker) commit(env *environment, interval func(), update bool, start ti
|
||||||
// If we're post merge, just ignore
|
// If we're post merge, just ignore
|
||||||
if !w.isTTDReached(block.Header()) {
|
if !w.isTTDReached(block.Header()) {
|
||||||
select {
|
select {
|
||||||
case w.taskCh <- &task{receipts: env.receipts, state: env.state, block: block, createdAt: time.Now()}:
|
case w.taskCh <- &task{receipts: env.receipts, state: env.state, block: block, createdAt: time.Now(), accRows: env.accRows, nextL1MsgIndex: env.nextL1MsgIndex}:
|
||||||
fees := totalFees(block, env.receipts)
|
fees := totalFees(block, env.receipts)
|
||||||
feesInEther := new(big.Float).Quo(new(big.Float).SetInt(fees), big.NewFloat(params.Ether))
|
feesInEther := new(big.Float).Quo(new(big.Float).SetInt(fees), big.NewFloat(params.Ether))
|
||||||
log.Info("Commit new sealing work", "number", block.Number(), "sealhash", w.engine.SealHash(block.Header()),
|
log.Info("Commit new sealing work", "number", block.Number(), "sealhash", w.engine.SealHash(block.Header()),
|
||||||
|
|
@ -1189,6 +1720,22 @@ func (w *worker) isTTDReached(header *types.Header) bool {
|
||||||
return td != nil && ttd != nil && td.Cmp(ttd) >= 0
|
return td != nil && ttd != nil && td.Cmp(ttd) >= 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *worker) checkCurrentTxNumWithCCC(expected int) {
|
||||||
|
match, got, err := w.circuitCapacityChecker.CheckTxNum(expected)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("failed to CheckTxNum in ccc", "err", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !match {
|
||||||
|
log.Error("tx count in miner is different with CCC", "w.current.tcount", w.current.tcount, "got", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *worker) collectPendingL1Messages(startIndex uint64) []types.L1MessageTx {
|
||||||
|
maxCount := w.chainConfig.Scroll.L1Config.NumL1MessagesPerBlock
|
||||||
|
return rawdb.ReadL1MessagesFrom(w.eth.ChainDb(), startIndex, maxCount)
|
||||||
|
}
|
||||||
|
|
||||||
// copyReceipts makes a deep copy of the given receipts.
|
// copyReceipts makes a deep copy of the given receipts.
|
||||||
func copyReceipts(receipts []*types.Receipt) []*types.Receipt {
|
func copyReceipts(receipts []*types.Receipt) []*types.Receipt {
|
||||||
result := make([]*types.Receipt, len(receipts))
|
result := make([]*types.Receipt, len(receipts))
|
||||||
|
|
@ -1223,3 +1770,11 @@ func signalToErr(signal int32) error {
|
||||||
panic(fmt.Errorf("undefined signal %d", signal))
|
panic(fmt.Errorf("undefined signal %d", signal))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func withTimer(timer metrics.Timer, f func()) {
|
||||||
|
if metrics.Enabled {
|
||||||
|
timer.Time(f)
|
||||||
|
} else {
|
||||||
|
f()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
"github.com/ethereum/go-ethereum/rollup/sync_service"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
@ -147,6 +148,9 @@ func newTestWorkerBackend(t *testing.T, chainConfig *params.ChainConfig, engine
|
||||||
func (b *testWorkerBackend) BlockChain() *core.BlockChain { return b.chain }
|
func (b *testWorkerBackend) BlockChain() *core.BlockChain { return b.chain }
|
||||||
func (b *testWorkerBackend) TxPool() *txpool.TxPool { return b.txPool }
|
func (b *testWorkerBackend) TxPool() *txpool.TxPool { return b.txPool }
|
||||||
|
|
||||||
|
func (b *testWorkerBackend) ChainDb() ethdb.Database { return b.db }
|
||||||
|
func (b *testWorkerBackend) SyncService() *sync_service.SyncService { return nil }
|
||||||
|
|
||||||
func (b *testWorkerBackend) newRandomTx(creation bool) *types.Transaction {
|
func (b *testWorkerBackend) newRandomTx(creation bool) *types.Transaction {
|
||||||
var tx *types.Transaction
|
var tx *types.Transaction
|
||||||
gasPrice := big.NewInt(10 * params.InitialBaseFee)
|
gasPrice := big.NewInt(10 * params.InitialBaseFee)
|
||||||
|
|
|
||||||
|
|
@ -169,6 +169,11 @@ func CreateTraceEnv(chainConfig *params.ChainConfig, chainContext core.ChainCont
|
||||||
}
|
}
|
||||||
|
|
||||||
func (env *TraceEnv) GetBlockTrace(block *types.Block) (*types.BlockTrace, error) {
|
func (env *TraceEnv) GetBlockTrace(block *types.Block) (*types.BlockTrace, error) {
|
||||||
|
if env == nil {
|
||||||
|
log.Warn("running in light mode? trace env is nil and do not support `GetBlockTrace`")
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Execute all the transaction contained within the block concurrently
|
// Execute all the transaction contained within the block concurrently
|
||||||
var (
|
var (
|
||||||
txs = block.Transactions()
|
txs = block.Transactions()
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue