Compare commits

..

2 Commits

Author SHA1 Message Date
colin
e843419397 fix(rollup-relayer): better estimate commit batch gas limit (#982)
Co-authored-by: colinlyguo <colinlyguo@users.noreply.github.com>
2023-10-09 08:31:31 +02:00
colin
badde3cba5 fix(rollup-relayer): better handle commit batch revert (#981)
Co-authored-by: colinlyguo <colinlyguo@users.noreply.github.com>
Co-authored-by: HAOYUatHZ <37070449+HAOYUatHZ@users.noreply.github.com>
2023-10-08 13:39:17 +02:00
8 changed files with 48 additions and 19 deletions

View File

@@ -15,9 +15,16 @@ import (
// CalldataNonZeroByteGas is the gas consumption per non zero byte in calldata.
const CalldataNonZeroByteGas = 16
// GetKeccak256Gas calculates keccak256 hash gas.
// GetKeccak256Gas calculates the gas cost for computing the keccak256 hash of a given size.
func GetKeccak256Gas(size uint64) uint64 {
return 30 + 6*((size+31)/32) // 30 + 6 * ceil(size / 32)
return GetMemoryExpansionCost(size) + 30 + 6*((size+31)/32)
}
// GetMemoryExpansionCost calculates the cost of memory expansion for a given memoryByteSize.
func GetMemoryExpansionCost(memoryByteSize uint64) uint64 {
memorySizeWord := (memoryByteSize + 31) / 32
memoryCost := (memorySizeWord*memorySizeWord)/512 + (3 * memorySizeWord)
return memoryCost
}
// WrappedBlock contains the block's Header, Transactions and WithdrawTrieRoot hash.
@@ -131,6 +138,12 @@ func (w *WrappedBlock) EstimateL1CommitGas() uint64 {
total += 100 * numL1Messages // numL1Messages times call to L1MessageQueue
total += 100 * numL1Messages // numL1Messages times warm address access to L1MessageQueue
total += GetMemoryExpansionCost(36) * numL1Messages // staticcall to proxy
total += 100 * numL1Messages // read admin in proxy
total += 100 * numL1Messages // read impl in proxy
total += 100 * numL1Messages // access impl
total += GetMemoryExpansionCost(36) * numL1Messages // delegatecall to impl
return total
}

View File

@@ -46,7 +46,7 @@ func TestChunkEncode(t *testing.T) {
},
}
assert.Equal(t, uint64(0), chunk.NumL1Messages(0))
assert.Equal(t, uint64(6006), chunk.EstimateL1CommitGas())
assert.Equal(t, uint64(6042), chunk.EstimateL1CommitGas())
bytes, err = chunk.Encode(0)
hexString := hex.EncodeToString(bytes)
assert.NoError(t, err)
@@ -68,7 +68,7 @@ func TestChunkEncode(t *testing.T) {
},
}
assert.Equal(t, uint64(11), chunk.NumL1Messages(0))
assert.Equal(t, uint64(5002), chunk.EstimateL1CommitGas())
assert.Equal(t, uint64(5329), chunk.EstimateL1CommitGas())
bytes, err = chunk.Encode(0)
hexString = hex.EncodeToString(bytes)
assert.NoError(t, err)
@@ -84,7 +84,7 @@ func TestChunkEncode(t *testing.T) {
},
}
assert.Equal(t, uint64(11), chunk.NumL1Messages(0))
assert.Equal(t, uint64(9958), chunk.EstimateL1CommitGas())
assert.Equal(t, uint64(10612), chunk.EstimateL1CommitGas())
bytes, err = chunk.Encode(0)
hexString = hex.EncodeToString(bytes)
assert.NoError(t, err)

View File

@@ -5,7 +5,7 @@ import (
"runtime/debug"
)
var tag = "v4.3.30"
var tag = "v4.3.32"
var commit = func() string {
if info, ok := debug.ReadBuildInfo(); ok {

View File

@@ -307,12 +307,12 @@ func (r *Layer2Relayer) ProcessGasPriceOracle() {
// ProcessPendingBatches processes the pending batches by sending commitBatch transactions to layer 1.
func (r *Layer2Relayer) ProcessPendingBatches() {
// get pending batches from database in ascending order by their index.
pendingBatches, err := r.batchOrm.GetPendingBatches(r.ctx, 5)
batches, err := r.batchOrm.GetFailedAndPendingBatches(r.ctx, 5)
if err != nil {
log.Error("Failed to fetch pending L2 batches", "err", err)
return
}
for _, batch := range pendingBatches {
for _, batch := range batches {
r.metrics.rollupL2RelayerProcessPendingBatchTotal.Inc()
// get current header and parent header.
currentBatchHeader, err := types.DecodeBatchHeader(batch.BatchHeader)
@@ -327,6 +327,12 @@ func (r *Layer2Relayer) ProcessPendingBatches() {
log.Error("Failed to get parent batch header", "index", batch.Index-1, "error", err)
return
}
if types.RollupStatus(parentBatch.RollupStatus) == types.RollupCommitFailed {
log.Error("Previous batch commit failed, halting further committing",
"index", parentBatch.Index, "tx hash", parentBatch.CommitTxHash)
return
}
}
// get the chunks for the batch
@@ -371,6 +377,11 @@ func (r *Layer2Relayer) ProcessPendingBatches() {
// send transaction
txID := batch.Hash + "-commit"
fallbackGasLimit := uint64(float64(batch.TotalL1CommitGas) * r.cfg.L1CommitGasLimitMultiplier)
if types.RollupStatus(batch.RollupStatus) == types.RollupCommitFailed {
// use eth_estimateGas if this batch has been committed failed.
fallbackGasLimit = 0
log.Warn("Batch commit previously failed, using eth_estimateGas for the re-submission", "hash", batch.Hash)
}
txHash, err := r.commitSender.SendTransaction(txID, &r.cfg.RollupContractAddress, big.NewInt(0), calldata, fallbackGasLimit)
if err != nil {
log.Error(

View File

@@ -207,9 +207,11 @@ func (p *BatchProposer) proposeBatchChunks() ([]*orm.Chunk, *types.BatchMeta, er
// adjust batch header hash gas cost, batch header size: 89 + 32 * ceil(l1MessagePopped / 256)
totalL1CommitGas -= types.GetKeccak256Gas(89 + 32*(totalL1MessagePopped+255)/256)
totalL1CommitGas -= types.CalldataNonZeroByteGas * (32 * (totalL1MessagePopped + 255) / 256)
totalL1CommitGas -= types.GetMemoryExpansionCost(uint64(totalL1CommitCalldataSize))
totalL1MessagePopped += uint64(chunk.TotalL1MessagesPoppedInChunk)
totalL1CommitGas += types.CalldataNonZeroByteGas * (32 * (totalL1MessagePopped + 255) / 256)
totalL1CommitGas += types.GetKeccak256Gas(89 + 32*(totalL1MessagePopped+255)/256)
totalL1CommitGas += types.GetMemoryExpansionCost(uint64(totalL1CommitCalldataSize))
totalOverEstimateL1CommitGas := uint64(p.gasCostIncreaseMultiplier * float64(totalL1CommitGas))
if totalL1CommitCalldataSize > p.maxL1CommitCalldataSizePerBatch ||
totalOverEstimateL1CommitGas > p.maxL1CommitGasPerBatch {

View File

@@ -109,9 +109,9 @@ func testBatchProposerLimits(t *testing.T) {
chunkOrm := orm.NewChunk(db)
chunks, err := chunkOrm.GetChunksInRange(context.Background(), 0, 1)
assert.NoError(t, err)
assert.Equal(t, uint64(6006), chunks[0].TotalL1CommitGas)
assert.Equal(t, uint64(6042), chunks[0].TotalL1CommitGas)
assert.Equal(t, uint32(298), chunks[0].TotalL1CommitCalldataSize)
assert.Equal(t, uint64(93982), chunks[1].TotalL1CommitGas)
assert.Equal(t, uint64(94586), chunks[1].TotalL1CommitGas)
assert.Equal(t, uint32(5735), chunks[1].TotalL1CommitCalldataSize)
bp := NewBatchProposer(context.Background(), &config.BatchProposerConfig{
@@ -168,9 +168,9 @@ func testBatchCommitGasAndCalldataSizeEstimation(t *testing.T) {
chunkOrm := orm.NewChunk(db)
chunks, err := chunkOrm.GetChunksInRange(context.Background(), 0, 1)
assert.NoError(t, err)
assert.Equal(t, uint64(6006), chunks[0].TotalL1CommitGas)
assert.Equal(t, uint64(6042), chunks[0].TotalL1CommitGas)
assert.Equal(t, uint32(298), chunks[0].TotalL1CommitCalldataSize)
assert.Equal(t, uint64(93982), chunks[1].TotalL1CommitGas)
assert.Equal(t, uint64(94586), chunks[1].TotalL1CommitGas)
assert.Equal(t, uint32(5735), chunks[1].TotalL1CommitCalldataSize)
bp := NewBatchProposer(context.Background(), &config.BatchProposerConfig{
@@ -199,6 +199,6 @@ func testBatchCommitGasAndCalldataSizeEstimation(t *testing.T) {
assert.Equal(t, types.ProvingTaskUnassigned, types.ProvingStatus(chunk.ProvingStatus))
}
assert.Equal(t, uint64(253916), batches[0].TotalL1CommitGas)
assert.Equal(t, uint64(254562), batches[0].TotalL1CommitGas)
assert.Equal(t, uint32(6033), batches[0].TotalL1CommitCalldataSize)
}

View File

@@ -192,22 +192,22 @@ func (o *Batch) GetRollupStatusByHashList(ctx context.Context, hashes []string)
return statuses, nil
}
// GetPendingBatches retrieves pending batches up to the specified limit.
// GetFailedAndPendingBatches retrieves batches with failed or pending status up to the specified limit.
// The returned batches are sorted in ascending order by their index.
func (o *Batch) GetPendingBatches(ctx context.Context, limit int) ([]*Batch, error) {
func (o *Batch) GetFailedAndPendingBatches(ctx context.Context, limit int) ([]*Batch, error) {
if limit <= 0 {
return nil, errors.New("limit must be greater than zero")
}
db := o.db.WithContext(ctx)
db = db.Model(&Batch{})
db = db.Where("rollup_status = ?", types.RollupPending)
db = db.Where("rollup_status = ? OR rollup_status = ?", types.RollupCommitFailed, types.RollupPending)
db = db.Order("index ASC")
db = db.Limit(limit)
var batches []*Batch
if err := db.Find(&batches).Error; err != nil {
return nil, fmt.Errorf("Batch.GetPendingBatches error: %w", err)
return nil, fmt.Errorf("Batch.GetFailedAndPendingBatches error: %w", err)
}
return batches, nil
}

View File

@@ -258,14 +258,17 @@ func TestBatchOrm(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, uint64(2), count)
pendingBatches, err := batchOrm.GetPendingBatches(context.Background(), 100)
err = batchOrm.UpdateRollupStatus(context.Background(), batchHash1, types.RollupCommitFailed)
assert.NoError(t, err)
pendingBatches, err := batchOrm.GetFailedAndPendingBatches(context.Background(), 100)
assert.NoError(t, err)
assert.Equal(t, 2, len(pendingBatches))
rollupStatus, err := batchOrm.GetRollupStatusByHashList(context.Background(), []string{batchHash1, batchHash2})
assert.NoError(t, err)
assert.Equal(t, 2, len(rollupStatus))
assert.Equal(t, types.RollupPending, rollupStatus[0])
assert.Equal(t, types.RollupCommitFailed, rollupStatus[0])
assert.Equal(t, types.RollupPending, rollupStatus[1])
err = batchOrm.UpdateProvingStatus(context.Background(), batchHash2, types.ProvingTaskVerified)