#![cfg_attr(docsrs, feature(doc_cfg))]
pub mod data_types;
use ant_bootstrap::BootstrapConfig;
pub use data_types::chunk;
pub use data_types::graph;
pub use data_types::pointer;
pub use data_types::scratchpad;
mod high_level;
pub use high_level::data;
pub use high_level::files;
pub use high_level::register;
pub use high_level::vault;
pub mod analyze;
pub mod config;
pub mod key_derivation;
pub mod merkle_payments;
pub mod payment;
pub mod quote;
#[cfg(feature = "external-signer")]
#[cfg_attr(docsrs, doc(cfg(feature = "external-signer")))]
pub mod external_signer;
mod chunk_cache;
mod data_map_restoration;
mod network;
mod put_error_state;
use payment::Receipt;
pub use put_error_state::ChunkBatchUploadState;
use quote::PaymentMode;
use ant_bootstrap::{bootstrap::Bootstrap, contacts_fetcher::ALPHANET_CONTACTS};
pub use ant_evm::Amount;
use ant_evm::EvmNetwork;
use config::ClientConfig;
use payment::PayError;
use quote::CostError;
use self_encryption::DataMap;
use std::collections::HashSet;
use tokio::sync::mpsc;
pub const CONNECT_TIMEOUT_SECS: u64 = 10;
const CLIENT_EVENT_CHANNEL_SIZE: usize = 100;
use crate::client::config::ClientOperatingStrategy;
use crate::client::merkle_payments::MerkleUploadError;
use crate::networking::{Multiaddr, Network, NetworkAddress, NetworkError, multiaddr_is_global};
pub use ant_protocol::CLOSE_GROUP_SIZE;
use ant_protocol::storage::RecordKind;
#[derive(Clone, Debug)]
pub struct Client {
pub(crate) network: Network,
pub(crate) client_event_sender: Option<mpsc::Sender<ClientEvent>>,
evm_network: EvmNetwork,
config: ClientOperatingStrategy,
retry_failed: u64,
payment_mode: PaymentMode,
}
#[derive(Debug, thiserror::Error)]
pub enum ConnectError {
#[error("Failed to populate our routing table with enough peers in time")]
TimedOut,
#[error("Failed to populate our routing table due to incompatible protocol: {0:?}")]
TimedOutWithIncompatibleProtocol(HashSet<String>, String),
#[error("Failed to bootstrap the client: {0}")]
Bootstrap(#[from] ant_bootstrap::Error),
#[error("No known peers available in the routing table to bootstrap the client")]
NoKnownPeers(#[from] libp2p::kad::NoKnownPeers),
#[error("Failed to initialize the EVM network: {0}")]
EvmNetworkError(String),
}
#[derive(Debug, thiserror::Error)]
pub enum PutError {
#[error("Failed to self-encrypt data.")]
SelfEncryption(#[from] crate::self_encryption::Error),
#[error("Error occurred during cost estimation: {0}")]
CostError(#[from] CostError),
#[error("Error occurred during payment: {0}")]
PayError(#[from] PayError),
#[error("Serialization error: {0}")]
Serialization(String),
#[error("A wallet error occurred: {0}")]
Wallet(#[from] ant_evm::EvmError),
#[error("The payment proof contains no payees.")]
PayeesMissing,
#[error("A network error occurred for {address}: {network_error}")]
Network {
address: Box<NetworkAddress>,
network_error: NetworkError,
payment: Option<Receipt>,
},
#[error("Batch upload: {0}")]
Batch(ChunkBatchUploadState),
#[error("Merkle batch upload: {0}")]
MerkleBatch(MerkleUploadError),
}
#[derive(Debug, thiserror::Error)]
pub enum GetError {
#[error("Could not deserialize data map.")]
InvalidDataMap(rmp_serde::decode::Error),
#[error("Failed to decrypt data.")]
Decryption(crate::self_encryption::Error),
#[error("Failed to deserialize")]
Deserialization(#[from] rmp_serde::decode::Error),
#[error("General networking error: {0}")]
Network(#[from] NetworkError),
#[error("General protocol error: {0}")]
Protocol(#[from] ant_protocol::Error),
#[error("Record could not be found.")]
RecordNotFound,
#[error("The RecordKind obtained from the Record did not match with the expected kind: {0}")]
RecordKindMismatch(RecordKind),
#[error("Configuration error: {0}")]
Configuration(String),
#[error("Unable to recogonize the so claimed DataMap: {0}")]
UnrecognizedDataMap(String),
#[error(
"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."
)]
TooLargeForMemory(DataMap),
}
impl Client {
pub async fn init() -> Result<Self, ConnectError> {
Self::init_with_config(ClientConfig {
bootstrap_config: BootstrapConfig::new(false),
..Default::default()
})
.await
}
pub async fn init_local() -> Result<Self, ConnectError> {
Self::init_with_config(ClientConfig {
evm_network: EvmNetwork::new(true)
.map_err(|e| ConnectError::EvmNetworkError(e.to_string()))?,
strategy: Default::default(),
network_id: None,
bootstrap_config: BootstrapConfig::new(true),
})
.await
}
pub async fn init_alpha() -> Result<Self, ConnectError> {
let client_config = ClientConfig {
bootstrap_config: BootstrapConfig {
network_contacts_url: ALPHANET_CONTACTS.iter().map(|s| s.to_string()).collect(),
..Default::default()
},
evm_network: EvmNetwork::ArbitrumSepoliaTest,
strategy: Default::default(),
network_id: Some(2),
};
Self::init_with_config(client_config).await
}
pub async fn init_with_peers(peers: Vec<Multiaddr>) -> Result<Self, ConnectError> {
let local = !peers.iter().any(multiaddr_is_global);
let bootstrap_config = BootstrapConfig {
local,
initial_peers: peers.clone(),
..Default::default()
};
Self::init_with_config(ClientConfig {
bootstrap_config,
evm_network: EvmNetwork::new(local).unwrap_or_default(),
strategy: Default::default(),
network_id: None,
})
.await
}
pub async fn init_with_config(config: ClientConfig) -> Result<Self, ConnectError> {
if let Some(network_id) = config.network_id {
ant_protocol::version::set_network_id(network_id);
}
let bootstrap = Bootstrap::new(config.bootstrap_config.clone()).await?;
let network = Network::new(bootstrap)?;
let connectivity_result = network.wait_for_connectivity().await;
if connectivity_result.is_err() && !config.bootstrap_config.disable_cache_reading {
warn!(
"Initial connection failed with bootstrap cache enabled. Retrying with cache disabled to use mainnet contacts..."
);
let retry_config = BootstrapConfig {
disable_cache_reading: true,
..config.bootstrap_config.clone()
};
let bootstrap_retry = Bootstrap::new(retry_config).await?;
let network_retry = Network::new(bootstrap_retry)?;
network_retry.wait_for_connectivity().await?;
info!(
"Successfully connected to the network using mainnet contacts after cache failure"
);
return Ok(Self {
network: network_retry,
client_event_sender: None,
evm_network: config.evm_network,
config: config.strategy,
retry_failed: 0,
payment_mode: PaymentMode::Standard,
});
}
connectivity_result?;
Ok(Self {
network,
client_event_sender: None,
evm_network: config.evm_network,
config: config.strategy,
retry_failed: 0,
payment_mode: PaymentMode::default(),
})
}
pub fn with_strategy(mut self, strategy: ClientOperatingStrategy) -> Self {
self.config = strategy;
self
}
pub fn with_retry_failed(mut self, retry_failed: u64) -> Self {
self.retry_failed = retry_failed;
self
}
pub fn with_payment_mode(mut self, payment_mode: PaymentMode) -> Self {
self.payment_mode = payment_mode;
self
}
pub fn enable_client_events(&mut self) -> mpsc::Receiver<ClientEvent> {
let (client_event_sender, client_event_receiver) =
tokio::sync::mpsc::channel(CLIENT_EVENT_CHANNEL_SIZE);
self.client_event_sender = Some(client_event_sender);
debug!("All events to the clients are enabled");
client_event_receiver
}
pub fn evm_network(&self) -> &EvmNetwork {
&self.evm_network
}
}
#[derive(Debug, Clone)]
pub enum ClientEvent {
UploadComplete(UploadSummary),
}
#[derive(Debug, Clone)]
pub struct UploadSummary {
pub records_paid: usize,
pub records_already_paid: usize,
pub tokens_spent: Amount,
}
#[cfg(test)]
mod tests {
use super::*;
use ant_logging::LogBuilder;
#[tokio::test]
async fn test_init_fails() {
let _guard = LogBuilder::init_single_threaded_tokio_test();
let initial_peers = vec![
"/ip4/127.0.0.1/udp/1/quic-v1/p2p/12D3KooWRBhwfeP2Y4TCx1SM6s9rUoHhR5STiGwxBhgFRcw3UERE"
.parse()
.unwrap(),
];
let bootstrap = Bootstrap::new(
BootstrapConfig::default()
.with_initial_peers(initial_peers)
.with_disable_cache_reading(true)
.with_disable_env_peers(true)
.with_local(true),
)
.await
.unwrap();
let network = Network::new(bootstrap).unwrap();
match network.wait_for_connectivity().await {
Err(ConnectError::TimedOut) => {} Ok(()) => panic!("Expected `ConnectError::TimedOut`, but got `Ok`"),
Err(err) => {
panic!("Expected `ConnectError::TimedOut`, but got `{err:?}`")
}
}
}
}