mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-28 23:56:43 +00:00
fix: skip L1 message that leads to gas limit exceeded (#455)
* skip L1 message that leads to gas limit exceeded * add test * do not skip l1 messages from the same account
This commit is contained in:
parent
2a1788f7f3
commit
eda99eec35
4 changed files with 91 additions and 9 deletions
|
|
@ -168,7 +168,7 @@ func (v *BlockValidator) ValidateL1Messages(block *types.Block) error {
|
|||
// skipped messages
|
||||
// TODO: consider verifying that skipped messages overflow
|
||||
for index := queueIndex; index < txQueueIndex; index++ {
|
||||
log.Debug("Skipped L1 message", "block", block.Hash().String(), "queueIndex", index)
|
||||
log.Debug("Skipped L1 message", "queueIndex", index, "tx", tx.Hash().String(), "block", block.Hash().String())
|
||||
|
||||
if exists := it.Next(); !exists {
|
||||
// the message in this block is not available in our local db.
|
||||
|
|
|
|||
|
|
@ -1002,6 +1002,18 @@ loop:
|
|||
|
||||
logs, err := w.commitTransaction(tx, coinbase)
|
||||
switch {
|
||||
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 w.current.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", w.current.header.Number, "reason", "gas limit exceeded")
|
||||
w.current.nextL1MsgIndex = queueIndex + 1
|
||||
txs.Shift()
|
||||
|
||||
case errors.Is(err, core.ErrGasLimitReached):
|
||||
// Pop the current out-of-gas transaction without shifting in the next from the account
|
||||
log.Trace("Gas limit exceeded for current block", "sender", from)
|
||||
|
|
@ -1021,8 +1033,10 @@ loop:
|
|||
// Everything ok, collect the logs and shift in the next transaction from the same account
|
||||
coalescedLogs = append(coalescedLogs, logs...)
|
||||
if tx.IsL1MessageTx() {
|
||||
queueIndex := tx.AsL1MessageTx().QueueIndex
|
||||
log.Debug("Including L1 message", "queueIndex", queueIndex, "tx", tx.Hash().String())
|
||||
w.current.l1TxCount++
|
||||
w.current.nextL1MsgIndex = tx.AsL1MessageTx().QueueIndex + 1
|
||||
w.current.nextL1MsgIndex = queueIndex + 1
|
||||
}
|
||||
w.current.tcount++
|
||||
w.current.blockSize += tx.Size()
|
||||
|
|
@ -1038,7 +1052,7 @@ loop:
|
|||
// 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", w.current.accRows, "tx", tx.Hash())
|
||||
log.Trace("Circuit capacity limit reached in a block", "acc_rows", w.current.accRows, "tx", tx.Hash().String())
|
||||
circuitCapacityReached = true
|
||||
break loop
|
||||
|
||||
|
|
@ -1047,33 +1061,39 @@ loop:
|
|||
// because we shouldn't skip the entire txs from the same account.
|
||||
// This is also useful for skipping "problematic" L1MessageTxs.
|
||||
queueIndex := tx.AsL1MessageTx().QueueIndex
|
||||
log.Trace("Circuit capacity limit reached for a single tx", "tx", tx.Hash(), "queueIndex", queueIndex)
|
||||
log.Trace("Circuit capacity limit reached for a single tx", "tx", tx.Hash().String(), "queueIndex", queueIndex)
|
||||
log.Info("Skipping L1 message", "queueIndex", queueIndex, "tx", tx.Hash().String(), "block", w.current.header.Number, "reason", "row consumption overflow")
|
||||
w.current.nextL1MsgIndex = queueIndex + 1
|
||||
txs.Shift()
|
||||
|
||||
case (errors.Is(err, circuitcapacitychecker.ErrTxRowConsumptionOverflow) && !tx.IsL1MessageTx()):
|
||||
// Circuit capacity check: L2MessageTx row consumption too high, skip the account.
|
||||
// This is also useful for skipping "problematic" L2MessageTxs.
|
||||
log.Trace("Circuit capacity limit reached for a single tx", "tx", tx.Hash())
|
||||
log.Trace("Circuit capacity limit reached for a single tx", "tx", tx.Hash().String())
|
||||
txs.Pop()
|
||||
|
||||
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(), "queueIndex", 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", w.current.header.Number, "reason", "unknown row consumption error")
|
||||
w.current.nextL1MsgIndex = queueIndex + 1
|
||||
txs.Shift()
|
||||
|
||||
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())
|
||||
log.Trace("Unknown circuit capacity checker error for L2MessageTx", "tx", tx.Hash().String())
|
||||
txs.Pop()
|
||||
|
||||
default:
|
||||
// Strange error, discard the transaction and get the next in line (note, the
|
||||
// nonce-too-high clause will prevent us from executing in vain).
|
||||
log.Debug("Transaction failed, account skipped", "hash", tx.Hash(), "err", err)
|
||||
log.Debug("Transaction failed, account skipped", "hash", tx.Hash().String(), "err", err)
|
||||
if tx.IsL1MessageTx() {
|
||||
queueIndex := tx.AsL1MessageTx().QueueIndex
|
||||
log.Info("Skipping L1 message", "queueIndex", queueIndex, "tx", tx.Hash().String(), "block", w.current.header.Number, "reason", "strange error", "err", err)
|
||||
}
|
||||
txs.Shift()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -731,3 +731,65 @@ func TestL1MsgCorrectOrder(t *testing.T) {
|
|||
t.Fatalf("timeout")
|
||||
}
|
||||
}
|
||||
|
||||
func TestL1MessageOverGasLimit(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
var (
|
||||
engine consensus.Engine
|
||||
chainConfig *params.ChainConfig
|
||||
db = rawdb.NewMemoryDatabase()
|
||||
)
|
||||
msgs := []types.L1MessageTx{
|
||||
{QueueIndex: 0, Gas: 10000000, To: &common.Address{1}, Data: []byte{0x01}, Sender: common.Address{2}},
|
||||
{QueueIndex: 1, Gas: 21016, To: &common.Address{1}, Data: []byte{0x01}, Sender: common.Address{2}}, // same sender
|
||||
{QueueIndex: 2, Gas: 21016, To: &common.Address{1}, Data: []byte{0x01}, Sender: common.Address{3}}} // different sender
|
||||
rawdb.WriteL1Messages(db, msgs)
|
||||
|
||||
chainConfig = params.AllCliqueProtocolChanges
|
||||
chainConfig.Clique = ¶ms.CliqueConfig{Period: 1, Epoch: 30000}
|
||||
engine = clique.New(chainConfig.Clique, db)
|
||||
chainConfig.Scroll.L1Config = ¶ms.L1Config{
|
||||
NumL1MessagesPerBlock: 3,
|
||||
}
|
||||
|
||||
chainConfig.LondonBlock = big.NewInt(0)
|
||||
w, b := newTestWorker(t, chainConfig, engine, db, 0)
|
||||
defer w.close()
|
||||
|
||||
// This test chain imports the mined blocks.
|
||||
b.genesis.MustCommit(db)
|
||||
chain, _ := core.NewBlockChain(db, nil, b.chain.Config(), engine, vm.Config{
|
||||
Debug: true,
|
||||
Tracer: vm.NewStructLogger(&vm.LogConfig{EnableMemory: true, EnableReturnData: true})}, nil, nil, false)
|
||||
defer chain.Stop()
|
||||
|
||||
// Ignore empty commit here for less noise.
|
||||
w.skipSealHook = func(task *task) bool {
|
||||
return len(task.receipts) == 0
|
||||
}
|
||||
|
||||
// Wait for mined blocks.
|
||||
sub := w.mux.Subscribe(core.NewMinedBlockEvent{})
|
||||
defer sub.Unsubscribe()
|
||||
|
||||
// Start mining!
|
||||
w.start()
|
||||
|
||||
select {
|
||||
case ev := <-sub.Chan():
|
||||
block := ev.Data.(core.NewMinedBlockEvent).Block
|
||||
if _, err := chain.InsertChain([]*types.Block{block}); err != nil {
|
||||
t.Fatalf("failed to insert new mined block %d: %v", block.NumberU64(), err)
|
||||
}
|
||||
|
||||
// Should contain 2, not 1
|
||||
// i.e. we should only skip 1 message
|
||||
assert.Equal(2, len(block.Transactions()))
|
||||
|
||||
queueIndex := rawdb.ReadFirstQueueIndexNotInL2Block(db, block.Hash())
|
||||
assert.NotNil(queueIndex)
|
||||
assert.Equal(uint64(3), *queueIndex)
|
||||
case <-time.After(3 * time.Second):
|
||||
t.Fatalf("timeout")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ import (
|
|||
const (
|
||||
VersionMajor = 4 // Major version component of the current release
|
||||
VersionMinor = 3 // Minor version component of the current release
|
||||
VersionPatch = 30 // Patch version component of the current release
|
||||
VersionPatch = 31 // Patch version component of the current release
|
||||
VersionMeta = "sepolia" // Version metadata to append to the version string
|
||||
)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue