Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wp/delete file blake3 hash http route #24

Closed
wants to merge 22 commits into from
Closed
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
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
127 changes: 78 additions & 49 deletions src/backend/fs.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#![allow(unused_variables)]

use std::{
fs::OpenOptions,
fs::{self, OpenOptions},
io::{Read, Write},
path::PathBuf,
};
Expand All @@ -22,6 +22,21 @@ pub async fn write_file(pk: Secp256k1PubKey, file_bytes: &[u8]) -> Result<Blake3
let file_hash = Blake3Hash(blake3::keyed_hash(&x_only_pk.serialize(), file_bytes));

trace!("TODO: Check if file catalog already exists");
let path = SYS_CFG
.volumes
.get(0)
.expect("First volume present")
.path
.join(CATALOG_DIR)
.join(file_hash.to_string());

trace!("Read catalog at {}", path.to_string_lossy());
let is_catalog = path.to_string_lossy().len();
//let is_catalog = std::path::Path::new(&path).exists();

if is_catalog > 0 {
return Err(anyhow!("This file already exists for this user."));
}

trace!("Segment files");
let segments_iter = file_bytes.par_chunks_exact(SEGMENT_SIZE);
Expand All @@ -39,53 +54,53 @@ pub async fn write_file(pk: Secp256k1PubKey, file_bytes: &[u8]) -> Result<Blake3
trace!("Get eight storage volumes from config");
if SYS_CFG.volumes.len() != 8 {
return Err(anyhow!("Eight volume paths must be configured"));
} else {
trace!("Create a shared secret using ECDH");
let sk = SYS_CFG.private_key;
let ss = SharedSecret::new(&pk.into_inner(), &sk);

trace!("Split each segment out into 8 separate chunks and write each chunk to the storage volume by filename");
let segment_hashes = encoded_segments
.par_iter()
.map(|encoded_segment| {
let Encoded(encoded_bytes, bao_hash, encode_info) = encoded_segment;
trace!("Encoded bytes len: {}", encoded_bytes.len());

let encoded_chunk_size = encode_info.bytes_verifiable / 8;
trace!("Encoded chunk size: {}", encoded_chunk_size);

encoded_bytes
.par_chunks_exact(encoded_chunk_size as usize)
.enumerate()
.map(|(chunk_index, encoded_segment_chunk)| {
let volume = SYS_CFG
.volumes
.get(chunk_index)
.expect("Get one of eight volumes");

write_segment(
&ss.secret_bytes(),
volume.path.join(SEGMENT_DIR),
bao_hash.as_bytes(),
NODE_FORMAT,
encoded_segment_chunk,
chunk_index,
encode_info.output_len,
encode_info.padding_len,
)
})
.collect::<Result<Vec<()>>>()?;

Ok(BaoHash(bao_hash.to_owned()))
})
.collect::<Result<Vec<BaoHash>>>()?;

trace!("Append each hash to its catalog, plus its format");
write_catalog(&file_hash, &segment_hashes)?;

debug!("Finished write_file");
Ok(file_hash)
}

trace!("Create a shared secret using ECDH");
let sk = SYS_CFG.private_key;
let ss = SharedSecret::new(&pk.into_inner(), &sk);

trace!("Split each segment out into 8 separate chunks and write each chunk to the storage volume by filename");
let segment_hashes = encoded_segments
.par_iter()
.map(|encoded_segment| {
let Encoded(encoded_bytes, bao_hash, encode_info) = encoded_segment;
trace!("Encoded bytes len: {}", encoded_bytes.len());

let encoded_chunk_size = encode_info.bytes_verifiable / 8;
trace!("Encoded chunk size: {}", encoded_chunk_size);

encoded_bytes
.par_chunks_exact(encoded_chunk_size as usize)
.enumerate()
.map(|(chunk_index, encoded_segment_chunk)| {
let volume = SYS_CFG
.volumes
.get(chunk_index)
.expect("Get one of eight volumes");

write_segment(
&ss.secret_bytes(),
volume.path.join(SEGMENT_DIR),
bao_hash.as_bytes(),
NODE_FORMAT,
encoded_segment_chunk,
chunk_index,
encode_info.output_len,
encode_info.padding_len,
)
})
.collect::<Result<Vec<()>>>()?;

Ok(BaoHash(bao_hash.to_owned()))
})
.collect::<Result<Vec<BaoHash>>>()?;

trace!("Append each hash to its catalog, plus its format");
write_catalog(&file_hash, &segment_hashes)?;

debug!("Finished write_file");
Ok(file_hash)
}

#[allow(clippy::too_many_arguments)]
Expand Down Expand Up @@ -179,7 +194,7 @@ pub async fn read_file(blake3_hash: &Blake3Hash) -> Result<Vec<u8>> {
.expect("Get first volume")
.path
.join(SEGMENT_DIR)
.join(format!("{}.c{}", segment_hash, NODE_FORMAT));
.join(format!("{segment_hash}.c{NODE_FORMAT}"));

trace!("Open segment at {}", path.to_string_lossy());
let file = OpenOptions::new().read(true).open(path).unwrap();
Expand All @@ -196,7 +211,7 @@ pub async fn read_file(blake3_hash: &Blake3Hash) -> Result<Vec<u8>> {
let path = volume
.path
.join(SEGMENT_DIR)
.join(format!("{}.c{}", segment_hash, NODE_FORMAT));
.join(format!("{segment_hash}.c{NODE_FORMAT}"));

let mut file = OpenOptions::new().read(true).open(path).unwrap();

Expand Down Expand Up @@ -246,3 +261,17 @@ pub fn read_catalog(file_hash: &Blake3Hash) -> Result<Vec<BaoHash>> {

Ok(bao_hashes)
}

pub fn delete_file(file_hash: &Blake3Hash) -> Result<()> {
let path = SYS_CFG
.volumes
.get(0)
.expect("First volume present")
.path
.join(CATALOG_DIR)
.join(file_hash.to_string());

fs::remove_file(path)?;

Ok(())
}
2 changes: 1 addition & 1 deletion src/bin/carbonadod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ async fn main() -> Result<()> {
error!("{}", err);
err.chain()
.skip(1)
.for_each(|cause| eprintln!("Error: {}", cause));
.for_each(|cause| eprintln!("Error: {cause}"));

handle.flush();
process::exit(1);
Expand Down
13 changes: 11 additions & 2 deletions src/frontend/http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ use axum::{
extract::Path,
http::StatusCode,
response::{IntoResponse, Response},
routing::{get, post},
routing::{delete, get, post},
Router,
};
use log::info;
use tower_http::cors::CorsLayer;

use crate::{
backend::fs::{read_file, write_file},
backend::fs::{delete_file, read_file, write_file},
prelude::*,
};

Expand All @@ -32,8 +32,17 @@ async fn get_file(Path(blake3_hash): Path<String>) -> Result<impl IntoResponse,
Ok((StatusCode::OK, file_bytes))
}

#[axum_macros::debug_handler]
async fn remove_file(Path(blake3_hash): Path<String>) -> Result<impl IntoResponse, AppError> {
let blake3_hash = Blake3Hash(blake3::Hash::from_str(&blake3_hash)?);
delete_file(&blake3_hash)?;

Ok(())
}

pub async fn start() -> Result<()> {
let app = Router::new()
.route("/remove/:blake3_hash", delete(remove_file))
.route("/upload/:pk", post(post_file))
.route("/file/:blake3_hash", get(get_file))
// .route("/catalog/:blake3_hash", get(get_catalog))
Expand Down
55 changes: 42 additions & 13 deletions tests/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,37 @@ use secp256k1::generate_keypair;

const RUST_LOG: &str = "carbonad_node=trace,carbonado=trace,file=trace";

// #[tokio::test]
// async fn write_read() -> Result<()> {
// carbonado::utils::init_logging(RUST_LOG);

// let (_sk, pk) = generate_keypair(&mut thread_rng());

// info!("Reading file bytes");
// let file_bytes = fs::read("tests/samples/cat.gif")?;
// debug!("{} bytes read", file_bytes.len());

// info!("Writing file");
// let blake3_hash = write_file(Secp256k1PubKey(pk), &file_bytes).await?;
// debug!("File hash: {blake3_hash}");

// // info!("Reading file by hash");
// // let new_file_bytes = read_file(&blake3_hash).await?;
// // debug!("{} new bytes read", new_file_bytes.len());

// // assert_eq!(
// // file_bytes, new_file_bytes,
// // "Written and read file matches bytes"
// // );

// // info!("File write/read test finished successfully!");

// Ok(())
// }

#[tokio::test]
async fn write_read() -> Result<()> {
// #[should_panic]
async fn check_catalog_exists() -> Result<()> {
carbonado::utils::init_logging(RUST_LOG);

let (_sk, pk) = generate_keypair(&mut thread_rng());
Expand All @@ -18,20 +47,20 @@ async fn write_read() -> Result<()> {
let file_bytes = fs::read("tests/samples/cat.gif")?;
debug!("{} bytes read", file_bytes.len());

info!("Writing file");
let blake3_hash = write_file(Secp256k1PubKey(pk), &file_bytes).await?;
debug!("File hash: {blake3_hash}");

// info!("Reading file by hash");
// let new_file_bytes = read_file(&blake3_hash).await?;
// debug!("{} new bytes read", new_file_bytes.len());
info!("Writing file if not exists");
let blake3_hash = write_file(Secp256k1PubKey(pk), &file_bytes).await.is_err();
debug!("Skip writing file as File hash exists: {blake3_hash}");
assert!(blake3_hash);

// assert_eq!(
// file_bytes, new_file_bytes,
// "Written and read file matches bytes"
// );
Ok(())
}

// info!("File write/read test finished successfully!");
#[tokio::test]
// #[should_panic]
async fn delete_file_by_blake3hash() -> Result<()> {
carbonado::utils::init_logging(RUST_LOG);
// don't generate key_pair but use keypair
let (_sk, _pk) = generate_keypair(&mut thread_rng());

Ok(())
}