Skip to content

Commit 384b354

Browse files
committed
Apply review comments
1 parent 034f793 commit 384b354

File tree

1 file changed

+45
-47
lines changed

1 file changed

+45
-47
lines changed

beacon_node/eth1/src/http.rs

Lines changed: 45 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use reqwest::{header::CONTENT_TYPE, ClientBuilder, StatusCode};
1515
use sensitive_url::SensitiveUrl;
1616
use serde::{Deserialize, Serialize};
1717
use serde_json::{json, Value};
18+
use std::fmt;
1819
use std::ops::Range;
1920
use std::str::FromStr;
2021
use std::time::Duration;
@@ -33,6 +34,9 @@ pub const DEPOSIT_COUNT_RESPONSE_BYTES: usize = 96;
3334
/// Number of bytes in deposit contract deposit root (value only).
3435
pub const DEPOSIT_ROOT_BYTES: usize = 32;
3536

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+
3640
/// Represents an eth1 chain/network id.
3741
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
3842
pub enum Eth1Id {
@@ -52,23 +56,25 @@ pub enum BlockQuery {
5256
#[derive(Debug, Serialize, Deserialize)]
5357
pub enum RpcError {
5458
NoResultField,
55-
InvalidJson,
59+
Eip155Error,
60+
InvalidJson(String),
5661
Error(String),
5762
}
5863

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+
}
6272
}
6373
}
6474

6575
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()
7278
}
7379
}
7480

@@ -107,9 +113,7 @@ impl FromStr for Eth1Id {
107113
pub async fn get_network_id(endpoint: &SensitiveUrl, timeout: Duration) -> Result<Eth1Id, String> {
108114
let response_body = send_rpc_request(endpoint, "net_version", json!([]), timeout).await?;
109115
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)?
113117
.as_str()
114118
.ok_or("Data was not string")?,
115119
)
@@ -120,23 +124,14 @@ pub async fn get_chain_id(endpoint: &SensitiveUrl, timeout: Duration) -> Result<
120124
let response_body: String =
121125
send_rpc_request(endpoint, "eth_chainId", json!([]), timeout).await?;
122126

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) {
128128
Ok(chain_id) => {
129129
hex_to_u64_be(chain_id.as_str().ok_or("Data was not string")?).map(|id| id.into())
130130
}
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()),
140135
}
141136
}
142137

@@ -154,8 +149,7 @@ pub async fn get_block_number(endpoint: &SensitiveUrl, timeout: Duration) -> Res
154149
let response_body = send_rpc_request(endpoint, "eth_blockNumber", json!([]), timeout).await?;
155150
hex_to_u64_be(
156151
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))?
159153
.as_str()
160154
.ok_or("Data was not string")?,
161155
)
@@ -180,10 +174,11 @@ pub async fn get_block(
180174
]);
181175

182176
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+
183180
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
187182
.get("hash")
188183
.ok_or("No hash for block")?
189184
.as_str()
@@ -196,19 +191,15 @@ pub async fn get_block(
196191
};
197192

198193
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
202195
.get("timestamp")
203196
.ok_or("No timestamp for block")?
204197
.as_str()
205198
.ok_or("Block timestamp was not string")?,
206199
)?;
207200

208201
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
212203
.get("number")
213204
.ok_or("No number for block")?
214205
.as_str()
@@ -324,16 +315,19 @@ async fn call(
324315
]);
325316

326317
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) => {
330321
let hex = result
331322
.as_str()
332323
.map(|s| s.to_string())
333324
.ok_or("'result' value was not a string")?;
334325

335326
Ok(Some(hex_to_bytes(&hex)?))
336327
}
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)),
337331
}
338332
}
339333

@@ -365,8 +359,7 @@ pub async fn get_deposit_logs_in_range(
365359

366360
let response_body = send_rpc_request(endpoint, "eth_getLogs", params, timeout).await?;
367361
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))?
370363
.as_array()
371364
.cloned()
372365
.ok_or("'result' value was not an array")?
@@ -453,13 +446,18 @@ pub async fn send_rpc_request(
453446

454447
/// Accepts an entire HTTP body (as a string) and returns either the `result` field or the `error['message']` field, as a serde `Value`.
455448
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()))?;
457451

458452
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+
}
460459
} else {
461-
let res = json.get("result").cloned().ok_or(RpcError::NoResultField)?;
462-
Ok(res)
460+
json.get("result").cloned().ok_or(RpcError::NoResultField)
463461
}
464462
}
465463

0 commit comments

Comments
 (0)