From fc4924ac872556e4478791b04dc4c6fa0fcf2f15 Mon Sep 17 00:00:00 2001 From: Niko PLP Date: Wed, 3 Apr 2024 14:26:21 +0300 Subject: [PATCH] Rust API for local_broker + refactor ng-wallet and apps for login + reorganize git repo --- .github/header.png | Bin 0 -> 139433 bytes .gitignore | 2 +- Cargo.lock | 337 ++++---- Cargo.toml | 33 +- README.md | 47 +- nextgraph/Cargo.toml | 32 + nextgraph/README.md | 57 ++ nextgraph/src/lib.rs | 1 + nextgraph/src/local_broker.rs | 751 ++++++++++++++++++ ng-app/src-tauri/Cargo.toml | 25 +- ng-app/src-tauri/src/lib.rs | 499 +++++++----- ng-app/src/App.svelte | 120 ++- ng-app/src/api.ts | 51 +- ng-app/src/lib/Home.svelte | 8 +- ng-app/src/lib/Login.svelte | 71 +- ng-app/src/routes/Home.svelte | 9 +- ng-app/src/routes/Install.svelte | 9 +- ng-app/src/routes/User.svelte | 124 ++- ng-app/src/routes/WalletCreate.svelte | 29 +- ng-app/src/routes/WalletLogin.svelte | 53 +- ng-app/src/store.ts | 20 +- ng-app/src/wallet_emojis.ts | 86 +- ng-app/src/worker.js | 2 +- {p2p-broker => ng-broker}/Cargo.toml | 30 +- ng-broker/README.md | 55 ++ .../src/broker_store/account.rs | 18 +- .../src/broker_store/config.rs | 8 +- .../src/broker_store/invitation.rs | 20 +- .../src/broker_store/mod.rs | 0 .../src/broker_store/overlay.rs | 10 +- .../src/broker_store/peer.rs | 8 +- .../src/broker_store/repostoreinfo.rs | 8 +- .../src/broker_store/topic.rs | 8 +- .../src/broker_store/wallet.rs | 12 +- {p2p-broker => ng-broker}/src/interfaces.rs | 4 +- {p2p-broker => ng-broker}/src/lib.rs | 0 {p2p-broker => ng-broker}/src/server.rs | 24 +- .../src/server_storage.rs | 63 +- {p2p-broker => ng-broker}/src/server_ws.rs | 24 +- {p2p-broker => ng-broker}/src/types.rs | 4 +- {p2p-broker => ng-broker}/src/utils.rs | 2 +- {p2p-client-ws => ng-client-ws}/Cargo.toml | 23 +- ng-client-ws/README.md | 55 ++ {p2p-client-ws => ng-client-ws}/src/lib.rs | 0 .../src/remote_ws.rs | 32 +- .../src/remote_ws_wasm.rs | 16 +- {p2p-net => ng-net}/Cargo.toml | 27 +- ng-net/README.md | 55 ++ {p2p-net => ng-net}/src/actor.rs | 0 .../src/actors/add_invitation.rs | 4 +- {p2p-net => ng-net}/src/actors/add_user.rs | 4 +- {p2p-net => ng-net}/src/actors/connecting.rs | 4 +- {p2p-net => ng-net}/src/actors/del_user.rs | 2 +- .../src/actors/list_invitations.rs | 4 +- {p2p-net => ng-net}/src/actors/list_users.rs | 4 +- {p2p-net => ng-net}/src/actors/mod.rs | 0 {p2p-net => ng-net}/src/actors/noise.rs | 0 {p2p-net => ng-net}/src/actors/probe.rs | 0 {p2p-net => ng-net}/src/actors/start.rs | 2 +- {p2p-net => ng-net}/src/broker.rs | 96 ++- {p2p-net => ng-net}/src/broker_connection.rs | 10 +- {p2p-net => ng-net}/src/connection.rs | 27 +- {p2p-net => ng-net}/src/errors.rs | 18 +- {p2p-net => ng-net}/src/lib.rs | 2 +- {p2p-net => ng-net}/src/server_storage.rs | 9 +- {p2p-net => ng-net}/src/tests/file.rs | 0 {p2p-net => ng-net}/src/tests/mod.rs | 0 {p2p-net => ng-net}/src/types.rs | 158 +--- {p2p-net => ng-net}/src/utils.rs | 6 +- {p2p-repo => ng-repo}/Cargo.toml | 25 +- ng-repo/README.md | 55 ++ {p2p-repo => ng-repo}/src/block.rs | 2 - {p2p-repo => ng-repo}/src/branch.rs | 65 +- {p2p-repo => ng-repo}/src/commit.rs | 418 ++++++---- {p2p-repo => ng-repo}/src/errors.rs | 19 +- ng-repo/src/event.rs | 119 +++ {p2p-repo => ng-repo}/src/file.rs | 11 +- {p2p-repo => ng-repo}/src/kcv_store.rs | 0 {p2p-repo => ng-repo}/src/lib.rs | 2 + {p2p-repo => ng-repo}/src/object.rs | 94 ++- ng-repo/src/repo.rs | 574 +++++++++++++ ng-repo/src/site.rs | 138 ++++ {p2p-repo => ng-repo}/src/store.rs | 2 - {p2p-repo => ng-repo}/src/types.rs | 701 ++++++++++++++-- {p2p-repo => ng-repo}/src/utils.rs | 10 +- {p2p-repo => ng-repo}/tests/test.jpg | Bin ng-sdk-js/Cargo.toml | 24 +- ng-sdk-js/README.md | 2 +- ng-sdk-js/app-node/LICENSE-APACHE | 201 +++++ ng-sdk-js/app-node/LICENSE-MIT | 25 + ng-sdk-js/app-node/package.json | 2 +- ng-sdk-js/app-react/LICENSE-APACHE | 201 +++++ ng-sdk-js/app-react/LICENSE-MIT | 25 + ng-sdk-js/app-react/README.md | 13 +- ng-sdk-js/app-react/package.json | 2 +- ng-sdk-js/app-web/README.md | 11 +- ng-sdk-js/app-web/package.json | 2 +- ng-sdk-js/js/node.js | 2 +- ng-sdk-js/src/lib.rs | 520 +++++++----- ng-stores-lmdb/Cargo.toml | 26 + .../src/kcv_store.rs | 10 +- {stores-lmdb => ng-stores-lmdb}/src/lib.rs | 0 .../src/repo_store.rs | 16 +- ng-stores-rocksdb/Cargo.toml | 25 + ng-stores-rocksdb/README.md | 55 ++ .../src/kcv_store.rs | 6 +- .../src/lib.rs | 0 .../src/repo_store.rs | 16 +- ng-verifier/Cargo.toml | 25 + ng-verifier/README.md | 55 ++ {p2p-verifier => ng-verifier}/src/lib.rs | 0 ng-wallet/Cargo.toml | 25 +- ng-wallet/README.md | 55 ++ ng-wallet/src/emojis.rs | 32 +- ng-wallet/src/lib.rs | 223 +----- ng-wallet/src/types.rs | 549 +++++++++---- ngaccount/.gitignore | 1 + ngaccount/Cargo.toml | 26 +- ngaccount/src/main.rs | 14 +- ngcli/Cargo.toml | 26 +- ngcli/README.md | 57 ++ ngcli/src/main.rs | 28 +- ngcli/src/old.rs | 10 +- ngd/Cargo.toml | 27 +- ngd/README.md | 62 +- ngd/src/main.rs | 32 +- ngone/Cargo.toml | 22 +- ngone/src/main.rs | 12 +- ngone/src/store/dynpeer.rs | 8 +- ngone/src/store/wallet_record.rs | 6 +- p2p-repo/src/repo.rs | 129 --- p2p-repo/src/site.rs | 72 -- p2p-verifier/Cargo.toml | 17 - stores-lmdb/Cargo.toml | 20 - stores-rocksdb/Cargo.toml | 20 - 135 files changed, 6052 insertions(+), 2142 deletions(-) create mode 100644 .github/header.png create mode 100644 nextgraph/Cargo.toml create mode 100644 nextgraph/README.md create mode 100644 nextgraph/src/lib.rs create mode 100644 nextgraph/src/local_broker.rs rename {p2p-broker => ng-broker}/Cargo.toml (53%) create mode 100644 ng-broker/README.md rename {p2p-broker => ng-broker}/src/broker_store/account.rs (96%) rename {p2p-broker => ng-broker}/src/broker_store/config.rs (96%) rename {p2p-broker => ng-broker}/src/broker_store/invitation.rs (95%) rename {p2p-broker => ng-broker}/src/broker_store/mod.rs (100%) rename {p2p-broker => ng-broker}/src/broker_store/overlay.rs (97%) rename {p2p-broker => ng-broker}/src/broker_store/peer.rs (97%) rename {p2p-broker => ng-broker}/src/broker_store/repostoreinfo.rs (96%) rename {p2p-broker => ng-broker}/src/broker_store/topic.rs (97%) rename {p2p-broker => ng-broker}/src/broker_store/wallet.rs (95%) rename {p2p-broker => ng-broker}/src/interfaces.rs (96%) rename {p2p-broker => ng-broker}/src/lib.rs (100%) rename {p2p-broker => ng-broker}/src/server.rs (98%) rename {p2p-broker => ng-broker}/src/server_storage.rs (73%) rename {p2p-broker => ng-broker}/src/server_ws.rs (98%) rename {p2p-broker => ng-broker}/src/types.rs (89%) rename {p2p-broker => ng-broker}/src/utils.rs (98%) rename {p2p-client-ws => ng-client-ws}/Cargo.toml (58%) create mode 100644 ng-client-ws/README.md rename {p2p-client-ws => ng-client-ws}/src/lib.rs (100%) rename {p2p-client-ws => ng-client-ws}/src/remote_ws.rs (95%) rename {p2p-client-ws => ng-client-ws}/src/remote_ws_wasm.rs (97%) rename {p2p-net => ng-net}/Cargo.toml (58%) create mode 100644 ng-net/README.md rename {p2p-net => ng-net}/src/actor.rs (100%) rename {p2p-net => ng-net}/src/actors/add_invitation.rs (98%) rename {p2p-net => ng-net}/src/actors/add_user.rs (98%) rename {p2p-net => ng-net}/src/actors/connecting.rs (95%) rename {p2p-net => ng-net}/src/actors/del_user.rs (98%) rename {p2p-net => ng-net}/src/actors/list_invitations.rs (98%) rename {p2p-net => ng-net}/src/actors/list_users.rs (98%) rename {p2p-net => ng-net}/src/actors/mod.rs (100%) rename {p2p-net => ng-net}/src/actors/noise.rs (100%) rename {p2p-net => ng-net}/src/actors/probe.rs (100%) rename {p2p-net => ng-net}/src/actors/start.rs (99%) rename {p2p-net => ng-net}/src/broker.rs (93%) rename {p2p-net => ng-net}/src/broker_connection.rs (98%) rename {p2p-net => ng-net}/src/connection.rs (98%) rename {p2p-net => ng-net}/src/errors.rs (93%) rename {p2p-net => ng-net}/src/lib.rs (97%) rename {p2p-net => ng-net}/src/server_storage.rs (86%) rename {p2p-net => ng-net}/src/tests/file.rs (100%) rename {p2p-net => ng-net}/src/tests/mod.rs (100%) rename {p2p-net => ng-net}/src/types.rs (95%) rename {p2p-net => ng-net}/src/utils.rs (99%) rename {p2p-repo => ng-repo}/Cargo.toml (56%) create mode 100644 ng-repo/README.md rename {p2p-repo => ng-repo}/src/block.rs (98%) rename {p2p-repo => ng-repo}/src/branch.rs (87%) rename {p2p-repo => ng-repo}/src/commit.rs (84%) rename {p2p-repo => ng-repo}/src/errors.rs (83%) create mode 100644 ng-repo/src/event.rs rename {p2p-repo => ng-repo}/src/file.rs (99%) rename {p2p-repo => ng-repo}/src/kcv_store.rs (100%) rename {p2p-repo => ng-repo}/src/lib.rs (99%) rename {p2p-repo => ng-repo}/src/object.rs (96%) create mode 100644 ng-repo/src/repo.rs create mode 100644 ng-repo/src/site.rs rename {p2p-repo => ng-repo}/src/store.rs (97%) rename {p2p-repo => ng-repo}/src/types.rs (71%) rename {p2p-repo => ng-repo}/src/utils.rs (95%) rename {p2p-repo => ng-repo}/tests/test.jpg (100%) create mode 100644 ng-sdk-js/app-node/LICENSE-APACHE create mode 100644 ng-sdk-js/app-node/LICENSE-MIT create mode 100644 ng-sdk-js/app-react/LICENSE-APACHE create mode 100644 ng-sdk-js/app-react/LICENSE-MIT create mode 100644 ng-stores-lmdb/Cargo.toml rename {stores-lmdb => ng-stores-lmdb}/src/kcv_store.rs (99%) rename {stores-lmdb => ng-stores-lmdb}/src/lib.rs (100%) rename {stores-lmdb => ng-stores-lmdb}/src/repo_store.rs (99%) create mode 100644 ng-stores-rocksdb/Cargo.toml create mode 100644 ng-stores-rocksdb/README.md rename {stores-rocksdb => ng-stores-rocksdb}/src/kcv_store.rs (99%) rename {stores-rocksdb => ng-stores-rocksdb}/src/lib.rs (100%) rename {stores-rocksdb => ng-stores-rocksdb}/src/repo_store.rs (99%) create mode 100644 ng-verifier/Cargo.toml create mode 100644 ng-verifier/README.md rename {p2p-verifier => ng-verifier}/src/lib.rs (100%) create mode 100644 ng-wallet/README.md create mode 100644 ngaccount/.gitignore create mode 100644 ngcli/README.md delete mode 100644 p2p-repo/src/repo.rs delete mode 100644 p2p-repo/src/site.rs delete mode 100644 p2p-verifier/Cargo.toml delete mode 100644 stores-lmdb/Cargo.toml delete mode 100644 stores-rocksdb/Cargo.toml diff --git a/.github/header.png b/.github/header.png new file mode 100644 index 0000000000000000000000000000000000000000..fa6a0f13b13472a21d10d01ba0e002c85c747d0f GIT binary patch literal 139433 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"] +repository = "https://git.nextgraph.org/NextGraph/nextgraph-rs" +homepage = "https://nextgraph.org" +keywords = [ +"crdt","dapp","decentralized","e2ee","local-first","p2p","semantic-web","eventual-consistency","json-ld","markdown", +"ocap","z-cap","offline-first","p2p-network","collaboration","privacy-protection","rdf","rich-text-editor","self-hosted", +"sparql","byzantine-fault-tolerance", +"web3" +] +documentation = "https://docs.nextgraph.org/" [profile.release] lto = true diff --git a/README.md b/README.md index 5554500..c27d2c9 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,14 @@ +

+ nextgraph-header +

+ # nextgraph-rs +![MSRV][rustc-image] +[![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) + Rust implementation of NextGraph 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. @@ -8,7 +17,7 @@ This repository is in active development at [https://git.nextgraph.org/NextGraph > NextGraph brings about the convergence between 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 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. > > More info here [https://nextgraph.org](https://nextgraph.org) @@ -18,6 +27,9 @@ Documentation can be found here [https://docs.nextgraph.org](https://docs.nextgr And our community forum where you can ask questions is here [https://forum.nextgraph.org](https://forum.nextgraph.org) +[![Mastodon](https://img.shields.io/badge/-MASTODON-%232B90D9?style=for-the-badge&logo=mastodon&logoColor=white)](https://mastodon.lescommuns.org/@nextgraph) +[![Twitter URL](https://img.shields.io/twitter/url?style=social&url=https%3A%2F%2Ftwitter.com%2Fnextgraph)](https://twitter.com/nextgraph) + ## How to use NextGraph 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/). @@ -48,19 +60,20 @@ cargo build The crates are organized as follow : -- p2p-repo : NextGraph repositories common library -- p2p-net : P2P network common library -- p2p-broker : the broker code (as server and core node) -- p2p-client-ws : the client connecting to a broker with WebSocket, used by the apps and verifier -- p2p-verifier : the code of the verifier -- stores-rocksdb : RocksDB backed stores. see [repo here](https://git.nextgraph.org/NextGraph/rust-rocksdb) -- ngcli : CLI tool to manipulate the repos and administrate the server -- ngd : binary executable of the daemon (that can run a broker, verifier and/or Rust services) +- [nextgraph](nextgraph/README.md) : Client library. Use this crate to embed NextGraph client in your Rust application +- [ngcli](ngcli/README.md) : CLI tool to manipulate the local documents and repos and administrate the server +- [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-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](ngone/README.md) : server for nextgraph.one (helps user bootstrap into the right app) -- [ngaccount](ngaccount/README.md) : server for nextgraph's Broker Service Provider account manager. +- ngone : server for nextgraph.one. helps user bootstrap into the right app. Not useful to you. Published here for transparency +- ngaccount : server for nextgraph's Broker Service Provider account manager. Not useful to you. Published here for transparency ### Run @@ -87,7 +100,7 @@ cargo test --all --verbose -- --show-output --nocapture Test a single module: ``` -cargo test --package p2p-repo --lib -- branch::test --show-output --nocapture +cargo test --package ng-repo --lib -- branch::test --show-output --nocapture ``` Test end-to-end client and server: @@ -106,7 +119,7 @@ wasm-pack test --chrome --headless Test Rust websocket ``` -cargo test --package p2p-client-ws --lib -- remote_ws::test::test_ws --show-output --nocapture +cargo test --package ng-client-ws --lib -- remote_ws::test::test_ws --show-output --nocapture ``` ### Build release binaries @@ -155,7 +168,7 @@ For building the apps, see this [documentation](ng-app/README.md). #### OpenBSD On OpenBSD, a conflict between the installed LibreSSL library and the reqwest crate, needs a bit of attention. -Before compiling the daemon for OpenBSD, please comment out lines 32-33 of `p2p-net/Cargo.toml`. This will be solved soon in a more appropriate way. +Before compiling the daemon for OpenBSD, please comment out lines 32-33 of `ng-net/Cargo.toml`. This will be solved soon in a more appropriate way. ``` #[target.'cfg(target_arch = "wasm32")'.dependencies] @@ -203,3 +216,9 @@ Licensed under either of --- NextGraph received funding through the [NGI Assure Fund](https://nlnet.nl/project/NextGraph/index.html), a fund established by [NLnet](https://nlnet.nl/) with financial support from the European Commission's [Next Generation Internet](https://ngi.eu/) programme, under the aegis of DG Communications Networks, Content and Technology under grant agreement No 957073. + +[rustc-image]: https://img.shields.io/badge/rustc-1.64+-blue.svg +[license-image]: https://img.shields.io/badge/license-Apache2.0-blue.svg +[license-link]: https://git.nextgraph.org/NextGraph/nextgraph-rs/raw/branch/master/LICENSE-APACHE2 +[license-image2]: https://img.shields.io/badge/license-MIT-blue.svg +[license-link2]: https://git.nextgraph.org/NextGraph/nextgraph-rs/src/branch/master/LICENSE-MIT diff --git a/nextgraph/Cargo.toml b/nextgraph/Cargo.toml new file mode 100644 index 0000000..f838918 --- /dev/null +++ b/nextgraph/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "nextgraph" +description = "NextGraph client library. Nextgraph is a decentralized, secure and local-first web 3.0 ecosystem based on Semantic Web and CRDTs" +categories = ["asynchronous","text-editors","web-programming","development-tools","database-implementations"] +version.workspace = true +edition.workspace = true +license.workspace = true +authors.workspace = true +repository.workspace = true +homepage.workspace = true +keywords.workspace = true +documentation.workspace = true +rust-version.workspace = true + +[badges] +maintenance = { status = "actively-developed" } + +[dependencies] +ng-repo = { path = "../ng-repo", version = "0.1.0" } +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" } +async-once-cell = "0.5.3" +once_cell = "1.17.1" +serde = { version = "1.0", features = ["derive"] } +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"] } +zeroize = { version = "1.6.0", features = ["zeroize_derive"] } diff --git a/nextgraph/README.md b/nextgraph/README.md new file mode 100644 index 0000000..e4471ec --- /dev/null +++ b/nextgraph/README.md @@ -0,0 +1,57 @@ +

+ nextgraph-header +

+ +# nextgraph + +![MSRV][rustc-image] +[![Apache 2.0 Licensed][license-image]][license-link] +[![MIT Licensed][license-image2]][license-link2] + +Rust client library of NextGraph + +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. + +## NextGraph + +> NextGraph brings about the convergence between 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. +> +> More info here [https://nextgraph.org](https://nextgraph.org) + +## Support + +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 + +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/). + +## License + +Licensed under either of + +- Apache License, Version 2.0 ([LICENSE-APACHE2](LICENSE-APACHE2) or http://www.apache.org/licenses/LICENSE-2.0) +- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + at your option. + +`SPDX-License-Identifier: Apache-2.0 OR MIT` + +### Contributions license + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you shall be dual licensed as below, without any +additional terms or conditions. + +--- + +NextGraph received funding through the [NGI Assure Fund](https://nlnet.nl/project/NextGraph/index.html), a fund established by [NLnet](https://nlnet.nl/) with financial support from the European Commission's [Next Generation Internet](https://ngi.eu/) programme, under the aegis of DG Communications Networks, Content and Technology under grant agreement No 957073. + +[rustc-image]: https://img.shields.io/badge/rustc-1.64+-blue.svg +[license-image]: https://img.shields.io/badge/license-Apache2.0-blue.svg +[license-link]: https://git.nextgraph.org/NextGraph/nextgraph-rs/raw/branch/master/LICENSE-APACHE2 +[license-image2]: https://img.shields.io/badge/license-MIT-blue.svg +[license-link2]: https://git.nextgraph.org/NextGraph/nextgraph-rs/src/branch/master/LICENSE-MIT diff --git a/nextgraph/src/lib.rs b/nextgraph/src/lib.rs new file mode 100644 index 0000000..4df2a90 --- /dev/null +++ b/nextgraph/src/lib.rs @@ -0,0 +1 @@ +pub mod local_broker; diff --git a/nextgraph/src/local_broker.rs b/nextgraph/src/local_broker.rs new file mode 100644 index 0000000..ca1589d --- /dev/null +++ b/nextgraph/src/local_broker.rs @@ -0,0 +1,751 @@ +// 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 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 once_cell::sync::Lazy; +use serde_bare::to_vec; +use std::collections::HashMap; +use std::fs::{read, write, File, OpenOptions}; +use std::path::PathBuf; +use zeroize::{Zeroize, ZeroizeOnDrop}; + +use ng_net::broker::*; +use ng_repo::errors::NgError; +use ng_repo::log::*; +use ng_repo::types::*; +use ng_wallet::{create_wallet_v0, types::*}; + +#[cfg(not(target_arch = "wasm32"))] +use ng_client_ws::remote_ws::ConnectionWebSocket; +#[cfg(target_arch = "wasm32")] +use ng_client_ws::remote_ws_wasm::ConnectionWebSocket; + +type JsStorageReadFn = dyn Fn(String) -> Result + 'static + Sync + Send; +type JsStorageWriteFn = dyn Fn(String, String) -> Result<(), NgError> + 'static + Sync + Send; +type JsCallback = dyn Fn() + 'static + Sync + Send; + +pub struct JsStorageConfig { + pub local_read: Box, + pub local_write: Box, + pub session_read: Box, + pub session_write: Box, +} + +impl fmt::Debug for JsStorageConfig { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "JsStorageConfig") + } +} + +#[derive(Debug)] +pub enum LocalBrokerConfig { + InMemory, + BasePath(PathBuf), + JsStorage(JsStorageConfig), +} + +impl LocalBrokerConfig { + pub fn is_in_memory(&self) -> bool { + match self { + Self::InMemory => true, + _ => false, + } + } + pub fn is_js(&self) -> bool { + match self { + Self::JsStorage(_) => true, + _ => false, + } + } + pub fn js_config(&self) -> Option<&JsStorageConfig> { + match self { + Self::JsStorage(c) => Some(c), + _ => None, + } + } +} + +//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, +pub type OutboxWriteFn = + dyn Fn(PubKey, u64, Vec) -> Result<(), NgError> + 'static + Sync + Send; + +/// 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 +pub struct SessionConfigV0 { + pub user_id: UserId, + pub wallet_name: String, + // pub last_seq_function: Box, + // pub outbox_write_function: Box, + // pub outbox_read_function: Box, +} + +/// Session Config to initiate a session at a local broker +pub enum SessionConfig { + V0(SessionConfigV0), +} + +#[derive(Debug)] +struct Session { + config: SessionConfig, + peer_key: PrivKey, + last_wallet_nonce: u64, + //verifier, +} + +impl SessionConfig { + pub fn user_id(&self) -> UserId { + match self { + Self::V0(v0) => v0.user_id, + } + } + pub fn wallet_name(&self) -> String { + match self { + Self::V0(v0) => v0.wallet_name.clone(), + } + } +} + +impl fmt::Debug for SessionConfigV0 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "SessionConfigV0 user={} wallet={}", + self.user_id, self.wallet_name + ) + } +} + +impl fmt::Debug for SessionConfig { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + SessionConfig::V0(v0) => v0.fmt(f), + } + } +} + +#[derive(Debug)] +struct LocalBroker { + pub config: LocalBrokerConfig, + + pub wallets: HashMap, + + pub opened_wallets: HashMap, + + pub sessions: HashMap, + + pub opened_sessions: HashMap, +} + +impl ILocalBroker for LocalBroker {} + +impl LocalBroker { + fn get_wallet_for_session(&self, config: &SessionConfig) -> Result<&SensitiveWallet, NgError> { + match config { + SessionConfig::V0(v0) => self + .opened_wallets + .get(&v0.wallet_name) + .ok_or(NgError::WalletNotFound), + } + } +} + +//static LOCAL_BROKER: OnceCell> = OnceCell::new(); + +static LOCAL_BROKER: OnceCell>, NgError>> = OnceCell::new(); //Lazy::new(|| Arc::new(RwLock::new(Broker::new()))); + +pub type ConfigInitFn = dyn Fn() -> LocalBrokerConfig + 'static + Sync + Send; + +async fn init_(config: LocalBrokerConfig) -> Result>, NgError> { + let wallets = match &config { + LocalBrokerConfig::InMemory => HashMap::new(), + LocalBrokerConfig::BasePath(base_path) => { + // load the wallets and sessions from disk + let mut path = base_path.clone(); + path.push("wallets"); + let map_ser = read(path); + if map_ser.is_ok() { + let wallets = LocalWalletStorage::v0_from_vec(&map_ser.unwrap())?; + let LocalWalletStorage::V0(wallets) = wallets; + wallets + } else { + HashMap::new() + } + } + LocalBrokerConfig::JsStorage(js_storage_config) => { + // load the wallets from JsStorage + match (js_storage_config.local_read)("ng_wallets".to_string()) { + Err(_) => HashMap::new(), + Ok(wallets_string) => { + let map_ser = base64_url::decode(&wallets_string) + .map_err(|_| NgError::SerializationError)?; + let wallets: LocalWalletStorage = serde_bare::from_slice(&map_ser)?; + let LocalWalletStorage::V0(v0) = wallets; + v0 + } + } + } + }; + + let local_broker = LocalBroker { + config, + wallets, + opened_wallets: HashMap::new(), + sessions: HashMap::new(), + opened_sessions: HashMap::new(), + }; + //log_debug!("{:?}", &local_broker); + + Ok(Arc::new(RwLock::new(local_broker))) +} + +//-> &'static Result>, NgError> +pub async fn init_local_broker_with_lazy(config_fn: &Lazy>) { + LOCAL_BROKER + .get_or_init(async { + let config = (&*config_fn)(); + init_(config).await + }) + .await; +} + +pub async fn init_local_broker(config_fn: Box) { + LOCAL_BROKER + .get_or_init(async { + let config = (config_fn)(); + init_(config).await + }) + .await; +} + +pub async fn get_all_wallets() -> Result, NgError> { + let broker = match LOCAL_BROKER.get() { + Some(Err(e)) => { + log_err!("LocalBrokerNotInitialized: {}", e); + return Err(NgError::LocalBrokerNotInitialized); + } + None => { + log_err!("Not initialized"); + return Err(NgError::LocalBrokerNotInitialized); + } + Some(Ok(broker)) => broker.read().await, + }; + Ok(broker.wallets.clone()) +} + +pub async fn wallet_create_v0(params: CreateWalletV0) -> Result { + let res = create_wallet_v0(params)?; + let lws: LocalWalletStorageV0 = (&res).into(); + wallet_add(lws).await?; + Ok(res) +} + +pub async fn reload_wallets() -> Result<(), NgError> { + let mut broker = match LOCAL_BROKER.get() { + None | Some(Err(_)) => return Err(NgError::LocalBrokerNotInitialized), + Some(Ok(broker)) => broker.write().await, + }; + match &broker.config { + LocalBrokerConfig::JsStorage(js_config) => { + // load the wallets from JsStorage + let wallets_string = (js_config.local_read)("ng_wallets".to_string())?; + let map_ser = + base64_url::decode(&wallets_string).map_err(|_| NgError::SerializationError)?; + let wallets: LocalWalletStorage = serde_bare::from_slice(&map_ser)?; + let LocalWalletStorage::V0(v0) = wallets; + broker.wallets.extend(v0); + } + _ => {} + } + Ok(()) +} + +pub async fn wallet_add(lws: LocalWalletStorageV0) -> Result<(), NgError> { + let mut broker = match LOCAL_BROKER.get() { + None | Some(Err(_)) => return Err(NgError::LocalBrokerNotInitialized), + Some(Ok(broker)) => broker.write().await, + }; + if !lws.in_memory && broker.config.is_in_memory() { + return Err(NgError::CannotSaveWhenInMemoryConfig); + } + if broker.wallets.get(&lws.wallet.name()).is_some() { + return Err(NgError::WalletAlreadyAdded); + } + let in_memory = lws.in_memory; + broker.wallets.insert(lws.wallet.name(), lws); + if in_memory { + // if broker.config.is_js() { + // (broker.config.js_config().unwrap().wallets_in_mem_changed)(); + // } + } else { + match &broker.config { + LocalBrokerConfig::JsStorage(js_config) => { + // JS save + let lws_ser = LocalWalletStorage::v0_to_vec(&broker.wallets); + let encoded = base64_url::encode(&lws_ser); + (js_config.local_write)("ng_wallets".to_string(), encoded)?; + } + LocalBrokerConfig::BasePath(base_path) => { + // save on disk + // TODO: use https://lib.rs/crates/keyring instead of AppLocalData on Tauri apps + let mut path = base_path.clone(); + std::fs::create_dir_all(path.clone()).unwrap(); + path.push("wallets"); + + let lws_ser = LocalWalletStorage::v0_to_vec(&broker.wallets); + let r = write(path.clone(), &lws_ser); + if r.is_err() { + log_debug!("write {:?} {}", path, r.unwrap_err()); + return Err(NgError::IoError); + } + } + _ => panic!("wrong LocalBrokerConfig"), + } + } + Ok(()) +} + +pub async fn wallet_read_file(file: Vec) -> Result { + let ngf: NgFile = file.try_into()?; + if let NgFile::V0(NgFileV0::Wallet(wallet)) = ngf { + let broker = match LOCAL_BROKER.get() { + None | Some(Err(_)) => return Err(NgError::LocalBrokerNotInitialized), + Some(Ok(broker)) => broker.read().await, + }; + // check that the wallet is not already present in local_broker + let wallet_name = wallet.name(); + if broker.wallets.get(&wallet_name).is_none() { + Ok(wallet) + } else { + Err(NgError::WalletAlreadyAdded) + } + } else { + Err(NgError::InvalidFileFormat) + } +} + +pub async fn wallet_download_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, + }; + // check that the wallet exists + match broker.wallets.get(wallet_name) { + None => Err(NgError::WalletNotFound), + Some(lws) => Ok(to_vec(&NgFile::V0(NgFileV0::Wallet(lws.wallet.clone()))).unwrap()), + } +} + +pub fn wallet_open_with_pazzle( + wallet: Wallet, + pazzle: Vec, + pin: [u8; 4], +) -> Result { + let opened_wallet = ng_wallet::open_wallet_with_pazzle(wallet, pazzle, pin)?; + + Ok(opened_wallet) +} + +pub async fn wallet_import( + encrypted_wallet: Wallet, + mut opened_wallet: SensitiveWallet, + in_memory: bool, +) -> Result { + { + // in a block to release lock before calling wallet_add + let broker = match LOCAL_BROKER.get() { + None | Some(Err(_)) => return Err(NgError::LocalBrokerNotInitialized), + Some(Ok(broker)) => broker.read().await, + }; + + let wallet_name = encrypted_wallet.name(); + if broker.wallets.get(&wallet_name).is_some() { + return Err(NgError::WalletAlreadyOpened); + } + } + + let lws = opened_wallet.import_v0(encrypted_wallet, in_memory)?; + + wallet_add(lws).await?; + + wallet_was_opened(opened_wallet).await +} + +pub async fn wallet_was_opened(mut wallet: SensitiveWallet) -> Result { + let mut broker = match LOCAL_BROKER.get() { + None | Some(Err(_)) => return Err(NgError::LocalBrokerNotInitialized), + Some(Ok(broker)) => broker.write().await, + }; + + if broker.opened_wallets.get(&wallet.id()).is_some() { + return Err(NgError::WalletAlreadyOpened); + } + + match broker.wallets.get(&(wallet.id())) { + Some(lws) => { + if wallet.client().is_none() { + // this case happens when the wallet is opened and not when it is imported (as the client is already there) + wallet.set_client(lws.to_client_v0(wallet.privkey())?); + } + } + None => { + return Err(NgError::WalletNotFound); + } + } + let client = wallet.client().as_ref().unwrap().clone(); + broker.opened_wallets.insert(wallet.id(), wallet); + Ok(client) +} + +pub async fn session_start(config: SessionConfig) -> Result { + let mut broker = match LOCAL_BROKER.get() { + None | Some(Err(_)) => return Err(NgError::LocalBrokerNotInitialized), + Some(Ok(broker)) => broker.write().await, + }; + + let wallet_name = config.wallet_name(); + let wallet_id: PubKey = (*wallet_name).try_into()?; + let user_id = config.user_id(); + + match broker.opened_wallets.get(&wallet_name) { + None => return Err(NgError::WalletNotFound), + Some(wallet) => { + if !wallet.has_user(&user_id) { + return Err(NgError::NotFound); + } + + let session = match broker.sessions.get(&user_id) { + Some(session) => session, + None => { + // creating the session now + let closed_wallet = broker.wallets.get(&wallet_name).unwrap(); + if closed_wallet.in_memory { + let session = SessionPeerStorageV0::new(user_id); + broker.sessions.insert(user_id, session); + broker.sessions.get(&user_id).unwrap() + } else { + // first check if there is a saved SessionWalletStorage + let mut sws = match &broker.config { + LocalBrokerConfig::InMemory => panic!("cannot open saved session"), + LocalBrokerConfig::JsStorage(js_config) => { + // read session wallet storage from JsStorage + let res = + (js_config.session_read)(format!("ng_wallet@{}", wallet_name)); + match res { + Ok(string) => { + let decoded = base64_url::decode(&string) + .map_err(|_| NgError::SerializationError)?; + Some(SessionWalletStorageV0::dec_session( + wallet.privkey(), + &decoded, + )?) + } + Err(_) => None, + } + } + LocalBrokerConfig::BasePath(base_path) => { + // read session wallet storage from disk + let mut path = base_path.clone(); + path.push("sessions"); + path.push(wallet_name.clone()); + let res = read(path); + if res.is_ok() { + Some(SessionWalletStorageV0::dec_session( + wallet.privkey(), + &res.unwrap(), + )?) + } else { + None + } + } + }; + let (session, new_sws) = match &mut sws { + None => { + let (s, sws_ser) = SessionWalletStorageV0::create_new_session( + &wallet_id, user_id, + )?; + broker.sessions.insert(user_id, s); + (broker.sessions.get(&user_id).unwrap(), sws_ser) + } + Some(sws) => { + match sws.users.get(&user_id.to_string()) { + Some(sps) => { + broker.sessions.insert(user_id, sps.clone()); + (broker.sessions.get(&user_id).unwrap(), vec![]) + } + None => { + // 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 + let sps = SessionPeerStorageV0::new(user_id); + sws.users.insert(user_id.to_string(), sps.clone()); + let encrypted = sws.enc_session(&wallet_id)?; + broker.sessions.insert(user_id, sps); + (broker.sessions.get(&user_id).unwrap(), encrypted) + } + } + } + }; + // save the new sws + if new_sws.len() > 0 { + match &broker.config { + LocalBrokerConfig::InMemory => { + panic!("cannot save session when InMemory mode") + } + LocalBrokerConfig::JsStorage(js_config) => { + // save session wallet storage to JsStorage + let encoded = base64_url::encode(&new_sws); + (js_config.session_write)( + format!("ng_wallet@{}", wallet_name), + encoded, + )?; + } + LocalBrokerConfig::BasePath(base_path) => { + // save session wallet storage to disk + let mut path = base_path.clone(); + path.push("sessions"); + std::fs::create_dir_all(path.clone()).unwrap(); + path.push(wallet_name); + //log_debug!("{}", path.clone().display()); + write(path.clone(), &new_sws).map_err(|_| NgError::IoError)?; + } + } + } + session + } + } + }; + let session = session.clone(); + broker.opened_sessions.insert( + user_id, + Session { + config, + peer_key: session.peer_key.clone(), + last_wallet_nonce: session.last_wallet_nonce, + }, + ); + // FIXME: is this return value useful ? + Ok(session) + } + } +} + +use web_time::SystemTime; +fn get_unix_time() -> f64 { + SystemTime::now() + .duration_since(SystemTime::UNIX_EPOCH) + .unwrap() + .as_millis() as f64 +} + +/// Result is a list of (user_id, server_id, server_ip, error, since_date) +pub async fn user_connect( + info: ClientInfo, + user_id: UserId, + location: Option, +) -> Result, f64)>, NgError> { + let local_broker = match LOCAL_BROKER.get() { + None | Some(Err(_)) => return Err(NgError::LocalBrokerNotInitialized), + Some(Ok(broker)) => broker.read().await, + }; + + let session = local_broker + .opened_sessions + .get(&user_id) + .ok_or(NgError::SessionNotFound)?; + let wallet = local_broker.get_wallet_for_session(&session.config)?; + + let mut result: Vec<(String, String, String, Option, f64)> = Vec::new(); + let arc_cnx: Arc> = Arc::new(Box::new(ConnectionWebSocket {})); + + 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; + // log_info!( + // "XXXX {} name={:?} auto_open={:?} {:?}", + // client_id.to_string(), + // client_name, + // auto_open, + // wallet + // ); + for user in auto_open { + let user_id = user.to_string(); + let peer_key = &session.peer_key; + let peer_id = peer_key.to_pub(); + let site = wallet.sites.get(&user_id); + if site.is_none() { + result.push(( + user_id, + "".into(), + "".into(), + Some("Site is missing".into()), + get_unix_time(), + )); + continue; + } + let site = site.unwrap(); + let user_priv = site.get_individual_user_priv_key().unwrap(); + let core = site.cores[0]; //TODO: cycle the other cores if failure to connect (failover) + let server_key = core.0; + let broker = wallet.brokers.get(&core.0.to_string()); + if broker.is_none() { + result.push(( + user_id, + core.0.to_string(), + "".into(), + Some("Broker is missing".into()), + get_unix_time(), + )); + continue; + } + let brokers = broker.unwrap(); + let mut tried: Option<(String, String, String, Option, f64)> = None; + //TODO: on tauri (or forward in local broker, or CLI), prefer a Public to a Domain. Domain always comes first though, so we need to reorder the list + //TODO: use site.bootstraps to order the list of brokerInfo. + for broker_info in brokers { + match broker_info { + BrokerInfoV0::ServerV0(server) => { + let url = server.get_ws_url(&location).await; + log_debug!("URL {:?}", url); + //Option<(String, Vec)> + if url.is_some() { + let url = url.unwrap(); + if url.1.len() == 0 { + // TODO deal with Box(Dyn)Public -> tunnel, and on tauri/forward/CLIs, deal with all Box -> direct connections (when url.1.len is > 0) + let res = BROKER + .write() + .await + .connect( + arc_cnx.clone(), + peer_key.clone(), + peer_id, + server_key, + StartConfig::Client(ClientConfig { + url: url.0.clone(), + name: client_name.clone(), + user_priv: user_priv.clone(), + client_priv: client_priv.clone(), + info: info.clone(), + registration: Some(core.1), + }), + ) + .await; + log_debug!("broker.connect : {:?}", res); + + tried = Some(( + user_id.clone(), + core.0.to_string(), + url.0.into(), + match &res { + Ok(_) => None, + Err(e) => Some(e.to_string()), + }, + get_unix_time(), + )); + } + if tried.is_some() && tried.as_ref().unwrap().3.is_none() { + // successful. we can stop here + break; + } else { + log_debug!("Failed connection {:?}", tried); + } + } + } + // Core information is discarded + _ => {} + } + } + if tried.is_none() { + tried = Some(( + user_id, + core.0.to_string(), + "".into(), + Some("No broker found".into()), + get_unix_time(), + )); + } + result.push(tried.unwrap()); + } + } + } + Ok(result) +} + +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() { + // TODO: change the logic here once it will be possible to have several users connected at the same time + Broker::close_all_connections().await; + } + + Ok(()) +} + +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() { + // TODO: change the logic here once it will be possible to have several users connected at the same time + Broker::close_all_connections().await; + } + + Ok(()) +} + +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) { + Some(mut wallet) => { + for user in wallet.sites() { + let key: PubKey = (user.as_str()).try_into().unwrap(); + broker.opened_sessions.remove(&key); + } + wallet.zeroize(); + } + None => return Err(NgError::WalletNotFound), + } + + Broker::close_all_connections().await; + + Ok(()) +} + +pub async fn wallet_remove(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, + }; + + todo!(); + // should close the wallet, then remove all the saved sessions and remove the wallet + + Ok(()) +} diff --git a/ng-app/src-tauri/Cargo.toml b/ng-app/src-tauri/Cargo.toml index 9c84936..fb238f7 100644 --- a/ng-app/src-tauri/Cargo.toml +++ b/ng-app/src-tauri/Cargo.toml @@ -1,18 +1,22 @@ [package] name = "ng-app" -version = "0.1.0" +# version = "0.1.0" description = "NextGraph App" -authors = ["Niko PLP "] -license = "MIT/Apache-2.0" -repository = "https://git.nextgraph.org/NextGraph/nextgraph-rs" -edition = "2021" +publish = false +version.workspace = true +edition.workspace = true +license.workspace = true +authors.workspace = true +repository.workspace = true +homepage.workspace = true +keywords.workspace = true +documentation.workspace = true +rust-version.workspace = true [lib] name = "nativelib" crate-type = ["staticlib", "cdylib", "rlib"] -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [build-dependencies] tauri-build = { version = "2.0.0-alpha.8", features = [] } # tauri-macros = { version = "=2.0.0-alpha.6" } @@ -25,10 +29,11 @@ tauri = { version = "2.0.0-alpha.14", features = [] } # tauri = { git = "https://github.com/simonhyll/tauri.git", branch="fix/ipc-mixup", features = [] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" -p2p-repo = { path = "../../p2p-repo" } -p2p-net = { path = "../../p2p-net" } -p2p-client-ws = { path = "../../p2p-client-ws" } +ng-repo = { path = "../../ng-repo" } +ng-net = { path = "../../ng-net" } +ng-client-ws = { path = "../../ng-client-ws" } ng-wallet = { path = "../../ng-wallet" } +nextgraph = { path = "../../nextgraph" } async-std = { version = "1.12.0", features = ["attributes", "unstable"] } # tauri-plugin-window = { git = "https://git.nextgraph.org/NextGraph/plugins-workspace.git", branch="window-alpha.1-nextgraph" } tauri-plugin-window = "2.0.0-alpha.1" diff --git a/ng-app/src-tauri/src/lib.rs b/ng-app/src-tauri/src/lib.rs index 933369e..9fb7e10 100644 --- a/ng-app/src-tauri/src/lib.rs +++ b/ng-app/src-tauri/src/lib.rs @@ -7,18 +7,20 @@ // notice may not be copied, modified, or distributed except // according to those terms. use async_std::stream::StreamExt; +use nextgraph::local_broker::*; +use ng_net::broker::*; +use ng_net::types::{ClientInfo, CreateAccountBSP, Invitation}; +use ng_net::utils::{decode_invitation_string, spawn_and_log_error, Receiver, ResultSend}; +use ng_repo::errors::NgError; +use ng_repo::log::*; +use ng_repo::types::*; use ng_wallet::types::*; use ng_wallet::*; -use p2p_client_ws::remote_ws::ConnectionWebSocket; -use p2p_net::broker::*; -use p2p_net::types::{ClientInfo, CreateAccountBSP, Invitation}; -use p2p_net::utils::{decode_invitation_string, spawn_and_log_error, Receiver, ResultSend}; -use p2p_repo::errors::NgError; -use p2p_repo::log::*; -use p2p_repo::types::*; use serde::{Deserialize, Serialize}; use std::collections::HashMap; -use std::fs::{read, write}; +use std::fs::{read, write, File, OpenOptions}; +use std::io::Write; +use std::path::PathBuf; use tauri::scope::ipc::RemoteDomainAccessScope; use tauri::utils::config::WindowConfig; use tauri::{path::BaseDirectory, App, Manager, Window}; @@ -30,15 +32,27 @@ pub use mobile::*; pub type SetupHook = Box Result<(), Box> + Send>; -// Learn more about Tauri commands at https://tauri.app/v1/guides/features/command -// #[tauri::command(rename_all = "snake_case")] -// fn greet(name: &str) -> String { -// format!("Hello, {}! You've been greeted from Rust!", name) -// } - #[tauri::command(rename_all = "snake_case")] async fn test(app: tauri::AppHandle) -> Result<(), ()> { - log_debug!("test is {}", BROKER.read().await.test()); + let path = app + .path() + .resolve("", BaseDirectory::AppLocalData) + .map_err(|_| NgError::SerializationError) + .unwrap(); + init_local_broker(Box::new(move || LocalBrokerConfig::BasePath(path.clone()))).await; + + // init_local_broker(&Lazy::new(|| { + // Box::new(move || LocalBrokerConfig::BasePath(path.clone())) + // })) + // .await; + //pub type LastSeqFn = dyn Fn(PubKey, u16) -> Result + 'static + Sync + Send; + + // let test: Box = Box::new(move |peer_id: PubKey, qty: u16| { + // take_some_peer_last_seq_numbers(peer_id, qty, path.clone()) + // }); + //pub type ConfigInitFn = dyn Fn() -> LocalBrokerConfig + 'static + Sync + Send; + + //log_debug!("test is {}", BROKER.read().await.test()); let path = app .path() .resolve("storage", BaseDirectory::AppLocalData) @@ -51,227 +65,270 @@ async fn test(app: tauri::AppHandle) -> Result<(), ()> { #[tauri::command(rename_all = "snake_case")] async fn wallet_gen_shuffle_for_pazzle_opening(pazzle_length: u8) -> Result { - log_debug!( - "wallet_gen_shuffle_for_pazzle_opening from rust {}", - pazzle_length - ); + // log_debug!( + // "wallet_gen_shuffle_for_pazzle_opening from rust {}", + // pazzle_length + // ); Ok(gen_shuffle_for_pazzle_opening(pazzle_length)) } #[tauri::command(rename_all = "snake_case")] async fn wallet_gen_shuffle_for_pin() -> Result, ()> { - log_debug!("wallet_gen_shuffle_for_pin from rust"); + //log_debug!("wallet_gen_shuffle_for_pin from rust"); Ok(gen_shuffle_for_pin()) } #[tauri::command(rename_all = "snake_case")] -async fn wallet_open_wallet_with_pazzle( +async fn wallet_open_with_pazzle( wallet: Wallet, pazzle: Vec, pin: [u8; 4], -) -> Result { - log_debug!("wallet_open_wallet_with_pazzle from rust {:?}", pazzle); - open_wallet_with_pazzle(wallet, pazzle, pin).map_err(|e| e.to_string()) + 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) + .map_err(|e| e.to_string())?; + Ok(wallet) } #[tauri::command(rename_all = "snake_case")] -async fn wallet_create_wallet( +async fn wallet_download_file(wallet_name: String, app: tauri::AppHandle) -> Result<(), String> { + let ser = nextgraph::local_broker::wallet_download_file(&wallet_name) + .await + .map_err(|e| e.to_string())?; + + // save wallet file to Downloads folder + let path = app + .path() + .resolve( + format!("wallet-{}.ngw", wallet_name), + BaseDirectory::Download, + ) + .unwrap(); + write(path, &ser).map_err(|e| e.to_string())?; + Ok(()) +} + +#[tauri::command(rename_all = "snake_case")] +async fn wallet_create( mut params: CreateWalletV0, app: tauri::AppHandle, -) -> Result<(CreateWalletResultV0, Option), String> { - //log_debug!("wallet_create_wallet from rust {:?}", params); +) -> Result { + //log_debug!("wallet_create from rust {:?}", params); params.result_with_wallet_file = !params.local_save; let local_save = params.local_save; - let res = create_wallet_v0(params).await.map_err(|e| e.to_string()); - if res.is_ok() { - let mut cwr = res.unwrap(); - if local_save { - // save in local store - - let session = save_wallet_locally(&cwr, app).await; - if session.is_err() { - return Err("Cannot save wallet locally".to_string()); - } - return Ok((cwr, Some(session.unwrap()))); - } else { - // save wallet file to Downloads folder - let path = app - .path() - .resolve( - format!("wallet-{}.ngw", cwr.wallet_name), - BaseDirectory::Download, - ) - .unwrap(); - let _r = write(path, &cwr.wallet_file); - cwr.wallet_file = vec![]; - return Ok((cwr, None)); - } + let mut cwr = nextgraph::local_broker::wallet_create_v0(params) + .await + .map_err(|e| e.to_string())?; + if !local_save { + // save wallet file to Downloads folder + let path = app + .path() + .resolve( + format!("wallet-{}.ngw", cwr.wallet_name), + BaseDirectory::Download, + ) + .unwrap(); + let _r = write(path, &cwr.wallet_file); + cwr.wallet_file = vec![]; } - Err(res.unwrap_err()) + Ok(cwr) } -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_wallets_from_localstorage(app.clone()) - .await - .unwrap_or(Some(HashMap::new())) - .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(()); +// // 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, + mut path: PathBuf, +) -> Result { + std::fs::create_dir_all(path.clone()).unwrap(); + path.push(peer_id.to_string()); + log_debug!("{}", path.display()); + + let file = read(path.clone()); + let (mut file_save, val) = match file { + Ok(ser) => { + let old_val = match SessionPeerLastSeq::deser(&ser)? { + SessionPeerLastSeq::V0(v) => v, + _ => unimplemented!(), + }; + ( + OpenOptions::new() + .write(true) + .open(path) + .map_err(|_| NgError::SerializationError)?, + old_val, + ) } - let sws = save_new_session(&res.wallet_name, res.wallet.id(), res.user, app)?; - Ok(sws) - } else { - Err(()) - } + Err(_) => ( + File::create(path).map_err(|_| NgError::SerializationError)?, + 0, + ), + }; + let new_val = val + qty as u64; + let spls = SessionPeerLastSeq::V0(new_val); + let ser = spls.ser()?; + file_save + .write_all(&ser) + .map_err(|_| NgError::SerializationError)?; + + file_save + .sync_data() + .map_err(|_| NgError::SerializationError)?; + + Ok(val) } #[tauri::command(rename_all = "snake_case")] -async fn wallet_open_file(file: Vec, app: tauri::AppHandle) -> Result { - let ngf: NgFile = file.try_into().map_err(|e: NgError| e.to_string())?; - if let NgFile::V0(NgFileV0::Wallet(wallet)) = ngf { - let mut wallets: HashMap = - get_wallets_from_localstorage(app.clone()) - .await - .unwrap_or(Some(HashMap::new())) - .unwrap_or(HashMap::new()); - // check that the wallet is not already present in localStorage - let wallet_name = wallet.name(); - if wallets.get(&wallet_name).is_none() { - Ok(wallet) - } else { - Err("Wallet already present on this device".to_string()) - } - } else { - Err("File does not contain a wallet".to_string()) - } +async fn wallet_read_file(file: Vec, app: tauri::AppHandle) -> Result { + nextgraph::local_broker::wallet_read_file(file) + .await + .map_err(|e: NgError| e.to_string()) +} + +#[tauri::command(rename_all = "snake_case")] +async fn wallet_was_opened( + opened_wallet: SensitiveWallet, + app: tauri::AppHandle, +) -> Result { + nextgraph::local_broker::wallet_was_opened(opened_wallet) + .await + .map_err(|e: NgError| e.to_string()) } #[tauri::command(rename_all = "snake_case")] async fn wallet_import( - previous_wallet: Wallet, - opened_wallet: EncryptedWallet, + encrypted_wallet: Wallet, + opened_wallet: SensitiveWallet, + in_memory: bool, app: tauri::AppHandle, -) -> Result<(String, ClientV0), String> { - let path = app - .path() - .resolve("wallets", BaseDirectory::AppLocalData) - .map_err(|_| "wallet directory error".to_string())?; - let mut wallets: HashMap = - get_wallets_from_localstorage(app.clone()) - .await - .unwrap_or(Some(HashMap::new())) - .unwrap_or(HashMap::new()); - // check that the wallet is not already present in localStorage - let EncryptedWallet::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 (wallet, client_id, 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 = 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_id, client)) - } - } else { - Err("Already present on this device".to_string()) - } +) -> Result { + 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")] -async fn get_wallets_from_localstorage( +async fn get_wallets( app: tauri::AppHandle, -) -> Result>, ()> { +) -> Result>, String> { let path = app .path() - .resolve("wallets", BaseDirectory::AppLocalData) - .map_err(|_| ())?; - let map_ser = read(path); - if map_ser.is_ok() { - let wallets = LocalWalletStorage::v0_from_vec(&map_ser.unwrap()); - let LocalWalletStorage::V0(v0) = wallets; - return Ok(Some(v0)); + .resolve("", BaseDirectory::AppLocalData) + .map_err(|_| NgError::SerializationError) + .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()); + }); + if res.is_ok() { + return Ok(Some(res.unwrap())); } 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) -} +// 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 get_local_session( - id: String, - key: PrivKey, +async fn session_start( + wallet_name: String, user: PubKey, app: tauri::AppHandle, -) -> Result { - let path = app - .path() - .resolve(format!("sessions/{id}"), BaseDirectory::AppLocalData) - .map_err(|_| ())?; - let res = read(path.clone()); - if res.is_ok() { - log_debug!("RESUMING SESSION"); - let v0 = dec_session(key, &res.unwrap()); - if v0.is_ok() { - return Ok(v0.unwrap()); - } - } - - // create a new session - let wallet_id: PubKey = id.as_str().try_into().unwrap(); - save_new_session(&id, wallet_id, user, app) +) -> Result { + let config = SessionConfig::V0(SessionConfigV0 { + user_id: user, + wallet_name, + }); + nextgraph::local_broker::session_start(config) + .await + .map_err(|e: NgError| e.to_string()) } #[tauri::command(rename_all = "snake_case")] @@ -368,6 +425,7 @@ async fn disconnections_subscribe(app: tauri::AppHandle) -> Result<(), ()> { main_window: tauri::Window, ) -> ResultSend<()> { while let Some(user_id) = reader.next().await { + log_debug!("DISCONNECTION FOR {user_id}"); main_window.emit("disconnections", user_id).unwrap(); } log_debug!("END OF disconnections listener"); @@ -406,8 +464,24 @@ async fn doc_get_file_from_store_with_object_ref( } #[tauri::command(rename_all = "snake_case")] -async fn broker_disconnect() { - Broker::close_all_connections().await; +async fn session_stop(user_id: UserId) -> Result<(), String> { + 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) + .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) + .await + .map_err(|e: NgError| e.to_string()) } #[derive(Serialize, Deserialize)] @@ -419,24 +493,16 @@ struct ConnectionInfo { } #[tauri::command(rename_all = "snake_case")] -async fn broker_connect( - client: PubKey, +async fn user_connect( info: ClientInfo, - session: HashMap, - opened_wallet: EncryptedWallet, + user_id: UserId, location: Option, ) -> Result, String> { let mut opened_connections: HashMap = HashMap::new(); - let results = connect_wallet( - client, - info, - session, - opened_wallet, - None, - Box::new(ConnectionWebSocket {}), - ) - .await?; + let results = nextgraph::local_broker::user_connect(info, user_id, None) + .await + .map_err(|e| e.to_string())?; log_debug!("{:?}", results); @@ -452,7 +518,7 @@ async fn broker_connect( ); } - BROKER.read().await.print_status(); + //BROKER.read().await.print_status(); Ok(opened_connections) } @@ -488,6 +554,7 @@ impl AppBuilder { if let Some(setup) = setup { (setup)(app)?; } + for domain in ALLOWED_BSP_DOMAINS { app.ipc_scope().configure_remote_access( RemoteDomainAccessScope::new(domain) @@ -506,18 +573,22 @@ impl AppBuilder { doc_get_file_from_store_with_object_ref, wallet_gen_shuffle_for_pazzle_opening, wallet_gen_shuffle_for_pin, - wallet_open_wallet_with_pazzle, - wallet_create_wallet, - wallet_open_file, + wallet_open_with_pazzle, + wallet_was_opened, + wallet_create, + wallet_read_file, + wallet_download_file, wallet_import, + wallet_close, encode_create_account, - get_local_session, - get_wallets_from_localstorage, + session_start, + session_stop, + get_wallets, open_window, decode_invitation, disconnections_subscribe, - broker_connect, - broker_disconnect, + user_connect, + user_disconnect, ]) .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 9f520c1..819cbd2 100644 --- a/ng-app/src/App.svelte +++ b/ng-app/src/App.svelte @@ -52,6 +52,11 @@ let wallet_channel; let unsub_main_close; + // window.refresh_wallets = async () => { + // let walls = await ng.get_wallets(); + // wallets.set(walls); + // }; + onMount(async () => { try { await disconnections_subscribe(); @@ -59,15 +64,27 @@ console.log("called disconnections_subscribe twice"); } let tauri_platform = import.meta.env.TAURI_PLATFORM; + //console.log(await ng.test()); if (tauri_platform) { - //console.log(await ng.test()); - let walls = await ng.get_wallets_from_localstorage(); + let walls = await ng.get_wallets(); wallets.set(walls); - unsubscribe = active_wallet.subscribe((value) => { - if (value && !value.wallet) { - active_wallet.set(undefined); - push("#/wallet/login"); + unsubscribe = active_wallet.subscribe(async (value) => { + if (value) { + if (value.wallet) { + opened_wallets.update((w) => { + w[value.id] = value.wallet; + return w; + }); + } else { + await ng.wallet_close(value.id); + active_wallet.set(undefined); + opened_wallets.update((w) => { + delete w[value.id]; + return w; + }); + push("#/wallet/login"); + } } }); @@ -87,26 +104,47 @@ } }); } else { + // ON WEB CLIENTS window.addEventListener("storage", async (event) => { if (event.storageArea != localStorage) return; if (event.key === "ng_wallets") { - wallets.set(await ng.get_wallets_from_localstorage()); + await ng.reload_wallets(); + wallets.set(await ng.get_wallets()); } }); - wallets.set(await ng.get_wallets_from_localstorage()); + wallets.set(await ng.get_wallets()); + // TODO: check the possibility of XS-Leaks. I don't see any, but it should be checked + // https://github.com/privacycg/storage-partitioning + // https://github.com/whatwg/html/issues/5803 + // https://w3cping.github.io/privacy-threat-model/ + // https://chromium.googlesource.com/chromium/src/+/fa17a6142f99d58de533d65cd8f3cd0e9a8ee58e + // https://bugs.webkit.org/show_bug.cgi?id=229814 wallet_channel = new BroadcastChannel("ng_wallet"); - wallet_channel.postMessage({ cmd: "is_opened" }, location.href); + window.wallet_channel = wallet_channel; + wallet_channel.postMessage({ cmd: "startup" }, location.href); wallet_channel.onmessage = async (event) => { console.log(event.data.cmd, event.data); if (!location.href.startsWith(event.origin)) return; switch (event.data.cmd) { - case "is_opened": - if ($active_wallet && $active_wallet.wallet) { - wallet_channel.postMessage( - { cmd: "opened", wallet: $active_wallet }, - location.href - ); + case "startup": + for (let saved_id of Object.keys($wallets)) { + if ($wallets[saved_id].in_memory) { + wallet_channel.postMessage( + { + cmd: "new_in_mem", + name: saved_id, + lws: $wallets[saved_id], + }, + location.href + ); + } } + // if ($active_wallet && $active_wallet.wallet) { + // wallet_channel.postMessage( + // { cmd: "opened", wallet: $active_wallet }, + // location.href + // ); + // } for (let opened of Object.keys($opened_wallets)) { wallet_channel.postMessage( { @@ -116,20 +154,54 @@ location.href ); } + 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 + ); + try { + await ng.wallet_was_opened(event.data.wallet.wallet); + } catch (e) { + console.error(e); + } opened_wallets.update((w) => { w[event.data.wallet.id] = event.data.wallet.wallet; return w; }); } break; + case "new_in_mem": + 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); + wallets.update((w) => { + w[event.data.name] = event.data.lws; + return w; + }); + } + } + if (event.data.opened) { + if (!$opened_wallets[event.data.name]) { + await ng.wallet_was_opened(event.data.opened); + opened_wallets.update((w) => { + w[event.data.name] = event.data.opened; + return w; + }); + } + } + break; case "closed": opened_wallets.update((w) => { delete w[event.data.walletid]; return w; }); + await ng.wallet_close(event.data.walletid); if ($active_wallet && $active_wallet.id == event.data.walletid) { await close_active_session(); active_wallet.set(undefined); @@ -138,9 +210,13 @@ break; } }; - unsubscribe = active_wallet.subscribe((value) => { + unsubscribe = active_wallet.subscribe(async (value) => { if (value) { if (value.wallet) { + opened_wallets.update((w) => { + w[value.id] = value.wallet; + return w; + }); wallet_channel.postMessage( { cmd: "opened", wallet: value }, location.href @@ -151,6 +227,7 @@ location.href ); active_wallet.set(undefined); + await ng.wallet_close(value.id); //active_session.set(undefined); opened_wallets.update((w) => { delete w[value.id]; @@ -171,9 +248,10 @@ + {!$active_session} + {JSON.stringify(Object.keys($wallets))} + {JSON.stringify($active_wallet)} + {JSON.stringify(Object.keys($opened_wallets))} + {JSON.stringify($active_session)} +

--> diff --git a/ng-app/src/api.ts b/ng-app/src/api.ts index 7af896f..f180108 100644 --- a/ng-app/src/api.ts +++ b/ng-app/src/api.ts @@ -16,20 +16,25 @@ const mapping = { "doc_get_file_from_store_with_object_ref": [ "nuri","obj_ref" ], "wallet_gen_shuffle_for_pazzle_opening": ["pazzle_length"], "wallet_gen_shuffle_for_pin": [], - "wallet_open_wallet_with_pazzle": ["wallet","pazzle","pin"], - "wallet_create_wallet": ["params"], - "wallet_open_file": ["file"], - "wallet_import": ["previous_wallet","opened_wallet"], + "wallet_open_with_pazzle": ["wallet","pazzle","pin"], + "wallet_was_opened": ["opened_wallet"], + "wallet_create": ["params"], + "wallet_read_file": ["file"], + "wallet_download_file": ["wallet_name"], + "wallet_import": ["encrypted_wallet","opened_wallet","in_memory"], + "wallet_close": ["wallet_name"], "encode_create_account": ["payload"], - "get_local_session": ["id","key","user"], - "get_wallets_from_localstorage": [], + "session_start": ["wallet_name","user"], + "session_stop": ["user_id"], + "get_wallets": [], "open_window": ["url","label","title"], "decode_invitation": ["invite"], - "broker_disconnect": [], - "broker_connect": ["client","info","session","opened_wallet","location"], + "user_connect": ["info","user_id","location"], + "user_disconnect": ["user_id"], "test": [ ] } + let lastStreamId = 0; const handler = { @@ -42,22 +47,14 @@ const handler = { client_info.V0.version=version; //console.log(client_info); return client_info; - } else if (path[0] === "get_wallets_from_localstorage") { + } else if (path[0] === "get_wallets") { let wallets = await Reflect.apply(sdk[path], caller, args); return Object.fromEntries(wallets || []); - } else if (path[0] === "get_local_session") { + } else if (path[0] === "session_start") { let res = await Reflect.apply(sdk[path], caller, args); - let v = res.users.values().next().value; - v.branches_last_seq = Object.fromEntries(v.branches_last_seq); - res.users = Object.fromEntries(res.users); return res; - } else if (path[0] === "wallet_create_wallet") { + } else if (path[0] === "wallet_create") { let res = await Reflect.apply(sdk[path], caller, args); - if (res[1]) { - let v = res[1].users.values().next().value; - v.branches_last_seq = Object.fromEntries(v.branches_last_seq); - res[1].users = Object.fromEntries(res[1].users); - } return res; } else { return Reflect.apply(sdk[path], caller, args) @@ -101,7 +98,7 @@ const handler = { return () => { unlisten(); } - } else if (path[0] === "broker_connect") { + } else if (path[0] === "user_connect") { let arg = {}; args.map((el,ix) => arg[mapping[path[0]][ix]]=el) let ret = await tauri.invoke(path[0],arg); @@ -132,31 +129,31 @@ const handler = { res['File'].V0.content = Uint8Array.from(res['File'].V0.content); res['File'].V0.metadata = Uint8Array.from(res['File'].V0.metadata); return res; - } else if (path[0] === "get_wallets_from_localstorage") { + } else if (path[0] === "get_wallets") { let res = await tauri.invoke(path[0],{}); if (res) for (let e of Object.entries(res)) { e[1].wallet.V0.content.security_img = Uint8Array.from(e[1].wallet.V0.content.security_img); } return res || {}; - } else if (path[0] === "wallet_create_wallet") { + } else if (path[0] === "wallet_create") { let params = args[0]; params.result_with_wallet_file = false; params.security_img = Array.from(new Uint8Array(params.security_img)); return await tauri.invoke(path[0],{params}) - } else if (path[0] === "wallet_open_file") { + } else if (path[0] === "wallet_read_file") { let file = args[0]; file = Array.from(new Uint8Array(file)); return await tauri.invoke(path[0],{file}) } else if (path[0] === "wallet_import") { - let previous_wallet = args[0]; - previous_wallet.V0.content.security_img = Array.from(new Uint8Array(previous_wallet.V0.content.security_img)); - return await tauri.invoke(path[0],{previous_wallet, opened_wallet:args[1]}) + let encrypted_wallet = args[0]; + encrypted_wallet.V0.content.security_img = Array.from(new Uint8Array(encrypted_wallet.V0.content.security_img)); + return await tauri.invoke(path[0],{encrypted_wallet, opened_wallet:args[1], in_memory:args[2]}) } else if (path[0] && path[0].startsWith("get_local_bootstrap")) { return false; } else if (path[0] === "get_local_url") { return false; - } else if (path[0] === "wallet_open_wallet_with_pazzle") { + } else if (path[0] === "wallet_open_with_pazzle") { let arg:any = {}; args.map((el,ix) => arg[mapping[path[0]][ix]]=el) let img = Array.from(new Uint8Array(arg.wallet.V0.content.security_img)); diff --git a/ng-app/src/lib/Home.svelte b/ng-app/src/lib/Home.svelte index c10ad3a..b04d2b0 100644 --- a/ng-app/src/lib/Home.svelte +++ b/ng-app/src/lib/Home.svelte @@ -10,13 +10,7 @@ --> {#if step == "load"} -
+

How to open your wallet, step by step :

  • - For each category of images, you will be presented with the 15 possible - image choices. The categories are shuffled at every login. They will not - always appear in the same order. + For each one of the 9 categories of images, you will be presented with + the 15 possible image choices. The categories are shuffled at every + login. They will not always appear in the same order.
  • At each category, only one of the 15 displayed choices is the correct @@ -230,7 +255,7 @@ The 15 images are shuffled too, they will not appear at the same position at each login. On a computer, you can also use the tab key on your keyboard to move to the desired item on the screen, then press the - space bar. + space bar to select each one.
  • Once you completed the last category, you will be presented with all the @@ -254,11 +279,11 @@
-
+
{#if !loaded} - Loading... + Loading pazzle... {/if}
+ {#if for_import} +
+ Do you trust this device?
+
+ Save my wallet on this device +
+

+ If you do, if this device is yours or is used by few trusted persons of + your family or workplace, and you would like to login again from this + device in the future, then you can save your wallet on this device. To + the contrary, if this device is public and shared by strangers, do not + save your wallet here. {#if !tauri_platform}By selecting this option, + you agree to save some cookies on your browser.{/if}
+

+
+ {/if} {:else if step == "pazzle"}