Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion crates/app/src/eth2wrap/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pub mod version;
/// Cache of Validators retrieved from the Beacon node
pub mod valcache;

/// Extensions module to the Eth2Api crate
/// Extensions module to the `Eth2Api` crate
///
/// Includes additional data types and functions to reduce the boilerplate when
/// interacting with `eth2api`.
Expand Down
59 changes: 36 additions & 23 deletions crates/app/src/eth2wrap/valcache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ struct ValidatorCacheInner {

impl ValidatorCache {
/// Creates a new, empty validator cache.
#[must_use]
pub fn new(eth2_cl: EthBeaconNodeApiClient, pubkeys: Vec<PubKey>) -> Self {
Self(Arc::new(RwLock::new(ValidatorCacheInner {
eth2_cl,
Expand All @@ -101,14 +102,20 @@ impl ValidatorCache {

if let (Some(active), Some(complete)) = (&inner.active, &inner.complete) {
return Ok((active.clone(), complete.clone()));
};
}

let request = PostStateValidatorsRequest {
path: PostStateValidatorsRequestPath {
state_id: "head".into(),
},
body: ValidatorRequestBody {
ids: Some(inner.pubkeys.iter().map(|pk| pk.to_string()).collect()),
ids: Some(
inner
.pubkeys
.iter()
.map(std::string::ToString::to_string)
.collect(),
),
..Default::default()
},
};
Expand Down Expand Up @@ -148,31 +155,37 @@ impl ValidatorCache {
state_id: slot.to_string(),
},
body: ValidatorRequestBody {
ids: Some(inner.pubkeys.iter().map(|pk| pk.to_string()).collect()),
ids: Some(
inner
.pubkeys
.iter()
.map(std::string::ToString::to_string)
.collect(),
),
..Default::default()
},
};

let (response, refreshed_by_slot) =
match inner.eth2_cl.post_state_validators(request.clone()).await {
Ok(PostStateValidatorsResponse::Ok(response)) => (response, true),
_ => {
// Failed to fetch by slot, fall back to head state
request.path.state_id = "head".into();

let response = inner
.eth2_cl
.post_state_validators(request)
.await
.map_err(EthBeaconNodeApiClientError::RequestError)
.and_then(|response| match response {
PostStateValidatorsResponse::Ok(response) => Ok(response),
_ => Err(EthBeaconNodeApiClientError::UnexpectedResponse),
})?;

(response, false)
}
};
let (response, refreshed_by_slot) = if let Ok(PostStateValidatorsResponse::Ok(response)) =
inner.eth2_cl.post_state_validators(request.clone()).await
{
(response, true)
} else {
// Failed to fetch by slot, fall back to head state
request.path.state_id = "head".into();

let response = inner
.eth2_cl
.post_state_validators(request)
.await
.map_err(EthBeaconNodeApiClientError::RequestError)
.and_then(|response| match response {
PostStateValidatorsResponse::Ok(response) => Ok(response),
_ => Err(EthBeaconNodeApiClientError::UnexpectedResponse),
})?;

(response, false)
};

let (active_validators, complete_validators) = validators_from_response(response)?;

Expand Down
20 changes: 12 additions & 8 deletions crates/app/src/featureset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ pub enum Feature {
/// gnosis|chiado, unless the user disabled this feature explicitly.
GnosisBlockHotfix,
/// Enables Linear round timer for consensus rounds.
/// When active has precedence over EagerDoubleLinear round timer.
/// When active has precedence over `EagerDoubleLinear` round timer.
Linear,
/// Enables Scheduler to refresh duties when reorg occurs.
SseReorgDuties,
Expand All @@ -90,6 +90,7 @@ pub enum Feature {

impl Feature {
/// Returns the string representation of the feature.
#[must_use]
pub fn as_str(self) -> &'static str {
match self {
Feature::MockAlpha => "mock_alpha",
Expand All @@ -109,6 +110,7 @@ impl Feature {
}

/// Returns all known features.
#[must_use]
pub fn all() -> &'static [Feature] {
&[
Feature::MockAlpha,
Expand Down Expand Up @@ -142,7 +144,7 @@ impl std::convert::TryFrom<&str> for Feature {
.iter()
.find(|feature| value.eq_ignore_ascii_case(feature.as_str()))
.copied()
.ok_or_else(|| format!("unknown feature: {}", value))
.ok_or_else(|| format!("unknown feature: {value}"))
}
}

Expand Down Expand Up @@ -175,8 +177,9 @@ impl Default for FeatureSet {

impl FeatureSet {
/// Creates a new feature set with default configuration.
#[must_use]
pub fn new() -> Self {
Self::from_config(Default::default()).expect("default config should always be valid")
Self::from_config(Config::default()).expect("default config should always be valid")
}

/// Creates a feature set from the given configuration.
Expand Down Expand Up @@ -224,11 +227,11 @@ impl FeatureSet {
})
}

/// Enables GnosisBlockHotfix if it was not disabled by the user.
/// Enables `GnosisBlockHotfix` if it was not disabled by the user.
///
/// This is still a temporary workaround for the gnosis chain.
/// When go-eth2-client is fully supporting custom specs, this function has
/// to be removed with GnosisBlockHotfix feature.
/// to be removed with `GnosisBlockHotfix` feature.
pub fn enable_gnosis_block_hotfix_if_not_disabled(&mut self, config: &Config) {
let disabled = config.disabled.contains(&Feature::GnosisBlockHotfix);

Expand All @@ -243,6 +246,7 @@ impl FeatureSet {
}

/// Returns true if the feature is enabled.
#[must_use]
pub fn enabled(&self, feature: Feature) -> bool {
// Get feature status, default to Disable (0) if not found
let feature_status = self.state.get(&feature).copied().unwrap_or(Status::Disable);
Expand All @@ -251,6 +255,7 @@ impl FeatureSet {
}

/// Returns all custom enabled features.
#[must_use]
pub fn custom_enabled_all(&self) -> Vec<Feature> {
let mut custom_enabled_features: Vec<Feature> = Vec::new();

Expand Down Expand Up @@ -302,11 +307,10 @@ mod tests {

for feature in features {
let status = featureset.state.get(feature);
assert!(status.is_some(), "feature {} should have status", feature);
assert!(status.is_some(), "feature {feature} should have status");
assert!(
*status.unwrap() != Status::Disable,
"feature {} should have positive status",
feature
"feature {feature} should have positive status"
);
}
}
Expand Down
34 changes: 17 additions & 17 deletions crates/app/src/obolapi/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,15 @@ pub struct Client {
}

/// Options for configuring the Obol API client.
#[derive(Debug, Default, Clone, Builder)]
#[derive(Debug, Default, Clone, Copy, Builder)]
pub struct ClientOptions {
/// Optional HTTP request timeout override (defaults to 10 seconds).
pub timeout: Option<Duration>,
}

impl Client {
/// Creates a new Obol API client.
pub fn new(url_str: &str, options: ClientOptions) -> Result<Self> {
pub fn new(url_str: &str, options: &ClientOptions) -> Result<Self> {
let req_timeout = options.timeout.unwrap_or(DEFAULT_TIMEOUT);

let http_client = reqwest::Client::builder().timeout(req_timeout).build()?;
Expand All @@ -46,7 +46,7 @@ impl Client {
let normalized_url = if url_str.ends_with('/') {
url_str.to_string()
} else {
format!("{}/", url_str)
format!("{url_str}/")
};
let base_url = Url::parse(&normalized_url)?;

Expand Down Expand Up @@ -187,7 +187,7 @@ fn launchpad_url_path(lock: &Lock) -> String {
#[cfg(test)]
mod tests {
use super::*;
use pluto_cluster::definition::Definition;
use pluto_cluster::definition::{Creator, Definition};

fn test_lock_with_hash(hash: Vec<u8>) -> Lock {
Lock {
Expand All @@ -198,13 +198,13 @@ mod tests {
timestamp: "2024-01-01T00:00:00Z".to_string(),
num_validators: 0,
threshold: 0,
dkg_algorithm: "".to_string(),
dkg_algorithm: String::new(),
fork_version: vec![],
operators: vec![],
creator: Default::default(),
creator: Creator::default(),
validator_addresses: vec![],
deposit_amounts: vec![],
consensus_protocol: "".to_string(),
consensus_protocol: String::new(),
target_gas_limit: 0,
compounding: false,
config_hash: vec![],
Expand All @@ -222,7 +222,7 @@ mod tests {
assert!(
Client::new(
"https://api.obol.tech",
ClientOptions::builder()
&ClientOptions::builder()
.timeout(Duration::from_secs(10))
.build()
)
Expand All @@ -232,27 +232,27 @@ mod tests {

#[test]
fn test_new_client_invalid_url() {
assert!(Client::new("not-a-url", ClientOptions::default()).is_err());
assert!(Client::new("not-a-url", &ClientOptions::default()).is_err());
}

#[test]
fn test_base_url_normalization() {
let c1 = Client::new("https://api.obol.tech", ClientOptions::default()).unwrap();
let c1 = Client::new("https://api.obol.tech", &ClientOptions::default()).unwrap();
assert_eq!(c1.base_url.as_str(), "https://api.obol.tech/");

let c2 = Client::new("https://api.obol.tech/", ClientOptions::default()).unwrap();
let c2 = Client::new("https://api.obol.tech/", &ClientOptions::default()).unwrap();
assert_eq!(c2.base_url.as_str(), "https://api.obol.tech/");

let c3 = Client::new("https://api.obol.tech/v1", ClientOptions::default()).unwrap();
let c3 = Client::new("https://api.obol.tech/v1", &ClientOptions::default()).unwrap();
assert_eq!(c3.base_url.as_str(), "https://api.obol.tech/v1/");

let c4 = Client::new("https://api.obol.tech/v1/", ClientOptions::default()).unwrap();
let c4 = Client::new("https://api.obol.tech/v1/", &ClientOptions::default()).unwrap();
assert_eq!(c4.base_url.as_str(), "https://api.obol.tech/v1/");
}

#[test]
fn test_build_url_root_base() {
let client = Client::new("https://api.obol.tech", ClientOptions::default()).unwrap();
let client = Client::new("https://api.obol.tech", &ClientOptions::default()).unwrap();
assert_eq!(
client.build_url("definition").unwrap().as_str(),
"https://api.obol.tech/definition"
Expand All @@ -272,7 +272,7 @@ mod tests {

#[test]
fn test_build_url_versioned_base() {
let client = Client::new("https://api.obol.tech/v1", ClientOptions::default()).unwrap();
let client = Client::new("https://api.obol.tech/v1", &ClientOptions::default()).unwrap();
assert_eq!(
client.build_url("definition").unwrap().as_str(),
"https://api.obol.tech/v1/definition"
Expand Down Expand Up @@ -300,13 +300,13 @@ mod tests {
fn test_launchpad_url_for_lock() {
let lock = test_lock_with_hash(vec![0x12, 0x34, 0xab, 0xcd]);

let c1 = Client::new("https://api.obol.tech", ClientOptions::default()).unwrap();
let c1 = Client::new("https://api.obol.tech", &ClientOptions::default()).unwrap();
assert_eq!(
c1.launchpad_url_for_lock(&lock).unwrap(),
"https://api.obol.tech/lock/0x1234ABCD/launchpad"
);

let c2 = Client::new("https://api.obol.tech/v1", ClientOptions::default()).unwrap();
let c2 = Client::new("https://api.obol.tech/v1", &ClientOptions::default()).unwrap();
assert_eq!(
c2.launchpad_url_for_lock(&lock).unwrap(),
"https://api.obol.tech/v1/lock/0x1234ABCD/launchpad"
Expand Down
13 changes: 5 additions & 8 deletions crates/app/src/obolapi/exit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ pub struct PartialExitRequest {
pub signature: Vec<u8>,
}

/// DTO for JSON serialization of PartialExitRequest.
/// DTO for JSON serialization of `PartialExitRequest`.
#[derive(Debug, Serialize, Deserialize)]
struct PartialExitRequestDto {
#[serde(flatten)]
Expand Down Expand Up @@ -409,20 +409,17 @@ impl Client {

/// Returns the partial exit Obol API URL for a given lock hash.
fn submit_partial_exit_url(lock_hash: &str) -> String {
format!("/exp/partial_exits/{}", lock_hash)
format!("/exp/partial_exits/{lock_hash}")
}

/// Returns the delete partial exit Obol API URL.
fn delete_partial_exit_url(val_pubkey: &str, lock_hash: &str, share_index: u64) -> String {
format!(
"/exp/partial_exits/{}/{}/{}",
lock_hash, share_index, val_pubkey
)
format!("/exp/partial_exits/{lock_hash}/{share_index}/{val_pubkey}")
}

/// Returns the full exit Obol API URL.
fn fetch_full_exit_url(val_pubkey: &str, lock_hash: &str, share_index: u64) -> String {
format!("/exp/exit/{}/{}/{}", lock_hash, share_index, val_pubkey)
format!("/exp/exit/{lock_hash}/{share_index}/{val_pubkey}")
}

#[cfg(test)]
Expand Down Expand Up @@ -460,7 +457,7 @@ mod tests {
let len = bytes.len();
let arr: [u8; 32] = bytes
.try_into()
.map_err(|_| format!("expected 32 bytes, got {}", len))?;
.map_err(|_| format!("expected 32 bytes, got {len}"))?;
Ok(arr)
}

Expand Down
Loading