Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
9 changes: 5 additions & 4 deletions .github/workflows/unit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,11 @@ jobs:
args: -p "reth-scroll-*" -p "scroll-alloy-*" --locked
partition: 1
total_partitions: 1
- type: book
args: --manifest-path book/sources/Cargo.toml
partition: 1
total_partitions: 1
# issue <https://github.com/scroll-tech/reth/issues/250>
# - type: book
# args: --manifest-path book/sources/Cargo.toml
# partition: 1
# total_partitions: 1
timeout-minutes: 30
steps:
- name: Free up disk space
Expand Down
182 changes: 182 additions & 0 deletions crates/scroll/alloy/evm/src/block/feynman.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
//! Feynman fork transition for Scroll.
use alloc::vec;
use revm::{
bytecode::Bytecode,
database::{states::StorageSlot, State},
primitives::{bytes, Bytes, U256},
state::AccountInfo,
Database,
};

use super::curie::L1_GAS_PRICE_ORACLE_ADDRESS;

/// Bytecode of L1 gas price oracle at Feynman transition.
const FEYNMAN_L1_GAS_PRICE_ORACLE_BYTECODE: Bytes = bytes!("608060405234801561000f575f80fd5b50600436106101a1575f3560e01c806384189161116100f3578063c63b9e2d11610093578063e88a60ad1161006e578063e88a60ad1461032e578063f2fde38b14610341578063f45e65d814610354578063fe5b04151461035d575f80fd5b8063c63b9e2d146102ff578063c91e514914610312578063de26c4a11461031b575f80fd5b8063944b247f116100ce578063944b247f146102be578063a911d77f146102d1578063aa5e9334146102d9578063bede39b5146102ec575f80fd5b806384189161146102785780638da5cb5b1461028157806393e59dc1146102ab575f80fd5b80633d0f963e1161015e5780636112d6db116101395780636112d6db1461024b5780636a5e67e514610254578063704655971461025d578063715018a614610270575f80fd5b80633d0f963e1461021c57806349948e0e1461022f578063519b4bd314610242575f80fd5b80630c18c162146101a557806313dad5be146101c157806323e524ac146101de5780633577afc5146101e757806339455d3a146101fc5780633b7656bb1461020f575b5f80fd5b6101ae60025481565b6040519081526020015b60405180910390f35b6008546101ce9060ff1681565b60405190151581526020016101b8565b6101ae60065481565b6101fa6101f5366004610c73565b610365565b005b6101fa61020a366004610c8a565b6103f7565b600b546101ce9060ff1681565b6101fa61022a366004610caa565b6104f4565b6101ae61023d366004610ceb565b610577565b6101ae60015481565b6101ae600a5481565b6101ae60075481565b6101fa61026b366004610c73565b6105b0565b6101fa61063e565b6101ae60055481565b5f54610293906001600160a01b031681565b6040516001600160a01b0390911681526020016101b8565b600454610293906001600160a01b031681565b6101fa6102cc366004610c73565b610672565b6101fa6106fe565b6101fa6102e7366004610c73565b61075a565b6101fa6102fa366004610c73565b6107f4565b6101fa61030d366004610c73565b6108b1565b6101ae60095481565b6101ae610329366004610ceb565b61094a565b6101fa61033c366004610c73565b610974565b6101fa61034f366004610caa565b610a00565b6101ae60035481565b6101fa610a8b565b5f546001600160a01b031633146103975760405162461bcd60e51b815260040161038e90610d96565b60405180910390fd5b621c9c388111156103bb57604051635742c80560e11b815260040160405180910390fd5b60028190556040518181527f32740b35c0ea213650f60d44366b4fb211c9033b50714e4a1d34e65d5beb9bb4906020015b60405180910390a150565b6004805460405163efc7840160e01b815233928101929092526001600160a01b03169063efc7840190602401602060405180830381865afa15801561043e573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104629190610dcd565b61047f576040516326b3506d60e11b815260040160405180910390fd5b600182905560058190556040518281527f351fb23757bb5ea0546c85b7996ddd7155f96b939ebaa5ff7bc49c75f27f2c449060200160405180910390a16040518181527f9a14bfb5d18c4c3cf14cae19c23d7cf1bcede357ea40ca1f75cd49542c71c214906020015b60405180910390a15050565b5f546001600160a01b0316331461051d5760405162461bcd60e51b815260040161038e90610d96565b600480546001600160a01b038381166001600160a01b031983168117909355604080519190921680825260208201939093527f22d1c35fe072d2e42c3c8f9bd4a0d34aa84a0101d020a62517b33fdb3174e5f791016104e8565b600b545f9060ff16156105935761058d82610ae7565b92915050565b60085460ff16156105a75761058d82610b45565b61058d82610b81565b5f546001600160a01b031633146105d95760405162461bcd60e51b815260040161038e90610d96565b6105e9633b9aca006103e8610e00565b81111561060957604051631e44fdeb60e11b815260040160405180910390fd5b60038190556040518181527f3336cd9708eaf2769a0f0dc0679f30e80f15dcd88d1921b5a16858e8b85c591a906020016103ec565b5f546001600160a01b031633146106675760405162461bcd60e51b815260040161038e90610d96565b6106705f610bc4565b565b5f546001600160a01b0316331461069b5760405162461bcd60e51b815260040161038e90610d96565b6106a9633b9aca0080610e00565b8111156106c95760405163874f603160e01b815260040160405180910390fd5b60068190556040518181527f2ab3f5a4ebbcbf3c24f62f5454f52f10e1a8c9dcc5acac8f19199ce881a6a108906020016103ec565b5f546001600160a01b031633146107275760405162461bcd60e51b815260040161038e90610d96565b60085460ff161561074b576040516379f9c57560e01b815260040160405180910390fd5b6008805460ff19166001179055565b5f546001600160a01b031633146107835760405162461bcd60e51b815260040161038e90610d96565b633b9aca008110806107a1575061079e633b9aca0080610e00565b81115b156107bf5760405163d9b5dcdf60e01b815260040160405180910390fd5b60098190556040518181527fd50d3079c77df569cd58d55d4e5614bfe7066449009425d22bde8e75242f50bb906020016103ec565b6004805460405163efc7840160e01b815233928101929092526001600160a01b03169063efc7840190602401602060405180830381865afa15801561083b573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061085f9190610dcd565b61087c576040516326b3506d60e11b815260040160405180910390fd5b60018190556040518181527f351fb23757bb5ea0546c85b7996ddd7155f96b939ebaa5ff7bc49c75f27f2c44906020016103ec565b5f546001600160a01b031633146108da5760405162461bcd60e51b815260040161038e90610d96565b633b9aca008110806108f857506108f5633b9aca0080610e00565b81115b156109155760405162ae184360e01b815260040160405180910390fd5b600a8190556040518181527f8647cebb7e57360673a28415c0bed2f68c42a86c5035f1c9b2eda2b09509288a906020016103ec565b600b545f9060ff168061095f575060085460ff165b1561096b57505f919050565b61058d82610c13565b5f546001600160a01b0316331461099d5760405162461bcd60e51b815260040161038e90610d96565b6109ab633b9aca0080610e00565b8111156109cb5760405163f37ec21560e01b815260040160405180910390fd5b60078190556040518181527f6b332a036d8c3ead57dcb06c87243bd7a2aed015ddf2d0528c2501dae56331aa906020016103ec565b5f546001600160a01b03163314610a295760405162461bcd60e51b815260040161038e90610d96565b6001600160a01b038116610a7f5760405162461bcd60e51b815260206004820152601d60248201527f6e6577206f776e657220697320746865207a65726f2061646472657373000000604482015260640161038e565b610a8881610bc4565b50565b5f546001600160a01b03163314610ab45760405162461bcd60e51b815260040161038e90610d96565b600b5460ff1615610ad857604051631a7c228b60e21b815260040160405180910390fd5b600b805460ff19166001179055565b5f633b9aca0080600a548451600554600754610b039190610e00565b600154600654610b139190610e00565b610b1d9190610e17565b610b279190610e00565b610b319190610e00565b610b3b9190610e2a565b61058d9190610e2a565b5f633b9aca006005548351600754610b5d9190610e00565b610b679190610e00565b600154600654610b779190610e00565b610b3b9190610e17565b5f80610b8c83610c13565b90505f60015482610b9d9190610e00565b9050633b9aca0060035482610bb29190610e00565b610bbc9190610e2a565b949350505050565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b80515f908190815b81811015610c6457848181518110610c3557610c35610e49565b01602001516001600160f81b0319165f03610c5557600483019250610c5c565b6010830192505b600101610c1b565b50506002540160400192915050565b5f60208284031215610c83575f80fd5b5035919050565b5f8060408385031215610c9b575f80fd5b50508035926020909101359150565b5f60208284031215610cba575f80fd5b81356001600160a01b0381168114610cd0575f80fd5b9392505050565b634e487b7160e01b5f52604160045260245ffd5b5f60208284031215610cfb575f80fd5b813567ffffffffffffffff80821115610d12575f80fd5b818401915084601f830112610d25575f80fd5b813581811115610d3757610d37610cd7565b604051601f8201601f19908116603f01168101908382118183101715610d5f57610d5f610cd7565b81604052828152876020848701011115610d77575f80fd5b826020860160208301375f928101602001929092525095945050505050565b60208082526017908201527f63616c6c6572206973206e6f7420746865206f776e6572000000000000000000604082015260600190565b5f60208284031215610ddd575f80fd5b81518015158114610cd0575f80fd5b634e487b7160e01b5f52601160045260245ffd5b808202811582820484141761058d5761058d610dec565b8082018082111561058d5761058d610dec565b5f82610e4457634e487b7160e01b5f52601260045260245ffd5b500490565b634e487b7160e01b5f52603260045260245ffdfea164736f6c6343000818000a");

/// L1 gas price oracle compression penalty threshold slot. Added in the Feynman fork.
const PENALTY_THRESHOLD_SLOT: U256 = U256::from_limbs([9, 0, 0, 0]);
/// L1 gas price oracle compression penalty factor slot. Added in the Feynman fork.
const PENALTY_FACTOR_SLOT: U256 = U256::from_limbs([10, 0, 0, 0]);
/// L1 gas price oracle "is Feynman" slot. Added in the Feynman fork.
const IS_FEYNMAN_SLOT: U256 = U256::from_limbs([11, 0, 0, 0]);

/// The initial compression penalty threshold used by the oracle contract.
const INITIAL_PENALTY_THRESHOLD: U256 = U256::from_limbs([1_000_000_000, 0, 0, 0]);
/// The initial compression penalty factor used by the oracle contract.
const INITIAL_PENALTY_FACTOR: U256 = U256::from_limbs([1_000_000_000, 0, 0, 0]);
/// Feynman slot is set to 1 (true) after the Feynman block fork.
const IS_FEYNMAN: U256 = U256::from_limbs([1, 0, 0, 0]);

/// Storage update of L1 gas price oracle at Feynman transition.
const FEYNMAN_L1_GAS_PRICE_ORACLE_STORAGE: [(U256, U256); 3] = [
(PENALTY_THRESHOLD_SLOT, INITIAL_PENALTY_THRESHOLD),
(PENALTY_FACTOR_SLOT, INITIAL_PENALTY_FACTOR),
(IS_FEYNMAN_SLOT, IS_FEYNMAN),
];

/// Applies the Scroll Feynman hard fork to the state:
/// - Updates the L1 oracle contract bytecode to reflect the DA cost reduction.
/// - Sets the initial compression penalty threshold and penalty factor values.
/// - Sets the `isFeynman` slot to 1 (true).
pub(super) fn apply_feynman_hard_fork<DB: Database>(
state: &mut State<DB>,
) -> Result<(), DB::Error> {
let oracle = state.load_cache_account(L1_GAS_PRICE_ORACLE_ADDRESS)?;

// No-op if already applied.
// Note: This requires a storage read for every Feynman block, and it means this
// read needs to be included in the execution witness. Unfortunately, there is no
// other reliable way to apply the change only at the transition block, since
// `ScrollBlockExecutor` does not have access to the parent timestamp.
if matches!(oracle.storage_slot(IS_FEYNMAN_SLOT), Some(val) if val == IS_FEYNMAN) {
return Ok(())
}

// compute the code hash
let bytecode = Bytecode::new_raw(FEYNMAN_L1_GAS_PRICE_ORACLE_BYTECODE);
let code_hash = bytecode.hash_slow();

// get the old oracle account info
let old_oracle_info = oracle.account_info().unwrap_or_default();

// init new oracle account information
let new_oracle_info = AccountInfo { code_hash, code: Some(bytecode), ..old_oracle_info };

// init new storage
let new_storage = FEYNMAN_L1_GAS_PRICE_ORACLE_STORAGE
.into_iter()
.map(|(slot, present_value)| {
(
slot,
StorageSlot {
present_value,
previous_or_original_value: oracle.storage_slot(slot).unwrap_or_default(),
},
)
})
.collect();

// create transition for oracle new account info and storage
let transition = oracle.change(new_oracle_info, new_storage);

// add transition
if let Some(s) = state.transition_state.as_mut() {
s.add_transitions(vec![(L1_GAS_PRICE_ORACLE_ADDRESS, transition)])
}

Ok(())
}

#[cfg(test)]
mod tests {
use super::*;
use revm::{
database::{
states::{bundle_state::BundleRetention, plain_account::PlainStorage, StorageSlot},
EmptyDB, State,
},
primitives::{keccak256, U256},
state::{AccountInfo, Bytecode},
Database,
};
use std::str::FromStr;

use super::super::curie::CURIE_L1_GAS_PRICE_ORACLE_BYTECODE;

#[test]
fn test_apply_feynman_fork() -> eyre::Result<()> {
// init state
let db = EmptyDB::new();
let mut state =
State::builder().with_database(db).with_bundle_update().without_state_clear().build();

// oracle pre fork state
let bytecode_pre_fork = Bytecode::new_raw(CURIE_L1_GAS_PRICE_ORACLE_BYTECODE);
let oracle_pre_fork = AccountInfo {
code_hash: bytecode_pre_fork.hash_slow(),
code: Some(bytecode_pre_fork),
..Default::default()
};
let oracle_storage_pre_fork = PlainStorage::from_iter([
// owner
(U256::ZERO, U256::from_str("0x13d24a7ff6f5ec5ff0e9c40fc3b8c9c01c65437b")?),
// l1BaseFee
(U256::from(1), U256::from(0x15f50e5e)),
// overhead
(U256::from(2), U256::from(0x38)),
// scalar
(U256::from(3), U256::from(0x3e95ba80)),
// whitelist
(U256::from(4), U256::from_str("0x5300000000000000000000000000000000000003")?),
// l1BlobBaseFee
(U256::from(5), U256::from(0x15f50e5e)),
// commitScalar
(U256::from(6), U256::from(0x3e95ba80)),
// blobScalar
(U256::from(7), U256::from(0x3e95ba80)),
// isCurie
(U256::from(8), U256::from(1)),
]);
state.insert_account_with_storage(
L1_GAS_PRICE_ORACLE_ADDRESS,
oracle_pre_fork.clone(),
oracle_storage_pre_fork.clone(),
);

// apply feynman fork
apply_feynman_hard_fork(&mut state)?;

// merge transitions
state.merge_transitions(BundleRetention::Reverts);
let bundle = state.take_bundle();

// check oracle account info
let oracle = bundle.state.get(&L1_GAS_PRICE_ORACLE_ADDRESS).unwrap().clone();
let code_hash = keccak256(&FEYNMAN_L1_GAS_PRICE_ORACLE_BYTECODE);
let bytecode = Bytecode::new_raw(FEYNMAN_L1_GAS_PRICE_ORACLE_BYTECODE);
let expected_oracle_info =
AccountInfo { code_hash, code: Some(bytecode.clone()), ..Default::default() };

assert_eq!(oracle.original_info.unwrap(), oracle_pre_fork);
assert_eq!(oracle.info.unwrap(), expected_oracle_info);

// check oracle storage changeset
let mut storage = oracle.storage.into_iter().collect::<Vec<(U256, StorageSlot)>>();
storage.sort_by(|(a, _), (b, _)| a.cmp(b));
for (got, expected) in storage.into_iter().zip(FEYNMAN_L1_GAS_PRICE_ORACLE_STORAGE) {
assert_eq!(got.0, expected.0);
assert_eq!(got.1, StorageSlot { present_value: expected.1, ..Default::default() });
}

// check oracle original storage
for (slot, value) in oracle_storage_pre_fork {
assert_eq!(state.storage(L1_GAS_PRICE_ORACLE_ADDRESS, slot)?, value)
}

// check deployed contract
assert_eq!(bundle.contracts.get(&code_hash).unwrap().clone(), bytecode);

Ok(())
}
}
26 changes: 22 additions & 4 deletions crates/scroll/alloy/evm/src/block/mod.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
pub mod curie;
mod feynman;

pub use receipt_builder::{ReceiptBuilderCtx, ScrollReceiptBuilder};
mod receipt_builder;

use crate::{
block::curie::{apply_curie_hard_fork, L1_GAS_PRICE_ORACLE_ADDRESS},
block::{
curie::{apply_curie_hard_fork, L1_GAS_PRICE_ORACLE_ADDRESS},
feynman::apply_feynman_hard_fork,
},
system_caller::ScrollSystemCaller,
ScrollEvm, ScrollEvmFactory, ScrollTransactionIntoTxEnv,
};
Expand Down Expand Up @@ -114,9 +118,7 @@ where
.load_cache_account(L1_GAS_PRICE_ORACLE_ADDRESS)
.map_err(BlockExecutionError::other)?;

// apply eip-2935.
self.system_caller.apply_blockhashes_contract_call(self.ctx.parent_hash, &mut self.evm)?;

// apply gas oracle predeploy upgrade at Curie transition block.
if self
.spec
.scroll_fork_activation(ScrollHardfork::Curie)
Expand All @@ -129,6 +131,22 @@ where
};
}

// apply gas oracle predeploy upgrade at Feynman transition block.
if self
.spec
.scroll_fork_activation(ScrollHardfork::Feynman)
.active_at_timestamp(self.evm.block().timestamp)
{
if let Err(err) = apply_feynman_hard_fork(self.evm.db_mut()) {
return Err(BlockExecutionError::msg(format!(
"error occurred at Feynman fork: {err:?}"
)));
};
}

// apply eip-2935.
self.system_caller.apply_blockhashes_contract_call(self.ctx.parent_hash, &mut self.evm)?;

Ok(())
}

Expand Down
Loading