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
This commit is contained in:
HAOYUatHZ 2023-11-28 15:24:21 +08:00 committed by GitHub
parent c70a901ed3
commit b639349788
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 73 additions and 0 deletions

View file

@ -38,4 +38,7 @@ var (
// ErrInvalidTerminalBlock is returned if a block is invalid wrt. the terminal // ErrInvalidTerminalBlock is returned if a block is invalid wrt. the terminal
// total difficulty. // total difficulty.
ErrInvalidTerminalBlock = errors.New("invalid terminal block") ErrInvalidTerminalBlock = errors.New("invalid terminal block")
// ErrInvalidTxCount is returned if a block contains too many transactions.
ErrInvalidTxCount = errors.New("invalid transaction count")
) )

View file

@ -55,6 +55,14 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error {
if v.bc.HasBlockAndState(block.Hash(), block.NumberU64()) { if v.bc.HasBlockAndState(block.Hash(), block.NumberU64()) {
return ErrKnownBlock 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 // Header validity is known at this point. Here we verify that uncles, transactions
// and withdrawals given in the block body match the header. // and withdrawals given in the block body match the header.

View file

@ -26,6 +26,9 @@ var (
// ErrKnownBlock is returned when a block to import is already known locally. // ErrKnownBlock is returned when a block to import is already known locally.
ErrKnownBlock = errors.New("block already known") 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 is returned if a block to import is on the banned list.
ErrBannedHash = errors.New("banned hash") ErrBannedHash = errors.New("banned hash")

View file

@ -415,6 +415,18 @@ func (b *Block) Size() uint64 {
return uint64(c) 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 // SanityCheck can be used to prevent that unbounded fields are
// stuffed with junk data to add processing overhead // stuffed with junk data to add processing overhead
func (b *Block) SanityCheck() error { func (b *Block) SanityCheck() error {

View file

@ -85,6 +85,7 @@ type environment struct {
signer types.Signer signer types.Signer
state *state.StateDB // apply state changes here state *state.StateDB // apply state changes here
tcount int // tx count in cycle tcount int // tx count in cycle
blockSize uint64 // approximate size of tx payload in bytes
gasPool *core.GasPool // available gas used to pack transactions gasPool *core.GasPool // available gas used to pack transactions
coinbase common.Address 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 // Keep track of transactions which return errors so they can be removed
env.tcount = 0 env.tcount = 0
env.blockSize = 0
return env, nil return env, nil
} }
@ -834,6 +836,18 @@ func (w *worker) commitTransactions(env *environment, txs *transactionsByPriceAn
txs.Pop() txs.Pop()
continue 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 // Error may be ignored here. The error has already been checked
// during transaction acceptance is the transaction pool. // during transaction acceptance is the transaction pool.
from, _ := types.Sender(env.signer, tx) from, _ := types.Sender(env.signer, tx)
@ -861,6 +875,12 @@ func (w *worker) commitTransactions(env *environment, txs *transactionsByPriceAn
env.tcount++ env.tcount++
txs.Shift() txs.Shift()
if tx.IsL1MessageTx() {
} else {
// only consider block size limit for L2 transactions
env.blockSize += tx.Size()
}
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.

View file

@ -333,6 +333,28 @@ type ChainConfig struct {
Ethash *EthashConfig `json:"ethash,omitempty"` Ethash *EthashConfig `json:"ethash,omitempty"`
Clique *CliqueConfig `json:"clique,omitempty"` Clique *CliqueConfig `json:"clique,omitempty"`
IsDevMode bool `json:"isDev,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. // EthashConfig is the consensus engine configs for proof-of-work based sealing.

View file

@ -9,6 +9,11 @@ import (
// TODO: // TODO:
// verify in consensus layer when decentralizing sequencer // verify in consensus layer when decentralizing sequencer
var (
ScrollMaxTxPerBlock = 100
ScrollMaxTxPayloadBytesPerBlock = 120 * 1024
)
var ( var (
// L2MessageQueueAddress is the address of the L2MessageQueue // L2MessageQueueAddress is the address of the L2MessageQueue
// predeploy // predeploy