@@ -1854,14 +1854,11 @@ async fn apply_message(
18541854) -> Result < ApiInvocResult , Error > {
18551855 if let Some ( ts) = & tipset
18561856 && ts. epoch ( ) > 0
1857- {
1858- let parent = ctx. chain_index ( ) . load_required_tipset ( ts. parents ( ) ) ?;
1859- if ctx
1857+ && ctx
18601858 . chain_config ( )
1861- . has_expensive_fork_between ( parent. epoch ( ) , ts. epoch ( ) + 1 )
1862- {
1863- return Err ( crate :: state_manager:: Error :: ExpensiveFork . into ( ) ) ;
1864- }
1859+ . has_expensive_fork_between ( ts. epoch ( ) , ts. epoch ( ) + 1 )
1860+ {
1861+ return Err ( crate :: state_manager:: Error :: ExpensiveFork { epoch : ts. epoch ( ) } . into ( ) ) ;
18651862 }
18661863
18671864 let ( invoc_res, _) = ctx
@@ -2182,19 +2179,24 @@ async fn eth_get_code(
21822179 ..Default :: default ( )
21832180 } ;
21842181
2185- let api_invoc_result = ' invoc: {
2186- for ts in ts. shallow_clone ( ) . chain ( ctx. db ( ) ) {
2187- match ctx
2188- . state_manager
2189- . call_on_state ( state_root, & message, Some ( ts) )
2190- {
2191- Ok ( res) => {
2192- break ' invoc res;
2193- }
2194- Err ( e) => tracing:: warn!( %e) ,
2182+ // Rewind ts to escape the fork guard, but keep state_root fixed to the requested epoch: the
2183+ // result comes from state_root (ts only supplies execution context), so recomputing it for the
2184+ // parent would read an earlier epoch's bytecode.
2185+ let mut ts = ts. shallow_clone ( ) ;
2186+ let api_invoc_result = loop {
2187+ match ctx
2188+ . state_manager
2189+ . call_on_state ( state_root, & message, Some ( ts. shallow_clone ( ) ) )
2190+ {
2191+ Ok ( res) => break res,
2192+ Err ( crate :: state_manager:: Error :: ExpensiveFork { .. } ) => {
2193+ ts = ctx
2194+ . chain_index ( )
2195+ . load_required_tipset ( ts. parents ( ) )
2196+ . map_err ( |e| anyhow:: anyhow!( "getting parent tipset: {e}" ) ) ?;
21952197 }
2198+ Err ( e) => return Err ( e. into ( ) ) ,
21962199 }
2197- return Err ( anyhow:: anyhow!( "Call failed" ) . into ( ) ) ;
21982200 } ;
21992201 let Some ( msg_rct) = api_invoc_result. msg_rct else {
22002202 return Err ( anyhow:: anyhow!( "no message receipt" ) . into ( ) ) ;
@@ -2275,19 +2277,24 @@ async fn get_storage_at(
22752277 params,
22762278 ..Default :: default ( )
22772279 } ;
2278- let api_invoc_result = ' invoc: {
2279- for ts in ts. chain ( ctx. db ( ) ) {
2280- match ctx
2281- . state_manager
2282- . call_on_state ( state_root, & message, Some ( ts) )
2283- {
2284- Ok ( res) => {
2285- break ' invoc res;
2286- }
2287- Err ( e) => tracing:: warn!( %e) ,
2280+ // Rewind ts to escape the fork guard, but keep state_root fixed to the requested epoch: the
2281+ // result comes from state_root (ts only supplies execution context), so recomputing it for the
2282+ // parent would read an earlier epoch's storage.
2283+ let mut ts = ts;
2284+ let api_invoc_result = loop {
2285+ match ctx
2286+ . state_manager
2287+ . call_on_state ( state_root, & message, Some ( ts. shallow_clone ( ) ) )
2288+ {
2289+ Ok ( res) => break res,
2290+ Err ( crate :: state_manager:: Error :: ExpensiveFork { .. } ) => {
2291+ ts = ctx
2292+ . chain_index ( )
2293+ . load_required_tipset ( ts. parents ( ) )
2294+ . map_err ( |e| anyhow:: anyhow!( "getting parent tipset: {e}" ) ) ?;
22882295 }
2296+ Err ( e) => return Err ( e. into ( ) ) ,
22892297 }
2290- return Err ( anyhow:: anyhow!( "Call failed" ) . into ( ) ) ;
22912298 } ;
22922299 let Some ( msg_rct) = api_invoc_result. msg_rct else {
22932300 return Err ( anyhow:: anyhow!( "no message receipt" ) . into ( ) ) ;
0 commit comments