pub(crate) mod download;
use crate::{
acc_packet::load_account_wallet_or_create_with_mnemonic, chunks::Error as ChunksError,
error::Result, wallet::StoragePaymentResult, Client, Error, WalletClient,
};
use bytes::Bytes;
use self_encryption::{self, MIN_ENCRYPTABLE_BYTES};
use sn_protocol::{
storage::{Chunk, ChunkAddress, RetryStrategy},
NetworkAddress,
};
use std::{
fs::{self, create_dir_all, File},
io::Write,
path::{Path, PathBuf},
};
use tempfile::tempdir;
use tracing::trace;
use xor_name::XorName;
pub const BATCH_SIZE: usize = 16;
#[derive(Clone)]
pub struct FilesApi {
pub(crate) client: Client,
pub(crate) wallet_dir: PathBuf,
}
type ChunkFileResult = Result<(ChunkAddress, Chunk, u64, Vec<(XorName, PathBuf)>)>;
impl FilesApi {
pub fn new(client: Client, wallet_dir: PathBuf) -> Self {
Self { client, wallet_dir }
}
pub fn build(client: Client, wallet_dir: PathBuf) -> Result<FilesApi> {
let wallet = load_account_wallet_or_create_with_mnemonic(&wallet_dir, None)?;
if wallet.balance().is_zero() {
Err(Error::AmountIsZero)
} else {
Ok(FilesApi::new(client, wallet_dir))
}
}
pub fn client(&self) -> &Client {
&self.client
}
pub fn wallet(&self) -> Result<WalletClient> {
let path = self.wallet_dir.as_path();
let wallet = load_account_wallet_or_create_with_mnemonic(path, None)?;
Ok(WalletClient::new(self.client.clone(), wallet))
}
pub fn chunk_file(
file_path: &Path,
chunk_dir: &Path,
include_data_map_in_chunks: bool,
) -> ChunkFileResult {
let file = File::open(file_path)?;
let metadata = file.metadata()?;
let file_size = metadata.len();
let (head_address, data_map_chunk, mut chunks_paths) =
if file_size < MIN_ENCRYPTABLE_BYTES as u64 {
Err(ChunksError::FileTooSmall)?
} else {
let (data_map_chunk, chunks) = encrypt_large(file_path, chunk_dir)?;
(*data_map_chunk.name(), data_map_chunk, chunks)
};
debug!("include_data_map_in_chunks {include_data_map_in_chunks:?}");
if include_data_map_in_chunks {
info!("Data_map_chunk to be written!");
let data_map_path = chunk_dir.join(hex::encode(*data_map_chunk.name()));
trace!("Data_map_chunk being written to {data_map_path:?}");
let mut output_file = File::create(data_map_path.clone())?;
output_file.write_all(&data_map_chunk.value)?;
chunks_paths.push((*data_map_chunk.name(), data_map_path))
}
Ok((
ChunkAddress::new(head_address),
data_map_chunk,
file_size,
chunks_paths,
))
}
pub async fn get_local_payment_and_upload_chunk(
&self,
chunk: Chunk,
verify_store: bool,
retry_strategy: Option<RetryStrategy>,
) -> Result<()> {
let chunk_addr = chunk.network_address();
trace!("Client upload started for chunk: {chunk_addr:?}");
let wallet_client = self.wallet()?;
let (payment, payee) = wallet_client.get_recent_payment_for_addr(&chunk_addr)?;
debug!("Payments for chunk: {chunk_addr:?} to {payee:?}: {payment:?}");
self.client
.store_chunk(chunk, payee, payment, verify_store, retry_strategy)
.await?;
wallet_client.remove_payment_for_addr(&chunk_addr)?;
trace!("Client upload completed for chunk: {chunk_addr:?}");
Ok(())
}
pub async fn pay_for_chunks(&self, chunks: Vec<XorName>) -> Result<StoragePaymentResult> {
let mut wallet_client = self.wallet()?;
info!("Paying for and uploading {:?} chunks", chunks.len());
let res = wallet_client
.pay_for_storage(
chunks
.iter()
.map(|name| NetworkAddress::ChunkAddress(ChunkAddress::new(*name))),
)
.await?;
wallet_client.store_local_wallet()?;
Ok(res)
}
pub async fn upload_test_bytes(&self, bytes: Bytes, verify: bool) -> Result<NetworkAddress> {
let temp_dir = tempdir()?;
let file_path = temp_dir.path().join("tempfile");
let mut file = File::create(&file_path)?;
file.write_all(&bytes)?;
let chunk_path = temp_dir.path().join("chunk_path");
create_dir_all(chunk_path.clone())?;
let (head_address, _data_map, _file_size, chunks_paths) =
Self::chunk_file(&file_path, &chunk_path, true)?;
for (_chunk_name, chunk_path) in chunks_paths {
let chunk = Chunk::new(Bytes::from(fs::read(chunk_path)?));
self.get_local_payment_and_upload_chunk(chunk, verify, None)
.await?;
}
Ok(NetworkAddress::ChunkAddress(head_address))
}
}
fn encrypt_large(file_path: &Path, output_dir: &Path) -> Result<(Chunk, Vec<(XorName, PathBuf)>)> {
Ok(crate::chunks::encrypt_large(file_path, output_dir)?)
}