diff --git a/common/types/block.go b/common/types/block.go index 6cd95d67a9..28de903782 100644 --- a/common/types/block.go +++ b/common/types/block.go @@ -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(size) +} + +// GetMemoryExpansionCost calculates the cost of memory expansion for a given memory_byte_size. +func GetMemoryExpansionCost(memory_byte_size uint64) uint64 { + memory_size_word := (memory_byte_size + 31) / 32 + memory_cost := (memory_size_word*memory_size_word)/512 + (3 * memory_size_word) + return memory_cost } // 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 } diff --git a/common/types/chunk_test.go b/common/types/chunk_test.go index 01ba2ffeeb..5bfb4c6fa5 100644 --- a/common/types/chunk_test.go +++ b/common/types/chunk_test.go @@ -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(6078), 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(5344), 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(10642), chunk.EstimateL1CommitGas()) bytes, err = chunk.Encode(0) hexString = hex.EncodeToString(bytes) assert.NoError(t, err) diff --git a/rollup/internal/controller/watcher/batch_proposer.go b/rollup/internal/controller/watcher/batch_proposer.go index 0e2fa66a2b..0c3b0ba1d6 100644 --- a/rollup/internal/controller/watcher/batch_proposer.go +++ b/rollup/internal/controller/watcher/batch_proposer.go @@ -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 { diff --git a/rollup/internal/controller/watcher/batch_proposer_test.go b/rollup/internal/controller/watcher/batch_proposer_test.go index e140bcd5e3..e530c3002e 100644 --- a/rollup/internal/controller/watcher/batch_proposer_test.go +++ b/rollup/internal/controller/watcher/batch_proposer_test.go @@ -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(6078), chunks[0].TotalL1CommitGas) assert.Equal(t, uint32(298), chunks[0].TotalL1CommitCalldataSize) - assert.Equal(t, uint64(93982), chunks[1].TotalL1CommitGas) + assert.Equal(t, uint64(95190), 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(6078), chunks[0].TotalL1CommitGas) assert.Equal(t, uint32(298), chunks[0].TotalL1CommitCalldataSize) - assert.Equal(t, uint64(93982), chunks[1].TotalL1CommitGas) + assert.Equal(t, uint64(95190), 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(255208), batches[0].TotalL1CommitGas) assert.Equal(t, uint32(6033), batches[0].TotalL1CommitCalldataSize) }