Skip to content
Closed
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 59 additions & 1 deletion src/l1block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ const L1_SCALAR_SLOT: U256 = U256::from_limbs([3u64, 0, 0, 0]);
const L1_BLOB_BASE_FEE_SLOT: U256 = U256::from_limbs([5u64, 0, 0, 0]);

/// The L1 commit scalar storage slot.
///
/// Post-FEYNMAN this represents the exec_scalar.
const L1_COMMIT_SCALAR_SLOT: U256 = U256::from_limbs([6u64, 0, 0, 0]);

/// The L1 blob scalar storage slot.
Expand Down Expand Up @@ -138,12 +140,68 @@ impl L1BlockInfo {
self.calldata_gas.unwrap().saturating_add(blob_gas).wrapping_div(TX_L1_FEE_PRECISION)
}

fn calculate_tx_l1_cost_feynman(&self, input: &[u8], spec_id: ScrollSpecId) -> U256 {
// rollup_fee(tx) = compression_ratio(tx) * size(tx) * (component_exec + component_blob)
//
// - compression_ratio(tx): estimated compressibility of the signed tx data. The tx is
// eventually a part of a L2 batch that should likely result in a better compression ratio,
// however a conservative estimate is the size of zstd-encoding of the signed tx.
//
// - size(tx): denotes the size of the signed tx.
//
// - component_exec: The component that accounts towards commiting this tx as part of a L2
// batch as well as gas costs for the eventual on-chain proof verification.
// => (compression_scalar + commit_scalar + verification_scalar) * l1_base_fee
// => (exec_scalar) * l1_base_fee
//
// - component_blob: The component that accounts the costs associated with data
// availability, i.e. the costs of posting this tx's data in the EIP-4844 blob.
// => (compression_scalar + blob_scalar) * l1_blob_base_fee
// => (new_blob_scalar) * l1_blob_base_fee
//
// Note that the same slots for L1_COMMIT_SCALAR_SLOT and L1_BLOB_SCALAR_SLOT are
// re-used/updated for the new values post-FEYNMAN.
let component_exec = {
let exec_scalar = self
.l1_commit_scalar
.unwrap_or_else(|| panic!("missing exec scalar in spec_id={:?}", spec_id));
exec_scalar.saturating_mul(self.l1_base_fee)
};
let component_blob = {
let blob_scalar = self
.l1_blob_scalar
.unwrap_or_else(|| panic!("missing l1 blob scalar in spec_id={:?}", spec_id));
let blob_base_fee = self
.l1_blob_base_fee
.unwrap_or_else(|| panic!("missing l1 blob base fee in spec_id={:?}", spec_id));
blob_scalar.saturating_mul(blob_base_fee)
};

// Assume compression_ratio = 1 until we have specification for estimating compression
// ratio based on previous finalised batches.
//
// We use the `TX_L1_FEE_PRECISION` to allow fractions. We then divide the overall product
// by the precision value as well.
let compression_ratio = |_input: &[u8]| -> U256 { TX_L1_FEE_PRECISION };
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From my understanding, we will assume the worst case in the prover, i.e. the compression ratio is 1. Is this the case, or will the prover also estimate compression? If the prover logic and execution node logic is different, how will we capture that here? Do we need to feature flag this here, depending on whether we are running as a prover or not?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The prover should not need to estimate compression ratio, specifically because zstd-encoding will add a lot of cycles to our batch prover (which we would like to avoid).

You are probably right, we will eventually need feature flag to either:

  1. prover: fix upper bound for rollup fee (assuming compression ratio == 1)
  2. node: calculating the actual rollup fee by estimating the compression_ratio with zstd-encoding

And we additionally must then constrain the rollup fee to be at the most the upper bound (in the prover).

Let's keep this PR unmerged until we resolve this conversation.


// size(tx) is just the length of the RLP-encoded signed tx data.
let tx_size = |input: &[u8]| -> U256 { U256::from(input.len()) };

compression_ratio(input)
.saturating_mul(tx_size(input))
.saturating_mul(component_exec.saturating_add(component_blob))
.wrapping_div(TX_L1_FEE_PRECISION)
.wrapping_div(TX_L1_FEE_PRECISION)
}

/// Calculate the gas cost of a transaction based on L1 block data posted on L2.
pub fn calculate_tx_l1_cost(&self, input: &[u8], spec_id: ScrollSpecId) -> U256 {
let l1_cost = if !spec_id.is_enabled_in(ScrollSpecId::CURIE) {
self.calculate_tx_l1_cost_shanghai(input, spec_id)
} else {
} else if !spec_id.is_enabled_in(ScrollSpecId::FEYNMAN) {
self.calculate_tx_l1_cost_curie(input, spec_id)
} else {
self.calculate_tx_l1_cost_feynman(input, spec_id)
};
l1_cost.min(U64_MAX)
}
Expand Down