From bada48143282a5edbb89fa39e88cf091d53edf45 Mon Sep 17 00:00:00 2001 From: Niko PLP Date: Fri, 5 Apr 2024 13:07:27 +0300 Subject: [PATCH] prepare crate for publishing. with examples. more docs --- Cargo.lock | 88 +++++++++ README.md | 10 +- nextgraph/Cargo.toml | 12 +- nextgraph/README.md | 32 +++- nextgraph/examples/README.md | 15 ++ nextgraph/examples/in_memory.md | 13 ++ nextgraph/examples/in_memory.rs | 133 +++++++++++++ nextgraph/examples/persistent.md | 13 ++ nextgraph/examples/persistent.rs | 28 +++ .../examples/wallet-security-image-demo.png | Bin 0 -> 21006 bytes nextgraph/src/lib.rs | 61 ++++++ nextgraph/src/local_broker.rs | 178 +++++++++++++++--- ng-app/src-tauri/src/lib.rs | 118 ++---------- ng-app/src/App.svelte | 16 +- ng-app/src/api.ts | 26 ++- ng-app/src/routes/User.svelte | 2 +- ng-app/src/routes/WalletCreate.svelte | 2 +- ng-app/src/wallet_emojis.ts | 12 +- ng-broker/README.md | 4 +- ng-broker/src/server_ws.rs | 5 +- ng-client-ws/README.md | 4 +- ng-net/README.md | 4 +- ng-net/src/actor.rs | 2 + ng-net/src/actors/mod.rs | 2 + ng-net/src/broker.rs | 2 + ng-net/src/connection.rs | 6 +- ng-net/src/lib.rs | 7 + ng-net/src/server_storage.rs | 2 + ng-net/src/tests/mod.rs | 1 + ng-net/src/types.rs | 106 ++++++++--- ng-repo/Cargo.toml | 3 + ng-repo/README.md | 4 +- ng-repo/src/block.rs | 2 +- ng-repo/src/commit.rs | 2 +- ng-repo/src/errors.rs | 44 ++++- ng-repo/src/event.rs | 2 +- ng-repo/src/file.rs | 2 +- ng-repo/src/kcv_store.rs | 2 + ng-repo/src/lib.rs | 2 + ng-repo/src/object.rs | 2 +- ng-repo/src/os_info.rs | 56 ++++++ ng-repo/src/repo.rs | 2 +- ng-repo/src/site.rs | 2 + ng-repo/src/store.rs | 2 +- ng-repo/src/types.rs | 79 +++++--- ng-sdk-js/README.md | 4 +- ng-sdk-js/app-react/README.md | 4 +- ng-sdk-js/app-web/README.md | 4 +- ng-sdk-js/js/node.js | 16 +- ng-sdk-js/src/lib.rs | 109 ++--------- ng-stores-rocksdb/README.md | 4 +- ng-verifier/README.md | 4 +- ng-wallet/README.md | 4 +- ng-wallet/src/emojis.rs | 82 +++++++- ng-wallet/src/lib.rs | 100 +++++----- ng-wallet/src/types.rs | 90 +++++++-- ngcli/README.md | 4 +- ngd/README.md | 4 +- ngd/src/cli.rs | 2 +- ngd/src/main.rs | 6 + 60 files changed, 1130 insertions(+), 417 deletions(-) create mode 100644 nextgraph/examples/README.md create mode 100644 nextgraph/examples/in_memory.md create mode 100644 nextgraph/examples/in_memory.rs create mode 100644 nextgraph/examples/persistent.md create mode 100644 nextgraph/examples/persistent.rs create mode 100644 nextgraph/examples/wallet-security-image-demo.png create mode 100644 ng-repo/src/os_info.rs diff --git a/Cargo.lock b/Cargo.lock index 2384823..d623a89 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1220,6 +1220,12 @@ dependencies = [ "rand 0.7.3", ] +[[package]] +name = "current_platform" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a74858bcfe44b22016cb49337d7b6f04618c58e5dbfdef61b06b8c434324a0bc" + [[package]] name = "curve25519-dalek" version = "3.2.0" @@ -3124,12 +3130,14 @@ dependencies = [ "ng-client-ws", "ng-net", "ng-repo", + "ng-stores-rocksdb", "ng-verifier", "ng-wallet", "once_cell", "serde", "serde_bare", "serde_bytes", + "serde_json", "web-time", "zeroize", ] @@ -3239,6 +3247,7 @@ dependencies = [ "base64-url", "blake3", "chacha20", + "current_platform", "curve25519-dalek 3.2.0", "debug_print", "ed25519-dalek", @@ -3249,10 +3258,12 @@ dependencies = [ "hex", "log", "once_cell", + "os_info", "rand 0.7.3", "serde", "serde_bare", "serde_bytes", + "serde_json", "slice_as_array", "threshold_crypto", "time 0.3.23", @@ -3688,6 +3699,17 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "os_info" +version = "3.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae99c7fa6dd38c7cafe1ec085e804f8f555a2f8659b0dbe03f1f9963a9b51092" +dependencies = [ + "log", + "serde", + "windows-sys 0.52.0", +] + [[package]] name = "overload" version = "0.1.1" @@ -6312,6 +6334,15 @@ dependencies = [ "windows-targets 0.48.0", ] +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.4", +] + [[package]] name = "windows-targets" version = "0.42.2" @@ -6342,6 +6373,21 @@ dependencies = [ "windows_x86_64_msvc 0.48.0", ] +[[package]] +name = "windows-targets" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +dependencies = [ + "windows_aarch64_gnullvm 0.52.4", + "windows_aarch64_msvc 0.52.4", + "windows_i686_gnu 0.52.4", + "windows_i686_msvc 0.52.4", + "windows_x86_64_gnu 0.52.4", + "windows_x86_64_gnullvm 0.52.4", + "windows_x86_64_msvc 0.52.4", +] + [[package]] name = "windows-tokens" version = "0.48.0" @@ -6360,6 +6406,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" + [[package]] name = "windows_aarch64_msvc" version = "0.42.2" @@ -6372,6 +6424,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" + [[package]] name = "windows_i686_gnu" version = "0.42.2" @@ -6384,6 +6442,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" +[[package]] +name = "windows_i686_gnu" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" + [[package]] name = "windows_i686_msvc" version = "0.42.2" @@ -6396,6 +6460,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" +[[package]] +name = "windows_i686_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" + [[package]] name = "windows_x86_64_gnu" version = "0.42.2" @@ -6408,6 +6478,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" @@ -6420,6 +6496,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" + [[package]] name = "windows_x86_64_msvc" version = "0.42.2" @@ -6432,6 +6514,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" + [[package]] name = "winnow" version = "0.4.7" diff --git a/README.md b/README.md index c27d2c9..b875354 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,8 @@ [![Apache 2.0 Licensed][license-image]][license-link] [![MIT Licensed][license-image2]][license-link2] [![project chat](https://img.shields.io/badge/zulip-join_chat-brightgreen.svg)](https://forum.nextgraph.org) +![Crates.io Version](https://img.shields.io/crates/v/nextgraph) +![docs.rs](https://img.shields.io/docsrs/nextgraph) Rust implementation of NextGraph @@ -15,9 +17,9 @@ This repository is in active development at [https://git.nextgraph.org/NextGraph ## NextGraph -> NextGraph brings about the convergence between P2P and Semantic Web technologies, towards a decentralized, secure and privacy-preserving cloud, based on CRDTs. +> NextGraph brings about the convergence of P2P and Semantic Web technologies, towards a decentralized, secure and privacy-preserving cloud, based on CRDTs. > -> This open source ecosystem provides solutions for end-users and software developers alike, wishing to use or create **decentralized** apps featuring: **live collaboration** on rich-text documents, peer to peer communication with **end-to-end encryption**, offline-first, **local-first**, portable and interoperable data, total ownership of data and software, security and privacy. Centered on repositories containing **semantic data** (RDF), **rich text**, and structured data formats like **JSON**, synced between peers belonging to permissioned groups of users, it offers strong eventual consistency, thanks to the use of **CRDTs**. Documents can be linked together, signed, shared securely, queried using the **SPARQL** language and organized into sites and containers. +> This open source ecosystem provides solutions for end-users (a platform) and software developers (a framework), wishing to use or create **decentralized** apps featuring: **live collaboration** on rich-text documents, peer to peer communication with **end-to-end encryption**, offline-first, **local-first**, portable and interoperable data, total ownership of data and software, security and privacy. Centered on repositories containing **semantic data** (RDF), **rich text**, and structured data formats like **JSON**, synced between peers belonging to permissioned groups of users, it offers strong eventual consistency, thanks to the use of **CRDTs**. Documents can be linked together, signed, shared securely, queried using the **SPARQL** language and organized into sites and containers. > > More info here [https://nextgraph.org](https://nextgraph.org) @@ -65,11 +67,11 @@ The crates are organized as follow : - [ngd](ngd/README.md) : binary executable of the daemon (that can run a broker, verifier and/or Rust services) - ng-repo : Repositories common library - ng-net : Network common library +- ng-verifier : Verifier library, that exposes the document API to the app +- ng-wallet : keeps the secret keys of all identities of the user in a safe wallet - ng-broker : Core and Server Broker library - ng-client-ws : Websocket client library -- ng-verifier : Verifier library, that exposes the document API to the app - ng-stores-rocksdb : RocksDB backed stores. see also dependency [repo here](https://git.nextgraph.org/NextGraph/rust-rocksdb) -- ng-wallet : keeps the secret keys of all identities of the user in a safe wallet - [ng-sdk-js](ng-sdk-js/README.md) : contains the JS SDK, with example apps: web app, react app, or node service. - [ng-app](ng-app/README.md) : all the native apps, based on Tauri, and the web app. - ngone : server for nextgraph.one. helps user bootstrap into the right app. Not useful to you. Published here for transparency diff --git a/nextgraph/Cargo.toml b/nextgraph/Cargo.toml index f838918..8eecf0b 100644 --- a/nextgraph/Cargo.toml +++ b/nextgraph/Cargo.toml @@ -21,6 +21,7 @@ ng-net = { path = "../ng-net", version = "0.1.0" } ng-wallet = { path = "../ng-wallet", version = "0.1.0" } ng-client-ws = { path = "../ng-client-ws", version = "0.1.0" } ng-verifier = { path = "../ng-verifier", version = "0.1.0" } +ng-stores-rocksdb = { path = "../ng-stores-rocksdb", version = "0.1.0" } async-once-cell = "0.5.3" once_cell = "1.17.1" serde = { version = "1.0", features = ["derive"] } @@ -28,5 +29,14 @@ serde_bare = "0.5.0" serde_bytes = "0.11.7" base64-url = "2.0.0" web-time = "0.2.0" -async-std = { version = "1.12.0", features = ["attributes","unstable"] } +async-std = { version = "1.12.0", features = [ "attributes", "unstable" ] } zeroize = { version = "1.6.0", features = ["zeroize_derive"] } +serde_json = "1.0" + +[[example]] +name = "in_memory" +required-features = [] + +[[example]] +name = "persistent" +required-features = [] \ No newline at end of file diff --git a/nextgraph/README.md b/nextgraph/README.md index e4471ec..c357d02 100644 --- a/nextgraph/README.md +++ b/nextgraph/README.md @@ -7,29 +7,51 @@ ![MSRV][rustc-image] [![Apache 2.0 Licensed][license-image]][license-link] [![MIT Licensed][license-image2]][license-link2] +![Crates.io Version](https://img.shields.io/crates/v/nextgraph) +![docs.rs](https://img.shields.io/docsrs/nextgraph) -Rust client library of NextGraph +Rust client library of NextGraph framework -This repository is in active development at [https://git.nextgraph.org/NextGraph/nextgraph-rs](https://git.nextgraph.org/NextGraph/nextgraph-rs), a Gitea instance. For bug reports, issues, merge requests, and in order to join the dev team, please visit the link above and create an account (you can do so with a github account). The [github repo](https://github.com/nextgraph-org/nextgraph-rs) is just a read-only mirror that does not accept issues. +This library is in active development at [https://git.nextgraph.org/NextGraph/nextgraph-rs](https://git.nextgraph.org/NextGraph/nextgraph-rs), a Gitea instance. For bug reports, issues, merge requests, and in order to join the dev team, please visit the link above and create an account (you can do so with a github account). The [github repo](https://github.com/nextgraph-org/nextgraph-rs) is just a read-only mirror that does not accept issues. ## NextGraph -> NextGraph brings about the convergence between P2P and Semantic Web technologies, towards a decentralized, secure and privacy-preserving cloud, based on CRDTs. +> NextGraph brings about the convergence of P2P and Semantic Web technologies, towards a decentralized, secure and privacy-preserving cloud, based on CRDTs. > -> This open source ecosystem provides solutions for end-users and software developers alike, wishing to use or create **decentralized** apps featuring: **live collaboration** on rich-text documents, peer to peer communication with **end-to-end encryption**, offline-first, **local-first**, portable and interoperable data, total ownership of data and software, security and privacy. Centered on repositories containing **semantic data** (RDF), **rich text**, and structured data formats like **JSON**, synced between peers belonging to permissioned groups of users, it offers strong eventual consistency, thanks to the use of **CRDTs**. Documents can be linked together, signed, shared securely, queried using the **SPARQL** language and organized into sites and containers. +> This open source ecosystem provides solutions for end-users (a platform) and software developers (a framework), wishing to use or create **decentralized** apps featuring: **live collaboration** on rich-text documents, peer to peer communication with **end-to-end encryption**, offline-first, **local-first**, portable and interoperable data, total ownership of data and software, security and privacy. Centered on repositories containing **semantic data** (RDF), **rich text**, and structured data formats like **JSON**, synced between peers belonging to permissioned groups of users, it offers strong eventual consistency, thanks to the use of **CRDTs**. Documents can be linked together, signed, shared securely, queried using the **SPARQL** language and organized into sites and containers. > > More info here [https://nextgraph.org](https://nextgraph.org) ## Support +This crate has official documentation at [docs.rs](https://docs.rs/nextgraph/0.1.0/nextgraph/) + Documentation can be found here [https://docs.nextgraph.org](https://docs.nextgraph.org) And our community forum where you can ask questions is here [https://forum.nextgraph.org](https://forum.nextgraph.org) -## How to use the library +## Status NextGraph is not ready yet. You can subscribe to [our newsletter](https://list.nextgraph.org/subscription/form) to get updates, and support us with a [donation](https://nextgraph.org/donate/). +## Dependencies + +Nextgraph library is dependent on [async-std](https://async.rs/). You must include it in your `Cargo.toml`. +A tokio-based version (as a feature) might be available in the future. + +```toml +[dependencies] +nextgraph = "0.1.0" +async-std = "1.12.0" +``` + +## Examples + +You can find some examples on how to use the library: + +- [in_memory](examples/in_memory) +- [persistent](examples/persistent) + ## License Licensed under either of diff --git a/nextgraph/examples/README.md b/nextgraph/examples/README.md new file mode 100644 index 0000000..23087c5 --- /dev/null +++ b/nextgraph/examples/README.md @@ -0,0 +1,15 @@ +# Examples + +Some examples of using `nextgraph` client library + +run them with: + +``` +cargo run -p nextgraph --example in_memory +cargo run -p nextgraph --example persistent +``` + +See the code: + +- [in_memory](in_memory) +- [persistent](persistent) diff --git a/nextgraph/examples/in_memory.md b/nextgraph/examples/in_memory.md new file mode 100644 index 0000000..900cad6 --- /dev/null +++ b/nextgraph/examples/in_memory.md @@ -0,0 +1,13 @@ +# in-memory LocalBroker + +Example of LocalBroker configured with in-memory (no persistence). + +run with: + +``` +cargo run -p nextgraph -r --example in_memory +``` + +we assume that you run this command from the root of the git repo (nextgraph-rs) + +the `-r` for release version is important, without it, the creation and opening of the wallet will take ages. diff --git a/nextgraph/examples/in_memory.rs b/nextgraph/examples/in_memory.rs new file mode 100644 index 0000000..6a00505 --- /dev/null +++ b/nextgraph/examples/in_memory.rs @@ -0,0 +1,133 @@ +// Copyright (c) 2022-2024 Niko Bonnieure, Par le Peuple, NextGraph.org developers +// All rights reserved. +// Licensed under the Apache License, Version 2.0 +// +// or the MIT license , +// at your option. All files in the project carrying such +// notice may not be copied, modified, or distributed except +// according to those terms. + +use nextgraph::local_broker::{ + init_local_broker, session_start, session_stop, user_connect, user_disconnect, wallet_close, + wallet_create_v0, wallet_get_file, wallet_import, wallet_open_with_pazzle_words, + wallet_read_file, wallet_was_opened, LocalBrokerConfig, SessionConfig, +}; +use nextgraph::net::types::BootstrapContentV0; +use nextgraph::repo::errors::NgError; +use nextgraph::repo::types::PubKey; +use nextgraph::wallet::types::CreateWalletV0; +use nextgraph::wallet::{display_mnemonic, emojis::display_pazzle}; + +use std::env::current_dir; +use std::fs::read; + +#[async_std::main] +async fn main() -> std::io::Result<()> { + // initialize the local_broker with in-memory config. + // all sessions will be lost when the program exits + init_local_broker(Box::new(|| LocalBrokerConfig::InMemory)).await; + + // load some image that will be used as security_img + // we assume here for the sake of this example, + // that the current directory contains this demo image file + let mut current_path = current_dir()?; + current_path.push("nextgraph"); + current_path.push("examples"); + current_path.push("wallet-security-image-demo.png"); + let security_img = read(current_path)?; + + // the peer_id should come from somewhere else. + // this is just given for the sake of an example + #[allow(deprecated)] + let peer_id_of_server_broker = PubKey::nil(); + + // Create your wallet + // this will take some time ! + println!("Creating the wallet. this will take some time..."); + + let wallet_result = wallet_create_v0(CreateWalletV0 { + security_img, + security_txt: "know yourself".to_string(), + pin: [1, 2, 1, 2], + pazzle_length: 9, + send_bootstrap: false, + send_wallet: false, + result_with_wallet_file: true, + local_save: false, + // we default to localhost:14400. this is just for the sake of an example + core_bootstrap: BootstrapContentV0::new_localhost(peer_id_of_server_broker), + core_registration: None, + additional_bootstrap: None, + }) + .await?; + + let pazzle = display_pazzle(&wallet_result.pazzle); + let mut pazzle_words = vec![]; + println!("Your pazzle is: {:?}", wallet_result.pazzle); + for emoji in pazzle { + println!(" {}:\t{}", emoji.0, emoji.1); + pazzle_words.push(emoji.1.to_string()); + } + println!("Your mnemonic is:"); + display_mnemonic(&wallet_result.mnemonic) + .iter() + .for_each(|word| print!("{} ", word.as_str())); + println!(""); + + // at this point, the wallet is kept in the internal memory of the LocalBroker, but it hasn't been saved to disk + // and it hasn't been opened yet, so it is not usable right away. + // now let's open the wallet, by providing the pazzle and PIN code + let opened_wallet = + wallet_open_with_pazzle_words(&wallet_result.wallet, &pazzle_words, [1, 2, 1, 2])?; + + let user_id = opened_wallet.personal_identity(); + + // once the wallet is opened, we notify the LocalBroker that we have opened it. + let _client = wallet_was_opened(opened_wallet).await?; + + // if you need the Wallet File again (if you didn't select `result_with_wallet_file` by example), you can retrieve it with: + let wallet_file = wallet_get_file(&wallet_result.wallet_name).await?; + + // if you did ask for `result_with_wallet_file`, as we did above, then the 2 vectors should be identical + assert_eq!(wallet_file, wallet_result.wallet_file); + + { + // this part should happen on another device. just given here as an example + + // on another device, you could use the Wallet File and import it there so it could be used for login. + // first you would read and decode the Wallet File + // this fails here because we already added this wallet in the LocalBroker (when we created it). But on another device, it would work. + let wallet = wallet_read_file(wallet_file).await; + assert_eq!(wallet.unwrap_err(), NgError::WalletAlreadyAdded); + + // on another device, we would then open the wallet (here we take the Wallet as we received it from wallet_create_v0, but in real case you would use `wallet`) + let opened_wallet2 = + wallet_open_with_pazzle_words(&wallet_result.wallet, &pazzle_words, [1, 2, 1, 2])?; + + // once it has been opened, the Wallet can be imported into the LocalBroker + // if you try to import the same wallet in a LocalBroker where it is already opened, it will fail. So here it fails. But on another device, it would work. + let client_fail = wallet_import(wallet_result.wallet.clone(), opened_wallet2, true).await; + assert_eq!(client_fail.unwrap_err(), NgError::WalletAlreadyAdded); + } + + // anyway, now that the wallet is opened, let's start a session. + // we pass the user_id and the wallet_name + let _session = session_start(SessionConfig::new(&user_id, &wallet_result.wallet_name)).await?; + + // if the user has internet access, they can now decide to connect to its Server Broker, in order to sync data + let status = user_connect(&user_id).await?; + + // The connection cannot succeed because we miss-configured the core_bootstrap of the wallet. it's Peer ID is invalid. + assert_eq!(status[0].3.as_ref().unwrap(), "NoiseHandshakeFailed"); + + // Then we should disconnect + user_disconnect(&user_id).await?; + + // stop the session + session_stop(&user_id).await?; + + // closes the wallet + wallet_close(&wallet_result.wallet_name).await?; + + Ok(()) +} diff --git a/nextgraph/examples/persistent.md b/nextgraph/examples/persistent.md new file mode 100644 index 0000000..83bd5ba --- /dev/null +++ b/nextgraph/examples/persistent.md @@ -0,0 +1,13 @@ +# persistent LocalBroker + +Example of LocalBroker configured with persistence to disk + +run with: + +``` +cargo run -p nextgraph -r --example persistent +``` + +we assume that you run this command from the root of the git repo (nextgraph-rs). + +the `-r` for release version is important, without it, the creation and opening of the wallet will take ages. diff --git a/nextgraph/examples/persistent.rs b/nextgraph/examples/persistent.rs new file mode 100644 index 0000000..a127b9a --- /dev/null +++ b/nextgraph/examples/persistent.rs @@ -0,0 +1,28 @@ +// Copyright (c) 2022-2024 Niko Bonnieure, Par le Peuple, NextGraph.org developers +// All rights reserved. +// Licensed under the Apache License, Version 2.0 +// +// or the MIT license , +// at your option. All files in the project carrying such +// notice may not be copied, modified, or distributed except +// according to those terms. + +use nextgraph::local_broker::{init_local_broker, LocalBrokerConfig}; +use std::env::current_dir; +use std::fs::create_dir_all; + +#[async_std::main] +async fn main() -> std::io::Result<()> { + // get the current working directory + let mut current_path = current_dir()?; + current_path.push("ng"); + create_dir_all(current_path.clone())?; + + // initialize the local_broker with config to save to disk in a folder called `ng` in the current directory + init_local_broker(Box::new(move || { + LocalBrokerConfig::BasePath(current_path.clone()) + })) + .await; + + Ok(()) +} diff --git a/nextgraph/examples/wallet-security-image-demo.png b/nextgraph/examples/wallet-security-image-demo.png new file mode 100644 index 0000000000000000000000000000000000000000..9a6cee969e8b85d53785ddb53be71d268f224a07 GIT binary patch literal 21006 zcmeFZbyS?&vM-7TCwS06aM#A&-QBf;#@*dLSRlB&1a}P%!5xBo2<{ecXRWpOUi;ni z&V6T$cmLZxy6NwmRrRa-YF5>>K}RYnNTML(BSJtxph!!Jser#zet+QKfdBGAI&MQi z5YBt4X}PEvyOTIL*_(rGfFv%S4nPv12gn=(!eeDVLpz0lJ5l_#Hp&2M-jh zYV7Q(C!SW721uj0s7O~xGC;Dn#nI8bxi#_neCPT)kh3r9+S*_sS(Pp5xrj_Hzq=k@-(%*RC;_~FUz-Pao0J5!A=L8NYv zm+->EN6)p3uQ|cm-GaZaZXRw_~6CWXyQZqWvQ zqft+sEtnX$(D68nrSLO&Q!gbZ&CagEHbbt7XH?*)b2>LgU$(0v!%%h2I$Bw_uVYnJ zh4^eP%g{VyWnI-gZLY|FH)Czz*)H%%=t$mE8^g15Xd1)KeY88Aq^P*gd=N!f)iGLL zQPnk=F7cq%WHO)bYSFhJ$7}0gvbg59VLFM4TI6y`Q##1_to`-#t(Gq1yaxXps(Y} zeZ?E*0u=)eyz|?W^45C?LE*kmry58(leT#uWn$8rh?)oVeiV`G3b-x{cx6XwZz1k0 zBs+~ptJm&Nl@keiGq2AbYgb?Q&OlZCJaW>`4DMQef?fvBYW}*vC)R@=?BT z9A#J|(c8W1lyTwH4MSWux4d@M@^wk*0Nz9;8Bjpwj1NAhm%BGo?U=^4xE@+t*IOU1 zH3sIpT3sU&8UX*52<4|yC}?20G{^grB=s)Nv;Zr^`s31%w2QgH>3vrgfY+T^cW-!? zhsCyh%9EaPPelOLk^{G%{_%niV(8c7cTPG@7}F*Rro2Iox|ONt`<(>rWn{G90=BF6 zkgc}7FkBE--gOk0lW{AQsWv(9QWTEbU8HPN9cm8c{}L32%xBnNM)~@wE%Sh$xmCc} zUx-FVGqu~4*6pQk_iI(>y|^JZwnb|&CJ{Lj*OGl{vjr#z%Zohx*Ao74diDfA zAs}v>S}?w&QBU}GdZDsH?A&wK3AnDtCCY{q0|6Z@%PJe0IpbnQ;T36D$4y9z|APU} z5W~D^uoXTCA07c-UhPpHq^u^zwA~qdYz|9uD$%73^mX8Nj3NPbEPM5=T_ZrI43t;| zA@1A4BY*hD*D$y7HSx*4MLdqzaZqsJAce8wD66izNsd8$yjZn7*awc#eUf%B|0NLi zka=dT#KvEH0sX^Vk}h?!WDaKzDqN${c_{Uj#N)SVDQs+>5~+p~C2_|F+VgiGd*EQU zcY5&UacSsBTa`r-kVA^3u8L$dTunK)qsyR#3W(YsLLBWsTNovRyj(yHvWrY4Qc|V-@II4?t62Cl9?!j zc~$)@MZ^YQleOVjsD|t%O}JZCL3dSVSSNuZ(XIfE5-FN`?HKq(xX9Pz5?(WnD)Qv2 zv^M?*IgKxnx`fEdYmqB;@$gYYS%Ku}zz+t2CO=x|ltbevQG-;oQ1fRxlQgNUpr4_- zo=5iu!%-f)XdLr1xp0ak7w5$|JdAmly5u0ZWzDwDoejt)4>fW@{ihjo10 z&5=9R^4T?RBY{ufMh|ho3w3B`_e1nt|NTVB_fZ+E6GSSg$RMca?6gC5EzDq{sc=#O zHxyvvY-a@NFv4QM#ccX=_fA=Q#X0$Ak^B%498_((a1c?VUH+wF-nGYGelW z^BrY(viMM#;Uh_#dL>2|6Lw!5cJV`|AUSalnC1dC&@ruCUSy0_Domv@R5Q4C=@MX3 zsy-CnIDJJ*T`MEQ-C)J@J?M$>KUc3$H{?TY!FLmG%w60%{K*jy8LzTA$G%NW8Z7#e z`4>IRb2PjfEM`G&4LW`ETnvkc#m%qd=?7wi`#n=Bj|4TILe-{gGFj`Aag6&WRYi|XJH{$6ufZ)B{f)ChGIZ3swEsx%&nm}_} zBtD%gn5zUYQV-Kx9M@(2ug$o2u)zt_M(+%CL3Z;T9TCpjD9sJ-g2fWD*qX+hP{2?e zy9PJTil2)WVG|P?$F{G&CYVELX!}vma+>$O~o5SSb*`as8 zpuCFrgxtNtlY~vfku0OW)jFFut1j29?uiJo3gN(}u|C6naKLGw!@AcrijU!g;z_r* zoFWH3qJC1}Qt~!I5v@Z;kFyL;Ei`NYIIP&9D5DvD)NgB}@*=}@EZo&6&$)z-X)bzZ(I527KAD9BeBeuJl zocE?s3L}mApR7r~83nG579F5@H`OrLlVS2vivMIq9>^(vJ86rWY@bp&Mo=7zkIq$6 z1_vRuf*r4T^E0hy)pHZH6}dbI{bjHdVy7`)%gi`Xf2xt~9Q#uz0TXr$%7`{LqD(V( zA$uUBt*yh{EleyV`L?Le5UX5Yo7R@J91;Aq(t6r1o`)fWN}4yv+&jiFD-r%&ez+(C z$TZGZI1~F`ZTLMnaulnHpGp=XxZG;b37XLkac@NPw}OY^p64~+T-rc@lEp{*K1$hL zut&rmPd0LY_+S!962`#daOkiZdMxah!9*u)sC5S~IpoO&J+-Z#z#CX$Wx@09n`0>6 zBi5SmQlKnkk78N_IU*II1}C2!GIIV8vU7*h@eaDSaDxT~?P;wq;v~TgdSU`e>f<;a zKN3``?`aJuG7++YgU}EQ1y0a-{d#lq2bYfo^&6(*qJ!cMt9^j7W*yG z77=(w!_x~j&IzLnQo*uX+^vm0SX;TwA?gL#R(YPxHU#SVd5s{u5av(00ihID3;`(A zgssxg*p)vZ*wA@Kc&AJY7z?@cILc4*Y2qtCiA+ktDJ63nyRr$iONE>E9zlOIFMOgw zj1FP3mJ)lD=Oab*P%*I?klbNxV=X>O6I&?Q3B8I6&(kZc#j~L(N)OoW8TEW&Pbs!`ViOqP9 z)+(LSvmjVL=@ZBSNlE$8{iCZRmrx<(NvoUqof!!h`_;r4bXyi|ekacq7pibn386+B z0J6eI4Lfd-Sx3wl9&)4wxwT%J$^=Vp8*5em{9-GqK3;YPHKmtX$_xv(H^>gj6XrQ` zqVN}kQFH(@N+~`plv<8BoQm5nr(|1s`LRRm3U(VJBu+9a^5L9#O)s7zT`>;-5haZh ztVJQ-;q_-0Z_U}yVJDewr74azi5+A)~w=608Q>o)9#Cw2x51x#}`R zuuyMZ@~RIFi^39N-p-39=4&B;M;F|}BSP+v4acX(pHq=-&=_+o^)SN>6UYN1+XRxM zrg?t~$BhX9xwJlUw-TnacJj30I~ld3(YhwDySD5bFw*YRjH zLKnf#;;OlCwBoY#xVV(Btmr&786kg!r)+=COe9%POLEhS0EThyRCpB*8>FA8lB~PE zU++wPd)#j?V$u&&S0!7oLtSQNMg@^vI5d~o%qG9?K+hWXC?O%HvPX0(R<|P$s4RtR z+IPpDrzna>g?0(Rh9Gt(8V$_07lne5q>o$=syBuYca%K*ihPJ`vT}~H0=r#Km(O0{ zNurRFZMKDm-~f{oUpTNP$|d49H|3^@$)aVg(hP|ZU9|1h29Kw^K93$wOlH6%!gdXh zL&2oj_)!n`);j7ZL{r3VYYq#~ZTYR-9vKHOlrd;biPV{}2(>jFb{T~aM7B3OWQCv< zgZtjyEj8^88X8WnyT?{NN_T2)LSGYPtl&^?o}iYJgFi3rTr+a?G#$#b zCH{31b<+Ar(Ao88DXvN`C6o`d62>3^v9iRLRNq;gV@N$izg2dC3TAvfvJYw3uOopT zlv0$Tj*PPW?u=3Lz9Hz9G|?mz(iLq&9-gpL$H?M&^2m9Y$o8N!PMo(qqCWE@DpuW? zlrdx0j3MQ3Y9p;z;Eqj)G-ia)6X4qv#m`mr zft`W9k9lv6Ow7Tqg46+{wCSmpvoHOQf|_k#?N_F#S7IrHZIf6(99^(s?W++Pzk%}t zBlQvFU?F5Hj}3#5I^ShVw7WEKa73%qNdlV$Rzz4bm0rb>Ab(QhK+s01S=(Zcu5QWN z(c8m7_n^+VhuA1&qgm>+dsMw;&+#+&HeK{e*jVSUP#+exQCfn6Q{V8AkjH*}fJCNT z2%J<~qEoqt|55FHdyy(`{7p~di8MbI4#Efq_dF;x+k{@M(<$;p?|b#+5X;;Phui{9 z8C1#OD6+YPddc$?gpY2&)H#IA~|%WgSj@ikXozzFVQn@rXXk4Yl!5WvuEcE zu~JKNLW)Ojy<^WCeF(Qb9eND1@ zLQyB$JPF%2QtgO{T|Q9dh|66$K%&ggXY`BpBqcWjrj00ZPTMUSmsCVXoFu;%E563N z8k)CL5bxw8@&ddWc{JOTATtP?XX$HX$%OUN(Gxl$D~VvfO`YQ|V=09vV=GCFxzIQw z-iY{<$m1x*H$;_-OO8VyCb7$HEa6doLB|*-?zh!_66e~hXnKFSjd@efnH!xxofXc^ z`-8%gj6S5Di~4b{jjIj=c0o*2IO%1Ro}fqc7!G-~bO1VUr6|bYUaZV4RzQSAgv3(0 zr(*cSP6G5}<1ecSac3aFVg?5RGT_tqj7hbRtM;K(iMls<&C+8B6tpS^^HwA`#Wn_M zO!$=8pjawqFE@w}*4ExcA*gACsD08)_$MtOMP)zKb~Cl24t!vr@-9UU_S16I6(M<^ zJ%$36zSL&WMUV5R=jkp81dwB&5wuKzgag~~F-2MobtmH6Uv)uM6Ygt5LcyS+@u8=v zc$}rfkWWev0etBYyq$;0F@1p>N`+NK^7BTfDj_2dO%VwkS+;9PZ#J_)HS8V}BeUgN zf<33{ZX?R3bBka5ww1U9{gteTlaRg;#U+baqvy;Vg`4g+XE3R6*Qa9&Gu`Z<`M$f&t87GvpNZ6drBAOvD-%6RH=)yb5xQ<{W zGCc`lsG1fjLcW=|Z>MM=6=D>S=k#Vt;5}Ja68-by-ibth-Zp|ZftQuY%x&AjVSG=p z0U^)VC-l(2ZJSOh$U72@Gp=zi4}F9DOW&u$jsT*ms(?dhIzm`?3bzE>--smo4RNv= zZ)xQ;{Pt860GlR(9iBKJR)L(sek`iaZoFbQj$6JRLugb`BOqn-S(3mX?Q+_P2xoCu z!3p_iBSgUs)j1$dT*9)Q++>l89}jMs`|=(RA0{R&E>z@HN(a$ulObWhCYDmM&>Do# za}2{Kl$~uVUi-o5TuJYP!npMpx(^0wC{idkT^xDS9IlYN^6ddws3SZ{#SA^=VUa7+ zu055{J#jZQ^0PF-Ai@{QpI1=i^l?E_iR(+49+-_`36%T-(fc%Zd{z5#^*ac?g@``v z9Xv1w?2_UNj{KCpVLzbCRrCFF)QSfhKH`i>W|-(G;o3;fNB8T5vn_gwP9Q?7d|ELi zSPA^F!(A`)O>9s@M|jy1?sSvxu{ths@(qkw?A|c*4NaR`h9wWqp}8j|vzmj3Pd@f} zoO)7ixYK6c=ETdolA%H)Uy1d(P)gH>LGKSQ6_;FapF6e5)(M9TdQim?p~6w($fZ1M z>Sl2E0|ipfkX-n1L{P$OAZTghR_NPQt3|@P{OZ-15kJ&R-fnTu2$)OVA>o8u3W@XiR6u5k{X~UW%~zSPJ0*MarOBngbD>&b z*~BJc!FG3Q%(LRc0=f*EIjB(5k^aQq-9~cpmnO~1rzsTdQn&cf!y(sU^7lc6J&=Jm zmN|HJI-6T)=3lm(OSoDr8?b+Mn>U0l#+V*TpaXDK;y6a1sr#=eH|f|jh2}Y$hfdPi z*7D-HGTheT69J6)zVCBVPG48_2tpxFf`VTKb}7h5n3?N1PMSJz9&t4UtIjD2G1w545&$?OW*^ln@U;{b|VK*s1V}l}5iwX|y)?k$^ zu1?oj%hw2^@B=S_yfcDbj^AD$QN+UV4P#07o)1aJBLO*hORE8A3t>JGIJ{Xzgd47s zH}0%OhCg;0FYuA)5AEf7A@O|P zdl2m2fYM4)s@I$-&(9At#5xcKh7oPQ=&dG-XqF3jm>(u^UkNF z6WVNN$uko8mz3>X*NJpWN96;@+N}%bc+g@Y*cGs8B-sMO&8C&BFej!M9Lw+>MCpGV zrnjp$p+&sOjIbKVHeUP{GTctwpcAX<&LNLdsW&BmTuN9-s*Kl`rHCI?Y50w{GuUhg zB3H`UKTv+h}lA7F7 zQ;}T5pje*4*vOtyU-#(CI&6T&!(H zpFsGgnicu^3eC<88tS!ddVe>74Do!S<|6Dzvzr3_@I|IuK}qq;ARC7Gj$i;g3B&a! zqrX92Aphm^4U6?;sLDB64;-7)%uG_l4UW(n`Y*w9YAIsWLRu>CVytXf^#A`E>3Io|QK9_iD*!t8BpJWm! zZQr)wy7onty)W_cF($c%+r-*fU& zYU7u}J7B!Wm-WM(#n-^vuT zHwn@@XH1-ba}=X>iwYy1K~9sxnTF^)CwWAJzzV&9dP-=IjEp|?kL|QJbvjT*UUN(? zA$oKoq{;XGLuwA?s3R>7`X`OES5hKvaHCLdWmO3dw0m08!)@;%9~f;%-K9ekPZ@-ZP9?hey&-{ zEf+N}KD`1Zd{p8(8f0v>)i122aBHACl+Zw*DBPg$0SOAK`DVQyx>3JhGxS$_$uay; zgP#t0^z<$~$c1@GQo5`gx=B!A-TThr6)7SyN%i$=7ajrvG87~#sw6Ec`i}!n@Udp5 zZvwwmpAc@?MlrTHZIBz9N4GEdxk%Ct&-yzJ`2qMV5izKV0uINAL zFQC+qzfU-h8nVwsVnpI&GbvWR>PI)Jof?!OsLwA$SHzH1moM@8uv0tg8C|bYUANEw zfYitB3rst12#fC_O(78u38Iyu?vsdd%fdpU;raM<52xQP#u%zc!R$nz3x!g54EGwm z(b;&3xnrQhh3eA5H8I3I)rthLio5dt$rX!9a*wQc#Nc0qD(9bQU=5lk%XKGcX|V&1 zzISMaM&`q-qiI)(Pl=y|KC`%%*Uo*#IW)L>T;Llzf{rgXps)`J7BwmqT0RyyJGDAn z?PeeU^#UP<>y#2Gm^TCg@irF(K5^EPm*X+Dw`DXovo`@Ude}OEPo%+T(t;ii#-`Rl z7ZMYo1;~z{^sK#`lmukPPpZi-&n)jC3bX`Cc{u@9y%f|;y{t{S%}50W5cxcKzyP*D z7h@6+TN^uP9uI!fKX7@#*T0*YNJ;)+ak1tn)sj~t5w&*$k^mS1jLZz;9w0YXQUOE~ zJ|{DC9u+Z(zaYSO{G^sHE)G0QOz!UPjP7iV_D&W|EZp4OOw6oItgH-R4hCmWI~QXQ z20Lf6-w=Odhyk5Voj?vQAbUHK-Ty#od5*E@XdK>R{vQ{CAzM&cNSIf7oqfX3hjQ>JQC- zmywi~SNf;SZ#PQ>NY-Yy7X8JcMX**{ZV>?seZzwQ0BM6KGWCL<> znX{QNm;ua9833FBV+JlDw>g6e2Y`c}9l*lI$P!yh?#( z?VUXTre784_~+f970CwlhpR|P{-^>x#-@L|-`Us=X!b`Jfc^L<$<)%=&H@N7w}0i@ zKiWb6MI5u60ocIt&c(pW4&-J4Z~y@e+-7F1494J0-~zC40XdofOoe}s1{ zKcz+O9X!FAZ0VxtZuj4+|1TK-M34iS0_~jb|2xzF&hn>Q{x%qaZT_Y75!BnX;v3*Vv?t0Ab~zxko*E!#TNv4b23OjACg; z!+>ACEMQrK=HTkk*oMZrR6_SA!QsgDyhK~W*CXQ}R(gOZU-}=G4H4|>xbI^xo7>mF z`L8#Y;`vv>%>o__KX&&j0kYVeq4+L};2f8*S<44s?-wd?TeTB&y3Y7LO*)%iu?)RU zh^HG2Zo5x6Rz||lmSe_1-is$FF=F>WcD%V9IMkZXdrQ(uq!5L z=<{5Kp%N*w)k56um|`#Ru+na4O`ft_|B;`QrVJvRo4IGvtuv;Y;HsRiQpSXR&QtsQ zCap^>G#SLTJ}w<^Q;8tF(5sPgjw(86(p6Z{zsQK_)Sx?ns_Py?@ZqwL&uKO>F`PJ# za$Q~5|8_f%Z#kpW?fugn5#P;T`28sHN$x`03EHg-9G6Q66M?dwcCV&jYr!r)uLkGU zS&lC;Z*x_HPi5KQ`0>i&vM)w}DLQ z-sUT+wz%@6C0kavj3DFmd+RKU#Yn1|IS+l!`N5d-i@D>@he>~rjW=yhDNf>=bj03g z_+5{4!Vv2`K5;14JY^THO35>;U3MT$r1WF@17DsRCuz3bh@1Tpk~X{Z+Yw_39bY${ zK9!KM3Rz5b_XFdnqv-h6RE?lh4ow2Qr5%WR}=de+y0EvIurG!F;aDz9J)^ z#jj1)A&Dz4IDQiZLAi(VE>9}Rqu3#9(*FRZ;}&dHxdGRKnE!ATVg+P0FaD)Jm3r{{ zFOny(`n5IN<5f_lYi&2D7@*4e@h6((#JhZ{_P)W9bz^}1!BQEL&(lB>E@p!wVV#r= zn~am}g&1Pv@Vh8-Rzg8%Na>V_l7?ruHD(t1BsmEN>pL?@D508RPH>C{mtM4KEuQW; z_@^*WcDeC&+n>zfSam3DGD8S@ojUmCtWs_@NSm?ZnM5MDySv;* zb2)yXpw~pyW|w>HMYENN_XgUSRi{k!*Soj&W@}Nm(qGuJ8EZ4|XnPjekq{NYyRldP z%^ZuQGOQdltIwM4!4JNRt_!45q$JF&IHGij*n%EoPB_g^7oM47+nT1rA*^+<5(WL3 zp!Ck$=i943aB_Wr$N|UjI?qyHV1WfUTcrQzGgkJsI<47A?C6ZZ9g5Wyj#fOBfob+E zS0eU@Q%6F5+s05Ps#L#DGAcOjh2KfQbp)G3_}!3aJvptP>RAYD)8B{@(g| z(TOXt-^!X+ji>?OVR^ZP8wZ5&@UkcVIUe?6z2Za2a5=Wlz)^j=dHuj_pM$h_Wg_TT zklc`wI_K=IOe>5fF3@0$F6hNTpd6Nnuh`|761T9zEm~QAif9}4)s?7dk0y%&HhOMa zrR~dlkSWp9NXAItr}mL9sC~$qhL0P- zMH*s`Y5nk#79L9AV^Z-OVuf~Aru?tvq#EQYCe*ke($X`yiNn_<3O00S^mY5wx2u_O zqtJ09cZsfA5v4K8zT8Zyorx!kMQf?Tt})}HP~aklvBW$mLz2KlH41Pt#0p?(rzdSV zN*LnFbK<^_WWj|WCr|-0G)PlpP+`flYO3E_(0?qldL!D)Y`VpDWs3qW43V=KI(cGL z9e4c=z80`%1U3U;FTGb>)r#YZ`lL0hKo8dRK9&_HZd_CaVQ#@pnquNY_y|Oi0{S|v zm6~{OA(hCh}bX8W!uokor+;&8;Fv3Du_1!~5Cm|PPa?Enjv*a0% z%;S1^3+)!cYsFLFwM45HM|}FgLd+OsX_@=R__JbY;#lInc1ag)MAUU^BGof}^2oFO z06Bm@RmyO@98gIeWtO=`$9bn_)zY?nV5fE`8v=(&gXZD?u_lDh$kONN` zE1vwK+(xl#v4>esP2K{KNrJDKUxu%_O~gaUfz!0G(-H-F;K3@&l_Ii;A|@=9EQY1s zf6`#Ax%)MmL@yd8YY^UpHr!>Vd}8@ZSf%V+L&znE_~4G?%n^xOf?lb3yd7%RJ6G1mV6`u?`aR2;vwqVJ4ng5soHL9gI2z)-^OU z>iTDM%iz-286#+s@{A#do*|#tH5l8ypzk`ppUfZXqC-X~FUMW>oe^#vMc})_Q-hd3 zhj{+T*}zU6CT=u(BqbWm`Qn3R>0PcKkb@sXF%PtY4TB&u!ZZtzE@4B4ty`$CUDs7t zK1fCzW0nib(=vdxrzP31TN+1UQ~j{%v~pOphG zY()qIG2_S0Djk%S)0nG@R5p}@6_&)Qn_&T%@kJL)C@mqololS~dHMK*#Adl-HyssYvPfWW1psqG|#9bNT!z=&~ z7d1%=v;-${t}hnkxXs=9BK!z96w#mPq34Ffcs{$lKPlhMw0VBvhtWDh0G>|GkF8uj zg#{UVJw+FvefimQRP!lQ)$q8uqCTyFYQp*SE46hhLvDSi@yxLTCFaAN?!kGB&AqkW zVVE#=`skT%YYRdvH8UPAA`3pv^_wvsf?7J2rfO?O_s|iwFg2a5Oc(G>%eygSQ87;L z2ifPV-7l;eS=+}SDcoGP0l>$S>@G27#EUur9(~bNT;OUPWmj#zGez*yY36KiA_sR3kkbSYKFhBnR z)bmNR!H49zrCFj)-=U|anqtg=k{m&;eC5ZIyDt_emmCVWsC%NXvw>XpL_B>}oN5v& zj)rHru8g7K_WEICpn|H_+x(%^rMjSbS>5~V*X68@rs}mJecd*P6=0@Y=<&Px%j4{y z(T@3o{Y^K^JB%w`29myCCPAP zw$MNX1>*U~oFz}`9DBl~5iv$kRZ(phT72cP6)l=IvkXrGc<{_*s53yaA_UBPUkS%5 zg$@K`lWC9^ndlV~V8H}djo~l9C@eVgW6GW14KwWHPYzEJ6Lm3V^5hHZauJ6W+Kk%TRWl9-8nm15MxV7v z$6Ke(lsONX9t}k1_iAX(Yvo9?l$r1@S8Q0_H(D}vIT^Q~l|JW9bNTiuquz#I`uJQN zqAfC5_wG?wnYLjdNZi2`r6eeI>^i$olJvFCGg@_2Ki@>fYH}rH8-=p5T`Dawf=1zZ zh9+XCx0zf%W!)$|Kj>vg(o3srOB&*t;O$dL9-3ZYQF%chhD8<=RBFu)&rDSIEZubW zJxeCwSkfmHxAscvRs~<>*(e-5JABr)(@GHBw{#bL3I2ekUASa6;lU@mejtBK?<&Tu@^-Xa7-spNlI8dK|gO<6e^L zF9?@{?>tO;O5yrA($;S*f)F}};I5uHsw`>!svMTg$Q|P-rIJ85iK@Fm?d>DZ$;6Qd z38E6H?~X4bHXLce+>s}afVoy>tjH&O?Lkv+L7T->)zb3bsDW5yWJ$_TM&K}cd!nU{ z@$xcn+HG`Ij&qxXJDP-Ow38n9yitL}dHKBcHTY@gi=d)rK}NPwMX=-QB?D?R%l#55#N5F8@TiPB>(Fsw&;EVX$?5!>!1qPDDaL$+<0i=6X6-k<@Y=! zh-afoyd~#i`YmnxUXPt(;PZakhior8f)}E8w7sQJ%MotkuA*pQv@YU--p0V4A)hd_ z#^J33+;#+%kApLuVuwMzuUrvZKPC;UxPTFP!v6gc(lV{0w#bCKS8;UQG)huTIT^gR z#S^E!*6_y-zSI*tHl8nTaG-McgGksKFX=$79j%q0#Z6LB(r6*&>Xl{iwbf!bcWrxB zb1f$w&u+w$a5B!UbUJ+Bee}J%ysukqmTVDo9*3(cH@t&N{9N<=1O*-8HXcU|Zg*7(>2y0f zUI!V~ZboSVE`36r$ZG22njVxi=x5c02aZ^rc|-4 z3$b)D+F<>J3(u@n1}irCBo9`3*0f$b&GR;*RDP^Ln!2NwUEmj5*55CV;=V@;#7kqDkzT7>}m%^>pbY z&zg=WuPvXmrcJRcy^iLe?N6uijZE?_nkPPKL_KW>dXCgga?Rkyb_Jr7OZQXCf*0p1 zj9(~5FgUco8t0_Bh=tA!e)5H98)2A*QJ=Sn(fVC^suo>yt#}mL6SK1Y(Kn_5@@nd$ z>L1k220|`2@&iaCrRrknqlfE44OG1hJQ_F~4A?0wcq$ zOB310Gfhp>jm)57oYH)WJUeBs7rHnM5~B0;!!ced7YOs|K-RC~RKw zSjbf=q=;GZ!BXJ$0hH%$JYzt(xaLjx90}jzM@ubr+#0?0Ii1pruy4akLbbF(qdz|2 zXoqgP(CVi>QNxobfoAZktZ?G6U?JP(p=8>l@IThapA3pE%A1U2GFKNTDO8xT{-`9; zpg2CDGq1JkKqZ~_80acY(8VvRZ|o{3mbev$7k^8|DOyUYd*t~6jgqT;B8YlXETt%F zc`*vbyPXixBy@}e;{xl#ENnTdp&T>1#9Z=6Kg@te>tUCP92yLZW`|~LI~RV`>?(2Q zCj{Bq5C?Kc<%DFbkRi|)Ef>SuA8*7bc-lWDuKPVLJ^X}0+?*6>2+Ug59H%uYVx=X^ zE7RWkw&O-BLz-Uv4i=e`(tLs^UDK9AT)O=8j)AfXLQp_o9*MMxxRcV8B92R;!P08q zct)z>r*AiMBAm!!lKHBnr*!a8u{ZBKT3KzADn}K03JUh5%nz60*$aM@rpMWR{%qbv z+r%fH_|kV&qwX@OD1Kb47x(jLx)^K<=W<>7r=-3TG&HU5%E%te>qHsG`H)SmOep{h z#bS1gUbGO$9YFiK(&|lmwJEN7oZ2#X{D=#OV(HPFFk|o<#j^ZPL(R8s8wnm6IUM}+ z4Ql#olH46c2r*}_l-ENb#&3X6twdil?qBH0d^ zV8+OP6?)6^E&d7~c*!I}u7#mtt=o2Y#!|X8bpWF$Hy=J(f{G(I)=GwzWyk0{@x*>> z%HDpgk>uTeT3_K*(cTj`G*m-f1?#)j11|RD&cQY};^@_v!?s?`_KiH=5}P~lZzt@! z7zVt)?5m%-Lo{cXv+dxPSx-<`*z z=oDa9vCFJVeB`Cu@^IyE)@d^ERQNW|#LnOiA1+pbLwvVM&_BJr>=DW!RML9|{qAm_ zkq^kI9v`-M>_5eqb6q^|a86u~QyGr=3pm?-W)d)vp$%9&L#{o37fxk+wnxr%cN2Hf zcMxabC^Jy3RsuhqtXuh0_mh6;v6Z=GH^I*%REM*DR6DKq_*6e%9;Q=tX;6p9J(T~i zo;Q4qyssCAaNL}U^Qrhz#jVVIgI}B8`2CU*w!A&PLGri`JfV*2zw>Y6%=zK5-vdGX z5(~p_@J`V8$a3WM_3&g&tE)7UjEd@r36(2a^zp-q^BT^ zt@i^G6l{@C;+(*Zk~8_6u5Nc&8C&S`pRG*|DmrbGNO}ip3elHqz?kVoz;4#ifOmvd z+bZ?EJEJ}L#0_-{x|&=*6mBDA4OAd8_Gt>3E^ zYb!*uxoMZhxwo;ug)Hh%r}livTBCC8aB7P`qith)Axl+Mgbz|uug|gw(aNXZ{Njok zO;uu}Wy+UTV5MVF^#v_dUl`+7iYe#y_SkbE)zRZbC2|9pz86iiOchfhulC_vQw1Lh zbxJF}1bLakG{38wbmLGAI-U0w5WEeCj1pcfWM&$Kp6>9H;1C3!NRY}V{W^01^ltc; z2$ioc0)oBKTPo7v}z)pFn$Q zA%K1?lYm-3fCMWpPl{ANJHJ8HJzzR>0>oN%SrL3NwzBEUYe~;Tg%-7Q#dRszTZ?;^ z`98nh{c~X6@LnXvB8%`C&}0&Z50f6nnk%jMMlxwPqb0+p zT#_ctiF+Lq58k5e77h5DI){r_x+nQr-aZ`?6L#*$Jhz|sk2-xTFP@&+%o{N^<)T7= z*gft%_hN$hy|S< zF<7HfKlBhetL%)gR&MLC=_s#yGf{yHXx5_|wK)yetn%Pt21bIEjVHE?UPZP7JkmF- zw*KtkS!143BN9fd!RDSRnS_04HpIcG_V;@AN~es2PN$4a2Cr@NhcWCHKfPO!&L1AF z)}Z^yezZyl4_NS=atW@jrJfgI2-k1TXZ{7(&6jyL!}BAPufv(bm7US#0?Qlq=3O;a zw6=bQ^tZS%5FMU(hY1p$ta_`tf;t-AYE}ezu{`aXB?Zt{PdRp4YI76L*77x#{&f@y zo%G)QEM)@2S|d;oF^Y7uTsxZ0MAJ4(%-WQJT|iUZLP?Pwr}4q_y86@oT9Phjj_7`~ z{mJ&@{0a8!hBh5%^geYunf|Mw1|L>AGL&$G!6M$OGDg}-IPHeu)0^++8aJ* zobj$sN?o#2&t+VSYhu_kIi`G^cBt!{3=J!IzkA&8tnxBppS*T3VZst-$VAL!gr-U? zD@YSsI{CbX2r%bup$_0=ieNuoqEe*==hn0qhUjA1dKY=S)90a++`Nh4im&z23?}lB zE`1~gKaf94L=LFnwkfc)>tVqXqVmf7F`atKm1;{?n)D~(4U$H7aIMPib&DwrB>*!O z2*4(E(duzdkuJ0clppnDfG|doYsC=N=j+MY#z(D#_U)2LqNx8@038M5`niRUTuwV~ zryYmgT9Rl^5i9S98Ch+3T{nm&(?sJbG8r9R)2=yXkzIFL!e*0kI&9Q>97vLgqDXT- z7d7vRHPNtS1usl=%^)04kxXmonvRi^^2v^vm}OIW1VKa)1w>ImmL=?pjMr^1EWsu) zV`94&u`F0%hRS9v%zeyyuQQR%5F9N`d}wN@nKhF-En-zMdiw+X`8R%*|NMVHeJ6`L z8m%X5b%hozVp*^-7rVmABpNTDelY3j3(!9}Ml_zn>vqt!u#vWw@)a)*AMYoT%FfoV zlnkY5n)LPu=^q>;5=-HA+v#YnXHk2@tg82)v&4FR|0%vSL85Wz+7@BbUp73 zOs>-MvlkCB6wP&OUOSI{>JFaz@7O`dm z>5R&=2M6+>w=VVb(EB&BxTBHVwyfmicdjfuC!=aS^?Gmq^B>-~fv(ObY>Lc^u2w$w z-ZlAsZyi22>Hlr@*?IKA?OeaMliz<}Tgkq08Oj^pvxGERlScjQKIp>rKLKZtGil&<0acdkp%G~S)tE= z=MB8}z4^~-kuKJW{>i(_740>Mxj|;{gjq6|lc5ZS<0REW7rSg>so2_zPCx&(FFPea zsTzbL37Q*fEMiS3CQb*NSmry~`Z|*>JUN+;J3ufxxn`Ge4J9|331!q1+w69h)@`NJ zmc^QOOcd6?5GZw;M?&S2->wM{?k^tR$hsAiPkWz=kl*ccl#+L>w2i|P6b9XHhvj2U zH#{CE?X`s)|I7V-gd>T3F6n14pC~)0w#H3okyCwsI80rQJO48M^6JHZeDAu7w^L`u||h8JMto#O5XS+`et)l(hM#P0P6V^e{jDbvB)` z_snQM`8tk!H+691re#*TVaEI{hm9}%(OulPWnq4s->vY4k8Nk;+R2A?BuV5`AGwv! zJh-8xTLM^6i5Qx$6J`002ovPDHLk FV1hMO;w%6F literal 0 HcmV?d00001 diff --git a/nextgraph/src/lib.rs b/nextgraph/src/lib.rs index 4df2a90..3f844f4 100644 --- a/nextgraph/src/lib.rs +++ b/nextgraph/src/lib.rs @@ -1 +1,62 @@ +#![doc(html_logo_url = "https://file.nextgraph.org/download/1fd175bb6d7d832156bd5ad4abcdee7e")] +#![doc(issue_tracker_base_url = "https://git.nextgraph.org/NextGraph/nextgraph-rs/issues")] +#![doc(html_favicon_url = "https://nextgraph.org/favicon.svg")] +//! # NextGraph framework client library +//! +//! NextGraph brings about the convergence of P2P and Semantic Web technologies, towards a decentralized, secure and privacy-preserving cloud, based on CRDTs. +//! +//! This open source ecosystem provides solutions for end-users (a platform) and software developers (a framework), wishing to use or create **decentralized** apps featuring: **live collaboration** on rich-text documents, peer to peer communication with **end-to-end encryption**, offline-first, **local-first**, portable and interoperable data, total ownership of data and software, security and privacy. Centered on repositories containing **semantic data** (RDF), **rich text**, and structured data formats like **JSON**, synced between peers belonging to permissioned groups of users, it offers strong eventual consistency, thanks to the use of **CRDTs**. Documents can be linked together, signed, shared securely, queried using the **SPARQL** language and organized into sites and containers. +//! +//! More info here [https://nextgraph.org](https://nextgraph.org). Documentation available here [https://docs.nextgraph.org](https://docs.nextgraph.org). +//! +//! ## LocalBroker, the entrypoint to NextGraph network +//! +//! `local_broker` contains the API for controlling the Local Broker, which is a reduced instance of the network Broker. +//! This is your entrypoint to NextGraph network. +//! It runs embedded in your client program, and once configured (by opening a Session), it can keep for you (on disk or in memory): +//! - the blocks of the repos, +//! - the connection(s) to your Server Broker +//! - the events that you send to the Overlay, if there is no connectivity (Outbox) +//! - A reference to the verifier (optional) +//! +//! In addition, the API for creating and managing your wallet is provided here. +//! +//! The same API is also made available in Javascript for the browser (and is used by our webapp), nodejs, in the CLI, and for all the Tauri-based Apps. +//! +//! The library requires `async-std` minimal version 1.12.0 +//! +//! See [examples](https://git.nextgraph.org/NextGraph/nextgraph-rs/src/branch/master/nextgraph/examples) for a quick start. +//! +//! ## In-memory +//! +//! With this config, no data will be persisted to disk. +//! +//! ``` +//! use nextgraph::local_broker::{init_local_broker, LocalBrokerConfig}; +//! +//! #[async_std::main] +//! async fn main() -> std::io::Result<()> { +//! // initialize the local_broker with in-memory config. +//! // all sessions will be lost when the program exits +//! init_local_broker(Box::new(|| LocalBrokerConfig::InMemory)).await; +//! Ok(()) +//! } +//! ``` + pub mod local_broker; + +pub mod repo { + pub use ng_repo::*; +} + +pub mod net { + pub use ng_net::*; +} + +pub mod verifier { + pub use ng_verifier::*; +} + +pub mod wallet { + pub use ng_wallet::*; +} diff --git a/nextgraph/src/local_broker.rs b/nextgraph/src/local_broker.rs index ca1589d..c0788f2 100644 --- a/nextgraph/src/local_broker.rs +++ b/nextgraph/src/local_broker.rs @@ -11,9 +11,12 @@ use async_once_cell::OnceCell; use async_std::sync::{Arc, RwLock}; use core::fmt; use ng_net::connection::{ClientConfig, IConnect, StartConfig}; -use ng_net::types::ClientInfo; +use ng_net::types::{ClientInfo, ClientType}; +use ng_repo::os_info::get_os_info; +use ng_wallet::emojis::encode_pazzle; use once_cell::sync::Lazy; use serde_bare::to_vec; +use serde_json::json; use std::collections::HashMap; use std::fs::{read, write, File, OpenOptions}; use std::path::PathBuf; @@ -34,6 +37,7 @@ type JsStorageReadFn = dyn Fn(String) -> Result + 'static + Syn type JsStorageWriteFn = dyn Fn(String, String) -> Result<(), NgError> + 'static + Sync + Send; type JsCallback = dyn Fn() + 'static + Sync + Send; +#[doc(hidden)] pub struct JsStorageConfig { pub local_read: Box, pub local_write: Box, @@ -47,10 +51,15 @@ impl fmt::Debug for JsStorageConfig { } } +/// Configuration for the LocalBroker. must be returned by a function or closure passed to [init_local_broker] #[derive(Debug)] pub enum LocalBrokerConfig { + /// Local broker will not save any wallet, session or user's data InMemory, + /// Local broker will save all wallets, sessions and user's data on disk, in the provided `Path` BasePath(PathBuf), + #[doc(hidden)] + /// used internally for the JS SDK JsStorage(JsStorageConfig), } @@ -61,12 +70,14 @@ impl LocalBrokerConfig { _ => false, } } + #[doc(hidden)] pub fn is_js(&self) -> bool { match self { Self::JsStorage(_) => true, _ => false, } } + #[doc(hidden)] pub fn js_config(&self) -> Option<&JsStorageConfig> { match self { Self::JsStorage(c) => Some(c), @@ -78,14 +89,14 @@ impl LocalBrokerConfig { //type LastSeqFn = fn(PubKey, u16) -> Result; pub type LastSeqFn = dyn Fn(PubKey, u16) -> Result + 'static + Sync + Send; -/// peer_id: PubKey, seq_num:u64, event_ser: vec, +// peer_id: PubKey, seq_num:u64, event_ser: vec, pub type OutboxWriteFn = dyn Fn(PubKey, u64, Vec) -> Result<(), NgError> + 'static + Sync + Send; -/// peer_id: PubKey, +// peer_id: PubKey, pub type OutboxReadFn = dyn Fn(PubKey) -> Result>, NgError> + 'static + Sync + Send; -/// Session Config to initiate a session at a local broker V0 +/// used to initiate a session at a local broker V0 pub struct SessionConfigV0 { pub user_id: UserId, pub wallet_name: String, @@ -94,7 +105,7 @@ pub struct SessionConfigV0 { // pub outbox_read_function: Box, } -/// Session Config to initiate a session at a local broker +/// used to initiate a session at a local broker pub enum SessionConfig { V0(SessionConfigV0), } @@ -118,6 +129,14 @@ impl SessionConfig { Self::V0(v0) => v0.wallet_name.clone(), } } + /// Creates a new SessionConfig with a UserId and a wallet name + /// that should be passed to [session_start] + pub fn new(user_id: &UserId, wallet_name: &String) -> Self { + SessionConfig::V0(SessionConfigV0 { + user_id: user_id.clone(), + wallet_name: wallet_name.clone(), + }) + } } impl fmt::Debug for SessionConfigV0 { @@ -164,9 +183,7 @@ impl LocalBroker { } } -//static LOCAL_BROKER: OnceCell> = OnceCell::new(); - -static LOCAL_BROKER: OnceCell>, NgError>> = OnceCell::new(); //Lazy::new(|| Arc::new(RwLock::new(Broker::new()))); +static LOCAL_BROKER: OnceCell>, NgError>> = OnceCell::new(); pub type ConfigInitFn = dyn Fn() -> LocalBrokerConfig + 'static + Sync + Send; @@ -213,7 +230,7 @@ async fn init_(config: LocalBrokerConfig) -> Result>, Ng Ok(Arc::new(RwLock::new(local_broker))) } -//-> &'static Result>, NgError> +#[doc(hidden)] pub async fn init_local_broker_with_lazy(config_fn: &Lazy>) { LOCAL_BROKER .get_or_init(async { @@ -223,6 +240,11 @@ pub async fn init_local_broker_with_lazy(config_fn: &Lazy>) { .await; } +/// Initialize the configuration of your local broker +/// +/// , by passing in a function (or closure) that returns a `LocalBrokerConfig`. +/// You must call `init_local_broker` at least once before you can start to use the broker. +/// After the first call, all subsequent calls will have no effect. pub async fn init_local_broker(config_fn: Box) { LOCAL_BROKER .get_or_init(async { @@ -232,7 +254,8 @@ pub async fn init_local_broker(config_fn: Box) { .await; } -pub async fn get_all_wallets() -> Result, NgError> { +/// Retrieves a HashMap of wallets known to the LocalBroker. The name of the Wallet is used as key +pub async fn wallets_get_all() -> Result, NgError> { let broker = match LOCAL_BROKER.get() { Some(Err(e)) => { log_err!("LocalBrokerNotInitialized: {}", e); @@ -247,14 +270,32 @@ pub async fn get_all_wallets() -> Result, Ok(broker.wallets.clone()) } +/// Creates a new Wallet for the user. Each user should create only one Wallet. +/// +/// See [CreateWalletV0] for a list of parameters. +/// +/// Wallets are transferable to to other devices (see [wallet_get_file] and [wallet_import]) pub async fn wallet_create_v0(params: CreateWalletV0) -> Result { + { + // entering sub-block to release the lock asap + let broker = match LOCAL_BROKER.get() { + None | Some(Err(_)) => return Err(NgError::LocalBrokerNotInitialized), + Some(Ok(broker)) => broker.read().await, + }; + if params.local_save && broker.config.is_in_memory() { + return Err(NgError::CannotSaveWhenInMemoryConfig); + } + } let res = create_wallet_v0(params)?; let lws: LocalWalletStorageV0 = (&res).into(); wallet_add(lws).await?; + Ok(res) } -pub async fn reload_wallets() -> Result<(), NgError> { +#[doc(hidden)] +/// Only used by JS SDK when the localStorage changes and brings out of sync for the Rust side copy of the wallets +pub async fn wallets_reload() -> Result<(), NgError> { let mut broker = match LOCAL_BROKER.get() { None | Some(Err(_)) => return Err(NgError::LocalBrokerNotInitialized), Some(Ok(broker)) => broker.write().await, @@ -274,6 +315,10 @@ pub async fn reload_wallets() -> Result<(), NgError> { Ok(()) } +#[doc(hidden)] +/// This should not be used by programmers. Only here because the JS SDK needs it. +/// +/// It will throw and error if you use it. pub async fn wallet_add(lws: LocalWalletStorageV0) -> Result<(), NgError> { let mut broker = match LOCAL_BROKER.get() { None | Some(Err(_)) => return Err(NgError::LocalBrokerNotInitialized), @@ -319,6 +364,11 @@ pub async fn wallet_add(lws: LocalWalletStorageV0) -> Result<(), NgError> { Ok(()) } +/// Reads a binary Wallet File and decodes it to a Wallet object. +/// +/// This object can be used to import the wallet into a new Device +/// with the help of the function [wallet_open_with_pazzle_words] +/// followed by [wallet_import] pub async fn wallet_read_file(file: Vec) -> Result { let ngf: NgFile = file.try_into()?; if let NgFile::V0(NgFileV0::Wallet(wallet)) = ngf { @@ -338,7 +388,8 @@ pub async fn wallet_read_file(file: Vec) -> Result { } } -pub async fn wallet_download_file(wallet_name: &String) -> Result, NgError> { +/// Retrieves the binary content of a Wallet File for the Wallet identified by its name +pub async fn wallet_get_file(wallet_name: &String) -> Result, NgError> { let broker = match LOCAL_BROKER.get() { None | Some(Err(_)) => return Err(NgError::LocalBrokerNotInitialized), Some(Ok(broker)) => broker.read().await, @@ -350,8 +401,11 @@ pub async fn wallet_download_file(wallet_name: &String) -> Result, NgErr } } +#[doc(hidden)] +/// This is a bit hard to use as the pazzle words are encoded in unsigned bytes. +/// prefer the function wallet_open_with_pazzle_words pub fn wallet_open_with_pazzle( - wallet: Wallet, + wallet: &Wallet, pazzle: Vec, pin: [u8; 4], ) -> Result { @@ -360,6 +414,25 @@ pub fn wallet_open_with_pazzle( Ok(opened_wallet) } +/// Opens a wallet by providing an ordered list of words, and the pin. +/// +/// If you are opening a wallet that is already known to the LocalBroker, you must then call [wallet_was_opened]. +/// Otherwise, if you are importing, then you must call [wallet_import]. +/// +/// For a list of words, see [list_all_words](crate::wallet::emojis::list_all_words) +pub fn wallet_open_with_pazzle_words( + wallet: &Wallet, + pazzle_words: &Vec, + pin: [u8; 4], +) -> Result { + wallet_open_with_pazzle(wallet, encode_pazzle(pazzle_words)?, pin) +} + +/// Imports a wallet into the LocalBroker so the user can then access its content. +/// +/// the wallet should have been previous opened with [wallet_open_with_pazzle_words]. +/// Once import is done, the wallet is already marked as opened, and the user can start a new session right away. +/// There is no need to call wallet_was_opened. pub async fn wallet_import( encrypted_wallet: Wallet, mut opened_wallet: SensitiveWallet, @@ -374,7 +447,7 @@ pub async fn wallet_import( let wallet_name = encrypted_wallet.name(); if broker.wallets.get(&wallet_name).is_some() { - return Err(NgError::WalletAlreadyOpened); + return Err(NgError::WalletAlreadyAdded); } } @@ -385,6 +458,7 @@ pub async fn wallet_import( wallet_was_opened(opened_wallet).await } +/// Must be called after [wallet_open_with_pazzle_words] if you are not importing pub async fn wallet_was_opened(mut wallet: SensitiveWallet) -> Result { let mut broker = match LOCAL_BROKER.get() { None | Some(Err(_)) => return Err(NgError::LocalBrokerNotInitialized), @@ -411,6 +485,11 @@ pub async fn wallet_was_opened(mut wallet: SensitiveWallet) -> Result Result { let mut broker = match LOCAL_BROKER.get() { None | Some(Err(_)) => return Err(NgError::LocalBrokerNotInitialized), @@ -489,7 +568,7 @@ pub async fn session_start(config: SessionConfig) -> Result { // the user was not found in the SWS. we need to create a SPS, add it, encrypt and serialize the new SWS, - // add the SPS to broker.sessions, and return the newly created SPS and the new SWS encryped serialization + // add the SPS to broker.sessions, and return the newly created SPS and the new SWS encrypted serialization let sps = SessionPeerStorageV0::new(user_id); sws.users.insert(user_id.to_string(), sps.clone()); let encrypted = sws.enc_session(&wallet_id)?; @@ -551,10 +630,58 @@ fn get_unix_time() -> f64 { .as_millis() as f64 } +/// Attempts a TCP connection to the Server Broker of the User. +/// +/// The configuration about which Server to contact is stored in the Wallet. +/// The LocalBroker will be in charge of maintaining this connection alive, +/// cycling through optional alternative servers to contact in case of failure, +/// and will notify the user if connection is lost permanently. /// Result is a list of (user_id, server_id, server_ip, error, since_date) +/// If error is None, it means the connection is successful +/// +/// Once the connection is established, the user can sync data, open documents, etc.. with the Verifier API +/// +/// In a future version, it will be possible to be connected to several brokers at the same time +/// (for different users/identities opened concurrently on the same Client) +// TODO: improve this return value +// TODO: give public access to the API for subscribing to disconnections pub async fn user_connect( + user_id: &UserId, +) -> Result, f64)>, NgError> { + let os_info = get_os_info(); + let info = json!({ + "platform": { + "type": "program", + "arch": os_info.get("rust").unwrap().get("arch"), + "debug": os_info.get("rust").unwrap().get("debug"), + "target": os_info.get("rust").unwrap().get("target"), + "arch_uname": os_info.get("uname").unwrap().get("arch"), + "bitness": os_info.get("uname").unwrap().get("bitness"), + "codename": os_info.get("uname").unwrap().get("codename"), + "edition": os_info.get("uname").unwrap().get("edition"), + }, + "os": { + "name": os_info.get("uname").unwrap().get("os_name"), + "family": os_info.get("rust").unwrap().get("family"), + "version": os_info.get("uname").unwrap().get("version"), + "name_rust": os_info.get("rust").unwrap().get("os_name"), + } + }); + + let client_info = ClientInfo::new( + ClientType::NativeService, + info.to_string(), + env!("CARGO_PKG_VERSION").to_string(), + ); + + user_connect_with_device_info(client_info, &user_id, None).await +} + +/// Used internally by JS SDK and Tauri Apps. Do not use "as is". See [user_connect] instead. +#[doc(hidden)] +pub async fn user_connect_with_device_info( info: ClientInfo, - user_id: UserId, + user_id: &UserId, location: Option, ) -> Result, f64)>, NgError> { let local_broker = match LOCAL_BROKER.get() { @@ -564,7 +691,7 @@ pub async fn user_connect( let session = local_broker .opened_sessions - .get(&user_id) + .get(user_id) .ok_or(NgError::SessionNotFound)?; let wallet = local_broker.get_wallet_for_session(&session.config)?; @@ -574,7 +701,6 @@ pub async fn user_connect( match wallet { SensitiveWallet::V0(wallet) => { let client = wallet.client.as_ref().unwrap(); - let client_id = client.id; let client_priv = &client.sensitive_client_storage.priv_key; let client_name = &client.name; let auto_open = &client.auto_open; @@ -688,13 +814,14 @@ pub async fn user_connect( Ok(result) } -pub async fn session_stop(user_id: UserId) -> Result<(), NgError> { +/// Stops the session, that can be resumed later on. All the local data is flushed from RAM. +pub async fn session_stop(user_id: &UserId) -> Result<(), NgError> { let mut broker = match LOCAL_BROKER.get() { None | Some(Err(_)) => return Err(NgError::LocalBrokerNotInitialized), Some(Ok(broker)) => broker.write().await, }; - if broker.opened_sessions.remove(&user_id).is_some() { + if broker.opened_sessions.remove(user_id).is_some() { // TODO: change the logic here once it will be possible to have several users connected at the same time Broker::close_all_connections().await; } @@ -702,13 +829,14 @@ pub async fn session_stop(user_id: UserId) -> Result<(), NgError> { Ok(()) } -pub async fn user_disconnect(user_id: UserId) -> Result<(), NgError> { +/// Disconnects the user from the Server Broker(s), but keep all the local data opened and ready. +pub async fn user_disconnect(user_id: &UserId) -> Result<(), NgError> { let broker = match LOCAL_BROKER.get() { None | Some(Err(_)) => return Err(NgError::LocalBrokerNotInitialized), Some(Ok(broker)) => broker.read().await, }; - if broker.opened_sessions.get(&user_id).is_some() { + if broker.opened_sessions.get(user_id).is_some() { // TODO: change the logic here once it will be possible to have several users connected at the same time Broker::close_all_connections().await; } @@ -716,13 +844,14 @@ pub async fn user_disconnect(user_id: UserId) -> Result<(), NgError> { Ok(()) } -pub async fn wallet_close(wallet_name: String) -> Result<(), NgError> { +/// Closes a wallet, which means that the pazzle will have to be entered again if the user wants to use it +pub async fn wallet_close(wallet_name: &String) -> Result<(), NgError> { let mut broker = match LOCAL_BROKER.get() { None | Some(Err(_)) => return Err(NgError::LocalBrokerNotInitialized), Some(Ok(broker)) => broker.write().await, }; - match broker.opened_wallets.remove(&wallet_name) { + match broker.opened_wallets.remove(wallet_name) { Some(mut wallet) => { for user in wallet.sites() { let key: PubKey = (user.as_str()).try_into().unwrap(); @@ -738,6 +867,7 @@ pub async fn wallet_close(wallet_name: String) -> Result<(), NgError> { Ok(()) } +/// (not implemented yet) pub async fn wallet_remove(wallet_name: String) -> Result<(), NgError> { let mut broker = match LOCAL_BROKER.get() { None | Some(Err(_)) => return Err(NgError::LocalBrokerNotInitialized), diff --git a/ng-app/src-tauri/src/lib.rs b/ng-app/src-tauri/src/lib.rs index 9fb7e10..bf89526 100644 --- a/ng-app/src-tauri/src/lib.rs +++ b/ng-app/src-tauri/src/lib.rs @@ -17,6 +17,7 @@ use ng_repo::types::*; use ng_wallet::types::*; use ng_wallet::*; use serde::{Deserialize, Serialize}; +use serde_json::Value; use std::collections::HashMap; use std::fs::{read, write, File, OpenOptions}; use std::io::Write; @@ -86,14 +87,14 @@ async fn wallet_open_with_pazzle( app: tauri::AppHandle, ) -> Result { //log_debug!("wallet_open_with_pazzle from rust {:?}", pazzle); - let wallet = nextgraph::local_broker::wallet_open_with_pazzle(wallet, pazzle, pin) + let wallet = nextgraph::local_broker::wallet_open_with_pazzle(&wallet, pazzle, pin) .map_err(|e| e.to_string())?; Ok(wallet) } #[tauri::command(rename_all = "snake_case")] -async fn wallet_download_file(wallet_name: String, app: tauri::AppHandle) -> Result<(), String> { - let ser = nextgraph::local_broker::wallet_download_file(&wallet_name) +async fn wallet_get_file(wallet_name: String, app: tauri::AppHandle) -> Result<(), String> { + let ser = nextgraph::local_broker::wallet_get_file(&wallet_name) .await .map_err(|e| e.to_string())?; @@ -135,34 +136,6 @@ async fn wallet_create( Ok(cwr) } -// // TODO: use https://lib.rs/crates/keyring instead of AppLocalData -// async fn save_wallet_locally( -// res: &CreateWalletResultV0, -// app: tauri::AppHandle, -// ) -> Result { -// let path = app -// .path() -// .resolve("wallets", BaseDirectory::AppLocalData) -// .map_err(|_| ())?; -// let mut wallets: HashMap = -// get_all_wallets().unwrap_or(HashMap::new()); -// // check that the wallet is not already present in localStorage -// if wallets.get(&res.wallet_name).is_none() { -// let lws: LocalWalletStorageV0 = res.into(); -// wallets.insert(res.wallet_name.clone(), lws); -// let lws_ser = LocalWalletStorage::v0_to_vec(&wallets); -// let r = write(path.clone(), &lws_ser); -// if r.is_err() { -// log_debug!("write {:?} {}", path, r.unwrap_err()); -// return Err(()); -// } -// let sws = save_new_session(&res.wallet_name, res.wallet.id(), res.user, app)?; -// Ok(sws) -// } else { -// Err(()) -// } -// } - fn take_some_peer_last_seq_numbers( peer_id: PubKey, qty: u16, @@ -233,41 +206,6 @@ async fn wallet_import( nextgraph::local_broker::wallet_import(encrypted_wallet, opened_wallet, in_memory) .await .map_err(|e: NgError| e.to_string()) - - // let path = app - // .path() - // .resolve("wallets", BaseDirectory::AppLocalData) - // .map_err(|_| "wallet directory error".to_string())?; - // let mut wallets: HashMap = - // get_all_wallets().unwrap_or(HashMap::new()); - // // check that the wallet is not already present in localStorage - // let SensitiveWallet::V0(mut opened_wallet_v0) = opened_wallet; - // let wallet_name = opened_wallet_v0.wallet_id.clone(); - // if wallets.get(&wallet_name).is_none() { - // let session = save_new_session( - // &wallet_name, - // opened_wallet_v0.wallet_privkey.to_pub(), - // opened_wallet_v0.personal_site, - // app, - // ) - // .map_err(|_| "Cannot create new session".to_string())?; - // let lws = opened_wallet_v0 - // .import(previous_wallet) - // .map_err(|e| e.to_string())?; - // //let lws = LocalWalletStorageV0::new(wallet, &client); - - // wallets.insert(wallet_name, lws); - // let lws_ser = LocalWalletStorage::v0_to_vec(&wallets); - // let r = write(path.clone(), &lws_ser); - // if r.is_err() { - // log_debug!("write {:?} {}", path, r.unwrap_err()); - // Err("Write error".to_string()) - // } else { - // Ok(client) - // } - // } else { - // Err("Already present on this device".to_string()) - // } } #[tauri::command(rename_all = "snake_case")] @@ -281,8 +219,8 @@ async fn get_wallets( .unwrap(); init_local_broker(Box::new(move || LocalBrokerConfig::BasePath(path.clone()))).await; - let res = get_all_wallets().await.map_err(|e| { - log_err!("get_all_wallets error {}", e.to_string()); + let res = wallets_get_all().await.map_err(|e| { + log_err!("wallets_get_all error {}", e.to_string()); }); if res.is_ok() { return Ok(Some(res.unwrap())); @@ -290,32 +228,6 @@ async fn get_wallets( Ok(None) } -// fn save_new_session( -// wallet_name: &String, -// wallet_id: PubKey, -// user: PubKey, -// app: tauri::AppHandle, -// ) -> Result { -// let mut path = app -// .path() -// .resolve("sessions", BaseDirectory::AppLocalData) -// .map_err(|_| ())?; -// let session_v0 = create_new_session(wallet_id, user); -// if session_v0.is_err() { -// log_debug!("create_new_session failed {}", session_v0.unwrap_err()); -// return Err(()); -// } -// let sws = session_v0.unwrap(); -// std::fs::create_dir_all(path.clone()).unwrap(); -// path.push(wallet_name); -// let res = write(path.clone(), &sws.1); -// if res.is_err() { -// log_debug!("write {:?} {}", path, res.unwrap_err()); -// return Err(()); -// } -// Ok(sws.0) -// } - #[tauri::command(rename_all = "snake_case")] async fn session_start( wallet_name: String, @@ -465,21 +377,21 @@ async fn doc_get_file_from_store_with_object_ref( #[tauri::command(rename_all = "snake_case")] async fn session_stop(user_id: UserId) -> Result<(), String> { - nextgraph::local_broker::session_stop(user_id) + nextgraph::local_broker::session_stop(&user_id) .await .map_err(|e: NgError| e.to_string()) } #[tauri::command(rename_all = "snake_case")] async fn user_disconnect(user_id: UserId) -> Result<(), String> { - nextgraph::local_broker::user_disconnect(user_id) + nextgraph::local_broker::user_disconnect(&user_id) .await .map_err(|e: NgError| e.to_string()) } #[tauri::command(rename_all = "snake_case")] async fn wallet_close(wallet_name: String) -> Result<(), String> { - nextgraph::local_broker::wallet_close(wallet_name) + nextgraph::local_broker::wallet_close(&wallet_name) .await .map_err(|e: NgError| e.to_string()) } @@ -500,7 +412,7 @@ async fn user_connect( ) -> Result, String> { let mut opened_connections: HashMap = HashMap::new(); - let results = nextgraph::local_broker::user_connect(info, user_id, None) + let results = nextgraph::local_broker::user_connect_with_device_info(info, &user_id, None) .await .map_err(|e| e.to_string())?; @@ -518,11 +430,14 @@ async fn user_connect( ); } - //BROKER.read().await.print_status(); - Ok(opened_connections) } +#[tauri::command(rename_all = "snake_case")] +fn client_info_rust() -> Result { + Ok(ng_repo::os_info::get_os_info()) +} + #[derive(Default)] pub struct AppBuilder { setup: Option, @@ -577,7 +492,7 @@ impl AppBuilder { wallet_was_opened, wallet_create, wallet_read_file, - wallet_download_file, + wallet_get_file, wallet_import, wallet_close, encode_create_account, @@ -589,6 +504,7 @@ impl AppBuilder { disconnections_subscribe, user_connect, user_disconnect, + client_info_rust, ]) .run(tauri::generate_context!()) .expect("error while running tauri application"); diff --git a/ng-app/src/App.svelte b/ng-app/src/App.svelte index 819cbd2..b9ab6c6 100644 --- a/ng-app/src/App.svelte +++ b/ng-app/src/App.svelte @@ -108,7 +108,7 @@ window.addEventListener("storage", async (event) => { if (event.storageArea != localStorage) return; if (event.key === "ng_wallets") { - await ng.reload_wallets(); + await ng.wallets_reload(); wallets.set(await ng.get_wallets()); } }); @@ -158,12 +158,12 @@ break; case "opened": if (!$opened_wallets[event.data.wallet.id]) { - console.log( - "ADDING TO OPENED", - event.data.wallet.id, - JSON.stringify($opened_wallets), - event.data.wallet.wallet - ); + // console.log( + // "ADDING TO OPENED", + // event.data.wallet.id, + // JSON.stringify($opened_wallets), + // event.data.wallet.wallet + // ); try { await ng.wallet_was_opened(event.data.wallet.wallet); } catch (e) { @@ -176,7 +176,7 @@ } break; case "new_in_mem": - console.log("GOT new_in_mem", event.data); + //console.log("GOT new_in_mem", event.data); if (event.data.lws) { if (!$wallets[event.data.name]) { await ng.add_in_memory_wallet(event.data.lws); diff --git a/ng-app/src/api.ts b/ng-app/src/api.ts index f180108..01dd2e3 100644 --- a/ng-app/src/api.ts +++ b/ng-app/src/api.ts @@ -20,7 +20,7 @@ const mapping = { "wallet_was_opened": ["opened_wallet"], "wallet_create": ["params"], "wallet_read_file": ["file"], - "wallet_download_file": ["wallet_name"], + "wallet_get_file": ["wallet_name"], "wallet_import": ["encrypted_wallet","opened_wallet","in_memory"], "wallet_close": ["wallet_name"], "encode_create_account": ["payload"], @@ -62,7 +62,8 @@ const handler = { } else { let tauri = await import("@tauri-apps/api/tauri"); if (path[0] === "client_info") { - + let from_rust = await tauri.invoke("client_info_rust",{}); + let tauri_platform = import.meta.env.TAURI_PLATFORM; let client_type; switch (tauri_platform) { @@ -73,20 +74,25 @@ const handler = { case 'ios': client_type = "NativeIos";break; } let info = Bowser.parse(window.navigator.userAgent); + info.os.type = import.meta.env.TAURI_PLATFORM_TYPE; + info.os.family = import.meta.env.TAURI_FAMILY; + info.os.version_tauri = import.meta.env.TAURI_PLATFORM_VERSION; + info.os.version_uname = from_rust.uname.version; + info.os.name_rust = from_rust.rust.os_name; + info.os.name_uname = from_rust.uname.os_name; info.platform.arch = import.meta.env.TAURI_ARCH; - info.platform.tauri = { - family: import.meta.env.TAURI_FAMILY, - os_version: import.meta.env.TAURI_PLATFORM_VERSION, - type: import.meta.env.TAURI_PLATFORM_TYPE, - debug: import.meta.env.TAURI_DEBUG, - target: import.meta.env.TAURI_TARGET_TRIPLE - }; + info.platform.debug = import.meta.env.TAURI_DEBUG; + info.platform.target = import.meta.env.TAURI_TARGET_TRIPLE; + info.platform.arch_uname = from_rust.uname.arch; + info.platform.bitness = from_rust.uname.bitness; + info.platform.codename = from_rust.uname.codename || undefined; + info.platform.edition = from_rust.uname.edition || undefined; info.browser.ua = window.navigator.userAgent; let res = { // TODO: install timestamp V0 : { client_type, details: JSON.stringify(info), version, timestamp_install:0, timestamp_updated:0 } }; - //console.log(res); + console.log(info,res); return res; } else if (path[0] === "disconnections_subscribe") { let { getCurrent } = await import("@tauri-apps/plugin-window"); diff --git a/ng-app/src/routes/User.svelte b/ng-app/src/routes/User.svelte index f787db1..1a33c36 100644 --- a/ng-app/src/routes/User.svelte +++ b/ng-app/src/routes/User.svelte @@ -95,7 +95,7 @@ async function download_wallet() { try { downloading = true; - let file = await ng.wallet_download_file($active_wallet.id); + let file = await ng.wallet_get_file($active_wallet.id); // @ts-ignore wallet_file_ready = "wallet-" + $active_wallet.id + ".ngw"; if (!tauri_platform) { diff --git a/ng-app/src/routes/WalletCreate.svelte b/ng-app/src/routes/WalletCreate.svelte index 58f3d64..0641b93 100644 --- a/ng-app/src/routes/WalletCreate.svelte +++ b/ng-app/src/routes/WalletCreate.svelte @@ -156,7 +156,7 @@ invitation = await ng.decode_invitation(param.get("i")); window.location.replace(window.location.href.split("?")[0]); } else if (param.get("i")) { - invitation = await ng.get_local_bootstrap( + invitation = await ng.get_local_bootstrap_with_public( location.href, param.get("i") ); diff --git a/ng-app/src/wallet_emojis.ts b/ng-app/src/wallet_emojis.ts index 5b7b450..545e4ab 100644 --- a/ng-app/src/wallet_emojis.ts +++ b/ng-app/src/wallet_emojis.ts @@ -33,7 +33,7 @@ let face = [ { hexcode: "1f60d", shortcode: "smiling_face_with_heart_eyes", - code: "two_hearts", + code: "with_two_hearts", }, { hexcode: "1f618", @@ -44,12 +44,12 @@ let face = [ { hexcode: "1f61d", shortcode: "squinting_face_with_tongue", - code: "tongue", + code: "with_tongue", }, { hexcode: "1f917", shortcode: "hugging_face", - code: "two_hands", + code: "with_two_hands", }, { hexcode: "1f92d", @@ -100,7 +100,7 @@ let face = [ { hexcode: "1f912", shortcode: "face_with_thermometer", - code: "thermometer", + code: "fever", }, { hexcode: "1f915", @@ -137,7 +137,7 @@ let face = [ { hexcode: "1f92f", shortcode: "exploding_head", - code: "explosion", + code: "exploding", }, { @@ -266,7 +266,7 @@ let face = [ { hexcode: "2764", shortcode: "red_heart", - code: "one_heart", + code: "red_heart", }, { hexcode: "1f495", diff --git a/ng-broker/README.md b/ng-broker/README.md index 987bea9..efa9d43 100644 --- a/ng-broker/README.md +++ b/ng-broker/README.md @@ -10,9 +10,9 @@ This repository is in active development at [https://git.nextgraph.org/NextGraph ## NextGraph -> NextGraph brings about the convergence between P2P and Semantic Web technologies, towards a decentralized, secure and privacy-preserving cloud, based on CRDTs. +> NextGraph brings about the convergence of P2P and Semantic Web technologies, towards a decentralized, secure and privacy-preserving cloud, based on CRDTs. > -> This open source ecosystem provides solutions for end-users and software developers alike, wishing to use or create **decentralized** apps featuring: **live collaboration** on rich-text documents, peer to peer communication with **end-to-end encryption**, offline-first, **local-first**, portable and interoperable data, total ownership of data and software, security and privacy. Centered on repositories containing **semantic data** (RDF), **rich text**, and structured data formats like **JSON**, synced between peers belonging to permissioned groups of users, it offers strong eventual consistency, thanks to the use of **CRDTs**. Documents can be linked together, signed, shared securely, queried using the **SPARQL** language and organized into sites and containers. +> This open source ecosystem provides solutions for end-users (a platform) and software developers (a framework), wishing to use or create **decentralized** apps featuring: **live collaboration** on rich-text documents, peer to peer communication with **end-to-end encryption**, offline-first, **local-first**, portable and interoperable data, total ownership of data and software, security and privacy. Centered on repositories containing **semantic data** (RDF), **rich text**, and structured data formats like **JSON**, synced between peers belonging to permissioned groups of users, it offers strong eventual consistency, thanks to the use of **CRDTs**. Documents can be linked together, signed, shared securely, queried using the **SPARQL** language and organized into sites and containers. > > More info here [https://nextgraph.org](https://nextgraph.org) diff --git a/ng-broker/src/server_ws.rs b/ng-broker/src/server_ws.rs index fbafbeb..b097910 100644 --- a/ng-broker/src/server_ws.rs +++ b/ng-broker/src/server_ws.rs @@ -30,8 +30,6 @@ use async_tungstenite::tungstenite::http::{ use async_tungstenite::tungstenite::protocol::Message; use futures::{SinkExt, StreamExt}; -use once_cell::sync::Lazy; -use once_cell::sync::OnceCell; use ng_client_ws::remote_ws::ConnectionWebSocket; use ng_net::broker::*; use ng_net::connection::IAccept; @@ -44,6 +42,8 @@ use ng_repo::log::*; use ng_repo::types::SymKey; use ng_repo::types::{PrivKey, PubKey}; use ng_repo::utils::generate_keypair; +use once_cell::sync::Lazy; +use once_cell::sync::OnceCell; use rust_embed::RustEmbed; use serde_json::json; use std::collections::HashMap; @@ -733,6 +733,7 @@ pub async fn run_server_v0( for server_type in server_types { servers.push(BrokerServerV0 { peer_id: common_peer_id.unwrap_or(peer_id), + can_verify: false, server_type, }) } diff --git a/ng-client-ws/README.md b/ng-client-ws/README.md index 8669058..0eb5805 100644 --- a/ng-client-ws/README.md +++ b/ng-client-ws/README.md @@ -10,9 +10,9 @@ This repository is in active development at [https://git.nextgraph.org/NextGraph ## NextGraph -> NextGraph brings about the convergence between P2P and Semantic Web technologies, towards a decentralized, secure and privacy-preserving cloud, based on CRDTs. +> NextGraph brings about the convergence of P2P and Semantic Web technologies, towards a decentralized, secure and privacy-preserving cloud, based on CRDTs. > -> This open source ecosystem provides solutions for end-users and software developers alike, wishing to use or create **decentralized** apps featuring: **live collaboration** on rich-text documents, peer to peer communication with **end-to-end encryption**, offline-first, **local-first**, portable and interoperable data, total ownership of data and software, security and privacy. Centered on repositories containing **semantic data** (RDF), **rich text**, and structured data formats like **JSON**, synced between peers belonging to permissioned groups of users, it offers strong eventual consistency, thanks to the use of **CRDTs**. Documents can be linked together, signed, shared securely, queried using the **SPARQL** language and organized into sites and containers. +> This open source ecosystem provides solutions for end-users (a platform) and software developers (a framework), wishing to use or create **decentralized** apps featuring: **live collaboration** on rich-text documents, peer to peer communication with **end-to-end encryption**, offline-first, **local-first**, portable and interoperable data, total ownership of data and software, security and privacy. Centered on repositories containing **semantic data** (RDF), **rich text**, and structured data formats like **JSON**, synced between peers belonging to permissioned groups of users, it offers strong eventual consistency, thanks to the use of **CRDTs**. Documents can be linked together, signed, shared securely, queried using the **SPARQL** language and organized into sites and containers. > > More info here [https://nextgraph.org](https://nextgraph.org) diff --git a/ng-net/README.md b/ng-net/README.md index c0126a3..4fabb6a 100644 --- a/ng-net/README.md +++ b/ng-net/README.md @@ -10,9 +10,9 @@ This repository is in active development at [https://git.nextgraph.org/NextGraph ## NextGraph -> NextGraph brings about the convergence between P2P and Semantic Web technologies, towards a decentralized, secure and privacy-preserving cloud, based on CRDTs. +> NextGraph brings about the convergence of P2P and Semantic Web technologies, towards a decentralized, secure and privacy-preserving cloud, based on CRDTs. > -> This open source ecosystem provides solutions for end-users and software developers alike, wishing to use or create **decentralized** apps featuring: **live collaboration** on rich-text documents, peer to peer communication with **end-to-end encryption**, offline-first, **local-first**, portable and interoperable data, total ownership of data and software, security and privacy. Centered on repositories containing **semantic data** (RDF), **rich text**, and structured data formats like **JSON**, synced between peers belonging to permissioned groups of users, it offers strong eventual consistency, thanks to the use of **CRDTs**. Documents can be linked together, signed, shared securely, queried using the **SPARQL** language and organized into sites and containers. +> This open source ecosystem provides solutions for end-users (a platform) and software developers (a framework), wishing to use or create **decentralized** apps featuring: **live collaboration** on rich-text documents, peer to peer communication with **end-to-end encryption**, offline-first, **local-first**, portable and interoperable data, total ownership of data and software, security and privacy. Centered on repositories containing **semantic data** (RDF), **rich text**, and structured data formats like **JSON**, synced between peers belonging to permissioned groups of users, it offers strong eventual consistency, thanks to the use of **CRDTs**. Documents can be linked together, signed, shared securely, queried using the **SPARQL** language and organized into sites and containers. > > More info here [https://nextgraph.org](https://nextgraph.org) diff --git a/ng-net/src/actor.rs b/ng-net/src/actor.rs index 4366cdb..fee6a53 100644 --- a/ng-net/src/actor.rs +++ b/ng-net/src/actor.rs @@ -9,6 +9,8 @@ * according to those terms. */ +//! Actor handles messages in the Protocol. common types are here + use async_std::stream::StreamExt; use async_std::sync::Mutex; use futures::{channel::mpsc, SinkExt}; diff --git a/ng-net/src/actors/mod.rs b/ng-net/src/actors/mod.rs index 2896297..0e27941 100644 --- a/ng-net/src/actors/mod.rs +++ b/ng-net/src/actors/mod.rs @@ -1,3 +1,5 @@ +//! List of actors, each one for a specific Protocol message + pub mod noise; pub use noise::*; diff --git a/ng-net/src/broker.rs b/ng-net/src/broker.rs index 63e7ff1..32a38f9 100644 --- a/ng-net/src/broker.rs +++ b/ng-net/src/broker.rs @@ -9,6 +9,8 @@ * according to those terms. */ +//! Broker singleton present in every instance of NextGraph (Client, Server, Core node) + use crate::connection::*; use crate::errors::*; use crate::server_storage::ServerStorage; diff --git a/ng-net/src/connection.rs b/ng-net/src/connection.rs index 869a7b8..80946dc 100644 --- a/ng-net/src/connection.rs +++ b/ng-net/src/connection.rs @@ -9,6 +9,8 @@ * according to those terms. */ +//! Finite State Machine of the connection/protocol/Noise channel + //static NOISE_CONFIG: &'static str = "Noise_XK_25519_ChaChaPoly_BLAKE2b"; use std::any::TypeId; @@ -28,11 +30,11 @@ use async_std::stream::StreamExt; use async_std::sync::Mutex; use either::Either; use futures::{channel::mpsc, select, FutureExt, SinkExt}; -use noise_protocol::{patterns::noise_xk, CipherState, HandshakeState}; -use noise_rust_crypto::*; use ng_repo::log::*; use ng_repo::types::{DirectPeerId, PrivKey, PubKey, X25519PrivKey}; use ng_repo::utils::{sign, verify}; +use noise_protocol::{patterns::noise_xk, CipherState, HandshakeState}; +use noise_rust_crypto::*; use serde_bare::from_slice; use unique_id::sequence::SequenceGenerator; use unique_id::Generator; diff --git a/ng-net/src/lib.rs b/ng-net/src/lib.rs index 1377503..fbe5e65 100644 --- a/ng-net/src/lib.rs +++ b/ng-net/src/lib.rs @@ -29,18 +29,25 @@ pub mod actors; pub mod utils; +#[doc(hidden)] pub mod tests; +#[doc(hidden)] pub static NG_BOOTSTRAP_LOCAL_PATH: &str = "/.ng_bootstrap"; #[cfg(debug_assertions)] +#[doc(hidden)] pub static WS_PORT: u16 = 14400; #[cfg(not(debug_assertions))] +#[doc(hidden)] pub static WS_PORT: u16 = 80; +#[doc(hidden)] pub static WS_PORT_ALTERNATE: [u16; 4] = [14400, 28800, 43200, 57600]; +#[doc(hidden)] pub static WS_PORT_ALTERNATE_SUPERUSER: u16 = 144; +#[doc(hidden)] pub static WS_PORT_REVERSE_PROXY: u16 = 1440; diff --git a/ng-net/src/server_storage.rs b/ng-net/src/server_storage.rs index f8fa83b..61fbe29 100644 --- a/ng-net/src/server_storage.rs +++ b/ng-net/src/server_storage.rs @@ -9,6 +9,8 @@ * according to those terms. */ +//! Trait for ServerStorage + use crate::{ errors::{ProtocolError, ServerError}, types::*, diff --git a/ng-net/src/tests/mod.rs b/ng-net/src/tests/mod.rs index 2e172cd..dbdc09f 100644 --- a/ng-net/src/tests/mod.rs +++ b/ng-net/src/tests/mod.rs @@ -1 +1,2 @@ +#[doc(hidden)] pub mod file; diff --git a/ng-net/src/types.rs b/ng-net/src/types.rs index 78a9747..e7d75ee 100644 --- a/ng-net/src/types.rs +++ b/ng-net/src/types.rs @@ -7,7 +7,7 @@ // notice may not be copied, modified, or distributed except // according to those terms. -//! P2P network protocol types +//! NextGraph network protocol types //! //! Corresponds to the BARE schema @@ -15,6 +15,7 @@ use crate::utils::{ get_domain_without_port_443, is_ipv4_private, is_ipv6_private, is_private_ip, is_public_ip, is_public_ipv4, is_public_ipv6, }; +use crate::WS_PORT_ALTERNATE; use crate::{actor::EActor, actors::*, errors::ProtocolError}; use core::fmt; use ng_repo::errors::NgError; @@ -109,7 +110,9 @@ impl From<&SocketAddr> for BindAddress { } } -/// BROKER common types +// +// BROKER common types +// /// Core Broker connection details Version 0 #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)] @@ -144,16 +147,33 @@ pub struct BrokerServerV0 { /// Network addresses pub server_type: BrokerServerTypeV0, + /// is this server capable of running a verifier + pub can_verify: bool, + /// peerId of the server pub peer_id: PubKey, } +impl BrokerServerV0 { + pub fn new_localhost(peer_id: PubKey) -> Self { + BrokerServerV0 { + server_type: BrokerServerTypeV0::Localhost(WS_PORT_ALTERNATE[0]), + can_verify: false, + peer_id, + } + } +} + +#[doc(hidden)] pub const APP_ACCOUNT_REGISTERED_SUFFIX: &str = "/#/user/registered"; +#[doc(hidden)] pub const NG_ONE_URL: &str = "https://nextgraph.one"; +#[doc(hidden)] pub const APP_NG_ONE_URL: &str = "https://app.nextgraph.one"; +#[doc(hidden)] pub const APP_NG_ONE_WS_URL: &str = "wss://app.nextgraph.one"; #[allow(dead_code)] @@ -161,16 +181,18 @@ fn api_dyn_peer_url(peer_id: &PubKey) -> String { format!("https://nextgraph.one/api/v1/dynpeer/{}", peer_id) } +#[doc(hidden)] pub const LOCAL_HOSTS: [&str; 3] = ["localhost", "127.0.0.1", "[::1]"]; fn local_ws_url(port: &u16) -> String { format!("ws://localhost:{}", if *port == 0 { 80 } else { *port }) } - -pub fn local_http_url(port: &u16) -> String { +#[doc(hidden)] +pub(crate) fn local_http_url(port: &u16) -> String { format!("http://localhost:{}", if *port == 0 { 80 } else { *port }) } +#[doc(hidden)] pub const LOCAL_URLS: [&str; 3] = ["http://localhost", "http://127.0.0.1", "http://[::1]"]; use url::{Host, Url}; @@ -451,7 +473,12 @@ pub struct BootstrapContentV0 { } impl BootstrapContentV0 { - pub fn new() -> Self { + pub fn new_localhost(peer_id: PubKey) -> Self { + BootstrapContentV0 { + servers: vec![BrokerServerV0::new_localhost(peer_id)], + } + } + pub fn new_empty() -> Self { BootstrapContentV0 { servers: vec![] } } pub fn merge(&mut self, with: &BootstrapContentV0) { @@ -576,7 +603,7 @@ impl InvitationV0 { } pub fn empty(name: Option) -> Self { InvitationV0 { - bootstrap: BootstrapContentV0::new(), + bootstrap: BootstrapContentV0::new_empty(), code: None, name, url: None, @@ -635,7 +662,7 @@ impl Invitation { pub fn intersects(&self, invite2: Invitation) -> Invitation { let Invitation::V0(v0) = self; let mut new_invite = InvitationV0 { - bootstrap: BootstrapContentV0::new(), + bootstrap: BootstrapContentV0::new_empty(), code: v0.code.clone(), name: v0.name.clone(), url: v0.url.clone(), @@ -816,6 +843,7 @@ pub struct ListenerInfo { } /// AcceptForwardForV0 type +/// /// allow answers to connection requests originating from a client behind a reverse proxy /// Format of last param in the tuple is a list of comma separated hosts or CIDR subnetworks IPv4 and/or IPv6 addresses accepted as X-Forwarded-For /// Empty string means all addresses are accepted @@ -1406,6 +1434,7 @@ pub enum OverlayLeave { } /// Content of PublisherAdvertV0 +/// /// the peer is matched with the InnerOverlayMessageV0.Session -> peerId. #[derive(Clone, Copy, Debug, Serialize, Deserialize)] pub struct PublisherAdvertContentV0 { @@ -1530,6 +1559,7 @@ pub enum BlockSearchTopic { } /// Block search along a random walk in the overlay +/// /// fanout is always 1 /// if result is none, tries another path if several paths available locally /// answered with a stream BlockResult @@ -1556,6 +1586,7 @@ pub enum BlockSearchRandom { } /// Response to a BlockSearch* request +/// /// can be a stream #[derive(Clone, Debug, Serialize, Deserialize)] pub struct BlockResultV0 { @@ -1564,6 +1595,7 @@ pub struct BlockResultV0 { } /// Response to a BlockSearch* request +/// /// can be a stream #[derive(Clone, Debug, Serialize, Deserialize)] pub enum BlockResult { @@ -1613,6 +1645,7 @@ pub enum PeerStatus { } /// ForwardedPeerAdvertV0 +/// /// peer_advert.forwarded_by is matched with sessionid->peerid #[derive(Clone, Debug, Serialize, Deserialize)] pub struct ForwardedPeerAdvertV0 { @@ -1636,6 +1669,7 @@ pub enum ForwardedPeerAdvert { } /// ForwardedPeerConflictV0 +/// /// peer_advert.forwarded_by is matched with sessionid->peerid /// When the forwarding broker receives the conflict (or notices it), it sends a notification /// In order to avoid conflicts, the highest version of PeerAdvert should win, when the Forwarding Broker is different. @@ -1803,6 +1837,7 @@ pub enum OverlayAdvert { } /// CoreBrokerJoinedAdvert V0 +/// /// Each broker that is already part of an overlay, when receiving the CoreBrokerJoinedAdvert, should answer with one direct message /// to the joining peer (found in OverlayAdvertPayloadV0.peer) for each overlay, containing an OverlayAdvertMarker containing their current sequence number. /// This is sent for each path (in case multiple paths arrive to the same broker). Only the first sequence number received by joining peer is kept locally @@ -1814,6 +1849,7 @@ pub struct CoreBrokerJoinedAdvertV0 { } /// CoreBrokerLeftAdvert V0 +/// /// A broker has disconnected from another broker, and the routes need to be updated /// this is not used to leave one specific overlay. see OverlayLeave message for that purpose #[derive(Clone, Debug, Serialize, Deserialize)] @@ -1871,6 +1907,7 @@ pub struct CoreAdvertV0 { } /// OverlayAdvertMarker V0 +/// /// when receiving a marker, the broker saves the ingress edge and the corresponding remote peer and /// overlay that can be reached (the OverlayAdvertPayloadV0.peer and .overlay) in the CoreRoutingTable /// It also saves the sessionId and seq number @@ -1905,6 +1942,7 @@ pub struct CoreBlockGetV0 { } /// Core Block Result V0 +/// /// can be a stream #[derive(Clone, Debug, Serialize, Deserialize)] pub struct CoreBlockResultV0 { @@ -1980,6 +2018,7 @@ pub struct CoreDirectMessageV0 { } /// CoreBrokerConnect V0 +/// /// replied with CoreBrokerConnectResponse #[derive(Clone, Debug, Serialize, Deserialize)] pub struct CoreBrokerConnectV0 { @@ -2017,7 +2056,8 @@ impl CoreBrokerConnect { pub type CoreBrokerDisconnectV0 = (); /// Content of CoreOverlayJoin V0 -/// // replied with an emptyResponse, and an error code if OverlayId not present on remote broker +/// +/// replied with an emptyResponse, and an error code if OverlayId not present on remote broker #[derive(Clone, Debug, Serialize, Deserialize)] pub enum CoreOverlayJoinV0 { Inner(OverlayAdvert), @@ -2045,6 +2085,7 @@ pub enum OuterOverlayRequestContentV0 { } /// OuterOverlayRequestV0 V0 +/// /// replied with OuterOverlayResponseV0 #[derive(Clone, Debug, Serialize, Deserialize)] pub struct OuterOverlayRequestV0 { @@ -2053,6 +2094,7 @@ pub struct OuterOverlayRequestV0 { } /// OuterOverlayResponse V0 +/// /// reply to an OuterOverlayRequest V0 #[derive(Clone, Debug, Serialize, Deserialize)] pub struct OuterOverlayResponseV0 { @@ -2100,6 +2142,7 @@ pub enum TopicSyncResV0 { } /// Topic synchronization response +/// /// it is a stream of blocks and or events. #[derive(Clone, Debug, Serialize, Deserialize)] pub enum TopicSyncRes { @@ -2134,6 +2177,7 @@ pub enum CoreRequestContentV0 { } /// CoreRequest V0 +/// /// replied with CoreResponse V0 #[derive(Clone, Debug, Serialize, Deserialize)] pub struct CoreRequestV0 { @@ -2153,6 +2197,7 @@ pub enum CoreRequest { } /// CoreBrokerConnectResponse V0 +/// /// reply to a CoreBrokerConnect V0 #[derive(Clone, Debug, Serialize, Deserialize)] pub struct CoreBrokerConnectResponseV0 { @@ -2176,7 +2221,8 @@ pub enum CoreResponseContentV0 { } /// CoreResponse V0 -/// reply to an CoreRequest V0 +/// +/// reply to a CoreRequest V0 #[derive(Clone, Debug, Serialize, Deserialize)] pub struct CoreResponseV0 { /// Request ID @@ -2467,6 +2513,7 @@ impl AdminResponse { // /// Request to open a repo in a non-durable way (without pinning it). +/// /// When client will disconnect, the subscriptions and publisherAdvert of the topics will be removed, /// except if a PinRepo occurred before or after the OpenRepo #[derive(Clone, Debug, Serialize, Deserialize)] @@ -2515,6 +2562,7 @@ impl OpenRepo { } /// Request to pin a repo on the broker. +/// /// When client will disconnect, the subscriptions and publisherAdvert of the topics will be remain active on the broker, #[derive(Clone, Debug, Serialize, Deserialize)] pub struct PinRepoV0 { @@ -2570,6 +2618,7 @@ impl PinRepo { } /// Request to refresh the Pinning of a previously pinned repo. +/// /// it can consist of updating the expose_outer, the list of ro_topics and/or rw_topics, /// and in case of a ban_member, the broker will effectively flush the topics locally after all local members except the banned one, have refreshed #[derive(Clone, Debug, Serialize, Deserialize)] @@ -2599,6 +2648,7 @@ pub enum RefreshPinRepo { } /// Request to unpin a repo on the broker. +/// /// When client will disconnect, the subscriptions and publisherAdvert of the topics will be removed on the broker /// (for that user only. other users might continue to have the repo pinned) @@ -2623,6 +2673,7 @@ impl UnpinRepo { } /// Request the status of pinning for a repo on the broker. V0 +/// /// returns an error code if not pinned, otherwise returns a RepoPinStatusV0 /// the overlay entered in ClientMessage is important. if it is the outer, only outer pinning will be checked. /// if it is the inner overlay, only the inner pinning will be checked. @@ -2715,6 +2766,7 @@ pub enum TopicUnsub { } /// Request a Block by ID +/// /// commit_header_key is always set to None in the reply when request is made on OuterOverlay of protected or Group overlays #[derive(Clone, Debug, Serialize, Deserialize)] pub struct BlockGetV0 { @@ -2775,6 +2827,7 @@ impl BlocksPut { } /// Request to know if some blocks are present locally +/// /// used by client before publishing an event, to know what to push #[derive(Clone, Debug, Serialize, Deserialize)] pub struct BlocksExistV0 { @@ -2841,6 +2894,7 @@ impl ObjectUnpin { } /// Request to delete an object +/// /// only effective if the refcount for this object is zero (basically it removes it from LRU) #[derive(Clone, Copy, Debug, Serialize, Deserialize)] pub struct ObjectDelV0 { @@ -3173,6 +3227,7 @@ pub struct ExtRequestV0 { } /// External request are made by clients directly to a core broker of their choice. +/// /// They differ from OuterOverlayRequests in the sense that the broker where the client is attached, is not involved in the request. /// It is a direct connection that is established between the client and the core broker that will give the response. #[derive(Clone, Debug, Serialize, Deserialize)] @@ -3248,11 +3303,12 @@ impl TryFrom for ExtResponse { } } -/// -/// PROTOCOL MESSAGES -/// - +// +// PROTOCOL MESSAGES +// +#[doc(hidden)] pub static MAGIC_NG_REQUEST: [u8; 2] = [78u8, 71u8]; +#[doc(hidden)] pub static MAGIC_NG_RESPONSE: [u8; 4] = [89u8, 88u8, 78u8, 75u8]; #[derive(Clone, Debug)] @@ -3392,9 +3448,9 @@ impl ProtocolMessage { } } -/// -/// AUTHENTICATION MESSAGES -/// +// +// AUTHENTICATION MESSAGES +// /// Content of ClientAuthV0 #[derive(Clone, Debug, Serialize, Deserialize)] @@ -3510,6 +3566,7 @@ impl From for ProtocolMessage { // /// Link to a repository +/// /// Consists of an identifier (repoid), a ReadCap or WriteCap, and a locator (peers and overlayLink) /// Those capabilities are not durable: They can be refreshed by the members and previously shared Caps will become obsolete/revoked. /// As long as the user is a member of the repo and subscribes to the root topic (of the repo, and of the store if needed/applicable), they will receive the updated capabilities. @@ -3555,11 +3612,13 @@ impl RepoLink { } } +/// Link for a Public Repo +/// /// The latest ReadCap of the branch (or main branch) will be downloaded from the outerOverlay, if the peer brokers listed below allow it. /// The snapshot can be downloaded instead -/// This locator is durable, because the public site are served differently by brokers. +/// This link is durable, because the public site are served differently by brokers. #[derive(Clone, Debug, Serialize, Deserialize)] -pub struct PublicRepoLocatorV0 { +pub struct PublicRepoLinkV0 { /// Repository ID pub repo: RepoId, @@ -3583,11 +3642,12 @@ pub struct PublicRepoLocatorV0 { /// Link to a public repository #[derive(Clone, Debug, Serialize, Deserialize)] -pub enum PublicRepoLocator { - V0(PublicRepoLocatorV0), +pub enum PublicRepoLink { + V0(PublicRepoLinkV0), } /// Read access to a branch of a Public, Protected or Group store. +/// /// The overlay to join can be the outer or the inner, depending on what was offered in the link. /// The difference between the two is that in the outer overlay, only one broker is contacted. /// In the inner overlay, all the publisher's brokers are contacted, so subscription to the pub/sub is more reliable, less prone to outage. @@ -3623,8 +3683,9 @@ pub enum ReadBranchLink { } /// Obtains one or more objects of a repo (Commit, File) by their ID. +/// /// On an outerOverlay, the header is always emptied (no way to reconstruct the DAG of commits) except on public overlays or if a topicId is provided -/// If the intent is to share a whole DAG of commits at a definite CommitID/HEAD, then ReadBranchLink should be used instead (or PublicRepoLocator if public site) +/// If the intent is to share a whole DAG of commits at a definite CommitID/HEAD, then ReadBranchLink should be used instead (or PublicRepoLink if public site) #[derive(Clone, Debug, Serialize, Deserialize)] pub struct ObjectLinkV0 { /// Repository ID: not used to make the request. but useful for commits, to know which repo they are from without needing to fetch and open the full DAG of commits. @@ -3658,7 +3719,7 @@ pub enum ObjectLink { #[derive(Clone, Debug, Serialize, Deserialize)] pub enum NgLinkV0 { Repo(RepoLink), - PublicRepo(PublicRepoLocator), + PublicRepo(PublicRepoLink), Branch(ReadBranchLink), Object(ObjectLink), } @@ -3683,6 +3744,7 @@ mod test { BootstrapContentV0 { servers: vec![BrokerServerV0 { server_type: BrokerServerTypeV0::Localhost(14400), + can_verify: false, peer_id: PubKey::Ed25519PubKey([ 95, 73, 225, 250, 3, 147, 24, 164, 177, 211, 34, 244, 45, 130, 111, 136, 229, 145, 53, 167, 50, 168, 140, 227, 65, 111, 203, 41, 210, 186, 162, 149, diff --git a/ng-repo/Cargo.toml b/ng-repo/Cargo.toml index 6fd6e12..f4b2ce6 100644 --- a/ng-repo/Cargo.toml +++ b/ng-repo/Cargo.toml @@ -39,6 +39,9 @@ threshold_crypto = "0.4.0" zeroize = { version = "1.6.0", features = ["zeroize_derive"] } time = { version= "0.3.23", features = ["formatting"] } once_cell = "1.17.1" +serde_json = "1.0" +os_info = "3" +current_platform = "0.2.0" [target.'cfg(not(target_arch = "wasm32"))'.dependencies] debug_print = "1.0.0" diff --git a/ng-repo/README.md b/ng-repo/README.md index 1c594bb..38b8a74 100644 --- a/ng-repo/README.md +++ b/ng-repo/README.md @@ -10,9 +10,9 @@ This repository is in active development at [https://git.nextgraph.org/NextGraph ## NextGraph -> NextGraph brings about the convergence between P2P and Semantic Web technologies, towards a decentralized, secure and privacy-preserving cloud, based on CRDTs. +> NextGraph brings about the convergence of P2P and Semantic Web technologies, towards a decentralized, secure and privacy-preserving cloud, based on CRDTs. > -> This open source ecosystem provides solutions for end-users and software developers alike, wishing to use or create **decentralized** apps featuring: **live collaboration** on rich-text documents, peer to peer communication with **end-to-end encryption**, offline-first, **local-first**, portable and interoperable data, total ownership of data and software, security and privacy. Centered on repositories containing **semantic data** (RDF), **rich text**, and structured data formats like **JSON**, synced between peers belonging to permissioned groups of users, it offers strong eventual consistency, thanks to the use of **CRDTs**. Documents can be linked together, signed, shared securely, queried using the **SPARQL** language and organized into sites and containers. +> This open source ecosystem provides solutions for end-users (a platform) and software developers (a framework), wishing to use or create **decentralized** apps featuring: **live collaboration** on rich-text documents, peer to peer communication with **end-to-end encryption**, offline-first, **local-first**, portable and interoperable data, total ownership of data and software, security and privacy. Centered on repositories containing **semantic data** (RDF), **rich text**, and structured data formats like **JSON**, synced between peers belonging to permissioned groups of users, it offers strong eventual consistency, thanks to the use of **CRDTs**. Documents can be linked together, signed, shared securely, queried using the **SPARQL** language and organized into sites and containers. > > More info here [https://nextgraph.org](https://nextgraph.org) diff --git a/ng-repo/src/block.rs b/ng-repo/src/block.rs index c44b696..8f271c8 100644 --- a/ng-repo/src/block.rs +++ b/ng-repo/src/block.rs @@ -7,7 +7,7 @@ // notice may not be copied, modified, or distributed except // according to those terms. -//! Immutable Block +//! Immutable Block, used to store and exchange File and Commit use crate::errors::*; use crate::log::*; diff --git a/ng-repo/src/commit.rs b/ng-repo/src/commit.rs index 545535c..5a094c1 100644 --- a/ng-repo/src/commit.rs +++ b/ng-repo/src/commit.rs @@ -7,7 +7,7 @@ // notice may not be copied, modified, or distributed except // according to those terms. -//! Commit +//! Commit that composes the DAG of a Branch use core::fmt; use ed25519_dalek::{PublicKey, Signature}; diff --git a/ng-repo/src/errors.rs b/ng-repo/src/errors.rs index 9f676e3..6475f3a 100644 --- a/ng-repo/src/errors.rs +++ b/ng-repo/src/errors.rs @@ -28,6 +28,7 @@ pub enum NgError { InvalidFileFormat, InvalidArgument, PermissionDenied, + InvalidPazzle, CommitLoadError(CommitLoadError), StorageError(StorageError), NotFound, @@ -42,7 +43,6 @@ pub enum NgError { WalletAlreadyOpened, WalletError(String), BrokerError, - LockError, SessionNotFound, } @@ -57,6 +57,48 @@ impl fmt::Display for NgError { } } +impl From for std::io::Error { + fn from(err: NgError) -> std::io::Error { + match err { + NgError::InvalidArgument => std::io::Error::from(std::io::ErrorKind::InvalidInput), + NgError::PermissionDenied => std::io::Error::from(std::io::ErrorKind::PermissionDenied), + NgError::CommitLoadError(commit_load_error) => std::io::Error::new( + std::io::ErrorKind::Other, + format!("CommitLoadError: {:?}", commit_load_error), + ), + NgError::StorageError(storage_error) => std::io::Error::new( + std::io::ErrorKind::Other, + format!("StorageError: {:?}", storage_error), + ), + NgError::NotFound => std::io::Error::from(std::io::ErrorKind::NotFound), + NgError::CommitVerifyError(commit_verify_error) => std::io::Error::new( + std::io::ErrorKind::Other, + format!("CommitVerifyError: {:?}", commit_verify_error), + ), + /*NgError::InvalidSignature => , + NgError::IncompleteSignature => + NgError::SerializationError => , + NgError::EncryptionError => , + NgError::InvalidKey => , + NgError::InvalidInvitation => , + NgError::InvalidCreateAccount => , + NgError::InvalidFileFormat => , + NgError::LocalBrokerNotInitialized => , + NgError::JsStorageReadError => , + NgError::JsStorageWriteError(String) => , + NgError::CannotSaveWhenInMemoryConfig => , + NgError::WalletNotFound => , + NgError::WalletAlreadyAdded => , + NgError::WalletAlreadyOpened => , + NgError::WalletError(String) => , + NgError::BrokerError => , + NgError::SessionNotFound, + NgError::IoError => ,*/ + _ => std::io::Error::new(std::io::ErrorKind::Other, err.to_string().as_str()), + } + } +} + impl From for NgError { fn from(_e: serde_bare::error::Error) -> Self { NgError::SerializationError diff --git a/ng-repo/src/event.rs b/ng-repo/src/event.rs index 2a7c07b..e567fd9 100644 --- a/ng-repo/src/event.rs +++ b/ng-repo/src/event.rs @@ -6,7 +6,7 @@ // notice may not be copied, modified, or distributed except // according to those terms. -//! Event +//! Event, a message sent in the PUB/SUB use crate::errors::*; use crate::object::*; diff --git a/ng-repo/src/file.rs b/ng-repo/src/file.rs index 8eb869d..8ac50fd 100644 --- a/ng-repo/src/file.rs +++ b/ng-repo/src/file.rs @@ -7,7 +7,7 @@ // notice may not be copied, modified, or distributed except // according to those terms. -//! File and RandomAccessFile objects +//! SmallFile and RandomAccessFile objects use core::fmt; use std::cmp::min; diff --git a/ng-repo/src/kcv_store.rs b/ng-repo/src/kcv_store.rs index 221ace5..e3a705d 100644 --- a/ng-repo/src/kcv_store.rs +++ b/ng-repo/src/kcv_store.rs @@ -6,6 +6,8 @@ // notice may not be copied, modified, or distributed except // according to those terms. +//! KeyColumnValue Store abstraction + use crate::store::StorageError; // TODO:remove mut on self for trait WriteTransaction methods diff --git a/ng-repo/src/lib.rs b/ng-repo/src/lib.rs index e2deff5..74648e5 100644 --- a/ng-repo/src/lib.rs +++ b/ng-repo/src/lib.rs @@ -32,6 +32,8 @@ pub mod errors; pub mod kcv_store; +pub mod os_info; + #[macro_use] extern crate slice_as_array; diff --git a/ng-repo/src/object.rs b/ng-repo/src/object.rs index 4308a04..deac4ff 100644 --- a/ng-repo/src/object.rs +++ b/ng-repo/src/object.rs @@ -7,7 +7,7 @@ // notice may not be copied, modified, or distributed except // according to those terms. -//! Merkle hash tree of Objects +//! Object: Merkle hash tree of Blocks use core::fmt; use std::borrow::BorrowMut; diff --git a/ng-repo/src/os_info.rs b/ng-repo/src/os_info.rs new file mode 100644 index 0000000..a502cc3 --- /dev/null +++ b/ng-repo/src/os_info.rs @@ -0,0 +1,56 @@ +use os_info; +use serde_json::{json, to_string_pretty, Value}; + +pub fn get_os_info() -> Value { + let arch = std::env::consts::ARCH; + let machine = match arch { + "ia32" => "x86", + "x64" => "x86_64", + "i386" => "x86", + "i686" => "x86", + "amd64" => "x86_64", + "arm64" => "aarch64", + "powerpc" => "ppc", + "powerpc64" => "ppc64", + _ => arch, + }; + + let info = os_info::get(); + let os_type = info.os_type(); + let os_name = match os_type { + os_info::Type::Macos => "macOS".to_string(), + _ => format!("{:?}", os_type), + }; + + let val = json!({ + "uname": { + "os_name": os_name, + "version": info.version().to_string(), + "arch": info.architecture().map(|s| s.into()).unwrap_or(Value::Null), + "bitness": format!("{:?}",info.bitness()), + "codename": info.codename().map(|s| s.into()).unwrap_or(Value::Null), + "edition": info.edition().map(|s| s.into()).unwrap_or(Value::Null), + }, + "rust": { + "family": std::env::consts::FAMILY, + "os_name": match std::env::consts::OS { + "linux" => "Linux", + "macos" => "macOS", + "ios" => "iOS", + "freebsd" => "FreeBSD", + "dragonfly" => "DragonFly", + "netbsd" => "NetBSD", + "openbsd" => "OpenBSD", + "solaris" => "Solaris", + "android" => "Android", + "windows" => "Windows", + _ => std::env::consts::OS, + }, + "arch": machine, + "debug": cfg!(debug_assertions), + "target": current_platform::CURRENT_PLATFORM, + } + }); + //println!("{}", to_string_pretty(&val).unwrap()); + val +} diff --git a/ng-repo/src/repo.rs b/ng-repo/src/repo.rs index dfc2d7e..8ea7c9d 100644 --- a/ng-repo/src/repo.rs +++ b/ng-repo/src/repo.rs @@ -7,7 +7,7 @@ // notice may not be copied, modified, or distributed except // according to those terms. -//! Repository serde implementation and in memory helper +//! Repository use crate::errors::*; use crate::event::*; diff --git a/ng-repo/src/site.rs b/ng-repo/src/site.rs index 1a38c47..a284055 100644 --- a/ng-repo/src/site.rs +++ b/ng-repo/src/site.rs @@ -9,6 +9,8 @@ * according to those terms. */ +//! Site (Public, Protected, Private) of Individual and Org + use crate::errors::NgError; use crate::types::*; use crate::utils::{generate_keypair, sign, verify}; diff --git a/ng-repo/src/store.rs b/ng-repo/src/store.rs index f7f3125..94d0770 100644 --- a/ng-repo/src/store.rs +++ b/ng-repo/src/store.rs @@ -7,7 +7,7 @@ // notice may not be copied, modified, or distributed except // according to those terms. -//! Block store +//! Storage of Blocks use futures::StreamExt; diff --git a/ng-repo/src/types.rs b/ng-repo/src/types.rs index 4b42448..7c9bec0 100644 --- a/ng-repo/src/types.rs +++ b/ng-repo/src/types.rs @@ -7,7 +7,7 @@ // notice may not be copied, modified, or distributed except // according to those terms. -//! P2P Repo types +//! NextGraph Repo types //! //! Corresponds to the BARE schema @@ -367,14 +367,13 @@ pub type RepoHash = Digest; /// Topic ID: public key of the topic pub type TopicId = PubKey; -/// User ID: user account for broker +/// User ID: user account for broker and member of a repo pub type UserId = PubKey; /// BranchId is a PubKey pub type BranchId = PubKey; -/// Block ID: -/// BLAKE3 hash over the serialized BlockContent (contains encrypted content) +/// Block ID: BLAKE3 hash over the serialized BlockContent (contains encrypted content) pub type BlockId = Digest; pub type BlockKey = SymKey; @@ -382,10 +381,10 @@ pub type BlockKey = SymKey; /// Block reference #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)] pub struct BlockRef { - /// Object ID + /// Block ID pub id: BlockId, - /// Key for decrypting the Object + /// Key for decrypting the Block pub key: BlockKey, } @@ -454,6 +453,7 @@ pub type ObjectKey = BlockKey; pub type ObjectRef = BlockRef; /// Read capability (for a commit, branch, whole repo, or store) +/// /// For a store: A ReadCap to the root repo of the store /// For a repo: A reference to the latest RootBranch definition commit /// For a branch: A reference to the latest Branch definition commit @@ -461,6 +461,7 @@ pub type ObjectRef = BlockRef; pub type ReadCap = ObjectRef; /// Read capability secret (for a commit, branch, whole repo, or store) +/// /// it is already included in the ReadCap (it is the key part of the reference) pub type ReadCapSecret = ObjectKey; @@ -718,20 +719,17 @@ pub struct SiteV0 { /// Reduced Site (for QRcode) #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub struct ReducedSiteV0 { - pub site_key: PrivKey, - - pub private_site_key: PrivKey, - - pub private_site_read_cap: ReadCap, + pub user_key: PrivKey, - pub private_site_write_cap: RepoWriteCapSecret, + pub private_store_read_cap: ReadCap, pub core: PubKey, - pub bootstraps: Vec, } -/// BLOCKS common types +// +// BLOCKS common types +// /// Internal node of a Merkle tree pub type InternalNode = Vec; @@ -746,6 +744,7 @@ pub enum ChunkContentV0 { DataChunk(Vec), } +/// Header of a Commit, can be embedded or as a ref #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] pub struct CommitHeaderV0 { /// optional Commit Header ID @@ -782,6 +781,7 @@ pub enum CommitHeader { V0(CommitHeaderV0), } +/// Keys for the corresponding IDs contained in the Header #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] pub struct CommitHeaderKeysV0 { /// Other objects this commit strongly depends on (ex: ADD for a REMOVE, files for an nfiles) @@ -842,6 +842,7 @@ impl CommitHeaderRef { } } +/// unencrypted part of the Block #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] pub struct BlockContentV0 { /// Reference (actually, only its ID or an embedded block if the size is small enough) @@ -913,7 +914,9 @@ pub enum Block { V0(BlockV0), } -/// REPO IMPLEMENTATION +// +// REPO IMPLEMENTATION +// /// Repository definition /// @@ -1106,7 +1109,7 @@ pub struct QuorumV0 { } /// Quorum definition, is part of the RootBranch commit -/// TODO: can it be sent in the root branch without being part of a RootBranch ? +// TODO: can it be sent in the root branch without being part of a RootBranch ? #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] pub enum Quorum { V0(QuorumV0), @@ -1199,6 +1202,7 @@ impl fmt::Display for BranchType { } /// Add a branch to the repository +/// /// DEPS: if update branch: previous AddBranch commit of the same branchId #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] pub struct AddBranchV0 { @@ -1260,7 +1264,8 @@ pub enum AddMember { } /// Remove member from a repo -/// An owner cannot be removed +/// +/// An owner cannot be removed (it cannot be added even) /// The overlay should be refreshed if user was malicious, after the user is removed from last repo. See REFRESH_READ_CAP on store repo. #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] pub struct RemoveMemberV0 { @@ -1412,6 +1417,7 @@ pub enum RepoNamedItem { } /// Add a new name in the repo that can point to a branch, a commit or a file +/// /// Or change the value of a name /// DEPS: if it is a change of value: all the previous AddName commits seen for this name #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] @@ -1433,6 +1439,7 @@ pub enum AddName { } /// Remove a name from the repo, using ORset CRDT logic +/// /// DEPS: all the AddName commits seen for this name #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] pub struct RemoveNameV0 { @@ -1454,6 +1461,7 @@ pub enum RemoveName { // /// Adds a repo into the store branch. +/// /// The repo's `store` field should match the store /// DEPS to the previous AddRepo commit(s) if it is an update. in this case, repo_id of the referenced rootbranch definition(s) should match #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] @@ -1471,6 +1479,7 @@ pub enum AddRepo { } /// Removes a repo from the store branch. +/// /// DEPS to the previous AddRepo commit(s) (ORset logic) with matching repo_id #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] pub struct RemoveRepoV0 { @@ -1493,6 +1502,7 @@ pub enum RemoveRepo { // /// Adds a link into the user branch, so that a user can share with all its device a new Link they received. +/// /// The repo's `store` field should not match with any store of the user. Only external repos are accepted here. /// DEPS to the previous AddLink commit(s) if it is an update. in this case, repo_id of the referenced rootbranch definition(s) should match #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] @@ -1510,6 +1520,7 @@ pub enum AddLink { } /// Removes a link from the `user` branch. +/// /// DEPS to the previous AddLink commit(s) (ORset logic) with matching repo_id #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] pub struct RemoveLinkV0 { @@ -1525,7 +1536,9 @@ pub enum RemoveLink { V0(RemoveLinkV0), } -/// Adds a SignerCap into the user branch, so that a user can share with all its device a new signing capability that was just created. +/// Adds a SignerCap into the user branch, +/// +/// so that a user can share with all its device a new signing capability that was just created. /// The cap's `epoch` field should be dereferenced and the user must be part of the quorum/owners. /// DEPS to the previous AddSignerCap commit(s) if it is an update. in this case, repo_ids have to match, /// and the the referenced rootbranch definition(s) should have compatible causal past (the newer AddSignerCap must have a newer epoch compared to the one of the replaced cap ) @@ -1544,6 +1557,7 @@ pub enum AddSignerCap { } /// Removes a SignerCap from the `user` branch. +/// /// DEPS to the previous AddSignerCap commit(s) (ORset logic) with matching repo_id #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] pub struct RemoveSignerCapV0 { @@ -1560,6 +1574,7 @@ pub enum RemoveSignerCap { } /// Adds a wallet operation so all the devices can sync their locally saved wallet on disk (at the next wallet opening) +/// /// DEPS are the last HEAD of wallet updates. #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] pub struct WalletUpdateV0 { @@ -1576,6 +1591,7 @@ pub enum WalletUpdate { } /// Updates the ReadCap of the public and protected sites (and potentially also Group stores) +/// /// DEPS to the previous ones. /// this is used to speedup joining the overlay of such stores, for new devices on new brokers #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] @@ -1613,6 +1629,7 @@ pub enum Transaction { } /// Add a new binary file in a branch +/// /// FILES: the file ObjectRef #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] pub struct AddFileV0 { @@ -1630,6 +1647,7 @@ pub enum AddFile { } /// Remove a file from the branch, using ORset CRDT logic +/// /// (removes the ref counting. not necessarily the file itself) /// NFILES: the file ObjectRef /// DEPS: all the visible AddFile commits in the branch (ORset) @@ -1687,9 +1705,10 @@ pub enum Compact { V0(CompactV0), } -/// Async Threshold Signature of a commit V0 based on the partial order quorum -/// Can sign Transaction, AddFile, and Snapshot, after they have been committed to the DAG. -/// DEPS: the signed commits +// Async Threshold Signature of a commit V0 based on the partial order quorum +// +// Can sign Transaction, AddFile, and Snapshot, after they have been committed to the DAG. +// DEPS: the signed commits // #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] // pub struct AsyncSignatureV0 { // /// An Object containing the Threshold signature @@ -1709,7 +1728,10 @@ impl AsyncSignature { } } -/// Sync Threshold Signature of one or a chain of commits . V0 . based on the total order quorum (or owners quorum) +/// Sync Threshold Signature of one or a chain of commits . V0 +/// +/// points to the new Signature Object +/// based on the total order quorum (or owners quorum) /// mandatory for UpdateRootBranch, UpdateBranch, some AddBranch, RemoveBranch, RemoveMember, RemovePermission, Quorum, Compact, sync Transaction, RefreshReadCap, RefreshWriteCap /// DEPS: the last signed commit in chain /// ACKS: previous head before the chain of signed commit(s). should be identical to the HEADS (marked as DEPS) of first commit in chain @@ -1718,9 +1740,6 @@ impl AsyncSignature { // /// An Object containing the Threshold signature // pub signature: ObjectRef, // } - -/// Threshold Signature of a commit -/// points to the new Signature Object #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] pub enum SyncSignature { V0(ObjectRef), @@ -1746,6 +1765,7 @@ impl fmt::Display for SyncSignature { } /// RefreshReadCap. renew the ReadCap of a `transactional` branch, or the root_branch, or all transactional branches and the root_branch. +/// /// Each branch forms its separate chain for that purpose. /// can refresh the topic ids, or not /// DEPS: current HEADS in the branch at the moment of refresh. @@ -1770,6 +1790,7 @@ pub enum RefreshReadCap { } /// RefreshWriteCap is always done on the root_branch, and always refreshes all the transaction branches WriteCaps, and TopicIDs. +/// /// DEPS: current HEADS in the branch at the moment of refresh. /// the chain on the root_branch is : RemovePermission/RemoveMember -> RefreshWriteCap -> RootBranch -> optional AddPermission(s) -> AddBranch /// and on each transactional branch: RefreshWriteCap -> Branch @@ -1877,7 +1898,9 @@ pub enum Signature { V0(SignatureV0), } -/// Enum for "orders" PKsets. Can be inherited from the store, in this case, it is an ObjectRef pointing to the latest Certificate of the store. +/// Enum for "orders" PKsets. +/// +/// Can be inherited from the store, in this case, it is an ObjectRef pointing to the latest Certificate of the store. /// Or can be 2 PublicKey defined specially for this repo, /// .0 one for the total_order (first one). it is a PublicKeysSet so that verifier can see the threshold value, and can also verify Shares individually /// .1 the other for the partial_order (second one. a PublicKey. is optional, as some repos are forcefully totally ordered and do not have this set). @@ -2086,7 +2109,8 @@ impl CommitContent { } /// Commit object -/// Signed by branch key, or a member key authorized to publish this commit type +/// +/// Signed by member key authorized to publish this commit type #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] pub struct CommitV0 { /// ID of containing Object @@ -2279,6 +2303,7 @@ impl fmt::Display for PeerId { } /// Content of EventV0 +/// /// Contains the objects of newly published Commit, its optional blocks, and optional FILES and their blocks. /// If a block is not present in the Event, its ID should be present in block_ids and the block should be put on the emitting broker beforehand with BlocksPut. #[derive(Clone, Debug, Serialize, Deserialize)] diff --git a/ng-sdk-js/README.md b/ng-sdk-js/README.md index 1846c69..0fa2f9b 100644 --- a/ng-sdk-js/README.md +++ b/ng-sdk-js/README.md @@ -4,9 +4,9 @@ JS/WASM crate containing the SDK of NextGraph ## NextGraph -> NextGraph brings about the convergence between P2P and Semantic Web technologies, towards a decentralized, secure and privacy-preserving cloud, based on CRDTs. +> NextGraph brings about the convergence of P2P and Semantic Web technologies, towards a decentralized, secure and privacy-preserving cloud, based on CRDTs. > -> This open source ecosystem provides solutions for end-users and software developers alike, wishing to use or create **decentralized** apps featuring: **live collaboration** on rich-text documents, peer to peer communication with **end-to-end encryption**, offline-first, **local-first**, portable and interoperable data, total ownership of data and software, security and privacy. Centered on repositories containing **semantic data** (RDF), **rich text**, and structured data formats like **JSON**, synced between peers belonging to permissioned groups of users, it offers strong eventual consistency, thanks to the use of **CRDTs**. Documents can be linked together, signed, shared securely, queried using the **SPARQL** language and organized into sites and containers. +> This open source ecosystem provides solutions for end-users (a platform) and software developers (a framework), wishing to use or create **decentralized** apps featuring: **live collaboration** on rich-text documents, peer to peer communication with **end-to-end encryption**, offline-first, **local-first**, portable and interoperable data, total ownership of data and software, security and privacy. Centered on repositories containing **semantic data** (RDF), **rich text**, and structured data formats like **JSON**, synced between peers belonging to permissioned groups of users, it offers strong eventual consistency, thanks to the use of **CRDTs**. Documents can be linked together, signed, shared securely, queried using the **SPARQL** language and organized into sites and containers. > > More info here [https://nextgraph.org](https://nextgraph.org) diff --git a/ng-sdk-js/app-react/README.md b/ng-sdk-js/app-react/README.md index 40a1766..57b1efb 100644 --- a/ng-sdk-js/app-react/README.md +++ b/ng-sdk-js/app-react/README.md @@ -4,9 +4,9 @@ React example app for NextGraph ## NextGraph -> NextGraph brings about the convergence between P2P and Semantic Web technologies, towards a decentralized, secure and privacy-preserving cloud, based on CRDTs. +> NextGraph brings about the convergence of P2P and Semantic Web technologies, towards a decentralized, secure and privacy-preserving cloud, based on CRDTs. > -> This open source ecosystem provides solutions for end-users and software developers alike, wishing to use or create **decentralized** apps featuring: **live collaboration** on rich-text documents, peer to peer communication with **end-to-end encryption**, offline-first, **local-first**, portable and interoperable data, total ownership of data and software, security and privacy. Centered on repositories containing **semantic data** (RDF), **rich text**, and structured data formats like **JSON**, synced between peers belonging to permissioned groups of users, it offers strong eventual consistency, thanks to the use of **CRDTs**. Documents can be linked together, signed, shared securely, queried using the **SPARQL** language and organized into sites and containers. +> This open source ecosystem provides solutions for end-users (a platform) and software developers (a framework), wishing to use or create **decentralized** apps featuring: **live collaboration** on rich-text documents, peer to peer communication with **end-to-end encryption**, offline-first, **local-first**, portable and interoperable data, total ownership of data and software, security and privacy. Centered on repositories containing **semantic data** (RDF), **rich text**, and structured data formats like **JSON**, synced between peers belonging to permissioned groups of users, it offers strong eventual consistency, thanks to the use of **CRDTs**. Documents can be linked together, signed, shared securely, queried using the **SPARQL** language and organized into sites and containers. > > More info here [https://nextgraph.org](https://nextgraph.org) diff --git a/ng-sdk-js/app-web/README.md b/ng-sdk-js/app-web/README.md index 9f94d3a..479475a 100644 --- a/ng-sdk-js/app-web/README.md +++ b/ng-sdk-js/app-web/README.md @@ -4,9 +4,9 @@ Web app client of NextGraph ## NextGraph -> NextGraph brings about the convergence between P2P and Semantic Web technologies, towards a decentralized, secure and privacy-preserving cloud, based on CRDTs. +> NextGraph brings about the convergence of P2P and Semantic Web technologies, towards a decentralized, secure and privacy-preserving cloud, based on CRDTs. > -> This open source ecosystem provides solutions for end-users and software developers alike, wishing to use or create **decentralized** apps featuring: **live collaboration** on rich-text documents, peer to peer communication with **end-to-end encryption**, offline-first, **local-first**, portable and interoperable data, total ownership of data and software, security and privacy. Centered on repositories containing **semantic data** (RDF), **rich text**, and structured data formats like **JSON**, synced between peers belonging to permissioned groups of users, it offers strong eventual consistency, thanks to the use of **CRDTs**. Documents can be linked together, signed, shared securely, queried using the **SPARQL** language and organized into sites and containers. +> This open source ecosystem provides solutions for end-users (a platform) and software developers (a framework), wishing to use or create **decentralized** apps featuring: **live collaboration** on rich-text documents, peer to peer communication with **end-to-end encryption**, offline-first, **local-first**, portable and interoperable data, total ownership of data and software, security and privacy. Centered on repositories containing **semantic data** (RDF), **rich text**, and structured data formats like **JSON**, synced between peers belonging to permissioned groups of users, it offers strong eventual consistency, thanks to the use of **CRDTs**. Documents can be linked together, signed, shared securely, queried using the **SPARQL** language and organized into sites and containers. > > More info here [https://nextgraph.org](https://nextgraph.org) diff --git a/ng-sdk-js/js/node.js b/ng-sdk-js/js/node.js index ab2e539..625e9ea 100644 --- a/ng-sdk-js/js/node.js +++ b/ng-sdk-js/js/node.js @@ -102,7 +102,11 @@ function osName(platform, release) { if (platform === 'win32') { return release ? windowsRelease(release) : {name: "Windows"}; } - + if (platform === 'aix') { platform = 'AIX'; } + else if (platform === 'freebsd') { platform = 'FreeBSD'; } + else if (platform === 'openbsd') { platform = 'OpenBSD'; } + else if (platform === 'android') { platform = 'Android'; } + else if (platform === 'sunos') { platform = 'SunOS'; } return {name:platform, version:release}; } module.exports.version = function () { @@ -111,18 +115,26 @@ module.exports.version = function () { module.exports.client_details = function () { const process = require('process'); + let arch = osnode.machine? osnode.machine() : process.arch; + if (arch=="ia32") {arch="x86"} + else if (arch=="x64") {arch="x86_64"} + else if (arch=="i386") {arch="x86"} + else if (arch=="i686") {arch="x86"} + else if (arch=="amd64") {arch="x86_64"} + else if (arch=="arm64") {arch="aarch64"} const osnode = require('os'); let os = osName(osnode.platform(),osnode.release()); if (osnode.version) os.uname = osnode.version(); os.type = osnode.type(); return JSON.stringify({ - platform: { type: "server", arch: osnode.machine? osnode.machine() : process.arch }, + platform: { type: "server", arch }, os, engine: { name: "nodejs", version: process.version, arch : process.arch, + machine: osnode.machine? osnode.machine() : undefined, versions: process.versions } }); diff --git a/ng-sdk-js/src/lib.rs b/ng-sdk-js/src/lib.rs index d12afd5..4a58582 100644 --- a/ng-sdk-js/src/lib.rs +++ b/ng-sdk-js/src/lib.rs @@ -125,7 +125,7 @@ pub fn wallet_open_with_pazzle( .map_err(|_| "Deserialization error of wallet")?; let mut pin = serde_wasm_bindgen::from_value::<[u8; 4]>(js_pin) .map_err(|_| "Deserialization error of pin")?; - let res = nextgraph::local_broker::wallet_open_with_pazzle(encrypted_wallet, pazzle, pin); + let res = nextgraph::local_broker::wallet_open_with_pazzle(&encrypted_wallet, pazzle, pin); match res { Ok(r) => Ok(r .serialize(&serde_wasm_bindgen::Serializer::new().serialize_maps_as_objects(true)) @@ -153,7 +153,7 @@ pub fn wallet_update(js_wallet_id: JsValue, js_operations: JsValue) -> Result Result { init_local_broker_with_lazy(&INIT_LOCAL_BROKER).await; - let res = get_all_wallets().await.map_err(|e| { + let res = wallets_get_all().await.map_err(|e| { log_err!("{}", e.to_string()); }); if res.is_ok() { @@ -162,25 +162,6 @@ pub async fn get_wallets() -> Result { Ok(JsValue::UNDEFINED) } -// fn save_new_session( -// wallet_name: &String, -// wallet_id: PubKey, -// user: PubKey, -// ) -> Result { -// let res = create_new_session(wallet_id, user); -// if res.is_ok() { -// let sws = res.unwrap(); -// let encoded = base64_url::encode(&sws.1); -// let r = session_save(format!("ng_wallet@{}", wallet_name), encoded); -// if r.is_some() { -// return Err(r.unwrap()); -// } -// Ok(sws.0) -// } else { -// Err(res.unwrap_err().to_string()) -// } -// } - fn take_some_peer_last_seq_numbers(peer_id: PubKey, qty: u16) -> Result { let res = session_get(format!("ng_peer_last_seq@{}", peer_id)); let val = match res { @@ -266,49 +247,11 @@ pub async fn session_start(wallet_name: String, user_js: JsValue) -> Result Result { -// let sws = save_new_session(&res.wallet_name, res.wallet.id(), res.user)?; -// let mut wallets: HashMap = -// get_all_wallets().unwrap_or(HashMap::new()); -// // TODO: check that the wallet is not already present in localStorage -// let lws: LocalWalletStorageV0 = res.into(); -// wallets.insert(res.wallet_name.clone(), lws); -// let lws_ser = serde_bare::to_vec(&LocalWalletStorage::V0(wallets)).unwrap(); -// let encoded = base64_url::encode(&lws_ser); -// let r = local_save("ng_wallets".to_string(), encoded); -// if r.is_some() { -// return Err(r.unwrap()); -// } -// Ok(sws) -// } - -// #[cfg(target_arch = "wasm32")] -// #[wasm_bindgen] -// pub async fn get_wallet_client( -// wallet_id: String, -// wallet_privkey_js: JsValue, -// ) -> Result { -// let mut wallet_privkey = serde_wasm_bindgen::from_value::(wallet_privkey_js) -// .map_err(|_| "Deserialization error of wallet_privkey")?; -// match get_all_wallets().await { -// Ok(wallets) => match wallets.get(&wallet_id) { -// Some(wallet) => { -// let res = wallet -// .to_client_v0(wallet_privkey) -// .map_err(|e| e.to_string())?; -// Ok(serde_wasm_bindgen::to_value(&res).unwrap()) -// } -// None => Err("Cannot find this wallet locally".to_string()), -// }, -// Err(_) => Err("Cannot find this wallet locally".to_string()), -// } -// } - #[cfg(target_arch = "wasm32")] #[wasm_bindgen] -pub async fn reload_wallets() -> Result<(), String> { +pub async fn wallets_reload() -> Result<(), String> { init_local_broker_with_lazy(&INIT_LOCAL_BROKER).await; - nextgraph::local_broker::reload_wallets() + nextgraph::local_broker::wallets_reload() .await .map_err(|e: NgError| e.to_string()) } @@ -398,10 +341,10 @@ pub async fn wallet_create(js_params: JsValue) -> Result { #[cfg(target_arch = "wasm32")] #[wasm_bindgen] -pub async fn wallet_download_file(wallet_name: String) -> Result { +pub async fn wallet_get_file(wallet_name: String) -> Result { init_local_broker_with_lazy(&INIT_LOCAL_BROKER).await; - let res = nextgraph::local_broker::wallet_download_file(&wallet_name).await; + let res = nextgraph::local_broker::wallet_get_file(&wallet_name).await; match res { Ok(r) => Ok(serde_wasm_bindgen::to_value(&serde_bytes::ByteBuf::from(r)).unwrap()), Err(e) => Err(e.to_string().into()), @@ -454,36 +397,6 @@ pub async fn wallet_import( .map_err(|e: NgError| e.to_string())?; Ok(serde_wasm_bindgen::to_value(&client).unwrap()) - - // let SensitiveWallet::V0(mut opened_wallet_v0) = opened_wallet; - // let mut wallets: HashMap = - // get_all_wallets().unwrap_or(HashMap::new()); - // // check that the wallet is not already present in localStorage - // let wallet_name = opened_wallet_v0.wallet_id.clone(); - // if wallets.get(&wallet_name).is_none() { - // let session = save_new_session( - // &wallet_name, - // opened_wallet_v0.wallet_privkey.to_pub(), - // opened_wallet_v0.personal_site, - // ) - // .map_err(|e| format!("Cannot create new session: {e}"))?; - // let (lws, client) = opened_wallet_v0 - // .import(previous_wallet, session) - // .map_err(|e| e.to_string())?; - // //let lws = LocalWalletStorageV0::new(wallet, &client); - - // wallets.insert(wallet_name, lws); - - // let lws_ser = serde_bare::to_vec(&LocalWalletStorage::V0(wallets)).unwrap(); - // let encoded = base64_url::encode(&lws_ser); - // let r = local_save("ng_wallets".to_string(), encoded); - // if r.is_some() { - // return Err(r.unwrap()); - // } - // Ok(serde_wasm_bindgen::to_value(&client).unwrap()) - // } else { - // Err("Wallet already present on this device".to_string()) - // } } #[cfg(target_arch = "wasm32")] @@ -497,7 +410,7 @@ pub fn test_create_wallet() -> JsValue { 9, false, false, - BootstrapContentV0::new(), + BootstrapContentV0::new_empty(), None, None, ); @@ -813,7 +726,7 @@ pub async fn session_stop(user_id_js: JsValue) -> Result<(), String> { let user_id = serde_wasm_bindgen::from_value::(user_id_js) .map_err(|_| "serde error on user_id")?; - nextgraph::local_broker::session_stop(user_id) + nextgraph::local_broker::session_stop(&user_id) .await .map_err(|e: NgError| e.to_string()) } @@ -824,7 +737,7 @@ pub async fn user_disconnect(user_id_js: JsValue) -> Result<(), String> { let user_id = serde_wasm_bindgen::from_value::(user_id_js) .map_err(|_| "serde error on user_id")?; - nextgraph::local_broker::user_disconnect(user_id) + nextgraph::local_broker::user_disconnect(&user_id) .await .map_err(|e: NgError| e.to_string()) } @@ -832,7 +745,7 @@ pub async fn user_disconnect(user_id_js: JsValue) -> Result<(), String> { #[cfg(target_arch = "wasm32")] #[wasm_bindgen] pub async fn wallet_close(wallet_name: String) -> Result<(), String> { - nextgraph::local_broker::wallet_close(wallet_name) + nextgraph::local_broker::wallet_close(&wallet_name) .await .map_err(|e: NgError| e.to_string()) } @@ -860,7 +773,7 @@ pub async fn user_connect( let mut opened_connections: HashMap = HashMap::new(); - let results = nextgraph::local_broker::user_connect(info, user_id, location) + let results = nextgraph::local_broker::user_connect_with_device_info(info, &user_id, location) .await .map_err(|e: NgError| e.to_string())?; diff --git a/ng-stores-rocksdb/README.md b/ng-stores-rocksdb/README.md index f2e61bf..f39fd3c 100644 --- a/ng-stores-rocksdb/README.md +++ b/ng-stores-rocksdb/README.md @@ -10,9 +10,9 @@ This repository is in active development at [https://git.nextgraph.org/NextGraph ## NextGraph -> NextGraph brings about the convergence between P2P and Semantic Web technologies, towards a decentralized, secure and privacy-preserving cloud, based on CRDTs. +> NextGraph brings about the convergence of P2P and Semantic Web technologies, towards a decentralized, secure and privacy-preserving cloud, based on CRDTs. > -> This open source ecosystem provides solutions for end-users and software developers alike, wishing to use or create **decentralized** apps featuring: **live collaboration** on rich-text documents, peer to peer communication with **end-to-end encryption**, offline-first, **local-first**, portable and interoperable data, total ownership of data and software, security and privacy. Centered on repositories containing **semantic data** (RDF), **rich text**, and structured data formats like **JSON**, synced between peers belonging to permissioned groups of users, it offers strong eventual consistency, thanks to the use of **CRDTs**. Documents can be linked together, signed, shared securely, queried using the **SPARQL** language and organized into sites and containers. +> This open source ecosystem provides solutions for end-users (a platform) and software developers (a framework), wishing to use or create **decentralized** apps featuring: **live collaboration** on rich-text documents, peer to peer communication with **end-to-end encryption**, offline-first, **local-first**, portable and interoperable data, total ownership of data and software, security and privacy. Centered on repositories containing **semantic data** (RDF), **rich text**, and structured data formats like **JSON**, synced between peers belonging to permissioned groups of users, it offers strong eventual consistency, thanks to the use of **CRDTs**. Documents can be linked together, signed, shared securely, queried using the **SPARQL** language and organized into sites and containers. > > More info here [https://nextgraph.org](https://nextgraph.org) diff --git a/ng-verifier/README.md b/ng-verifier/README.md index 629b79d..712c6f3 100644 --- a/ng-verifier/README.md +++ b/ng-verifier/README.md @@ -10,9 +10,9 @@ This repository is in active development at [https://git.nextgraph.org/NextGraph ## NextGraph -> NextGraph brings about the convergence between P2P and Semantic Web technologies, towards a decentralized, secure and privacy-preserving cloud, based on CRDTs. +> NextGraph brings about the convergence of P2P and Semantic Web technologies, towards a decentralized, secure and privacy-preserving cloud, based on CRDTs. > -> This open source ecosystem provides solutions for end-users and software developers alike, wishing to use or create **decentralized** apps featuring: **live collaboration** on rich-text documents, peer to peer communication with **end-to-end encryption**, offline-first, **local-first**, portable and interoperable data, total ownership of data and software, security and privacy. Centered on repositories containing **semantic data** (RDF), **rich text**, and structured data formats like **JSON**, synced between peers belonging to permissioned groups of users, it offers strong eventual consistency, thanks to the use of **CRDTs**. Documents can be linked together, signed, shared securely, queried using the **SPARQL** language and organized into sites and containers. +> This open source ecosystem provides solutions for end-users (a platform) and software developers (a framework), wishing to use or create **decentralized** apps featuring: **live collaboration** on rich-text documents, peer to peer communication with **end-to-end encryption**, offline-first, **local-first**, portable and interoperable data, total ownership of data and software, security and privacy. Centered on repositories containing **semantic data** (RDF), **rich text**, and structured data formats like **JSON**, synced between peers belonging to permissioned groups of users, it offers strong eventual consistency, thanks to the use of **CRDTs**. Documents can be linked together, signed, shared securely, queried using the **SPARQL** language and organized into sites and containers. > > More info here [https://nextgraph.org](https://nextgraph.org) diff --git a/ng-wallet/README.md b/ng-wallet/README.md index 461b503..69485e9 100644 --- a/ng-wallet/README.md +++ b/ng-wallet/README.md @@ -10,9 +10,9 @@ This repository is in active development at [https://git.nextgraph.org/NextGraph ## NextGraph -> NextGraph brings about the convergence between P2P and Semantic Web technologies, towards a decentralized, secure and privacy-preserving cloud, based on CRDTs. +> NextGraph brings about the convergence of P2P and Semantic Web technologies, towards a decentralized, secure and privacy-preserving cloud, based on CRDTs. > -> This open source ecosystem provides solutions for end-users and software developers alike, wishing to use or create **decentralized** apps featuring: **live collaboration** on rich-text documents, peer to peer communication with **end-to-end encryption**, offline-first, **local-first**, portable and interoperable data, total ownership of data and software, security and privacy. Centered on repositories containing **semantic data** (RDF), **rich text**, and structured data formats like **JSON**, synced between peers belonging to permissioned groups of users, it offers strong eventual consistency, thanks to the use of **CRDTs**. Documents can be linked together, signed, shared securely, queried using the **SPARQL** language and organized into sites and containers. +> This open source ecosystem provides solutions for end-users (a platform) and software developers (a framework), wishing to use or create **decentralized** apps featuring: **live collaboration** on rich-text documents, peer to peer communication with **end-to-end encryption**, offline-first, **local-first**, portable and interoperable data, total ownership of data and software, security and privacy. Centered on repositories containing **semantic data** (RDF), **rich text**, and structured data formats like **JSON**, synced between peers belonging to permissioned groups of users, it offers strong eventual consistency, thanks to the use of **CRDTs**. Documents can be linked together, signed, shared securely, queried using the **SPARQL** language and organized into sites and containers. > > More info here [https://nextgraph.org](https://nextgraph.org) diff --git a/ng-wallet/src/emojis.rs b/ng-wallet/src/emojis.rs index 1a017c9..cf19799 100644 --- a/ng-wallet/src/emojis.rs +++ b/ng-wallet/src/emojis.rs @@ -7,6 +7,7 @@ // notice may not be copied, modified, or distributed except // according to those terms. +use ng_repo::errors::NgError; use std::collections::HashMap; pub struct EmojiDef<'a> { pub hexcode: &'a str, @@ -38,7 +39,7 @@ const face: [EmojiDef<'static>; 15] = [ EmojiDef { hexcode: "1f60d", shortcode: "smiling_face_with_heart_eyes", - code: "two_hearts", + code: "with_two_hearts", }, EmojiDef { hexcode: "1f618", @@ -48,12 +49,12 @@ const face: [EmojiDef<'static>; 15] = [ EmojiDef { hexcode: "1f61d", shortcode: "squinting_face_with_tongue", - code: "tongue", + code: "with_tongue", }, EmojiDef { hexcode: "1f917", shortcode: "hugging_face", - code: "two_hands", + code: "with_two_hands", }, EmojiDef { hexcode: "1f92d", @@ -102,7 +103,7 @@ const face_unwell: [EmojiDef<'static>; 15] = [ EmojiDef { hexcode: "1f912", shortcode: "face_with_thermometer", - code: "thermometer", + code: "fever", }, EmojiDef { hexcode: "1f915", @@ -137,7 +138,7 @@ const face_unwell: [EmojiDef<'static>; 15] = [ EmojiDef { hexcode: "1f92f", shortcode: "exploding_head", - code: "explosion", + code: "exploding", }, EmojiDef { hexcode: "2639", @@ -258,7 +259,7 @@ const emotion: [EmojiDef<'static>; 15] = [ EmojiDef { hexcode: "2764", shortcode: "red_heart", - code: "one_heart", + code: "red_heart", }, EmojiDef { hexcode: "1f495", @@ -1201,13 +1202,13 @@ lazy_static! { ("travel", travel), ("sky", sky), ("play", play), - ("house", play), + ("house", house), ] .into_iter() .collect(); } -pub const EMOJI_CAT: [&str; 15] = [ +pub const EMOJI_CAT: [&'static str; 15] = [ "face", "sport", "big_animal", @@ -1224,3 +1225,68 @@ pub const EMOJI_CAT: [&str; 15] = [ "face_costume", "emotion", ]; + +lazy_static! { + pub static ref EMOJI_CODES: HashMap<&'static str, u8> = generate_tuples(); +} + +fn generate_tuples() -> HashMap<&'static str, u8> { + let mut tuples = vec![]; + for (icat, cat_name) in EMOJI_CAT.iter().enumerate() { + for (iemoji, emoji) in EMOJIS.get(cat_name).unwrap().iter().enumerate() { + let nbr = (icat << 4) + iemoji; + tuples.push((emoji.code, nbr as u8)); + } + } + // let mut map = HashMap::new(); + // for t in tuples.into_iter() { + // match map.insert(t.0, t.1) { + // Some(double) => log_info!("{} {} {}", t.0, t.1, double), + // None => {} + // } + // } + // map + tuples.into_iter().collect() +} + +/// returns a list of tuples of 2 strings (category,emoji) +pub fn display_pazzle(pazzle: &Vec) -> Vec<(&'static str, &'static str)> { + let mut res = vec![]; + for emoji in pazzle { + let cat = (emoji & 240) >> 4; + let idx = emoji & 15; + res.push(( + EMOJI_CAT[cat as usize], + EMOJIS.get(&EMOJI_CAT[cat as usize]).unwrap()[idx as usize].code, + )); + } + res +} +//use ng_repo::log::*; + +/// taking a list of pazzle words, returns a list of u8 codes +pub fn encode_pazzle(words: &Vec) -> Result, NgError> { + //assert_eq!(EMOJI_CODES.len(), 15 * 15); + let mut res = vec![]; + for word in words { + res.push( + *EMOJI_CODES + .get(word.as_str()) + .ok_or(NgError::InvalidPazzle)?, + ); + } + //log_info!("{:?}", res); + Ok(res) +} + +/// lists all the words available for a pazzle, together with its category and u8 code +pub fn list_all_words() -> Vec<(&'static str, &'static str, u8)> { + let mut tuples = vec![]; + for (icat, cat_name) in EMOJI_CAT.iter().enumerate() { + for (iemoji, emoji) in EMOJIS.get(cat_name).unwrap().iter().enumerate() { + let nbr = (icat << 4) + iemoji; + tuples.push((emoji.code, *cat_name, nbr as u8)); + } + } + tuples +} diff --git a/ng-wallet/src/lib.rs b/ng-wallet/src/lib.rs index 3b53fd0..3e7fd5c 100644 --- a/ng-wallet/src/lib.rs +++ b/ng-wallet/src/lib.rs @@ -69,6 +69,12 @@ impl Wallet { } } + /// `nonce` : The current nonce used for encrypting this wallet by the user on this device. + /// It should be incremented BEFORE encrypting the wallet again + /// when some new operations have been added to the log of the Wallet. + /// The nonce is by PeerId. It is saved together with the PeerId in the SessionPeerStorage. + /// If the session is not saved (in-memory) it is lost, but it is fine, as the PeerId is also lost, and a new one + /// will be generated for the next session. pub fn encrypt( &self, wallet_log: &WalletLog, @@ -208,33 +214,33 @@ pub fn enc_wallet_log( Ok(buffer) } -pub fn dec_session(key: PrivKey, vec: &Vec) -> Result { - let session_ser = crypto_box::seal_open(&(*key.to_dh().slice()).into(), vec) - .map_err(|_| NgWalletError::DecryptionError)?; - let session: SessionWalletStorage = - serde_bare::from_slice(&session_ser).map_err(|_| NgWalletError::SerializationError)?; - let SessionWalletStorage::V0(v0) = session; - Ok(v0) -} - -pub fn create_new_session( - wallet_id: PubKey, - user: PubKey, -) -> Result<(SessionWalletStorageV0, Vec), NgWalletError> { - let peer = generate_keypair(); - let mut sws = SessionWalletStorageV0::new(); - let sps = SessionPeerStorageV0 { - user, - peer_key: peer.0, - last_wallet_nonce: 0, - }; - sws.users.insert(user.to_string(), sps); - let sws_ser = serde_bare::to_vec(&SessionWalletStorage::V0(sws.clone())).unwrap(); - let mut rng = crypto_box::aead::OsRng {}; - let cipher = crypto_box::seal(&mut rng, &wallet_id.to_dh_slice().into(), &sws_ser) - .map_err(|_| NgWalletError::EncryptionError)?; - Ok((sws, cipher)) -} +// pub fn dec_session(key: PrivKey, vec: &Vec) -> Result { +// let session_ser = crypto_box::seal_open(&(*key.to_dh().slice()).into(), vec) +// .map_err(|_| NgWalletError::DecryptionError)?; +// let session: SessionWalletStorage = +// serde_bare::from_slice(&session_ser).map_err(|_| NgWalletError::SerializationError)?; +// let SessionWalletStorage::V0(v0) = session; +// Ok(v0) +// } + +// pub fn create_new_session( +// wallet_id: PubKey, +// user: PubKey, +// ) -> Result<(SessionWalletStorageV0, Vec), NgWalletError> { +// let peer = generate_keypair(); +// let mut sws = SessionWalletStorageV0::new(); +// let sps = SessionPeerStorageV0 { +// user, +// peer_key: peer.0, +// last_wallet_nonce: 0, +// }; +// sws.users.insert(user.to_string(), sps); +// let sws_ser = serde_bare::to_vec(&SessionWalletStorage::V0(sws.clone())).unwrap(); +// let mut rng = crypto_box::aead::OsRng {}; +// let cipher = crypto_box::seal(&mut rng, &wallet_id.to_dh_slice().into(), &sws_ser) +// .map_err(|_| NgWalletError::EncryptionError)?; +// Ok((sws, cipher)) +// } pub fn dec_encrypted_block( mut ciphertext: Vec, @@ -264,6 +270,7 @@ pub fn dec_encrypted_block( // `ciphertext` now contains the decrypted block //log_debug!("decrypted_block {:?}", ciphertext); + ciphertext.zeroize(); match decrypted_log { WalletLog::V0(v0) => v0.reduce(master_key), @@ -297,7 +304,7 @@ pub fn derive_key_from_pass(mut pass: Vec, salt: [u8; 16], wallet_id: Wallet } pub fn open_wallet_with_pazzle( - wallet: Wallet, + wallet: &Wallet, mut pazzle: Vec, mut pin: [u8; 4], ) -> Result { @@ -330,9 +337,9 @@ pub fn open_wallet_with_pazzle( "opening of wallet with pazzle took: {} ms", opening_pazzle.elapsed().as_millis() ); - + let cipher = v0.content.encrypted.clone(); Ok(SensitiveWallet::V0(dec_encrypted_block( - v0.content.encrypted, + cipher, master_key, v0.content.peer_id, v0.content.nonce, @@ -445,8 +452,8 @@ pub fn create_wallet_v0(mut params: CreateWalletV0) -> Result Result Result Result Result Result 0 { let opening_pazzle = Instant::now(); - let w = open_wallet_with_pazzle(Wallet::V0(v0.clone()), res.pazzle.clone(), pin) + let w = open_wallet_with_pazzle(&Wallet::V0(v0.clone()), res.pazzle.clone(), pin) .expect("open with pazzle"); log_debug!( "opening of wallet with pazzle took: {} ms", diff --git a/ng-wallet/src/types.rs b/ng-wallet/src/types.rs index 7a9709b..36849dd 100644 --- a/ng-wallet/src/types.rs +++ b/ng-wallet/src/types.rs @@ -144,6 +144,12 @@ impl SessionWalletStorageV0 { pub struct SessionPeerStorageV0 { pub user: UserId, pub peer_key: PrivKey, + /// The current nonce used for encrypting this wallet by the user on this device. + /// It should be incremented BEFORE encrypting the wallet again + /// when some new operations have been added to the log of the Wallet. + /// The nonce is by PeerId. It is saved together with the PeerId in the SessionPeerStorage. + /// If the session is not saved (in-memory) it is lost, but it is fine, as the PeerId is also lost, and a new one + /// will be generated for the next session. pub last_wallet_nonce: u64, } @@ -222,7 +228,7 @@ pub struct LocalWalletStorageV0 { impl From<&CreateWalletResultV0> for LocalWalletStorageV0 { fn from(res: &CreateWalletResultV0) -> Self { LocalWalletStorageV0 { - bootstrap: BootstrapContent::V0(BootstrapContentV0::new()), + bootstrap: BootstrapContent::V0(BootstrapContentV0::new_empty()), wallet: res.wallet.clone(), in_memory: res.in_memory, client_id: res.client.id, @@ -238,6 +244,7 @@ impl From<&CreateWalletResultV0> for LocalWalletStorageV0 { } impl LocalWalletStorageV0 { + #[doc(hidden)] pub fn new( encrypted_wallet: Wallet, wallet_priv_key: PrivKey, @@ -245,7 +252,7 @@ impl LocalWalletStorageV0 { in_memory: bool, ) -> Result { Ok(LocalWalletStorageV0 { - bootstrap: BootstrapContent::V0(BootstrapContentV0::new()), + bootstrap: BootstrapContent::V0(BootstrapContentV0::new_empty()), wallet: encrypted_wallet, in_memory, client_id: client.id, @@ -256,7 +263,7 @@ impl LocalWalletStorageV0 { .encrypt(client.id, wallet_priv_key)?, }) } - + #[doc(hidden)] pub fn to_client_v0(&self, wallet_privkey: PrivKey) -> Result { Ok(ClientV0 { id: self.client_id, @@ -266,7 +273,8 @@ impl LocalWalletStorageV0 { }) } - fn local_client_storage_v0( + /// decrypts the client_storage field, given the wallet PrivKey + pub fn local_client_storage_v0( &self, wallet_privkey: PrivKey, ) -> Result { @@ -294,16 +302,18 @@ impl LocalWalletStorage { #[derive(Clone, Debug, Zeroize, ZeroizeOnDrop, Serialize, Deserialize)] pub struct ClientV0 { #[zeroize(skip)] + /// ClientID pub id: PubKey, /// list of users that should be opened automatically (at launch, after wallet opened) on this device #[zeroize(skip)] pub auto_open: Vec, - /// Device name + /// user supplied Device name. can be useful to distinguish between several devices (phone, tablet, laptop, office desktop, etc...) #[zeroize(skip)] pub name: Option, + /// contains the decrypted information needed when user is opening their wallet on this client. pub sensitive_client_storage: LocalClientStorageV0, } @@ -433,6 +443,9 @@ impl SensitiveWallet { Self::V0(v0) => v0.wallet_id.clone(), } } + pub fn name(&self) -> String { + self.id() + } pub fn client(&self) -> &Option { match self { Self::V0(v0) => &v0.client, @@ -453,6 +466,11 @@ impl SensitiveWallet { Self::V0(v0) => v0.sites.get(&user_id.to_string()).is_some(), } } + pub fn personal_identity(&self) -> UserId { + match self { + Self::V0(v0) => v0.personal_site, + } + } pub fn import_v0( &mut self, encrypted_wallet: Wallet, @@ -897,15 +915,14 @@ impl WalletOperation { pub struct WalletOpCreateV0 { pub wallet_privkey: PrivKey, - #[serde(skip)] - pub pazzle: Vec, + // #[serde(skip)] + // pub pazzle: Vec, - #[serde(skip)] - pub mnemonic: [u16; 12], - - #[serde(skip)] - pub pin: [u8; 4], + // #[serde(skip)] + // pub mnemonic: [u16; 12], + // #[serde(skip)] + // pub pin: [u8; 4], #[zeroize(skip)] pub save_to_ng_one: SaveToNGOne, @@ -1068,28 +1085,58 @@ impl AddWallet { } } -/// Create Wallet Version 0, used by the API create_wallet_v0 +/// Create Wallet Version 0, used by the API create_wallet_v0 as a list of arguments #[derive(Clone, Zeroize, ZeroizeOnDrop, Debug, Serialize, Deserialize)] pub struct CreateWalletV0 { + /// a vector containing the binary content of an image file that will be used at every login, displayed (on devices that can) + /// to the user so they can check the wallet is theirs and that entering their pazzle and PIN is safe and there is no phishing attack. + /// an attacker would redirect the user to a clone of the wallet opener app, and would try to steal what the user enters + /// but this attacker would not possess the security_img of the user as it is only present locally in the wallet file. + /// the image should be bigger than 150x150px. There is no need to provide more than 400x400px as it will be scaled down anyway. + /// We accept several formats like JPEG, PNG, GIF, WEBP and more. + /// The image should be unique to the user. But it should not be too personal neither. Do not upload face picture, this is not a profile pic. + /// The best would be any picture that the user recognizes as unique. + /// Please be aware that other users who are sharing the same device, will be able to see this image. #[zeroize(skip)] #[serde(with = "serde_bytes")] pub security_img: Vec, + /// A string of characters of minimum length 10. + /// This phrase will be presented to the user every time they are about to enter their pazzle and PIN in order to unlock their wallet. + /// It should be something the user will remember, but not something too personal. + /// Do not enter full name, nor address, nor phone number. + /// Instead, the user can enter a quote, a small phrase that they like, or something meaningless to others, but unique to them. + /// Please be aware that other users who are sharing the same device, will be able to see this phrase. pub security_txt: String, + /// chose a PIN code. + /// We recommend the user to choose a PIN code they already know very well (unlock phone, credit card). + /// The PIN and the rest of the Wallet will never be sent to NextGraph or any other third party (check the source code if you don't believe us). + /// It cannot be a series like 1234 or 8765. The same digit cannot repeat more than once. By example 4484 is invalid. + /// Try to avoid birth date, last digits of phone number, or zip code for privacy concern pub pin: [u8; 4], + /// For now, only 9 is supported. 12 and 15 are planned. + /// A value of 0 will deactivate the pazzle mechanism on this Wallet, and only the mnemonic could be used to open it. pub pazzle_length: u8, #[zeroize(skip)] + /// Not implemented yet. Will send the bootstrap to our cloud servers, if needed pub send_bootstrap: bool, #[zeroize(skip)] + /// Not implemented yet. Will send an encrypted Wallet file to our cloud servers, if needed. (and no, it does not contain the user's pazzle nor PIN) pub send_wallet: bool, #[zeroize(skip)] + /// Do you want a binary file containing the whole Wallet ? pub result_with_wallet_file: bool, #[zeroize(skip)] + /// Should the wallet be saved locally on disk, by the LocalBroker. It will not work on a in-memory LocalBroker, obviously. pub local_save: bool, #[zeroize(skip)] + /// What Broker Server to contact when there is internet and we want to sync. pub core_bootstrap: BootstrapContentV0, #[zeroize(skip)] + /// What is the registration code at that Broker Server. Only useful the first time you connect to the Server. + /// Can be None the rest of the time, or if your server does not need an Invitation. pub core_registration: Option<[u8; 32]>, #[zeroize(skip)] + /// Bootstrap of another server that you might use in order to connect to NextGraph network. It can be another interface on the same `core` server. pub additional_bootstrap: Option, } @@ -1124,25 +1171,32 @@ impl CreateWalletV0 { #[derive(Clone, Zeroize, ZeroizeOnDrop, Debug, Serialize, Deserialize)] pub struct CreateWalletResultV0 { #[zeroize(skip)] + /// The encrypted form of the Wallet object that was created. + /// basically the same as what the file contains. pub wallet: Wallet, #[serde(skip)] + /// The private key of the Wallet. Used for signing the wallet and other internal purposes. + /// it is contained in the opened wallet. No need to save it anywhere. pub wallet_privkey: PrivKey, #[serde(with = "serde_bytes")] #[zeroize(skip)] + /// The binary file that can be saved to disk and given to the user pub wallet_file: Vec, + /// randomly generated pazzle pub pazzle: Vec, + /// randomly generated mnemonic. It is an alternate way to open the wallet. + /// A BIP39 list of 12 words. We argue that the Pazzle is easier to remember than this. pub mnemonic: [u16; 12], #[zeroize(skip)] + /// a string identifying uniquely the wallet pub wallet_name: String, - #[zeroize(skip)] - pub peer_id: PubKey, - pub peer_key: PrivKey, - #[zeroize(skip)] - pub nonce: u64, + /// newly created Client that uniquely identifies the device where the wallet has been created. pub client: ClientV0, #[zeroize(skip)] + /// UserId of the "personal identity" of the user pub user: PubKey, #[zeroize(skip)] + /// is this an in_memory wallet that should not be saved to disk by the LocalBroker? pub in_memory: bool, } diff --git a/ngcli/README.md b/ngcli/README.md index dc5a51b..5d41a28 100644 --- a/ngcli/README.md +++ b/ngcli/README.md @@ -14,9 +14,9 @@ This repository is in active development at [https://git.nextgraph.org/NextGraph ## NextGraph -> NextGraph brings about the convergence between P2P and Semantic Web technologies, towards a decentralized, secure and privacy-preserving cloud, based on CRDTs. +> NextGraph brings about the convergence of P2P and Semantic Web technologies, towards a decentralized, secure and privacy-preserving cloud, based on CRDTs. > -> This open source ecosystem provides solutions for end-users and software developers alike, wishing to use or create **decentralized** apps featuring: **live collaboration** on rich-text documents, peer to peer communication with **end-to-end encryption**, offline-first, **local-first**, portable and interoperable data, total ownership of data and software, security and privacy. Centered on repositories containing **semantic data** (RDF), **rich text**, and structured data formats like **JSON**, synced between peers belonging to permissioned groups of users, it offers strong eventual consistency, thanks to the use of **CRDTs**. Documents can be linked together, signed, shared securely, queried using the **SPARQL** language and organized into sites and containers. +> This open source ecosystem provides solutions for end-users (a platform) and software developers (a framework), wishing to use or create **decentralized** apps featuring: **live collaboration** on rich-text documents, peer to peer communication with **end-to-end encryption**, offline-first, **local-first**, portable and interoperable data, total ownership of data and software, security and privacy. Centered on repositories containing **semantic data** (RDF), **rich text**, and structured data formats like **JSON**, synced between peers belonging to permissioned groups of users, it offers strong eventual consistency, thanks to the use of **CRDTs**. Documents can be linked together, signed, shared securely, queried using the **SPARQL** language and organized into sites and containers. > > More info here [https://nextgraph.org](https://nextgraph.org) diff --git a/ngd/README.md b/ngd/README.md index 2ec5da7..de3e264 100644 --- a/ngd/README.md +++ b/ngd/README.md @@ -14,9 +14,9 @@ This repository is in active development at [https://git.nextgraph.org/NextGraph ## NextGraph -> NextGraph brings about the convergence between P2P and Semantic Web technologies, towards a decentralized, secure and privacy-preserving cloud, based on CRDTs. +> NextGraph brings about the convergence of P2P and Semantic Web technologies, towards a decentralized, secure and privacy-preserving cloud, based on CRDTs. > -> This open source ecosystem provides solutions for end-users and software developers alike, wishing to use or create **decentralized** apps featuring: **live collaboration** on rich-text documents, peer to peer communication with **end-to-end encryption**, offline-first, **local-first**, portable and interoperable data, total ownership of data and software, security and privacy. Centered on repositories containing **semantic data** (RDF), **rich text**, and structured data formats like **JSON**, synced between peers belonging to permissioned groups of users, it offers strong eventual consistency, thanks to the use of **CRDTs**. Documents can be linked together, signed, shared securely, queried using the **SPARQL** language and organized into sites and containers. +> This open source ecosystem provides solutions for end-users (a platform) and software developers (a framework), wishing to use or create **decentralized** apps featuring: **live collaboration** on rich-text documents, peer to peer communication with **end-to-end encryption**, offline-first, **local-first**, portable and interoperable data, total ownership of data and software, security and privacy. Centered on repositories containing **semantic data** (RDF), **rich text**, and structured data formats like **JSON**, synced between peers belonging to permissioned groups of users, it offers strong eventual consistency, thanks to the use of **CRDTs**. Documents can be linked together, signed, shared securely, queried using the **SPARQL** language and organized into sites and containers. > > More info here [https://nextgraph.org](https://nextgraph.org) diff --git a/ngd/src/cli.rs b/ngd/src/cli.rs index f6217bf..be647a5 100644 --- a/ngd/src/cli.rs +++ b/ngd/src/cli.rs @@ -47,7 +47,7 @@ pub(crate) struct Cli { #[arg(long, requires("core"))] pub core_with_clients: bool, - /// Quick config to forward all requests to another BROKER. format is "[DOMAIN/IP:PORT]@PEER_ID". An IPv6 should be encased in square brackets [IPv6] and the whole option should be between double quotes. Port defaults to 80 for IPs and 443 for domains + /// Quick config to forward all requests to another BROKER. format is "[DOMAIN/IP:PORT]@PEER_ID". An IPv6 should be encased in square brackets `[IPv6]` and the whole option should be between double quotes. Port defaults to 80 for IPs and 443 for domains #[arg( short, long, diff --git a/ngd/src/main.rs b/ngd/src/main.rs index 18cf68b..b199a52 100644 --- a/ngd/src/main.rs +++ b/ngd/src/main.rs @@ -49,12 +49,16 @@ use regex::Regex; //For windows: {846EE342-7039-11DE-9D20-806E6F6E6963} //For the other OSes: en0 lo ... + #[cfg(not(target_os = "windows"))] lazy_static! { + #[doc(hidden)] static ref RE_INTERFACE: Regex = Regex::new(r"^([0-9a-z]{2,16})(\:\d{1,5})?$").unwrap(); } + #[cfg(target_os = "windows")] lazy_static! { + #[doc(hidden)] static ref RE_INTERFACE: Regex = Regex::new( r"^(\{[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}\})(\:\d{1,5})?$" ) @@ -62,6 +66,7 @@ lazy_static! { } lazy_static! { + #[doc(hidden)] static ref RE_IPV6_WITH_PORT: Regex = Regex::new(r"^\[([0-9a-fA-F:]{3,39})\](\:\d{1,5})?$").unwrap(); } @@ -944,6 +949,7 @@ async fn main_inner() -> Result<(), ()> { }; overlays_config.forward = vec![BrokerServerV0 { server_type, + can_verify: false, peer_id, }]; }