1#![cfg_attr(docsrs, feature(doc_cfg))]
11
12pub mod data_types;
18use ant_bootstrap::BootstrapConfig;
19pub use data_types::chunk;
20pub use data_types::graph;
21pub use data_types::pointer;
22pub use data_types::scratchpad;
23
24mod high_level;
27pub use high_level::data;
28pub use high_level::files;
29pub use high_level::register;
30pub use high_level::vault;
31
32pub mod analyze;
33pub mod config;
34pub mod key_derivation;
35pub mod payment;
36pub mod quote;
37
38#[cfg(feature = "external-signer")]
39#[cfg_attr(docsrs, doc(cfg(feature = "external-signer")))]
40pub mod external_signer;
41
42mod chunk_cache;
44mod data_map_restoration;
45mod network;
46mod put_error_state;
47
48use payment::Receipt;
49pub use put_error_state::ChunkBatchUploadState;
50use quote::PaymentMode;
51
52use ant_bootstrap::{bootstrap::Bootstrap, contacts_fetcher::ALPHANET_CONTACTS};
53pub use ant_evm::Amount;
54use ant_evm::EvmNetwork;
55use config::ClientConfig;
56use payment::PayError;
57use quote::CostError;
58use self_encryption::DataMap;
59use std::collections::HashSet;
60use tokio::sync::mpsc;
61
62pub const CONNECT_TIMEOUT_SECS: u64 = 10;
64
65const CLIENT_EVENT_CHANNEL_SIZE: usize = 100;
66
67use crate::client::config::ClientOperatingStrategy;
69use crate::networking::{Multiaddr, Network, NetworkAddress, NetworkError, multiaddr_is_global};
70pub use ant_protocol::CLOSE_GROUP_SIZE;
71use ant_protocol::storage::RecordKind;
72
73#[derive(Clone, Debug)]
88pub struct Client {
89 pub(crate) network: Network,
91 pub(crate) client_event_sender: Option<mpsc::Sender<ClientEvent>>,
93 evm_network: EvmNetwork,
95 config: ClientOperatingStrategy,
97 retry_failed: u64,
100 payment_mode: PaymentMode,
102}
103
104#[derive(Debug, thiserror::Error)]
106pub enum ConnectError {
107 #[error("Failed to populate our routing table with enough peers in time")]
109 TimedOut,
110
111 #[error("Failed to populate our routing table due to incompatible protocol: {0:?}")]
113 TimedOutWithIncompatibleProtocol(HashSet<String>, String),
114
115 #[error("Failed to bootstrap the client: {0}")]
117 Bootstrap(#[from] ant_bootstrap::Error),
118
119 #[error("No known peers available in the routing table to bootstrap the client")]
121 NoKnownPeers(#[from] libp2p::kad::NoKnownPeers),
122
123 #[error("Failed to initialize the EVM network: {0}")]
125 EvmNetworkError(String),
126}
127
128#[derive(Debug, thiserror::Error)]
130pub enum PutError {
131 #[error("Failed to self-encrypt data.")]
132 SelfEncryption(#[from] crate::self_encryption::Error),
133 #[error("Error occurred during cost estimation: {0}")]
134 CostError(#[from] CostError),
135 #[error("Error occurred during payment: {0}")]
136 PayError(#[from] PayError),
137 #[error("Serialization error: {0}")]
138 Serialization(String),
139 #[error("A wallet error occurred: {0}")]
140 Wallet(#[from] ant_evm::EvmError),
141 #[error("The payment proof contains no payees.")]
142 PayeesMissing,
143 #[error("A network error occurred for {address}: {network_error}")]
144 Network {
145 address: Box<NetworkAddress>,
146 network_error: NetworkError,
147 payment: Option<Receipt>,
149 },
150 #[error("Batch upload: {0}")]
151 Batch(ChunkBatchUploadState),
152}
153
154#[derive(Debug, thiserror::Error)]
156pub enum GetError {
157 #[error("Could not deserialize data map.")]
158 InvalidDataMap(rmp_serde::decode::Error),
159 #[error("Failed to decrypt data.")]
160 Decryption(crate::self_encryption::Error),
161 #[error("Failed to deserialize")]
162 Deserialization(#[from] rmp_serde::decode::Error),
163 #[error("General networking error: {0}")]
164 Network(#[from] NetworkError),
165 #[error("General protocol error: {0}")]
166 Protocol(#[from] ant_protocol::Error),
167 #[error("Record could not be found.")]
168 RecordNotFound,
169 #[error("The RecordKind obtained from the Record did not match with the expected kind: {0}")]
171 RecordKindMismatch(RecordKind),
172 #[error("Configuration error: {0}")]
173 Configuration(String),
174 #[error("Unable to recogonize the so claimed DataMap: {0}")]
175 UnrecognizedDataMap(String),
176 #[error(
179 "DataMap points to a file too large to be handled in memory, you can increase the MAX_IN_MEMORY_DOWNLOAD_SIZE env var or use streaming to avoid this error."
180 )]
181 TooLargeForMemory(DataMap),
182}
183
184impl Client {
185 pub async fn init() -> Result<Self, ConnectError> {
189 Self::init_with_config(ClientConfig {
190 bootstrap_config: BootstrapConfig::new(false),
191 ..Default::default()
192 })
193 .await
194 }
195
196 pub async fn init_local() -> Result<Self, ConnectError> {
200 Self::init_with_config(ClientConfig {
201 evm_network: EvmNetwork::new(true)
202 .map_err(|e| ConnectError::EvmNetworkError(e.to_string()))?,
203 strategy: Default::default(),
204 network_id: None,
205 bootstrap_config: BootstrapConfig::new(true),
206 })
207 .await
208 }
209
210 pub async fn init_alpha() -> Result<Self, ConnectError> {
212 let client_config = ClientConfig {
213 bootstrap_config: BootstrapConfig {
214 network_contacts_url: ALPHANET_CONTACTS.iter().map(|s| s.to_string()).collect(),
215 ..Default::default()
216 },
217 evm_network: EvmNetwork::ArbitrumSepoliaTest,
218 strategy: Default::default(),
219 network_id: Some(2),
220 };
221 Self::init_with_config(client_config).await
222 }
223
224 pub async fn init_with_peers(peers: Vec<Multiaddr>) -> Result<Self, ConnectError> {
238 let local = !peers.iter().any(multiaddr_is_global);
240 let bootstrap_config = BootstrapConfig {
241 local,
242 initial_peers: peers.clone(),
243 ..Default::default()
244 };
245
246 Self::init_with_config(ClientConfig {
247 bootstrap_config,
248 evm_network: EvmNetwork::new(local).unwrap_or_default(),
249 strategy: Default::default(),
250 network_id: None,
251 })
252 .await
253 }
254
255 pub async fn init_with_config(config: ClientConfig) -> Result<Self, ConnectError> {
270 if let Some(network_id) = config.network_id {
271 ant_protocol::version::set_network_id(network_id);
272 }
273
274 let bootstrap = Bootstrap::new(config.bootstrap_config.clone()).await?;
275 let network = Network::new(bootstrap)?;
276
277 let connectivity_result = network.wait_for_connectivity().await;
279
280 if connectivity_result.is_err() && !config.bootstrap_config.disable_cache_reading {
283 warn!(
284 "Initial connection failed with bootstrap cache enabled. Retrying with cache disabled to use mainnet contacts..."
285 );
286
287 let retry_config = BootstrapConfig {
289 disable_cache_reading: true,
290 ..config.bootstrap_config.clone()
291 };
292
293 let bootstrap_retry = Bootstrap::new(retry_config).await?;
295 let network_retry = Network::new(bootstrap_retry)?;
296
297 network_retry.wait_for_connectivity().await?;
299
300 info!(
301 "Successfully connected to the network using mainnet contacts after cache failure"
302 );
303
304 return Ok(Self {
305 network: network_retry,
306 client_event_sender: None,
307 evm_network: config.evm_network,
308 config: config.strategy,
309 retry_failed: 0,
310 payment_mode: PaymentMode::Standard,
311 });
312 }
313
314 connectivity_result?;
316
317 Ok(Self {
318 network,
319 client_event_sender: None,
320 evm_network: config.evm_network,
321 config: config.strategy,
322 retry_failed: 0,
323 payment_mode: PaymentMode::default(),
324 })
325 }
326
327 pub fn with_strategy(mut self, strategy: ClientOperatingStrategy) -> Self {
329 self.config = strategy;
330 self
331 }
332
333 pub fn with_retry_failed(mut self, retry_failed: u64) -> Self {
335 self.retry_failed = retry_failed;
336 self
337 }
338
339 pub fn with_payment_mode(mut self, payment_mode: PaymentMode) -> Self {
341 self.payment_mode = payment_mode;
342 self
343 }
344
345 pub fn enable_client_events(&mut self) -> mpsc::Receiver<ClientEvent> {
347 let (client_event_sender, client_event_receiver) =
348 tokio::sync::mpsc::channel(CLIENT_EVENT_CHANNEL_SIZE);
349 self.client_event_sender = Some(client_event_sender);
350 debug!("All events to the clients are enabled");
351
352 client_event_receiver
353 }
354
355 pub fn evm_network(&self) -> &EvmNetwork {
357 &self.evm_network
358 }
359}
360
361#[derive(Debug, Clone)]
363pub enum ClientEvent {
364 UploadComplete(UploadSummary),
365}
366
367#[derive(Debug, Clone)]
369pub struct UploadSummary {
370 pub records_paid: usize,
372 pub records_already_paid: usize,
374 pub tokens_spent: Amount,
376}
377
378#[cfg(test)]
379mod tests {
380 use super::*;
381 use ant_logging::LogBuilder;
382
383 #[tokio::test]
384 async fn test_init_fails() {
385 let _guard = LogBuilder::init_single_threaded_tokio_test();
386
387 let initial_peers = vec![
388 "/ip4/127.0.0.1/udp/1/quic-v1/p2p/12D3KooWRBhwfeP2Y4TCx1SM6s9rUoHhR5STiGwxBhgFRcw3UERE"
389 .parse()
390 .unwrap(),
391 ];
392 let bootstrap = Bootstrap::new(
393 BootstrapConfig::default()
394 .with_initial_peers(initial_peers)
395 .with_disable_cache_reading(true)
396 .with_disable_env_peers(true)
397 .with_local(true),
398 )
399 .await
400 .unwrap();
401 let network = Network::new(bootstrap).unwrap();
402
403 match network.wait_for_connectivity().await {
404 Err(ConnectError::TimedOut) => {} Ok(()) => panic!("Expected `ConnectError::TimedOut`, but got `Ok`"),
406 Err(err) => {
407 panic!("Expected `ConnectError::TimedOut`, but got `{err:?}`")
408 }
409 }
410 }
411}