Skip to content

Commit

Permalink
Apply brotli compression statistic (#1543)
Browse files Browse the repository at this point in the history
  • Loading branch information
exeokan authored Dec 3, 2024
1 parent 5f6aa70 commit 5d1eee6
Show file tree
Hide file tree
Showing 2 changed files with 176 additions and 3 deletions.
17 changes: 15 additions & 2 deletions crates/evm/src/evm/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ const CODE_KEY_SIZE: usize = 39;
/// The full calculation can be found here: https://github.com/chainwayxyz/citrea/blob/erce/l1-fee-overhead-calculations/l1_fee_overhead.md
pub const L1_FEE_OVERHEAD: usize = 3;

/// The brotli average compression ratio (compressed size / uncompressed size) was calculated as 0.33 by measuring the size of state diffs of batches before and after brotli compression.
/// calculated diff size * BROTLI_COMPRESSION_PERCENTAGE/100 gives the estimated size of the state diff that is written to the da.
pub const BROTLI_COMPRESSION_PERCENTAGE: usize = 33;

/// We want to charge the user for the amount of data written as fairly as possible, the problem is at the time of when we write batch proof to the da we cannot know the exact state diff
/// So we calculate the state diff created by a single transaction and use that to charge user
/// However at the time of the batch proof some state diffs will be merged and some users will be overcharged.
Expand Down Expand Up @@ -401,8 +405,17 @@ impl<SPEC: Spec, EXT: CitreaExternalExt, DB: Database> CitreaHandler<SPEC, EXT,
context: &mut Context<EXT, DB>,
result: FrameResult,
) -> Result<ResultAndState, EVMError<<DB as Database>::Error>> {
let diff_size =
calc_diff_size::<EXT, SPEC, DB>(context).map_err(EVMError::Database)? as u64;
let uncompressed_size =
calc_diff_size::<EXT, SPEC, DB>(context).map_err(EVMError::Database)?;

let compression_percentage = if SPEC::enabled(SpecId::CANCUN) {
// Estimate the size of the state diff after the brotli compression
BROTLI_COMPRESSION_PERCENTAGE
} else {
100
};
let diff_size = (uncompressed_size * compression_percentage / 100) as u64;

let l1_fee_rate = context.external.l1_fee_rate();
let l1_fee =
U256::from(l1_fee_rate) * (U256::from(diff_size) + U256::from(L1_FEE_OVERHEAD));
Expand Down
162 changes: 161 additions & 1 deletion crates/evm/src/tests/call_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use sov_rollup_interface::spec::SpecId as SovSpecId;
use crate::call::CallMessage;
use crate::evm::primitive_types::Receipt;
use crate::evm::DbAccount;
use crate::handler::L1_FEE_OVERHEAD;
use crate::handler::{BROTLI_COMPRESSION_PERCENTAGE, L1_FEE_OVERHEAD};
use crate::smart_contracts::{
BlockHashContract, InfiniteLoopContract, LogsContract, SelfDestructorContract,
SimpleStorageContract, TestContract,
Expand Down Expand Up @@ -1445,6 +1445,166 @@ fn test_l1_fee_halt() {
);
}

#[test]
fn test_l1_fee_compression_discount() {
let (config, dev_signer, _) =
get_evm_config_starting_base_fee(U256::from_str("100000000000000").unwrap(), None, 1);

let (mut evm, mut working_set) = get_evm(&config);
let l1_fee_rate = 1;

let soft_confirmation_info = HookSoftConfirmationInfo {
l2_height: 2,
da_slot_hash: [5u8; 32],
da_slot_height: 1,
da_slot_txs_commitment: [42u8; 32],
pre_state_root: [10u8; 32].to_vec(),
current_spec: SovSpecId::Genesis,
pub_key: vec![],
deposit_data: vec![],
l1_fee_rate,
timestamp: 0,
};

evm.begin_soft_confirmation_hook(&soft_confirmation_info, &mut working_set);
{
let sender_address = generate_address::<C>("sender");
let context = C::new(sender_address, 2, SovSpecId::Genesis, l1_fee_rate);
let call_tx = dev_signer
.sign_default_transaction_with_priority_fee(
TxKind::Call(Address::random()),
vec![],
0,
1000,
20000000,
1,
)
.unwrap();

evm.call(
CallMessage { txs: vec![call_tx] },
&context,
&mut working_set,
)
.unwrap();
}
evm.end_soft_confirmation_hook(&soft_confirmation_info, &mut working_set);
evm.finalize_hook(&[99u8; 32].into(), &mut working_set.accessory_state());

let db_account = evm
.accounts
.get(&dev_signer.address(), &mut working_set)
.unwrap();

let base_fee_vault = evm.accounts.get(&BASE_FEE_VAULT, &mut working_set).unwrap();
let l1_fee_vault = evm.accounts.get(&L1_FEE_VAULT, &mut working_set).unwrap();

let coinbase_account = evm
.accounts
.get(&config.coinbase, &mut working_set)
.unwrap();
assert_eq!(config.coinbase, PRIORITY_FEE_VAULT);

let gas_fee_paid = 21000;
let tx1_diff_size = 140;

let mut expected_db_balance = U256::from(
100000000000000u64
- 1000
- gas_fee_paid * 10000001
- tx1_diff_size
- L1_FEE_OVERHEAD as u64,
);
let mut expected_base_fee_vault_balance = U256::from(gas_fee_paid * 10000000);
let mut expected_coinbase_balance = U256::from(gas_fee_paid);
let mut expected_l1_fee_vault_balance = U256::from(tx1_diff_size + L1_FEE_OVERHEAD as u64);

assert_eq!(db_account.balance, expected_db_balance);
assert_eq!(base_fee_vault.balance, expected_base_fee_vault_balance);
assert_eq!(coinbase_account.balance, expected_coinbase_balance);
assert_eq!(l1_fee_vault.balance, expected_l1_fee_vault_balance);

// Set up the next transaction with the fork 1 activated
let soft_confirmation_info = HookSoftConfirmationInfo {
l2_height: 3,
da_slot_hash: [5u8; 32],
da_slot_height: 1,
da_slot_txs_commitment: [42u8; 32],
pre_state_root: [99u8; 32].to_vec(),
current_spec: SovSpecId::Fork1, // Compression discount is enabled
pub_key: vec![],
deposit_data: vec![],
l1_fee_rate,
timestamp: 0,
};

evm.begin_soft_confirmation_hook(&soft_confirmation_info, &mut working_set);
{
let sender_address = generate_address::<C>("sender");
let context = C::new(sender_address, 3, SovSpecId::Fork1, l1_fee_rate);
let simple_tx = dev_signer
.sign_default_transaction_with_priority_fee(
TxKind::Call(Address::random()),
vec![],
1,
1000,
20000000,
1,
)
.unwrap();
evm.call(
CallMessage {
txs: vec![simple_tx],
},
&context,
&mut working_set,
)
.unwrap();
}
evm.end_soft_confirmation_hook(&soft_confirmation_info, &mut working_set);
evm.finalize_hook(&[98u8; 32].into(), &mut working_set.accessory_state());

let db_account = evm
.accounts
.get(&dev_signer.address(), &mut working_set)
.unwrap();
let base_fee_vault = evm.accounts.get(&BASE_FEE_VAULT, &mut working_set).unwrap();
let l1_fee_vault = evm.accounts.get(&L1_FEE_VAULT, &mut working_set).unwrap();

let coinbase_account = evm
.accounts
.get(&config.coinbase, &mut working_set)
.unwrap();

// gas fee remains the same
let tx2_diff_size = 46;

expected_db_balance -=
U256::from(gas_fee_paid * 10000001 + 1000 + tx2_diff_size + L1_FEE_OVERHEAD as u64);
expected_base_fee_vault_balance += U256::from(gas_fee_paid * 10000000);
expected_coinbase_balance += U256::from(gas_fee_paid);
expected_l1_fee_vault_balance += U256::from(tx2_diff_size + L1_FEE_OVERHEAD as u64);

assert_eq!(db_account.balance, expected_db_balance);
assert_eq!(base_fee_vault.balance, expected_base_fee_vault_balance);
assert_eq!(coinbase_account.balance, expected_coinbase_balance);
assert_eq!(l1_fee_vault.balance, expected_l1_fee_vault_balance);

// assert comression discount
assert_eq!(
tx1_diff_size * BROTLI_COMPRESSION_PERCENTAGE as u64 / 100,
tx2_diff_size
);

assert_eq!(
evm.receipts
.iter(&mut working_set.accessory_state())
.map(|r| r.l1_diff_size)
.collect::<Vec<_>>(),
[255, 561, 1019, 561, tx1_diff_size, tx2_diff_size]
);
}

#[test]
fn test_call_with_block_overrides() {
let (config, dev_signer, contract_addr) =
Expand Down

0 comments on commit 5d1eee6

Please sign in to comment.