diff --git a/nextgraph/src/local_broker.rs b/nextgraph/src/local_broker.rs index 8203c7d..5c6bd07 100644 --- a/nextgraph/src/local_broker.rs +++ b/nextgraph/src/local_broker.rs @@ -1313,9 +1313,13 @@ impl LocalBroker { // key // ); - let site = opened_wallet.wallet.site(&user_id)?; - let core = site.cores[0]; //TODO: cycle the other cores if failure to connect (failover) - let brokers = opened_wallet.wallet.broker(core.0)?; + let locator = if let Ok(site) = opened_wallet.wallet.site(&user_id) { + let core = site.cores[0]; //TODO: cycle the other cores if failure to connect (failover) + let brokers = opened_wallet.wallet.broker(core.0)?; + BrokerInfoV0::vec_into_locator(brokers) + } else { + Locator::empty() + }; key_material.zeroize(); let mut verifier = Verifier::new( @@ -1330,7 +1334,7 @@ impl LocalBroker { private_store_id: credentials.2, protected_store_id: credentials.3, public_store_id: credentials.4, - locator: BrokerInfoV0::vec_into_locator(brokers), + locator, }, block_storage, )?; diff --git a/ng-app/src/lib/Document.svelte b/ng-app/src/lib/Document.svelte index f3e93f5..939124d 100644 --- a/ng-app/src/lib/Document.svelte +++ b/ng-app/src/lib/Document.svelte @@ -152,7 +152,7 @@ {:then app} -
+
{/await} diff --git a/ng-app/src/lib/Login.svelte b/ng-app/src/lib/Login.svelte index 7411aee..6718e0a 100644 --- a/ng-app/src/lib/Login.svelte +++ b/ng-app/src/lib/Login.svelte @@ -258,7 +258,7 @@ } } catch (e) { console.error(e); - if (e.message.includes("constructor") || (typeof e === "string" && e.includes("constructor") )) e = "BrowserTooOld"; + if (e.message && e.message.includes("constructor") || (typeof e === "string" && e.includes("constructor") )) e = "BrowserTooOld"; error = e; step = "end"; dispatch("error", { error: e }); diff --git a/ng-app/src/lib/panes/History.svelte b/ng-app/src/lib/panes/History.svelte index 75dfa7c..e7d5332 100644 --- a/ng-app/src/lib/panes/History.svelte +++ b/ng-app/src/lib/panes/History.svelte @@ -107,7 +107,7 @@ } }); get(branch).history.start(); - },1); + },100); }); onDestroy( ()=>{ @@ -140,7 +140,7 @@
- + {#each history as commit}
openCommit(commit[0])} on:keypress={()=>openCommit(commit[0])}> diff --git a/ng-app/src/lib/popups/Signature.svelte b/ng-app/src/lib/popups/Signature.svelte index 9916729..e0e7178 100644 --- a/ng-app/src/lib/popups/Signature.svelte +++ b/ng-app/src/lib/popups/Signature.svelte @@ -91,9 +91,8 @@
- Signature - - Current heads : + {$t("doc.signature.title")} + {$t("current_heads")} : {#each heads as head} {#if head[1]}
{$t("doc.sign_snapshot")} - or click on one of the signed heads to get its link. + {$t("doc.signature.or_click_on_head")} {:else if can_sign}
diff --git a/ng-app/src/locales/en.json b/ng-app/src/locales/en.json index a474cac..754f8dd 100644 --- a/ng-app/src/locales/en.json +++ b/ng-app/src/locales/en.json @@ -74,6 +74,14 @@ "chat": "Chat" } }, + "signature": { + "title": "Signature", + "current_heads": "Current heads", + "or_click_on_head": "or click on one of the signed heads to get its link.", + "here_is_the_link": "Here is its link that you can share.", + "cli_warning": "For now the link is only usable with the CLI, by running the following command", + "signed_snap_at_head": "A signed snapshot is currently at the head." + }, "file": { "download": "Download", "upload_progress": "Uploading...", @@ -475,7 +483,7 @@ "download_wallet_done": "Your wallet file has been downloaded into your \"Downloads\" folder, with the name
{download_name}
Please move it to a safe and durable place.
", "download_pdf_description": "Please download your Recovery PDF, print it, and delete it.", "download_pdf": "Download my PDF", - "download_pdf_done": "Your Recovery PDF file has been downloaded into your \"Downloads\" folder, with the name
{pdf_name}
Please print it and then delete it.
", + "download_pdf_done": "Your Recovery PDF file has been downloaded into your \"Downloads\" folder,
with the name {pdf_name}

Please print it and then delete it.

", "your_pazzle": "Here below is your Pazzle.
The order of each image is important!", "your_mnemonic": "And here is your mnemonic (your alternative passphrase):", "unlock_tips_1": "You can use both the pazzle or the mnemonic to unlock your wallet. The pazzle is easier to remember. The mnemonic is useful in some special cases. We recommend that you use the pazzle. Copy both on a piece of paper. You should try to memorize the pazzle. Once you did, you won't need the paper anymore.", diff --git a/ng-app/src/routes/WalletLogin.svelte b/ng-app/src/routes/WalletLogin.svelte index 70ee83d..48c77c5 100644 --- a/ng-app/src/routes/WalletLogin.svelte +++ b/ng-app/src/routes/WalletLogin.svelte @@ -73,6 +73,8 @@ }); active_wallet_unsub = active_wallet.subscribe(async (value) => { if (value && value.wallet) { + step = "loggedin"; + await tick(); if (!$active_session) { try { let session = await ng.session_start( @@ -85,6 +87,7 @@ loggedin(); } } catch (e) { + step = "open"; error = e; importing = false; wallet = undefined; @@ -105,7 +108,7 @@ }); - function loggedin() { + async function loggedin() { step = "loggedin"; if ($redirect_after_login) { let redir=$redirect_after_login; diff --git a/ng-net/src/actors/client/pin_repo.rs b/ng-net/src/actors/client/pin_repo.rs index 4f09e33..cc2aed1 100644 --- a/ng-net/src/actors/client/pin_repo.rs +++ b/ng-net/src/actors/client/pin_repo.rs @@ -27,6 +27,35 @@ impl PinRepo { pub fn get_actor(&self, id: i64) -> Box { Actor::::new_responder(id) } + pub fn for_branch(repo: &Repo, branch: &BranchId, broker_id: &DirectPeerId) -> PinRepo { + let overlay = OverlayAccess::new_write_access_from_store(&repo.store); + let mut rw_topics = Vec::with_capacity(1); + let mut ro_topics = vec![]; + let branch = repo.branches.get(branch).unwrap(); + + if let Some(privkey) = &branch.topic_priv_key { + rw_topics.push(PublisherAdvert::new( + branch.topic.unwrap(), + privkey.clone(), + *broker_id, + )); + } else { + ro_topics.push(branch.topic.unwrap()); + } + + PinRepo::V0(PinRepoV0 { + hash: repo.id.into(), + overlay, + // TODO: overlay_root_topic + overlay_root_topic: None, + expose_outer: false, + peers: vec![], + max_peer_count: 0, + //allowed_peers: vec![], + ro_topics, + rw_topics, + }) + } pub fn from_repo(repo: &Repo, broker_id: &DirectPeerId) -> PinRepo { let overlay = OverlayAccess::new_write_access_from_store(&repo.store); let mut rw_topics = Vec::with_capacity(repo.branches.len()); diff --git a/ng-net/src/app_protocol.rs b/ng-net/src/app_protocol.rs index 89ca27d..c5b147f 100644 --- a/ng-net/src/app_protocol.rs +++ b/ng-net/src/app_protocol.rs @@ -41,7 +41,7 @@ lazy_static! { Regex::new(r"^did:ng:o:([A-Za-z0-9-_]*):v:([A-Za-z0-9-_]*):a:([A-Za-z0-9-_%]*)$").unwrap(); //TODO: allow international chars. disallow digit as first char #[doc(hidden)] static ref RE_OBJECTS: Regex = - Regex::new(r"^did:ng(?::o:([A-Za-z0-9-_]{44}))?:v:([A-Za-z0-9-_]{44})((?::c:[A-Za-z0-9-_]{44}:k:[A-Za-z0-9-_]{44})+)(?::s:([A-Za-z0-9-_]{44}):k:([A-Za-z0-9-_]{44}))?:l:([A-Za-z0-9-_]*)$").unwrap(); + Regex::new(r"^did:ng(?::o:([A-Za-z0-9-_]{44}))?:v:([A-Za-z0-9-_]{44})((?::[cj]:[A-Za-z0-9-_]{44}:k:[A-Za-z0-9-_]{44})+)(?::s:([A-Za-z0-9-_]{44}):k:([A-Za-z0-9-_]{44}))?:l:([A-Za-z0-9-_]*)$").unwrap(); #[doc(hidden)] static ref RE_OBJECT_READ_CAPS: Regex = Regex::new(r":[cj]:([A-Za-z0-9-_]{44}):k:([A-Za-z0-9-_]{44})").unwrap(); diff --git a/ng-repo/src/branch.rs b/ng-repo/src/branch.rs index 0c0bd13..94b1eff 100644 --- a/ng-repo/src/branch.rs +++ b/ng-repo/src/branch.rs @@ -196,14 +196,21 @@ impl Branch { // check if this commit object is present in theirs or has already been visited in the current walk // load deps, stop at the root(including it in visited) or if this is a commit object from known_heads - let found_in_filter = if let Some(filter) = theirs_filter { - let hash = id.get_hash(); - filter.contains_hash(hash) - } else { - false - }; + let mut found_in_theirs = theirs.contains(&id); + if !found_in_theirs { + found_in_theirs = if let Some(filter) = theirs_filter { + let hash = id.get_hash(); + filter.contains_hash(hash) + } else { + false + }; + } - if !found_in_filter && !theirs.contains(&id) { + if found_in_theirs { + if theirs_found.is_some() { + theirs_found.as_mut().unwrap().insert(id); + } + } else { if let Some(past) = visited.get_mut(&id) { // we update the future if let Some(f) = future { @@ -238,8 +245,6 @@ impl Branch { // } // } } - } else if theirs_found.is_some() { - theirs_found.as_mut().unwrap().insert(id); } } Err(ObjectParseError::MissingBlocks(blocks)) => { diff --git a/ng-repo/src/file.rs b/ng-repo/src/file.rs index a15e55c..adc7457 100644 --- a/ng-repo/src/file.rs +++ b/ng-repo/src/file.rs @@ -1498,7 +1498,7 @@ mod test { ) .expect("open"); - // this only works because we chose a big block size (1MB) so the small JPG file files in one block. + // this only works because we chose a big block size (1MB) so the small JPG file fits in one block. // if not, we would have to call read repeatedly and append the results into a buffer, in order to get the full file let res = file.read(0, len).expect("read all"); diff --git a/ng-repo/src/types.rs b/ng-repo/src/types.rs index de6793f..44539c5 100644 --- a/ng-repo/src/types.rs +++ b/ng-repo/src/types.rs @@ -83,6 +83,32 @@ impl Digest { } hasher.finish() } + + pub fn print_all(all: &[Digest]) -> String { + all.iter() + .map(|d| d.to_string()) + .collect::>() + .join(" ") + } + + pub fn print_iter(all: impl Iterator) -> String { + all.map(|d| d.to_string()) + .collect::>() + .join(" ") + } + + pub fn print_iter_ref<'a>(all: impl Iterator) -> String { + all.map(|d| d.to_string()) + .collect::>() + .join(" ") + } + + pub fn print_all_ref(all: &[&Digest]) -> String { + all.into_iter() + .map(|d| d.to_string()) + .collect::>() + .join(" ") + } } impl fmt::Display for Digest { diff --git a/ng-verifier/src/verifier.rs b/ng-verifier/src/verifier.rs index 2f53d85..41ebb08 100644 --- a/ng-verifier/src/verifier.rs +++ b/ng-verifier/src/verifier.rs @@ -154,6 +154,11 @@ impl Verifier { self.config.public_store_id.as_ref().unwrap() } + pub fn update_locator(&mut self, locator: Locator) { + self.outer = NuriV0::locator(&locator); + self.config.locator = locator; + } + pub async fn close(&self) { log_debug!("VERIFIER CLOSED {}", self.user_id()); BROKER @@ -301,7 +306,7 @@ impl Verifier { branch_id: BranchId, store_repo: StoreRepo, ) -> Result<(Receiver, CancelFn), VerifierError> { - //log_info!("#### create_branch_subscription {}", branch); + //log_info!("#### create_branch_subscription {}", branch_id); let (tx, rx) = mpsc::unbounded::(); //log_info!("SUBSCRIBE"); if let Some(returned) = self.branch_subscriptions.insert(branch_id, tx.clone()) { @@ -1181,9 +1186,15 @@ impl Verifier { let user = self.user_id().clone(); let broker = BROKER.read().await; - log_debug!("looping on branches {:?}", branches); + // log_debug!( + // "looping on branches {:?}", + // branches + // .iter() + // .map(|(_, b, _)| b.to_string()) + // .collect::>() + // ); for (repo, branch, publisher) in branches { - log_debug!("open_branch_ repo {} branch {}", repo, branch); + //log_debug!("open_branch_ repo {} branch {}", repo, branch); let _e = self .open_branch_( &repo, @@ -1195,12 +1206,12 @@ impl Verifier { false, ) .await; - log_debug!( - "END OF open_branch_ repo {} branch {} with {:?}", - repo, - branch, - _e - ); + // log_debug!( + // "END OF open_branch_ repo {} branch {} with {:?}", + // repo, + // branch, + // _e + // ); // discarding error. } Ok(()) @@ -1296,7 +1307,11 @@ impl Verifier { } } }; - //log_info!("need_open {} need_sub {}", need_open, need_sub); + // log_info!( + // "OPEN BRANCH {branch} need_open {} need_sub {}", + // need_open, + // need_sub + // ); let remote = remote_broker.into(); @@ -1316,7 +1331,9 @@ impl Verifier { let (pin_req, topic_id) = { let repo = self.repos.get(repo_id).ok_or(NgError::RepoNotFound)?; let topic_id = repo.branch(branch).unwrap().topic.unwrap(); - //TODO: only pin the requested branch. + // TODO only pinning the requested branch. + // let pin_req = + // PinRepo::for_branch(repo, branch, remote_broker.broker_peer_id()); let pin_req = PinRepo::from_repo(repo, remote_broker.broker_peer_id()); (pin_req, topic_id) }; @@ -1330,19 +1347,25 @@ impl Verifier { //TODO: check that in the returned opened_repo, the branch we are interested in has effectively been subscribed as publisher by the broker. for topic in opened { - if topic.topic_id() == &topic_id { - self.do_sync_req_if_needed( - broker, - user, - &remote, - branch, - repo_id, - topic.known_heads(), - topic.commits_nbr(), - ) - .await?; - break; - } + let (_, branch_id) = self + .topics + .get(&(overlay, *topic.topic_id())) + .ok_or(NgError::TopicNotFound)? + .to_owned(); + + //if topic.topic_id() == &topic_id { + self.do_sync_req_if_needed( + broker, + user, + &remote, + &branch_id, + repo_id, + topic.known_heads(), + topic.commits_nbr(), + ) + .await?; + //break; + //} } } Ok(_) => return Err(NgError::InvalidResponse), @@ -1408,7 +1431,6 @@ impl Verifier { Ok(SoS::Single(sub)) => { let repo = self.repos.get_mut(&repo_id).ok_or(NgError::RepoNotFound)?; Self::branch_was_opened(&self.topics, repo, &sub)?; - self.do_sync_req_if_needed( broker, user, @@ -1706,7 +1728,12 @@ impl Verifier { remote_commits_nbr: u64, ) -> Result<(), NgError> { let (store, msg, branch_secret) = { - //log_info!("do_sync_req_if_needed for branch {}", branch_id); + // log_info!( + // "do_sync_req_if_needed for branch {} {} {}", + // branch_id, + // remote_commits_nbr, + // Digest::print_all(remote_heads) + // ); if remote_commits_nbr == 0 || remote_heads.is_empty() { log_debug!("branch is new on the broker. doing nothing"); return Ok(()); @@ -1726,7 +1753,11 @@ impl Verifier { && theirs.difference(&ours_set).count() == 0 { // no need to sync - log_info!("branch {} is up to date", branch_id); + log_debug!( + "branch {} is up to date at heads {}", + branch_id, + Digest::print_iter(ours) + ); return Ok(()); } @@ -1736,6 +1767,11 @@ impl Verifier { let mut recursor: Vec<(ObjectId, Option)> = ours_set.iter().map(|h| (h.clone(), None)).collect(); + // log_debug!( + // "SEARCHING FOR THEIR HEADS from OURS {}", + // Digest::print_iter(ours) + // ); + let _ = Branch::load_causal_past( &mut recursor, &repo.store, @@ -1745,6 +1781,12 @@ impl Verifier { &mut Some(&mut theirs_found), &None, ); + + // log_debug!( + // "FOUND THEIR HEADS {}", + // Digest::print_iter_ref(theirs_found.iter()) + // ); + // for our in ours_set.iter() { // //log_info!("OUR HEADS {}", our); // if let Ok(cobj) = Object::load(*our, None, &repo.store) { @@ -1756,6 +1798,7 @@ impl Verifier { theirs.difference(&theirs_found).cloned().collect(); let known_commits = if theirs_not_found.is_empty() { + //log_debug!("local heads are newer than remote"); return Ok(()); } else { if visited.is_empty() { @@ -1892,10 +1935,13 @@ impl Verifier { ) .await?; + let repo = self.get_repo_mut(&repo_id, store.get_store_repo())?; + // for (b, _) in repo.branches.iter() { + // let _ = repo.opened_branches.insert(b.clone(), true); + // } // adding the Store branch to the opened_branches // TODO: only do it if the Store is 3P. if let Some(store_branch_id) = store_branch { - let repo = self.get_repo_mut(&repo_id, store.get_store_repo())?; let _ = repo.opened_branches.insert(store_branch_id, true); } diff --git a/ng-wallet/src/lib.rs b/ng-wallet/src/lib.rs index 2f25d11..470ee0e 100644 --- a/ng-wallet/src/lib.rs +++ b/ng-wallet/src/lib.rs @@ -25,6 +25,7 @@ use aes_gcm_siv::{ use argon2::{Algorithm, Argon2, AssociatedData, ParamsBuilder, Version}; use chacha20poly1305::XChaCha20Poly1305; use image::{imageops::FilterType, io::Reader as ImageReader, ImageOutputFormat}; +use ng_net::types::Locator; use rand::distributions::{Distribution, Uniform}; use rand::prelude::*; use safe_transmute::transmute_to_bytes; @@ -650,8 +651,10 @@ pub async fn create_wallet_second_step_v0( if let Some(additional) = ¶ms.additional_bootstrap { params.core_bootstrap.merge(additional); } - + let mut locator = Locator::empty(); for server in ¶ms.core_bootstrap.servers { + locator.add(server.clone()); + wallet_log.add(WalletOperation::AddBrokerServerV0(server.clone())); wallet_log.add(WalletOperation::AddSiteBootstrapV0((user, server.peer_id))); site.bootstraps.push(server.peer_id); @@ -666,6 +669,7 @@ pub async fn create_wallet_second_step_v0( } list.unwrap().push(broker); } + verifier.update_locator(locator); let mut master_key = [0u8; 32]; getrandom::getrandom(&mut master_key).map_err(|_e| NgWalletError::InternalError)?; diff --git a/ngaccount/web/tailwind.config.cjs b/ngaccount/web/tailwind.config.cjs index e338bf8..957a2a5 100644 --- a/ngaccount/web/tailwind.config.cjs +++ b/ngaccount/web/tailwind.config.cjs @@ -17,7 +17,7 @@ const config = { plugins: [ require('flowbite/plugin') ], - darkMode: 'class', + darkMode: 'selector', }; module.exports = config; diff --git a/ngcli/src/main.rs b/ngcli/src/main.rs index 815d6f4..bbac257 100644 --- a/ngcli/src/main.rs +++ b/ngcli/src/main.rs @@ -14,16 +14,19 @@ use std::collections::HashSet; use std::error::Error; use std::fs::{read_to_string, write}; use std::net::IpAddr; +use std::path::Path; use std::path::PathBuf; use std::str::FromStr; +use std::sync::Arc; -use clap::{arg, command, value_parser, Command}; +use clap::{arg, command, value_parser, ArgMatches, Command}; use duration_str::parse; use serde::{Deserialize, Serialize}; use serde_json::{from_str, to_string_pretty}; use zeroize::Zeroize; use ng_repo::errors::*; +use ng_repo::file::{FileError, RandomAccessFile, ReadFile}; use ng_repo::log::*; use ng_repo::object::Object; use ng_repo::store::Store; @@ -76,6 +79,7 @@ pub enum NgcliError { OtherConfigError(String), OtherConfigErrorStr(&'static str), CannotSaveConfig(String), + FileError(FileError), } impl Error for NgcliError {} @@ -97,6 +101,12 @@ impl From for NgcliError { } } +impl From for NgcliError { + fn from(err: FileError) -> NgcliError { + Self::FileError(err) + } +} + impl From for NgcliError { fn from(err: ProtocolError) -> NgcliError { Self::ProtocolError(err) @@ -154,6 +164,73 @@ async fn main() -> std::io::Result<()> { } Ok(()) } + +fn get_random_access_file( + file_meta: RandomAccessFileMeta, + reference: ObjectRef, + mut filename: Option, + sub_matches: &ArgMatches, + store: &Arc, +) -> Result<(), NgcliError> { + println!("File content_type {}", file_meta.content_type()); + println!("File size {}", file_meta.total_size()); + let file = RandomAccessFile::open(reference.id, reference.key, Arc::clone(&store))?; + let filename_opt = sub_matches.get_one::("output"); + let save = sub_matches.get_flag("save") || filename_opt.is_some(); + if save { + if filename.is_none() && filename_opt.is_some() { + filename = Some(filename_opt.unwrap().clone()); + } + if let Some(filename) = filename { + let total = file_meta.total_size() as usize; + let mut file_content = Vec::with_capacity(total); + let mut pos = 0; + loop { + let mut res = file.read(pos, 1024 * 1024 * 1024)?; + pos += res.len(); + file_content.append(&mut res); + if pos >= total { + break; + } + } + + let mut i: usize = 0; + let mut dest_filename; + loop { + dest_filename = if i == 0 { + filename.clone() + } else { + filename + .rsplit_once(".") + .map(|(l, r)| format!("{l} ({}).{r}", i.to_string())) + .or_else(|| Some(format!("{filename} ({})", i.to_string()))) + .unwrap() + }; + + let path = Path::new(&dest_filename); + + if path.exists() { + i = i + 1; + } else { + write(path, &file_content)?; + break; + } + } + println!( + "The file has been saved in the current directory with the filename: {}", + dest_filename + ); + } else { + println!("The file doesn't have a name and you didn't set the argument -o or --output , so we cannot save it."); + } + } else { + println!( + "You didn't set the argument -s or --save or -o or --output so we are not downloading the file locally" + ); + } + Ok(()) +} + async fn main_inner() -> Result<(), NgcliError> { let matches = command!() .arg(arg!( @@ -237,6 +314,8 @@ async fn main_inner() -> Result<(), NgcliError> { Command::new("get") .about("fetches one or several commits, or a binary object, with an optional signature, from a broker, using the Ext Protocol, and connecting on the Outer Overlay. The request is anonymous and doesn't need any authentication") .arg(arg!([NURI] "NextGraph URI of the commit(s) or object, containing the ReadCap in the form :c:k :j:k and optionally :s:k and the usual :o:v:l").required(true)) + .arg(arg!(-s --save "Saves the binary file(s) of the commits that have the type AddFile").required(false)) + .arg(arg!(-o --output "Gives a filename for the binary file(s) to be saved locally. only used if no filename is present in metadata").required(false)) ) .get_matches(); @@ -377,13 +456,41 @@ async fn main_inner() -> Result<(), NgcliError> { let mut signature = None; + let arc_store = Arc::new(store); + for obj_ref in nuri.objects.into_iter() { + match Object::load(obj_ref.id, Some(obj_ref.key.clone()), &arc_store) { + Err(e) => println!("Error: {:?}", e), + Ok(o) => match o.content_v0() { + Err(e) => println!("Error: {:?}", e), + Ok(ObjectContentV0::Commit(c)) => { + next_round.push((c.body_ref().clone(), Some(obj_ref.id), c.files())); + } + Ok(ObjectContentV0::RandomAccessFileMeta(file_meta)) => { + if let Err(e) = get_random_access_file( + file_meta, + obj_ref, + None, + sub_matches, + &arc_store, + ) { + println!("An error occurred: {:?}", e); + } + } + _ => println!("unsupported format"), + }, + } + } + + let store = Arc::try_unwrap(arc_store) + .map_err(|_| NgcliError::NgError(NgError::InternalError))?; + if let Some(sign_ref) = nuri.signature { if let Ok(o) = Object::load(sign_ref.id, Some(sign_ref.key), &store) { match o.content_v0() { Ok(ObjectContentV0::Signature(Signature::V0(v0))) => { let in_sig = HashSet::from_iter(v0.content.commits().iter().cloned()); if in_nuri.is_subset(&in_sig) { - next_round.push((v0.certificate_ref.clone(), None)); + next_round.push((v0.certificate_ref.clone(), None, vec![])); signature = Some(v0); } else { println!("Signature is invalid"); @@ -394,20 +501,9 @@ async fn main_inner() -> Result<(), NgcliError> { } } } - - nuri.objects.into_iter().for_each(|obj_ref| { - match Object::load(obj_ref.id, Some(obj_ref.key), &store) { - Err(e) => println!("Error: {:?}", e), - Ok(o) => match o.content_v0() { - Err(e) => println!("Error: {:?}", e), - Ok(ObjectContentV0::Commit(c)) => { - next_round.push((c.body_ref().clone(), Some(obj_ref.id))); - } - _ => println!("unsupported format"), - }, - } - }); - + if next_round.is_empty() { + return Ok(()); + } let blocks: Vec = Broker::ext( Box::new(ConnectionWebSocket {}), peer_privk.clone(), @@ -418,7 +514,7 @@ async fn main_inner() -> Result<(), NgcliError> { overlay: overlay_id, ids: next_round .iter() - .map(|(o, _)| o.id) + .map(|(o, _, _)| o.id) .collect::>(), include_files: true, }, @@ -428,7 +524,7 @@ async fn main_inner() -> Result<(), NgcliError> { let mut third_round = Vec::with_capacity(2); let mut certificate = None; - for (body_ref, commit_id) in next_round.into_iter() { + for (body_ref, commit_id, mut files) in next_round.into_iter() { match Object::load(body_ref.id, Some(body_ref.key), &store) { Err(e) => println!("Error: {:?}", e), Ok(o) => match o.content_v0() { @@ -437,16 +533,34 @@ async fn main_inner() -> Result<(), NgcliError> { CommitBodyV0::Snapshot(Snapshot::V0(snap)), ))) => { println!("Snapshot: {}", commit_id.unwrap()); - third_round.push(snap.content); + third_round.push((snap.content, None)); + } + Ok(ObjectContentV0::CommitBody(CommitBody::V0(CommitBodyV0::AddFile( + AddFile::V0(add_file), + )))) => { + println!( + "File added: {}", + add_file.name.as_deref().unwrap_or("(no name)") + ); + if files.len() != 1 { + println!("Error: invalid AddFile commit"); + } + third_round.push((files.pop().unwrap(), add_file.name)); } Ok(ObjectContentV0::CommitBody(CommitBody::V0( - CommitBodyV0::AsyncTransaction(_t), + CommitBodyV0::AsyncTransaction(t), + ))) + | Ok(ObjectContentV0::CommitBody(CommitBody::V0( + CommitBodyV0::SyncTransaction(t), ))) => { println!("Transaction: {}", commit_id.unwrap()); + if t.body_type() == 0 || t.body_type() == 2 { + //TODO display ADDs and REMOVEs + } } Ok(ObjectContentV0::Certificate(Certificate::V0(certif))) => { if commit_id.is_none() && signature.is_some() { - third_round.push(certif.content.previous.clone()); + third_round.push((certif.content.previous.clone(), None)); certificate = Some(certif); } } @@ -454,7 +568,9 @@ async fn main_inner() -> Result<(), NgcliError> { }, } } - + if third_round.is_empty() { + return Ok(()); + } let blocks: Vec = Broker::ext( Box::new(ConnectionWebSocket {}), peer_privk, @@ -463,15 +579,18 @@ async fn main_inner() -> Result<(), NgcliError> { broker_server.get_ws_url(&None).await.unwrap().0, // for now we are only connecting to NextGraph SaaS cloud (nextgraph.eu) so it is safe. ExtObjectGetV0 { overlay: overlay_id, - ids: third_round.iter().map(|o| o.id).collect::>(), + ids: third_round + .iter() + .map(|(o, _)| o.id) + .collect::>(), include_files: true, }, ) .await?; blocks.into_iter().for_each(|b| _ = store.put(&b)); - - for third_ref in third_round.into_iter() { - match Object::load(third_ref.id, Some(third_ref.key), &store) { + let store = Arc::new(store); + for (third_ref, filename) in third_round.into_iter() { + match Object::load(third_ref.id, Some(third_ref.key.clone()), &store) { Err(e) => println!("Error: {:?}", e), Ok(o) => match o.content_v0() { Err(e) => println!("Error: {:?}", e), @@ -479,6 +598,15 @@ async fn main_inner() -> Result<(), NgcliError> { Err(e) => println!("Error: {:?}", e), Ok(s) => println!("Here is the snapshot data :\n\r{}", s), }, + Ok(ObjectContentV0::RandomAccessFileMeta(file_meta)) => { + get_random_access_file( + file_meta, + third_ref, + filename, + sub_matches, + &store, + )?; + } Ok(ObjectContentV0::CommitBody(CommitBody::V0( CommitBodyV0::Repository(repo), ))) => {