linera_faucet_client/
lib.rs1use linera_base::{crypto::ValidatorPublicKey, data_types::ChainDescription};
9use linera_client::config::GenesisConfig;
10use linera_version::VersionInfo;
11use thiserror_context::Context;
12
13#[derive(Debug, thiserror::Error)]
14#[non_exhaustive]
15pub enum ErrorInner {
16 #[error("JSON parsing error: {0:?}")]
17 Json(#[from] serde_json::Error),
18 #[error("GraphQL error: {0:?}")]
19 GraphQl(Vec<serde_json::Value>),
20 #[error("HTTP error: {0:?}")]
21 Http(#[from] reqwest::Error),
22}
23
24thiserror_context::impl_context!(Error(ErrorInner));
25
26#[derive(Debug, Clone)]
28pub struct Faucet {
29 url: String,
30}
31
32impl Faucet {
33 pub fn new(url: String) -> Self {
34 Self { url }
35 }
36
37 pub fn url(&self) -> &str {
38 &self.url
39 }
40
41 async fn query<Response: serde::de::DeserializeOwned>(
42 &self,
43 query: impl AsRef<str>,
44 ) -> Result<Response, Error> {
45 let query = query.as_ref();
46
47 #[derive(serde::Deserialize)]
48 struct GraphQlResponse<T> {
49 data: Option<T>,
50 errors: Option<Vec<serde_json::Value>>,
51 }
52
53 let builder = reqwest::ClientBuilder::new();
54
55 #[cfg(not(target_arch = "wasm32"))]
56 let builder = builder.timeout(std::time::Duration::from_secs(30));
57
58 let response: GraphQlResponse<Response> = builder
59 .build()
60 .unwrap()
61 .post(&self.url)
62 .json(&serde_json::json!({
63 "query": query,
64 }))
65 .send()
66 .await
67 .with_context(|| format!("executing query {query:?}"))?
68 .error_for_status()?
69 .json()
70 .await?;
71
72 if let Some(errors) = response.errors {
73 Err(ErrorInner::GraphQl(errors).into())
74 } else {
75 Ok(response
76 .data
77 .expect("no errors present but no data returned"))
78 }
79 }
80
81 pub async fn genesis_config(&self) -> Result<GenesisConfig, Error> {
82 #[derive(serde::Deserialize)]
83 #[serde(rename_all = "camelCase")]
84 struct Response {
85 genesis_config: GenesisConfig,
86 }
87
88 Ok(self
89 .query::<Response>("query { genesisConfig }")
90 .await?
91 .genesis_config)
92 }
93
94 pub async fn version_info(&self) -> Result<VersionInfo, Error> {
95 #[derive(serde::Deserialize)]
96 struct Response {
97 version: VersionInfo,
98 }
99
100 Ok(self.query::<Response>("query { version }").await?.version)
101 }
102
103 pub async fn claim(
104 &self,
105 owner: &linera_base::identifiers::AccountOwner,
106 ) -> Result<ChainDescription, Error> {
107 #[derive(serde::Deserialize)]
108 struct Response {
109 claim: ChainDescription,
110 }
111
112 Ok(self
113 .query::<Response>(format!("mutation {{ claim(owner: \"{owner}\") }}"))
114 .await?
115 .claim)
116 }
117
118 pub async fn current_validators(&self) -> Result<Vec<(ValidatorPublicKey, String)>, Error> {
119 #[derive(serde::Deserialize)]
120 #[serde(rename_all = "camelCase")]
121 struct Validator {
122 public_key: ValidatorPublicKey,
123 network_address: String,
124 }
125
126 #[derive(serde::Deserialize)]
127 #[serde(rename_all = "camelCase")]
128 struct Response {
129 current_validators: Vec<Validator>,
130 }
131
132 Ok(self
133 .query::<Response>("query { currentValidators { publicKey networkAddress } }")
134 .await?
135 .current_validators
136 .into_iter()
137 .map(|validator| (validator.public_key, validator.network_address))
138 .collect())
139 }
140}