diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index 50f86a938a1..1cfacc4dbae 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -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 + # - type: book + # args: --manifest-path book/sources/Cargo.toml + # partition: 1 + # total_partitions: 1 timeout-minutes: 30 steps: - name: Free up disk space diff --git a/crates/scroll/alloy/evm/src/block/feynman.rs b/crates/scroll/alloy/evm/src/block/feynman.rs new file mode 100644 index 00000000000..081d9a3bb39 --- /dev/null +++ b/crates/scroll/alloy/evm/src/block/feynman.rs @@ -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( + state: &mut State, +) -> 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::>(); + 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(()) + } +} diff --git a/crates/scroll/alloy/evm/src/block/mod.rs b/crates/scroll/alloy/evm/src/block/mod.rs index e56620ed803..d6071bfa2a2 100644 --- a/crates/scroll/alloy/evm/src/block/mod.rs +++ b/crates/scroll/alloy/evm/src/block/mod.rs @@ -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, }; @@ -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) @@ -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(()) }