Skip to content

Commit 53408ef

Browse files
authored
feat: Galileo rollup fee (#1251)
* feat: Galileo rollup fee * bound compressed size * update comment * update fee formula * add comment * add galileo fee test cases * bump version * nit * fix
1 parent 150cba3 commit 53408ef

File tree

3 files changed

+175
-33
lines changed

3 files changed

+175
-33
lines changed

params/version.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import (
2424
const (
2525
VersionMajor = 5 // Major version component of the current release
2626
VersionMinor = 9 // Minor version component of the current release
27-
VersionPatch = 7 // Patch version component of the current release
27+
VersionPatch = 8 // Patch version component of the current release
2828
VersionMeta = "mainnet" // Version metadata to append to the version string
2929
)
3030

rollup/fees/rollup_fee.go

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,33 @@ func estimateTxCompressionRatio(data []byte, blockNumber uint64, blockTime uint6
210210
return ratio, nil
211211
}
212212

213+
// calculateTxCompressedSize calculates the size of `data` after compression using da-codec.
214+
// We constrain compressed_size so that it cannot exceed the original size:
215+
//
216+
// compressed_size(tx) = min(size(zstd(rlp(tx))), size(rlp(tx)))
217+
//
218+
// This provides an upper bound on the rollup fee for a given transaction, regardless
219+
// what compression algorithm the sequencer/prover uses.
220+
func calculateTxCompressedSize(data []byte, blockNumber uint64, blockTime uint64, config *params.ChainConfig) (*big.Int, error) {
221+
// Compressed size of empty data is 0.
222+
// In practice, the rlp-encoded transaction is always non-empty.
223+
if len(data) == 0 {
224+
return common.Big0, nil
225+
}
226+
227+
// Compress data using da-codec
228+
compressed, err := encoding.CompressScrollBatchBytes(data, blockNumber, blockTime, config)
229+
if err != nil {
230+
log.Error("Transaction compression failed", "error", err, "data size", len(data), "data", common.Bytes2Hex(data), "blockNumber", blockNumber, "blockTime", blockTime, "galileoTime", config.GalileoTime)
231+
return nil, fmt.Errorf("transaction compression failed: %w", err)
232+
}
233+
234+
if len(compressed) < len(data) {
235+
return new(big.Int).SetUint64(uint64(len(compressed))), nil
236+
}
237+
return new(big.Int).SetUint64(uint64(len(data))), nil
238+
}
239+
213240
// calculatePenalty computes the penalty multiplier based on compression ratio
214241
// penalty(tx) = compression_ratio(tx) >= penalty_threshold ? 1 * PRECISION : penalty_factor
215242
func calculatePenalty(compressionRatio, penaltyThreshold, penaltyFactor *big.Int) *big.Int {
@@ -290,6 +317,48 @@ func calculateEncodedL1DataFeeFeynman(
290317
return l1DataFee
291318
}
292319

320+
// calculateEncodedL1DataFeeGalileo computes the rollup fee for an RLP-encoded tx, post Galileo
321+
//
322+
// Post Galileo rollup fee formula:
323+
// rollupFee(tx) = feePerByte * compressedSize(tx) * (1 + penalty(tx)) / PRECISION
324+
//
325+
// Where:
326+
// feePerByte = (execScalar * l1BaseFee + blobScalar * l1BlobBaseFee)
327+
// compressedSize(tx) = min(len(zstd(rlp(tx))), len(rlp(tx)))
328+
// penalty(tx) = compressedSize(tx) / penaltyFactor
329+
func calculateEncodedL1DataFeeGalileo(
330+
l1BaseFee *big.Int,
331+
l1BlobBaseFee *big.Int,
332+
execScalar *big.Int,
333+
blobScalar *big.Int,
334+
penaltyFactor *big.Int,
335+
compressedSize *big.Int,
336+
) *big.Int {
337+
// Sanitize penalty factor.
338+
if penaltyFactor.Cmp(common.Big0) == 0 {
339+
penaltyFactor = common.Big1
340+
}
341+
342+
// feePerByte = (execScalar * l1BaseFee) + (blobScalar * l1BlobBaseFee)
343+
execGas := new(big.Int).Mul(execScalar, l1BaseFee)
344+
blobGas := new(big.Int).Mul(blobScalar, l1BlobBaseFee)
345+
feePerByte := new(big.Int).Add(execGas, blobGas)
346+
347+
// baseTerm = feePerByte * compressedSize
348+
baseTerm := new(big.Int).Mul(feePerByte, compressedSize)
349+
350+
// penaltyTerm = (baseTerm * compressedSize) / penaltyFactor
351+
// Note: We divide by penaltyFactor after multiplication to preserve precision.
352+
penaltyTerm := new(big.Int).Mul(baseTerm, compressedSize)
353+
penaltyTerm.Div(penaltyTerm, penaltyFactor)
354+
355+
// rollupFee = (baseTerm + penaltyTerm) / PRECISION
356+
rollupFee := new(big.Int).Add(baseTerm, penaltyTerm)
357+
rollupFee.Div(rollupFee, rcfg.Precision) // execScalar and blobScalar are scaled by PRECISION
358+
359+
return rollupFee
360+
}
361+
293362
// calculateL1GasUsed computes the L1 gas used based on the calldata and
294363
// constant sized overhead. The overhead can be decreased as the cost of the
295364
// batch submission goes down via contract optimizations. This will not overflow
@@ -341,7 +410,7 @@ func CalculateL1DataFee(tx *types.Transaction, state StateDB, config *params.Cha
341410
l1DataFee = calculateEncodedL1DataFee(raw, gpoState.overhead, gpoState.l1BaseFee, gpoState.scalar)
342411
} else if !config.IsFeynman(blockTime) {
343412
l1DataFee = calculateEncodedL1DataFeeCurie(raw, gpoState.l1BaseFee, gpoState.l1BlobBaseFee, gpoState.commitScalar, gpoState.blobScalar)
344-
} else {
413+
} else if !config.IsGalileo(blockTime) {
345414
// Calculate compression ratio for Feynman
346415
// Note: We compute the transaction ratio on tx.data, not on the full encoded transaction.
347416
compressionRatio, err := estimateTxCompressionRatio(tx.Data(), blockNumber.Uint64(), blockTime, config)
@@ -360,6 +429,21 @@ func CalculateL1DataFee(tx *types.Transaction, state StateDB, config *params.Cha
360429
gpoState.penaltyFactor,
361430
compressionRatio,
362431
)
432+
} else {
433+
// Note: In Galileo, we take the compressed size of the full RLP-encoded transaction.
434+
compressedSize, err := calculateTxCompressedSize(raw, blockNumber.Uint64(), blockTime, config)
435+
if err != nil {
436+
return nil, fmt.Errorf("failed to calculate compressed size: tx hash=%s: %w", tx.Hash().Hex(), err)
437+
}
438+
439+
l1DataFee = calculateEncodedL1DataFeeGalileo(
440+
gpoState.l1BaseFee,
441+
gpoState.l1BlobBaseFee,
442+
gpoState.commitScalar, // now represents execScalar
443+
gpoState.blobScalar,
444+
gpoState.penaltyFactor, // in Galileo, penaltyFactor is repurposed as a coefficient of the blob utilization penalty
445+
compressedSize,
446+
)
363447
}
364448

365449
// ensure l1DataFee fits into uint64 for circuit compatibility

0 commit comments

Comments
 (0)