Skip to content

Commit

Permalink
feat(relayer): estimate l1 batch commit gas
Browse files Browse the repository at this point in the history
  • Loading branch information
colinlyguo committed Jul 20, 2023
1 parent 577cc90 commit 801f109
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 40 deletions.
37 changes: 35 additions & 2 deletions bridge/internal/controller/watcher/batch_proposer.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package watcher

import (
"context"
"errors"
"fmt"
"time"

Expand Down Expand Up @@ -99,7 +100,32 @@ func (p *BatchProposer) proposeBatchChunks() ([]*orm.Chunk, error) {
firstChunk := dbChunks[0]
totalL1CommitCalldataSize := firstChunk.TotalL1CommitCalldataSize
totalL1CommitGas := firstChunk.TotalL1CommitGas
var totalChunks uint64 = 1
totalChunks := uint64(1)
totalL1MessagePopped := firstChunk.TotalL1MessagesPoppedBefore + uint64(firstChunk.TotalL1MessagesPoppedInChunk)

parentBatch, err := p.batchOrm.GetLatestBatch(p.ctx)
if err != nil && !errors.Is(errors.Unwrap(err), gorm.ErrRecordNotFound) {
return nil, err
}

getKeccakGas := func(size uint64) uint64 {
return 30 + 6*((size+31)/32) // 30 + 6 * ceil(size / 32)
}

// Add extra gas costs
totalL1CommitGas += 4 * 2100 // 4 one-time cold sload for commitBatch
totalL1CommitGas += 20000 // 1 time sstore
// adjusting gas:
// add 1 time cold sload (2100 gas) for L1MessageQueue
// add 1 time cold address access (2600 gas) for L1MessageQueue
// minus 1 time warm sload (100 gas) & 1 time warm address access (100 gas)
totalL1CommitGas += (2100 + 2600 - 100 - 100)
totalL1CommitGas += getKeccakGas(32 * totalChunks) // batch data hash
if parentBatch != nil { // parent batch header hash
totalL1CommitGas += getKeccakGas(uint64(len(parentBatch.BatchHeader)))
}
// batch header size: 89 + 32 * ceil(l1MessagePopped / 256)
totalL1CommitGas += getKeccakGas(89 + 32*(totalL1MessagePopped+255)/256)

// Check if the first chunk breaks hard limits.
// If so, it indicates there are bugs in chunk-proposer, manual fix is needed.
Expand All @@ -124,9 +150,16 @@ func (p *BatchProposer) proposeBatchChunks() ([]*orm.Chunk, error) {
}

for i, chunk := range dbChunks[1:] {
totalChunks++
totalL1CommitCalldataSize += chunk.TotalL1CommitCalldataSize
totalL1CommitGas += chunk.TotalL1CommitGas
// adjust batch data hash gas cost: add one chunk
totalL1CommitGas -= getKeccakGas(32 * totalChunks)
totalChunks++
totalL1CommitGas += getKeccakGas(32 * totalChunks)
// adjust batch header hash gas cost: adjust totalL1MessagePopped in calculating header length
totalL1CommitGas -= getKeccakGas(89 + 32*(totalL1MessagePopped+255)/256)
totalL1MessagePopped += uint64(chunk.TotalL1MessagesPoppedInChunk)
totalL1CommitGas += getKeccakGas(89 + 32*(totalL1MessagePopped+255)/256)
if totalChunks > p.maxChunkNumPerBatch ||
totalL1CommitCalldataSize > p.maxL1CommitCalldataSizePerBatch ||
totalL1CommitGas > p.maxL1CommitGasPerBatch {
Expand Down
25 changes: 25 additions & 0 deletions bridge/internal/controller/watcher/chunk_proposer.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package watcher

import (
"context"
"errors"
"fmt"
"time"

Expand Down Expand Up @@ -88,11 +89,29 @@ func (p *ChunkProposer) proposeChunk() (*types.Chunk, error) {
return nil, nil
}

parentChunk, err := p.chunkOrm.GetLatestChunk(p.ctx)
if err != nil && !errors.Is(errors.Unwrap(err), gorm.ErrRecordNotFound) {
return nil, err
}

firstBlock := blocks[0]
totalTxGasUsed := firstBlock.Header.GasUsed
totalL2TxNum := firstBlock.L2TxsNum()
totalL1CommitCalldataSize := firstBlock.EstimateL1CommitCalldataSize()
totalBlocks := uint64(1)
totalL1CommitGas := firstBlock.EstimateL1CommitGas()
totalL1CommitGas += 100 // warm sload per block

getKeccakGas := func(size uint64) uint64 {
return 30 + 6*((size+31)/32) // 30 + 6 * ceil(size / 32)
}

var totalL1MessagesPoppedBefore uint64
if parentChunk != nil {
totalL1MessagesPoppedBefore = parentChunk.TotalL1MessagesPoppedBefore + uint64(parentChunk.TotalL1MessagesPoppedInChunk)
}
totalL1MessagesPoppedInChunk := firstBlock.NumL1Messages(totalL1MessagesPoppedBefore)
totalL1CommitGas += getKeccakGas(58*totalBlocks + 32*(totalL1MessagesPoppedInChunk+totalL2TxNum))

// Check if the first block breaks hard limits.
// If so, it indicates there are bugs in sequencer, manual fix is needed.
Expand Down Expand Up @@ -138,6 +157,12 @@ func (p *ChunkProposer) proposeChunk() (*types.Chunk, error) {
totalL2TxNum += block.L2TxsNum()
totalL1CommitCalldataSize += block.EstimateL1CommitCalldataSize()
totalL1CommitGas += block.EstimateL1CommitGas()
totalL1CommitGas += 100 // warm sload per block
// adjust chunk hash gas cost: add one block
totalL1CommitGas -= getKeccakGas(58*totalBlocks + 32*(totalL1MessagesPoppedInChunk+totalL2TxNum))
totalBlocks++
totalL1MessagesPoppedInChunk += block.NumL1Messages(totalL1MessagesPoppedBefore + totalL1MessagesPoppedInChunk)
totalL1CommitGas += getKeccakGas(58*totalBlocks + 32*(totalL1MessagesPoppedInChunk+totalL2TxNum))
if totalTxGasUsed > p.maxTxGasPerChunk ||
totalL2TxNum > p.maxL2TxNumPerChunk ||
totalL1CommitCalldataSize > p.maxL1CommitCalldataSizePerChunk ||
Expand Down
15 changes: 13 additions & 2 deletions bridge/internal/orm/chunk.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,18 @@ func (o *Chunk) InsertChunk(ctx context.Context, chunk *types.Chunk, dbTX ...*go
totalL1CommitGas += block.EstimateL1CommitGas()
}

numBlocks := len(chunk.Blocks)
numBlocks := uint64(len(chunk.Blocks))

// Calculate additional gas costs
totalL1CommitGas += numBlocks * 100 // numBlocks times warm sload

getKeccakGas := func(size uint64) uint64 {
return 30 + 6*((size+31)/32) // 30 + 6 * ceil(size / 32)
}

totalL1MessagesPoppedInChunk := chunk.NumL1Messages(totalL1MessagePoppedBefore)
totalL1CommitGas += getKeccakGas(58*numBlocks + 32*(totalL1MessagesPoppedInChunk+totalL2TxNum)) // chunk hash

newChunk := Chunk{
Index: chunkIndex,
Hash: hash.Hex(),
Expand All @@ -163,7 +174,7 @@ func (o *Chunk) InsertChunk(ctx context.Context, chunk *types.Chunk, dbTX ...*go
TotalL1CommitGas: totalL1CommitGas,
StartBlockTime: chunk.Blocks[0].Header.Time,
TotalL1MessagesPoppedBefore: totalL1MessagePoppedBefore,
TotalL1MessagesPoppedInChunk: uint32(chunk.NumL1Messages(totalL1MessagePoppedBefore)),
TotalL1MessagesPoppedInChunk: uint32(totalL1MessagesPoppedInChunk),
ProvingStatus: int16(types.ProvingTaskUnassigned),
}

Expand Down
44 changes: 9 additions & 35 deletions common/types/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"math"

"github.com/scroll-tech/go-ethereum/common"
"github.com/scroll-tech/go-ethereum/common/hexutil"
"github.com/scroll-tech/go-ethereum/core/types"
)

Expand Down Expand Up @@ -79,48 +78,23 @@ func (w *WrappedBlock) EstimateL1CommitCalldataSize() uint64 {
}

// EstimateL1CommitGas calculates the calldata gas in l1 commit approximately.
// TODO: This will need to be adjusted.
// The part added here is only the calldata cost,
// but we have execution cost for verifying blocks / chunks / batches and storing the batch hash.
func (w *WrappedBlock) EstimateL1CommitGas() uint64 {
var total uint64
var numL1Messages uint64
for _, txData := range w.Transactions {
if txData.Type == types.L1MessageTxType {
numL1Messages++
continue
}
data, _ := hexutil.Decode(txData.Data)
tx := types.NewTx(&types.LegacyTx{
Nonce: txData.Nonce,
To: txData.To,
Value: txData.Value.ToInt(),
Gas: txData.Gas,
GasPrice: txData.GasPrice.ToInt(),
Data: data,
V: txData.V.ToInt(),
R: txData.R.ToInt(),
S: txData.S.ToInt(),
})
rlpTxData, _ := tx.MarshalBinary()

for _, b := range rlpTxData {
if b == 0 {
total += zeroByteGas
} else {
total += nonZeroByteGas
}
}
}

var txLen [4]byte
binary.BigEndian.PutUint32(txLen[:], uint32(len(rlpTxData)))
// sload
total += numL1Messages * 2100 // numL1Messages times cold sload in L1MessageQueue

// staticcall
total += numL1Messages * 100 // numL1Messages times call to L1MessageQueue
total += numL1Messages * 100 // numL1Messages times warm address access to L1MessageQueue

for _, b := range txLen {
if b == 0 {
total += zeroByteGas
} else {
total += nonZeroByteGas
}
}
}
return total
}

Expand Down
2 changes: 1 addition & 1 deletion common/version/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"runtime/debug"
)

var tag = "v4.0.23"
var tag = "v4.0.24"

var commit = func() string {
if info, ok := debug.ReadBuildInfo(); ok {
Expand Down

0 comments on commit 801f109

Please sign in to comment.