From b63934978838e7316ebf87b89142fe760585af84 Mon Sep 17 00:00:00 2001 From: HAOYUatHZ <37070449+HAOYUatHZ@users.noreply.github.com> Date: Tue, 28 Nov 2023 15:24:21 +0800 Subject: [PATCH] feat: add BlockTxCount limit and BlockPayloadSize limit (#584) * update consensus/errors.go * update core/error.go * update core/types/block.go * update rollup/rcfg/config.go * update params/config.go * update core/block_validator.go * fix * update miner/worker.go * fix --- consensus/errors.go | 3 +++ core/block_validator.go | 8 ++++++++ core/error.go | 3 +++ core/types/block.go | 12 ++++++++++++ miner/worker.go | 20 ++++++++++++++++++++ params/config.go | 22 ++++++++++++++++++++++ rollup/rcfg/config.go | 5 +++++ 7 files changed, 73 insertions(+) diff --git a/consensus/errors.go b/consensus/errors.go index d508b6580f55..d72e713a00fb 100644 --- a/consensus/errors.go +++ b/consensus/errors.go @@ -38,4 +38,7 @@ var ( // ErrInvalidTerminalBlock is returned if a block is invalid wrt. the terminal // total difficulty. ErrInvalidTerminalBlock = errors.New("invalid terminal block") + + // ErrInvalidTxCount is returned if a block contains too many transactions. + ErrInvalidTxCount = errors.New("invalid transaction count") ) diff --git a/core/block_validator.go b/core/block_validator.go index f3d65cea25ff..fc5890af7c1d 100644 --- a/core/block_validator.go +++ b/core/block_validator.go @@ -55,6 +55,14 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error { if v.bc.HasBlockAndState(block.Hash(), block.NumberU64()) { return ErrKnownBlock } + // Check if block tx count is is smaller than the max count + if !v.config.Scroll.IsValidTxCount(len(block.Transactions())) { + return consensus.ErrInvalidTxCount + } + // Check if block payload size is smaller than the max size + if !v.config.Scroll.IsValidBlockSize(block.PayloadSize()) { + return ErrInvalidBlockPayloadSize + } // Header validity is known at this point. Here we verify that uncles, transactions // and withdrawals given in the block body match the header. diff --git a/core/error.go b/core/error.go index 4214ed207a91..f0205fb590ed 100644 --- a/core/error.go +++ b/core/error.go @@ -26,6 +26,9 @@ var ( // ErrKnownBlock is returned when a block to import is already known locally. ErrKnownBlock = errors.New("block already known") + // ErrInvalidBlockPayloadSize is returned when a block to import has an oversized payload. + ErrInvalidBlockPayloadSize = errors.New("invalid block payload size") + // ErrBannedHash is returned if a block to import is on the banned list. ErrBannedHash = errors.New("banned hash") diff --git a/core/types/block.go b/core/types/block.go index 1a357baa3a41..c09628724cb2 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -415,6 +415,18 @@ func (b *Block) Size() uint64 { return uint64(c) } +// PayloadSize returns the encoded storage size sum of all transactions in a block. +func (b *Block) PayloadSize() uint64 { + // add up all txs sizes + var totalSize uint64 + for _, tx := range b.transactions { + if !tx.IsL1MessageTx() { + totalSize += tx.Size() + } + } + return totalSize +} + // SanityCheck can be used to prevent that unbounded fields are // stuffed with junk data to add processing overhead func (b *Block) SanityCheck() error { diff --git a/miner/worker.go b/miner/worker.go index f68070281454..165467264528 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -85,6 +85,7 @@ type environment struct { signer types.Signer state *state.StateDB // apply state changes here tcount int // tx count in cycle + blockSize uint64 // approximate size of tx payload in bytes gasPool *core.GasPool // available gas used to pack transactions coinbase common.Address @@ -722,6 +723,7 @@ func (w *worker) makeEnv(parent *types.Header, header *types.Header, coinbase co } // Keep track of transactions which return errors so they can be removed env.tcount = 0 + env.blockSize = 0 return env, nil } @@ -834,6 +836,18 @@ func (w *worker) commitTransactions(env *environment, txs *transactionsByPriceAn txs.Pop() continue } + // If we have collected enough transactions then we're done + // Originally we only limit l2txs count, but now strictly limit total txs number. + // log.Info("w.chainConfig", "w.chainConfig.Scroll", w.chainConfig.Scroll) + if !w.chainConfig.Scroll.IsValidTxCount(env.tcount + 1) { + log.Trace("Transaction count limit reached", "have", env.tcount, "want", w.chainConfig.Scroll.MaxTxPerBlock) + break + } + if !tx.IsL1MessageTx() && !w.chainConfig.Scroll.IsValidBlockSize(env.blockSize+tx.Size()) { + log.Trace("Block size limit reached", "have", env.blockSize, "want", w.chainConfig.Scroll.MaxTxPayloadBytesPerBlock, "tx", tx.Size()) + txs.Pop() // skip transactions from this account + continue + } // Error may be ignored here. The error has already been checked // during transaction acceptance is the transaction pool. from, _ := types.Sender(env.signer, tx) @@ -861,6 +875,12 @@ func (w *worker) commitTransactions(env *environment, txs *transactionsByPriceAn env.tcount++ txs.Shift() + if tx.IsL1MessageTx() { + } else { + // only consider block size limit for L2 transactions + env.blockSize += tx.Size() + } + default: // Transaction is regarded as invalid, drop all consecutive transactions from // the same sender because of `nonce-too-high` clause. diff --git a/params/config.go b/params/config.go index 6b1dec74b861..03a88c26cb81 100644 --- a/params/config.go +++ b/params/config.go @@ -333,6 +333,28 @@ type ChainConfig struct { Ethash *EthashConfig `json:"ethash,omitempty"` Clique *CliqueConfig `json:"clique,omitempty"` IsDevMode bool `json:"isDev,omitempty"` + + // Scroll genesis extension: enable scroll rollup-related traces & state transition + Scroll ScrollConfig `json:"scroll,omitempty"` +} + +type ScrollConfig struct { + // Maximum number of transactions per block [optional] + MaxTxPerBlock *int `json:"maxTxPerBlock,omitempty"` + + // Maximum tx payload size of blocks that we produce [optional] + MaxTxPayloadBytesPerBlock *int `json:"maxTxPayloadBytesPerBlock,omitempty"` +} + +// IsValidTxCount returns whether the given block's transaction count is below the limit. +// This limit corresponds to the number of ECDSA signature checks that we can fit into the zkEVM. +func (s ScrollConfig) IsValidTxCount(count int) bool { + return s.MaxTxPerBlock == nil || count <= *s.MaxTxPerBlock +} + +// IsValidBlockSize returns whether the given block's transaction payload size is below the limit. +func (s ScrollConfig) IsValidBlockSize(size uint64) bool { + return s.MaxTxPayloadBytesPerBlock == nil || size <= uint64(*s.MaxTxPayloadBytesPerBlock) } // EthashConfig is the consensus engine configs for proof-of-work based sealing. diff --git a/rollup/rcfg/config.go b/rollup/rcfg/config.go index 0f986f93ac79..5efdc64798d3 100644 --- a/rollup/rcfg/config.go +++ b/rollup/rcfg/config.go @@ -9,6 +9,11 @@ import ( // TODO: // verify in consensus layer when decentralizing sequencer +var ( + ScrollMaxTxPerBlock = 100 + ScrollMaxTxPayloadBytesPerBlock = 120 * 1024 +) + var ( // L2MessageQueueAddress is the address of the L2MessageQueue // predeploy