@@ -42,17 +42,17 @@ const FEYNMAN_L1_GAS_PRICE_ORACLE_STORAGE: [(U256, U256); 3] = [
4242pub ( super ) fn apply_feynman_hard_fork < DB : Database > (
4343 state : & mut State < DB > ,
4444) -> Result < ( ) , DB :: Error > {
45- let oracle = state. load_cache_account ( L1_GAS_PRICE_ORACLE_ADDRESS ) ?;
46-
4745 // No-op if already applied.
4846 // Note: This requires a storage read for every Feynman block, and it means this
4947 // read needs to be included in the execution witness. Unfortunately, there is no
5048 // other reliable way to apply the change only at the transition block, since
5149 // `ScrollBlockExecutor` does not have access to the parent timestamp.
52- if matches ! ( oracle . storage_slot ( IS_FEYNMAN_SLOT ) , Some ( val ) if val == IS_FEYNMAN ) {
50+ if state . storage ( L1_GAS_PRICE_ORACLE_ADDRESS , IS_FEYNMAN_SLOT ) ? == IS_FEYNMAN {
5351 return Ok ( ( ) )
5452 }
5553
54+ let oracle = state. load_cache_account ( L1_GAS_PRICE_ORACLE_ADDRESS ) ?;
55+
5656 // compute the code hash
5757 let bytecode = Bytecode :: new_raw ( FEYNMAN_L1_GAS_PRICE_ORACLE_BYTECODE ) ;
5858 let code_hash = bytecode. hash_slow ( ) ;
@@ -94,7 +94,7 @@ mod tests {
9494 use revm:: {
9595 database:: {
9696 states:: { bundle_state:: BundleRetention , plain_account:: PlainStorage , StorageSlot } ,
97- EmptyDB , State ,
97+ CacheDB , EmptyDB , State ,
9898 } ,
9999 primitives:: { keccak256, U256 } ,
100100 state:: { AccountInfo , Bytecode } ,
@@ -195,4 +195,70 @@ mod tests {
195195
196196 Ok ( ( ) )
197197 }
198+
199+ #[ test]
200+ fn test_apply_feynman_fork_only_once ( ) -> eyre:: Result < ( ) > {
201+ let bytecode = Bytecode :: new_raw ( FEYNMAN_L1_GAS_PRICE_ORACLE_BYTECODE ) ;
202+
203+ let oracle_account = AccountInfo {
204+ code_hash : bytecode. hash_slow ( ) ,
205+ code : Some ( bytecode) ,
206+ ..Default :: default ( )
207+ } ;
208+
209+ let oracle_storage = PlainStorage :: from_iter ( [
210+ // owner
211+ ( U256 :: ZERO , U256 :: from_str ( "0x13d24a7ff6f5ec5ff0e9c40fc3b8c9c01c65437b" ) ?) ,
212+ // l1BaseFee
213+ ( U256 :: from ( 1 ) , U256 :: from ( 0x15f50e5e ) ) ,
214+ // overhead
215+ ( U256 :: from ( 2 ) , U256 :: from ( 0x38 ) ) ,
216+ // scalar
217+ ( U256 :: from ( 3 ) , U256 :: from ( 0x3e95ba80 ) ) ,
218+ // whitelist
219+ ( U256 :: from ( 4 ) , U256 :: from_str ( "0x5300000000000000000000000000000000000003" ) ?) ,
220+ // l1BlobBaseFee
221+ ( U256 :: from ( 5 ) , U256 :: from ( 0x15f50e5e ) ) ,
222+ // commitScalar
223+ ( U256 :: from ( 6 ) , U256 :: from ( 0x3e95ba80 ) ) ,
224+ // blobScalar
225+ ( U256 :: from ( 7 ) , U256 :: from ( 0x3e95ba80 ) ) ,
226+ // isCurie
227+ ( U256 :: from ( 8 ) , U256 :: from ( 1 ) ) ,
228+ // penaltyThreshold
229+ ( U256 :: from ( 9 ) , U256 :: from ( 1_100_000_000u64 ) ) ,
230+ // penaltyFactor
231+ ( U256 :: from ( 10 ) , U256 :: from ( 3_000_000_000u64 ) ) ,
232+ // isFeynman
233+ ( U256 :: from ( 11 ) , U256 :: from ( 1 ) ) ,
234+ ] ) ;
235+
236+ // init state,
237+ // we write to db directly to make sure we do not have account storage in cache
238+ let mut db = CacheDB :: new ( EmptyDB :: default ( ) ) ;
239+
240+ db. insert_account_info ( L1_GAS_PRICE_ORACLE_ADDRESS , oracle_account) ;
241+
242+ for ( slot, value) in oracle_storage {
243+ db. insert_account_storage ( L1_GAS_PRICE_ORACLE_ADDRESS , slot, value) . unwrap ( ) ;
244+ }
245+
246+ let mut state =
247+ State :: builder ( ) . with_database ( db) . with_bundle_update ( ) . without_state_clear ( ) . build ( ) ;
248+
249+ // make sure account is in cache
250+ state. load_cache_account ( L1_GAS_PRICE_ORACLE_ADDRESS ) ?;
251+
252+ // apply feynman fork
253+ apply_feynman_hard_fork ( & mut state) ?;
254+
255+ // merge transitions
256+ state. merge_transitions ( BundleRetention :: Reverts ) ;
257+ let bundle = state. take_bundle ( ) ;
258+
259+ // isFeynman is already set, apply_feynman_hard_fork should be a no-op
260+ assert_eq ! ( bundle. state. get( & L1_GAS_PRICE_ORACLE_ADDRESS ) , None ) ;
261+
262+ Ok ( ( ) )
263+ }
198264}
0 commit comments