@@ -15,6 +15,7 @@ use reqwest::{header::CONTENT_TYPE, ClientBuilder, StatusCode};
15
15
use sensitive_url:: SensitiveUrl ;
16
16
use serde:: { Deserialize , Serialize } ;
17
17
use serde_json:: { json, Value } ;
18
+ use std:: fmt;
18
19
use std:: ops:: Range ;
19
20
use std:: str:: FromStr ;
20
21
use std:: time:: Duration ;
@@ -33,6 +34,9 @@ pub const DEPOSIT_COUNT_RESPONSE_BYTES: usize = 96;
33
34
/// Number of bytes in deposit contract deposit root (value only).
34
35
pub const DEPOSIT_ROOT_BYTES : usize = 32 ;
35
36
37
+ /// This error is returned during a `chainId` call by Geth.
38
+ pub const EIP155_ERROR_STR : & str = "chain not synced beyond EIP-155 replay-protection fork block" ;
39
+
36
40
/// Represents an eth1 chain/network id.
37
41
#[ derive( Debug , PartialEq , Clone , Serialize , Deserialize ) ]
38
42
pub enum Eth1Id {
@@ -52,23 +56,25 @@ pub enum BlockQuery {
52
56
#[ derive( Debug , Serialize , Deserialize ) ]
53
57
pub enum RpcError {
54
58
NoResultField ,
55
- InvalidJson ,
59
+ Eip155Error ,
60
+ InvalidJson ( String ) ,
56
61
Error ( String ) ,
57
62
}
58
63
59
- impl From < serde_json:: Error > for RpcError {
60
- fn from ( _value : serde_json:: Error ) -> Self {
61
- RpcError :: InvalidJson
64
+ impl fmt:: Display for RpcError {
65
+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
66
+ match self {
67
+ RpcError :: NoResultField => write ! ( f, "No result field in response" ) ,
68
+ RpcError :: Eip155Error => write ! ( f, "Not synced past EIP-155" ) ,
69
+ RpcError :: InvalidJson ( e) => write ! ( f, "Malformed JSON received: {}" , e) ,
70
+ RpcError :: Error ( s) => write ! ( f, "{}" , s) ,
71
+ }
62
72
}
63
73
}
64
74
65
75
impl From < RpcError > for String {
66
- fn from ( value : RpcError ) -> Self {
67
- match value {
68
- RpcError :: NoResultField => "No JSON error returned" . into ( ) ,
69
- RpcError :: InvalidJson => "Malformed JSON received" . into ( ) ,
70
- RpcError :: Error ( s) => s,
71
- }
76
+ fn from ( e : RpcError ) -> String {
77
+ e. to_string ( )
72
78
}
73
79
}
74
80
@@ -107,9 +113,7 @@ impl FromStr for Eth1Id {
107
113
pub async fn get_network_id ( endpoint : & SensitiveUrl , timeout : Duration ) -> Result < Eth1Id , String > {
108
114
let response_body = send_rpc_request ( endpoint, "net_version" , json ! ( [ ] ) , timeout) . await ?;
109
115
Eth1Id :: from_str (
110
- response_result_or_error ( & response_body)
111
- . ok ( )
112
- . ok_or ( "No result was returned for network id" ) ?
116
+ response_result_or_error ( & response_body) ?
113
117
. as_str ( )
114
118
. ok_or ( "Data was not string" ) ?,
115
119
)
@@ -120,23 +124,14 @@ pub async fn get_chain_id(endpoint: &SensitiveUrl, timeout: Duration) -> Result<
120
124
let response_body: String =
121
125
send_rpc_request ( endpoint, "eth_chainId" , json ! ( [ ] ) , timeout) . await ?;
122
126
123
- // Extract response text here.
124
- let response_result: Result < Value , RpcError > = response_result_or_error ( & response_body) ;
125
-
126
- // Specifically handle Geth's pre-EIP-155 sync error message.
127
- match response_result {
127
+ match response_result_or_error ( & response_body) {
128
128
Ok ( chain_id) => {
129
129
hex_to_u64_be ( chain_id. as_str ( ) . ok_or ( "Data was not string" ) ?) . map ( |id| id. into ( ) )
130
130
}
131
- Err ( rpc_err) => match rpc_err {
132
- RpcError :: Error ( err_string) => match err_string. as_str ( ) {
133
- "\" chain not synced beyond EIP-155 replay-protection fork block\" " => {
134
- Ok ( Eth1Id :: Custom ( 0 ) )
135
- }
136
- _ => Err ( err_string) ,
137
- } ,
138
- _ => Err ( rpc_err. into ( ) ) ,
139
- } ,
131
+ // Geth returns this error when it's syncing lower blocks. Simply map this into `0` since
132
+ // Lighthouse does not raise errors for `0`, it simply waits for it to change.
133
+ Err ( RpcError :: Eip155Error ) => Ok ( Eth1Id :: Custom ( 0 ) ) ,
134
+ Err ( e) => Err ( e. to_string ( ) ) ,
140
135
}
141
136
}
142
137
@@ -154,8 +149,7 @@ pub async fn get_block_number(endpoint: &SensitiveUrl, timeout: Duration) -> Res
154
149
let response_body = send_rpc_request ( endpoint, "eth_blockNumber" , json ! ( [ ] ) , timeout) . await ?;
155
150
hex_to_u64_be (
156
151
response_result_or_error ( & response_body)
157
- . ok ( )
158
- . ok_or ( "No result field was returned for block number" ) ?
152
+ . map_err ( |e| format ! ( "eth_blockNumber failed: {}" , e) ) ?
159
153
. as_str ( )
160
154
. ok_or ( "Data was not string" ) ?,
161
155
)
@@ -180,10 +174,11 @@ pub async fn get_block(
180
174
] ) ;
181
175
182
176
let response_body = send_rpc_request ( endpoint, "eth_getBlockByNumber" , params, timeout) . await ?;
177
+ let response = response_result_or_error ( & response_body)
178
+ . map_err ( |e| format ! ( "eth_getBlockByNumber failed: {}" , e) ) ?;
179
+
183
180
let hash: Vec < u8 > = hex_to_bytes (
184
- response_result_or_error ( & response_body)
185
- . ok ( )
186
- . ok_or ( "No result field was returned for block" ) ?
181
+ response
187
182
. get ( "hash" )
188
183
. ok_or ( "No hash for block" ) ?
189
184
. as_str ( )
@@ -196,19 +191,15 @@ pub async fn get_block(
196
191
} ;
197
192
198
193
let timestamp = hex_to_u64_be (
199
- response_result_or_error ( & response_body)
200
- . ok ( )
201
- . ok_or ( "No result field was returned for timestamp" ) ?
194
+ response
202
195
. get ( "timestamp" )
203
196
. ok_or ( "No timestamp for block" ) ?
204
197
. as_str ( )
205
198
. ok_or ( "Block timestamp was not string" ) ?,
206
199
) ?;
207
200
208
201
let number = hex_to_u64_be (
209
- response_result_or_error ( & response_body)
210
- . ok ( )
211
- . ok_or ( "No result field was returned for number" ) ?
202
+ response
212
203
. get ( "number" )
213
204
. ok_or ( "No number for block" ) ?
214
205
. as_str ( )
@@ -324,16 +315,19 @@ async fn call(
324
315
] ) ;
325
316
326
317
let response_body = send_rpc_request ( endpoint, "eth_call" , params, timeout) . await ?;
327
- match response_result_or_error ( & response_body ) . ok ( ) {
328
- None => Ok ( None ) ,
329
- Some ( result) => {
318
+
319
+ match response_result_or_error ( & response_body ) {
320
+ Ok ( result) => {
330
321
let hex = result
331
322
. as_str ( )
332
323
. map ( |s| s. to_string ( ) )
333
324
. ok_or ( "'result' value was not a string" ) ?;
334
325
335
326
Ok ( Some ( hex_to_bytes ( & hex) ?) )
336
327
}
328
+ // It's valid for `eth_call` to return without a result.
329
+ Err ( RpcError :: NoResultField ) => Ok ( None ) ,
330
+ Err ( e) => Err ( format ! ( "eth_call failed: {}" , e) ) ,
337
331
}
338
332
}
339
333
@@ -365,8 +359,7 @@ pub async fn get_deposit_logs_in_range(
365
359
366
360
let response_body = send_rpc_request ( endpoint, "eth_getLogs" , params, timeout) . await ?;
367
361
Ok ( response_result_or_error ( & response_body)
368
- . ok ( )
369
- . ok_or ( "No result field was returned for deposit logs" ) ?
362
+ . map_err ( |e| format ! ( "eth_getLogs failed: {}" , e) ) ?
370
363
. as_array ( )
371
364
. cloned ( )
372
365
. ok_or ( "'result' value was not an array" ) ?
@@ -453,13 +446,18 @@ pub async fn send_rpc_request(
453
446
454
447
/// Accepts an entire HTTP body (as a string) and returns either the `result` field or the `error['message']` field, as a serde `Value`.
455
448
fn response_result_or_error ( response : & str ) -> Result < Value , RpcError > {
456
- let json = serde_json:: from_str :: < Value > ( & response) ?;
449
+ let json = serde_json:: from_str :: < Value > ( & response)
450
+ . map_err ( |e| RpcError :: InvalidJson ( e. to_string ( ) ) ) ?;
457
451
458
452
if let Some ( error) = json. get ( "error" ) . map ( |e| e. get ( "message" ) ) . flatten ( ) {
459
- Err ( RpcError :: Error ( error. to_string ( ) ) )
453
+ let error = error. to_string ( ) ;
454
+ if error. contains ( EIP155_ERROR_STR ) {
455
+ Err ( RpcError :: Eip155Error )
456
+ } else {
457
+ Err ( RpcError :: Error ( error) )
458
+ }
460
459
} else {
461
- let res = json. get ( "result" ) . cloned ( ) . ok_or ( RpcError :: NoResultField ) ?;
462
- Ok ( res)
460
+ json. get ( "result" ) . cloned ( ) . ok_or ( RpcError :: NoResultField )
463
461
}
464
462
}
465
463
0 commit comments