@@ -42,6 +42,9 @@ pub(crate) mod implementation_defined_errors {
4242 /// node. Note that it's not the same as not found, as we are explicitly not supporting it,
4343 /// e.g., because it's deprecated or Lotus is doing the same.
4444 pub ( crate ) const UNSUPPORTED_METHOD : i32 = -32001 ;
45+ /// EIP-1474 "resource unavailable": explicit call targets a block whose state cannot be
46+ /// served without running an expensive migration on demand. Matches Lotus `ExpensiveFork`.
47+ pub ( crate ) const EXPENSIVE_FORK_CODE : i32 = -32002 ;
4548}
4649
4750impl ServerError {
@@ -98,6 +101,9 @@ impl From<anyhow::Error> for ServerError {
98101 if let Some ( eth_error) = error. downcast_ref :: < EthErrors > ( ) {
99102 return eth_error. clone ( ) . into ( ) ;
100103 }
104+ if let Some ( sm_error) = error. downcast_ref :: < crate :: state_manager:: Error > ( ) {
105+ return sm_error. clone ( ) . into ( ) ;
106+ }
101107
102108 // Default fallback, not using `format!("{e:#}")` here to match Lotus error
103109 Self :: internal_error ( error. to_string ( ) , None )
@@ -158,7 +164,6 @@ from2internal! {
158164 crate :: key_management:: Error ,
159165 crate :: libp2p:: ParseError ,
160166 crate :: message_pool:: Error ,
161- crate :: state_manager:: Error ,
162167 fil_actors_shared:: fvm_ipld_amt:: Error ,
163168 futures:: channel:: oneshot:: Canceled ,
164169 fvm_ipld_encoding:: Error ,
@@ -178,6 +183,19 @@ from2internal! {
178183 jsonrpsee:: core:: client:: error:: Error ,
179184}
180185
186+ impl From < crate :: state_manager:: Error > for ServerError {
187+ fn from ( error : crate :: state_manager:: Error ) -> Self {
188+ match error {
189+ crate :: state_manager:: Error :: ExpensiveFork { epoch } => Self :: new (
190+ implementation_defined_errors:: EXPENSIVE_FORK_CODE ,
191+ error. to_string ( ) ,
192+ Some ( serde_json:: Value :: from ( epoch) ) ,
193+ ) ,
194+ other => Self :: internal_error ( other. to_string ( ) , None ) ,
195+ }
196+ }
197+ }
198+
181199impl From < ServerError > for ClientError {
182200 fn from ( value : ServerError ) -> Self {
183201 Self :: Call ( value. inner )
@@ -195,3 +213,50 @@ impl<T> From<tokio::sync::mpsc::error::SendError<T>> for ServerError {
195213 Self :: internal_error ( e, None )
196214 }
197215}
216+
217+ #[ cfg( test) ]
218+ mod tests {
219+ use super :: * ;
220+ use crate :: state_manager:: Error as StateManagerError ;
221+
222+ #[ test]
223+ fn expensive_fork_converts_to_server_error_with_correct_code ( ) {
224+ let err = StateManagerError :: ExpensiveFork { epoch : 42 } ;
225+ let server_err: ServerError = err. into ( ) ;
226+
227+ assert_eq ! (
228+ server_err. known_code( ) ,
229+ implementation_defined_errors:: EXPENSIVE_FORK_CODE . into( )
230+ ) ;
231+ assert_eq ! (
232+ server_err. inner. message( ) ,
233+ "required historical state unavailable: refusing explicit call due to state fork at epoch 42"
234+ ) ;
235+ assert_eq ! (
236+ server_err
237+ . inner
238+ . data( )
239+ . and_then( |d| d. get( ) . parse:: <i64 >( ) . ok( ) ) ,
240+ Some ( 42 )
241+ ) ;
242+ }
243+
244+ #[ test]
245+ fn expensive_fork_via_anyhow_preserves_code ( ) {
246+ let sm_err = StateManagerError :: ExpensiveFork { epoch : 99 } ;
247+ let anyhow_err: anyhow:: Error = sm_err. into ( ) ;
248+ let server_err: ServerError = anyhow_err. into ( ) ;
249+
250+ assert_eq ! (
251+ server_err. known_code( ) ,
252+ implementation_defined_errors:: EXPENSIVE_FORK_CODE . into( )
253+ ) ;
254+ assert_eq ! (
255+ server_err
256+ . inner
257+ . data( )
258+ . and_then( |d| d. get( ) . parse:: <i64 >( ) . ok( ) ) ,
259+ Some ( 99 )
260+ ) ;
261+ }
262+ }
0 commit comments