Skip to content

Commit cc0a62e

Browse files
authored
feat: add eth2wrap/valcache (#122)
* Initial `eth2wrap` module * Add initial `version` * Add tests * Merge remote-tracking branch 'origin/main' into emlautarom1/eth2wrap * Use `SemVer::parse` instead of `SemVer::try_from` for version parsing * Replace function with static HashMap - Avoids parsing on each request * Fix clippy lint errors * Add `RUSTC_BOOTSTRAP` - Prevents VSCode from rebuilding everything during tests * Prefer `&str` for `TryFrom` - Makes it easier to use `String` and `str` at the same time * Add `eth2api` and `anyhow` as deps * Add `valcache` as module * Add `eth2api` extensions module - Redices boilerplate when dealing with the `eth2api` lib - Use `eth2api` types in `valcache` * Implement `get_by_head` * Add `get_by_slot` * Extract common mapping code * Reduce the scope of locks * Apply suggestions - Remove redundant code - Use `From` for `PoisonError` - Code cleanup * Update docs * Use raw Validator type - Prefer `GetStateValidatorsResponseResponseDatum` over custom `Validator` * Apply clippy suggestions * Update docs - Make them closer to the original implementation * Update lockfile * Include `Serialize` for all types - Useful for mocking requests/responses in tests * Initial test for `valcache` * Add test for failing request - Use `POST` endpoint since reqwest cannot deal with complex query params - Use `wiremock` to fake an HTTP server * Test cache population * Test for cache population - Beacon should be called once * Port `validator_cache` test * Remove tests - Superseded by Go tests * Add `get_by_slot_successful_fetch` * Refactor - Extract common code - Rename tests - Inline methods * Add `get_by_slot_fallback_to_head` * Update lockfile * Apply Slippy suggestions * Apply Copilot suggestions * Use tokio mutex - Hold lock across awaits - Match the original Go implementation - Simplify internals * Use `tokio::sync::RwLock` instead * Merge remote-tracking branch 'origin/main' into emlautarom1/eth2wrap/valcache
1 parent 4b6aa1f commit cc0a62e

9 files changed

Lines changed: 690 additions & 9 deletions

File tree

Cargo.lock

Lines changed: 53 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ members = [
1414
"crates/tracing",
1515
"crates/peerinfo",
1616
"crates/eth2api",
17-
"crates/relay-server"
17+
"crates/relay-server",
1818
]
1919
resolver = "3"
2020

@@ -72,6 +72,7 @@ oas3-gen-support = "0.24"
7272
bon = "3.8"
7373
testcontainers = "0.26"
7474
vise = "0.3"
75+
wiremock = "0.6"
7576

7677
# Crates in the workspace
7778
charon = { path = "crates/charon" }

crates/charon-core/src/types.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -220,11 +220,11 @@ impl Serialize for PubKey {
220220
}
221221
}
222222

223-
impl TryFrom<String> for PubKey {
223+
impl TryFrom<&str> for PubKey {
224224
type Error = PubKeyError;
225225

226-
fn try_from(value: String) -> Result<Self, Self::Error> {
227-
let value = value.strip_prefix("0x").unwrap_or(&value);
226+
fn try_from(value: &str) -> Result<Self, Self::Error> {
227+
let value = value.strip_prefix("0x").unwrap_or(value);
228228
let hex_value = hex::decode(value).map_err(|_| PubKeyError::InvalidString)?;
229229
PubKey::try_from(hex_value.as_slice())
230230
}
@@ -890,7 +890,7 @@ mod tests {
890890
#[test]
891891
fn test_pub_key_from_string() {
892892
let pk_str = "0x7f790ba343adf8891fac21a94b02d6ca93d0bc2199a5ec083ff6676e8c2f9f78b08bb122f1093675f9d24c8b5e7af241".to_string();
893-
let pk = PubKey::try_from(pk_str).unwrap();
893+
let pk = PubKey::try_from(pk_str.as_str()).unwrap();
894894
assert_eq!(
895895
pk,
896896
PubKey::new([
@@ -904,7 +904,7 @@ mod tests {
904904
#[test]
905905
fn test_pub_key_from_string_invalid_length() {
906906
let pk_str = "0x7f790ba343adf8891fac21a94b02d6ca93d0bc2199a5ec083ff6676e8c2f9f78b08bb121093675f9d24c8b5e7af241".to_string();
907-
let result = PubKey::try_from(pk_str);
907+
let result = PubKey::try_from(pk_str.as_str());
908908
assert!(result.is_err());
909909
}
910910
}

crates/charon/Cargo.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ backon.workspace = true
1111
chrono.workspace = true
1212
charon-core.workspace = true
1313
charon-tracing.workspace = true
14+
eth2api.workspace = true
15+
anyhow.workspace = true
1416
tokio.workspace = true
1517
tokio-util.workspace = true
1618
prost.workspace = true
@@ -30,10 +32,12 @@ bon.workspace = true
3032
charon-cluster = { workspace = true }
3133
charon-k1util = { workspace = true }
3234
charon-crypto = { workspace = true }
33-
eth2api = { workspace = true }
3435

3536
[build-dependencies]
3637
charon-build-proto.workspace = true
3738

39+
[dev-dependencies]
40+
wiremock.workspace = true
41+
3842
[lints]
3943
workspace = true
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
use eth2api::ValidatorStatus;
2+
3+
/// Error that can occur when using the [`eth2api::EthBeaconNodeApiClient`].
4+
#[derive(Debug, thiserror::Error)]
5+
pub enum EthBeaconNodeApiClientError {
6+
/// Underlying error from [`eth2api::EthBeaconNodeApiClient`] when making a
7+
/// request.
8+
#[error("Request error: {0}")]
9+
RequestError(#[from] anyhow::Error),
10+
11+
/// Unexpected response, e.g, got an error when an Ok response was expected
12+
#[error("Unexpected response")]
13+
UnexpectedResponse,
14+
15+
/// Unexpected type in response
16+
#[error("Unexpected type in response")]
17+
UnexpectedType,
18+
}
19+
20+
/// Type alias for validator index.
21+
pub type ValidatorIndex = u64;
22+
23+
/// Extension methods on [`ValidatorStatus`].
24+
pub trait ValidatorStatusExt {
25+
/// Returns true if the validator is in one of the active states.
26+
fn is_active(&self) -> bool;
27+
}
28+
29+
impl ValidatorStatusExt for ValidatorStatus {
30+
fn is_active(&self) -> bool {
31+
matches!(
32+
self,
33+
ValidatorStatus::ActiveOngoing
34+
| ValidatorStatus::ActiveExiting
35+
| ValidatorStatus::ActiveSlashed
36+
)
37+
}
38+
}

crates/charon/src/eth2wrap/mod.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,11 @@
1-
/// Validate Beacon Node versions
1+
/// Validate Beacon node versions
22
pub mod version;
3+
4+
/// Cache of Validators retrieved from the Beacon node
5+
pub mod valcache;
6+
7+
/// Extensions module to the Eth2Api crate
8+
///
9+
/// Includes additional data types and functions to reduce the boilerplate when
10+
/// interacting with `eth2api`.
11+
pub mod eth2api;

0 commit comments

Comments
 (0)