We could not find a wallet saved on this device. If you already have
a wallet, select "Log in", otherwise, select "Create Wallet" here below
@@ -80,5 +81,5 @@
- {/if}
-
+
+{:else if step == "security"}{:else if step == "qrcode"}{:else if step == "drop"}{:else if step == "cloud"}{:else if step == "loggedin"}you
+ are logged in{/if}
+
+
diff --git a/ng-app/src/store.ts b/ng-app/src/store.ts
index 75157fe..85813c0 100644
--- a/ng-app/src/store.ts
+++ b/ng-app/src/store.ts
@@ -1,33 +1,113 @@
-import { writable } from "svelte/store";
+import { writable, readonly, derived } from "svelte/store";
import ng from "./api";
+let all_branches = {};
+
+export const opened_wallets = writable({});
+
+/// { wallet:, id: }
+export const active_wallet = writable(undefined);
+
+export const wallets = writable({});
+
+export const has_wallets = derived(wallets,($wallets) => Object.keys($wallets).length);
+
+export const active_session = writable(undefined);
+
+export const set_active_session = function(session) {
+ let v = session.users.values().next().value;
+ v.branches_last_seq = Object.fromEntries(v.branches_last_seq);
+ let users = Object.fromEntries(session.users);
+ active_session.set(users);
+};
+
+export { writable, readonly, derived };
+
+const close_active_wallet = function() {
+
+ active_session.set(undefined);
+ active_wallet.update((w) => {
+ delete w.wallet;
+ });
+
+}
+
const branch_commits = (nura, sub) => {
+ // console.log("branch_commits")
+ // const { subscribe, set, update } = writable([]); // create the underlying writable store
+
+ // let unsub = () => {};
+ // return {
+ // load: async () => {
+ // console.log("load")
+ // unsub = await ng.doc_sync_branch(nura, async (commit) => {
+ // console.log(commit);
+ // update( (old) => {old.unshift(commit); return old;} )
+ // });
+ // },
+ // subscribe: (run, invalid) => {
+ // console.log("sub")
+ // let upper_unsub = subscribe(run, invalid);
- const { subscribe, set, update } = writable([]); // create the underlying writable store
+ // return () => {
+ // upper_unsub();
+ // unsub();
+ // }
+ // }
+ // // set: (value) => {
+ // // localStorage.setItem(key, toString(value)); // save also to local storage as a string
+ // // return set(value);
+ // // },
+ // // update,
+ // };
- let unsub = () => {};
+
return {
load: async () => {
- unsub = await ng.doc_sync_branch(nura, async (commit) => {
- console.log(commit);
- update( (old) => {old.unshift(commit); return old;} )
- });
+ let already_subscribed = all_branches[nura];
+ if (!already_subscribed) return;
+ if (already_subscribed.load) {
+ await already_subscribed.load();
+ already_subscribed.load = undefined;
+ }
},
subscribe: (run, invalid) => {
-
- let upper_unsub = subscribe(run, invalid);
-
+ let already_subscribed = all_branches[nura];
+ if (!already_subscribed) {
+ const { subscribe, set, update } = writable([]); // create the underlying writable store
+ let count = 0;
+ let unsub = () => {};
+ already_subscribed = {
+ load: async () => {
+ unsub = await ng.doc_sync_branch(nura, async (commit) => {
+ console.log(commit);
+ update( (old) => {old.unshift(commit); return old;} )
+ });
+ },
+ increase: () => {
+ count += 1;
+ return readonly({subscribe});
+ },
+ decrease: () => {
+ count -= 1;
+ if (count == 0) {
+ unsub();
+ delete all_branches[nura];
+ }
+ },
+ }
+ all_branches[nura] = already_subscribed;
+ }
+
+ let new_store = already_subscribed.increase();
+ let read_unsub = new_store.subscribe(run, invalid);
return () => {
- upper_unsub();
- unsub();
+ read_unsub();
+ already_subscribed.decrease();
}
+
}
- // set: (value) => {
- // localStorage.setItem(key, toString(value)); // save also to local storage as a string
- // return set(value);
- // },
- // update,
- };
+ }
};
export default branch_commits;
\ No newline at end of file
diff --git a/ng-app/src/styles.css b/ng-app/src/styles.css
index f50cfdb..e14b629 100644
--- a/ng-app/src/styles.css
+++ b/ng-app/src/styles.css
@@ -63,7 +63,7 @@ body {
}
#app {
- max-width: 1280px;
+ /*max-width: 1280px;*/
margin: 0 auto;
padding: 0rem;
text-align: center;
diff --git a/ng-sdk-js/Cargo.toml b/ng-sdk-js/Cargo.toml
index 5990f0e..5f492d9 100644
--- a/ng-sdk-js/Cargo.toml
+++ b/ng-sdk-js/Cargo.toml
@@ -29,6 +29,9 @@ serde_bytes = "0.11.7"
# snow = "0.9.2"
getrandom = { version = "0.1.1", features = ["wasm-bindgen"] }
serde_json = "1.0"
+crypto_box = { version = "0.8.2", features = ["seal"] }
+rand = { version = "0.7", features = ["getrandom"] }
+base64-url = "2.0.0"
# [target.'cfg(target_arch = "wasm32")'.dependencies.getrandom]
# version = "0.2.7"
diff --git a/ng-sdk-js/js/browser.js b/ng-sdk-js/js/browser.js
index e0edfa3..ae46a5f 100644
--- a/ng-sdk-js/js/browser.js
+++ b/ng-sdk-js/js/browser.js
@@ -3,7 +3,7 @@ export function client_details() {
}
export function client_details2(obj,version) {
- console.log("version",version)
+ //console.log("version",version)
obj.browser.appVersion = navigator?.appVersion;
obj.browser.arch = navigator?.platform;
obj.browser.vendor = navigator?.vendor;
@@ -11,3 +11,45 @@ export function client_details2(obj,version) {
obj.engine.sdk = version;
return JSON.stringify(obj);
}
+
+export function session_save(key,value) {
+ try {
+ sessionStorage.setItem(key, value);
+
+ } catch(e) {
+ console.error(e);
+ return e.message;
+ }
+}
+
+export function session_get(key) {
+
+ try {
+ return sessionStorage.getItem(key);
+
+ } catch(e) {
+ console.error(e);
+ }
+
+}
+
+export function local_save(key,value) {
+ try {
+ localStorage.setItem(key, value);
+
+ } catch(e) {
+ console.error(e);
+ return e.message;
+ }
+}
+
+export function local_get(key) {
+
+ try {
+ return localStorage.getItem(key);
+
+ } catch(e) {
+ console.error(e);
+ }
+
+}
\ No newline at end of file
diff --git a/ng-sdk-js/js/node.js b/ng-sdk-js/js/node.js
index 88f9360..6704151 100644
--- a/ng-sdk-js/js/node.js
+++ b/ng-sdk-js/js/node.js
@@ -119,4 +119,20 @@ module.exports.client_details = function () {
versions: process.versions
}
});
-};
\ No newline at end of file
+};
+
+module.exports.session_save = function(key,value) {
+
+}
+
+module.exports.session_get = function(key) {
+
+}
+
+module.exports.local_save = function(key,value) {
+
+}
+
+module.exports.local_get = function(key) {
+
+}
\ No newline at end of file
diff --git a/ng-sdk-js/src/lib.rs b/ng-sdk-js/src/lib.rs
index b09f314..237152c 100644
--- a/ng-sdk-js/src/lib.rs
+++ b/ng-sdk-js/src/lib.rs
@@ -22,15 +22,20 @@ use p2p_client_ws::remote_ws_wasm::ConnectionWebSocket;
use p2p_net::broker::*;
use p2p_net::connection::{ClientConfig, StartConfig};
use p2p_net::types::{
- BootstrapContentV0, ClientInfo, ClientInfoV0, ClientType, CreateAccountBSP, DirectPeerId, IP,
+ BootstrapContent, BootstrapContentV0, ClientId, ClientInfo, ClientInfoV0, ClientType,
+ CreateAccountBSP, DirectPeerId, UserId, IP,
+};
+use p2p_net::utils::{
+ decode_invitation_string, retrieve_local_bootstrap, retrieve_local_url, spawn_and_log_error,
+ Receiver, ResultSend, Sender,
};
-use p2p_net::utils::{retrieve_local_bootstrap, spawn_and_log_error, Receiver, ResultSend, Sender};
use p2p_net::WS_PORT;
use p2p_repo::log::*;
use p2p_repo::types::*;
use p2p_repo::utils::generate_keypair;
use serde::{Deserialize, Serialize};
use serde_json::json;
+use std::collections::HashMap;
use std::net::IpAddr;
use std::str::FromStr;
use std::sync::Arc;
@@ -49,6 +54,28 @@ pub async fn get_local_bootstrap(location: String, invite: JsValue) -> JsValue {
}
}
+#[cfg(target_arch = "wasm32")]
+#[wasm_bindgen]
+pub async fn decode_invitation(invite: String) -> JsValue {
+ let res = decode_invitation_string(invite);
+ if res.is_some() {
+ serde_wasm_bindgen::to_value(&res.unwrap()).unwrap()
+ } else {
+ JsValue::FALSE
+ }
+}
+
+#[cfg(target_arch = "wasm32")]
+#[wasm_bindgen]
+pub async fn get_local_url(location: String) -> JsValue {
+ let res = retrieve_local_url(location).await;
+ if res.is_some() {
+ serde_wasm_bindgen::to_value(&res.unwrap()).unwrap()
+ } else {
+ JsValue::FALSE
+ }
+}
+
#[cfg(target_arch = "wasm32")]
#[wasm_bindgen]
pub async fn get_local_bootstrap_with_public(location: String, invite: JsValue) -> JsValue {
@@ -91,6 +118,190 @@ pub fn wallet_open_wallet_with_pazzle(
}
}
+#[cfg(target_arch = "wasm32")]
+#[wasm_bindgen]
+pub fn wallet_update(js_wallet_id: JsValue, js_operations: JsValue) -> Result {
+ let wallet = serde_wasm_bindgen::from_value::(js_wallet_id)
+ .map_err(|_| "Deserialization error of WalletId")?;
+ let operations = serde_wasm_bindgen::from_value::>(js_operations)
+ .map_err(|_| "Deserialization error of operations")?;
+ unimplemented!();
+ // match res {
+ // Ok(r) => Ok(serde_wasm_bindgen::to_value(&r).unwrap()),
+ // Err(e) => Err(e.to_string().into()),
+ // }
+}
+
+#[derive(Clone, Debug, Serialize, Deserialize)]
+struct SessionWalletStorageV0 {
+ // string is base64_url encoding of userId(pubkey)
+ users: HashMap,
+}
+
+#[derive(Clone, Debug, Serialize, Deserialize)]
+enum SessionWalletStorage {
+ V0(SessionWalletStorageV0),
+}
+
+impl SessionWalletStorageV0 {
+ fn new() -> Self {
+ SessionWalletStorageV0 {
+ users: HashMap::new(),
+ }
+ }
+}
+
+#[derive(Clone, Debug, Serialize, Deserialize)]
+struct SessionPeerStorageV0 {
+ user: UserId,
+ peer_key: PrivKey,
+ last_wallet_nonce: u64,
+ // string is base64_url encoding of branchId(pubkey)
+ branches_last_seq: HashMap,
+}
+
+#[derive(Clone, Debug, Serialize, Deserialize)]
+struct LocalWalletStorageV0 {
+ bootstrap: BootstrapContent,
+ wallet: Wallet,
+ client: ClientId,
+}
+
+#[derive(Clone, Debug, Serialize, Deserialize)]
+enum LocalWalletStorage {
+ V0(HashMap),
+}
+
+fn get_local_wallets_v0() -> Result, ()> {
+ let wallets_string = local_get("ng_wallets".to_string());
+ if wallets_string.is_some() {
+ let map_ser = base64_url::decode(&wallets_string.unwrap()).unwrap();
+ let wallets: LocalWalletStorage = serde_bare::from_slice(&map_ser).unwrap();
+ let LocalWalletStorage::V0(v0) = wallets;
+ Ok(v0)
+ } else {
+ Err(())
+ }
+}
+
+#[cfg(target_arch = "wasm32")]
+#[wasm_bindgen]
+pub fn get_wallets_from_localstorage() -> JsValue {
+ let res = get_local_wallets_v0();
+ if res.is_ok() {
+ return serde_wasm_bindgen::to_value(&res.unwrap()).unwrap();
+ }
+ JsValue::UNDEFINED
+}
+
+#[cfg(target_arch = "wasm32")]
+#[wasm_bindgen]
+pub fn get_local_session(id: String, key_js: JsValue, user_js: JsValue) -> JsValue {
+ let res = session_get(format!("ng_wallet@{}", id));
+ if res.is_some() {
+ log_debug!("RESUMING SESSION");
+ let key = serde_wasm_bindgen::from_value::(key_js).unwrap();
+ let decoded = base64_url::decode(&res.unwrap()).unwrap();
+ let session_ser = crypto_box::seal_open(&(*key.to_dh().slice()).into(), &decoded).unwrap();
+ let session: SessionWalletStorage = serde_bare::from_slice(&session_ser).unwrap();
+ let SessionWalletStorage::V0(v0) = session;
+ return serde_wasm_bindgen::to_value(&v0).unwrap();
+ } else {
+ // create a new session
+ let user = serde_wasm_bindgen::from_value::(user_js).unwrap();
+ let wallet_id: PubKey = id.as_str().try_into().unwrap();
+ let session_v0 = create_new_session(&id, wallet_id, user);
+ if session_v0.is_err() {
+ return JsValue::UNDEFINED;
+ }
+ return serde_wasm_bindgen::to_value(&session_v0.unwrap()).unwrap();
+ }
+ JsValue::UNDEFINED
+}
+
+fn create_new_session(
+ wallet_name: &String,
+ wallet_id: PubKey,
+ user: PubKey,
+) -> Result {
+ let peer = generate_keypair();
+ let mut sws = SessionWalletStorageV0::new();
+ let sps = SessionPeerStorageV0 {
+ user,
+ peer_key: peer.0,
+ last_wallet_nonce: 0,
+ branches_last_seq: HashMap::new(),
+ };
+ 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);
+ if cipher.is_ok() {
+ let encoded = base64_url::encode(&cipher.unwrap());
+ let r = session_save(format!("ng_wallet@{}", wallet_name), encoded);
+ if r.is_some() {
+ return Err(r.unwrap());
+ }
+ }
+ Ok(sws)
+}
+
+fn save_wallet_locally(res: &CreateWalletResultV0) -> Result {
+ // let mut sws = SessionWalletStorageV0::new();
+ // let sps = SessionPeerStorageV0 {
+ // user: res.user,
+ // peer_key: res.peer_key.clone(),
+ // last_wallet_nonce: res.nonce,
+ // branches_last_seq: HashMap::new(),
+ // };
+ // sws.users.insert(res.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, &res.wallet.id().to_dh_slice().into(), &sws_ser);
+ // if cipher.is_ok() {
+ // let encoded = base64_url::encode(&cipher.unwrap());
+ // let r = session_save(format!("ng_wallet@{}", res.wallet_name), encoded);
+ // if r.is_some() {
+ // return Err(r.unwrap());
+ // }
+ // }
+ let sws = create_new_session(&res.wallet_name, res.wallet.id(), res.user)?;
+ let mut wallets: HashMap =
+ get_local_wallets_v0().unwrap_or(HashMap::new());
+ // TODO: check that the wallet is not already present in localStorage
+ let lws = LocalWalletStorageV0 {
+ bootstrap: BootstrapContent::V0(BootstrapContentV0 { servers: vec![] }),
+ wallet: res.wallet.clone(),
+ client: res.client.priv_key.to_pub(),
+ };
+ 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(not(wasmpack_target = "nodejs"))]
+#[wasm_bindgen(module = "/js/browser.js")]
+extern "C" {
+ fn session_save(key: String, value: String) -> Option;
+ fn session_get(key: String) -> Option;
+ fn local_save(key: String, value: String) -> Option;
+ fn local_get(key: String) -> Option;
+}
+
+#[cfg(wasmpack_target = "nodejs")]
+#[wasm_bindgen(module = "/js/node.js")]
+extern "C" {
+ fn session_save(key: String, value: String) -> Option;
+ fn session_get(key: String) -> Option;
+ fn local_save(key: String, value: String) -> Option;
+ fn local_get(key: String) -> Option;
+}
+
#[cfg(target_arch = "wasm32")]
#[wasm_bindgen]
pub async fn wallet_create_wallet(js_params: JsValue) -> Result {
@@ -99,7 +310,10 @@ pub async fn wallet_create_wallet(js_params: JsValue) -> Result Ok(serde_wasm_bindgen::to_value(&r).unwrap()),
+ Ok(r) => {
+ let session = save_wallet_locally(&r)?;
+ Ok(serde_wasm_bindgen::to_value(&(r, session)).unwrap())
+ }
Err(e) => Err(e.to_string().into()),
}
}
@@ -133,7 +347,7 @@ extern "C" {
#[cfg(wasmpack_target = "nodejs")]
#[wasm_bindgen]
-pub fn client_info() -> ClientInfoV0 {
+pub fn client_info() -> JsValue {
let res = ClientInfoV0 {
client_type: ClientType::NodeService,
details: client_details(),
@@ -141,8 +355,8 @@ pub fn client_info() -> ClientInfoV0 {
timestamp_install: 0,
timestamp_updated: 0,
};
- res
- //serde_wasm_bindgen::to_value(&res).unwrap()
+ //res
+ serde_wasm_bindgen::to_value(&res).unwrap()
}
#[cfg(target_arch = "wasm32")]
@@ -177,8 +391,7 @@ extern "C" {
}
#[cfg(all(not(wasmpack_target = "nodejs"), target_arch = "wasm32"))]
-#[wasm_bindgen]
-pub fn client_info() -> ClientInfoV0 {
+pub fn client_info_() -> ClientInfoV0 {
let ua = client_details();
let bowser = Bowser::parse(ua);
@@ -197,6 +410,13 @@ pub fn client_info() -> ClientInfoV0 {
//serde_wasm_bindgen::to_value(&res).unwrap()
}
+#[cfg(all(not(wasmpack_target = "nodejs"), target_arch = "wasm32"))]
+#[wasm_bindgen]
+pub fn client_info() -> JsValue {
+ let res = client_info_();
+ serde_wasm_bindgen::to_value(&res).unwrap()
+}
+
#[cfg(target_arch = "wasm32")]
#[wasm_bindgen]
pub async fn test() {
@@ -336,7 +556,8 @@ pub async fn start() {
user_priv,
client,
client_priv,
- info: ClientInfo::V0(client_info()),
+ info: ClientInfo::V0(client_info_()),
+ registration: None,
}),
)
.await;
diff --git a/ng-wallet/src/types.rs b/ng-wallet/src/types.rs
index bb90743..100ba3a 100644
--- a/ng-wallet/src/types.rs
+++ b/ng-wallet/src/types.rs
@@ -104,6 +104,9 @@ pub enum SaveToNGOne {
pub struct EncryptedWalletV0 {
pub wallet_privkey: PrivKey,
+ #[zeroize(skip)]
+ pub wallet_id: String,
+
#[serde(with = "serde_bytes")]
pub pazzle: Vec,
@@ -210,7 +213,7 @@ pub struct WalletContentV0 {
/// Wallet Log V0
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct WalletLogV0 {
- pub log: Vec<(u128, WalletOperationV0)>,
+ pub log: Vec<(u128, WalletOperation)>,
}
/// Wallet Log
@@ -228,11 +231,11 @@ impl WalletLog {
impl WalletLogV0 {
pub fn new(create_op: WalletOpCreateV0) -> Self {
let mut wallet = WalletLogV0 { log: vec![] };
- wallet.add(WalletOperationV0::CreateWalletV0(create_op));
+ wallet.add(WalletOperation::CreateWalletV0(create_op));
wallet
}
- pub fn add(&mut self, op: WalletOperationV0) {
+ pub fn add(&mut self, op: WalletOperation) {
let duration = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap()
@@ -244,49 +247,49 @@ impl WalletLogV0 {
pub fn reduce(&self) -> Result {
if self.log.len() < 1 {
Err(NgWalletError::NoCreateWalletPresent)
- } else if let (_, WalletOperationV0::CreateWalletV0(create_op)) = &self.log[0] {
+ } else if let (_, WalletOperation::CreateWalletV0(create_op)) = &self.log[0] {
let mut wallet: EncryptedWalletV0 = create_op.into();
for op in &self.log {
match &op.1 {
- WalletOperationV0::CreateWalletV0(_) => { /* intentionally left blank. this op is already reduced */
+ WalletOperation::CreateWalletV0(_) => { /* intentionally left blank. this op is already reduced */
}
- WalletOperationV0::AddSiteV0(o) => {
+ WalletOperation::AddSiteV0(o) => {
if self.is_first_and_not_deleted_afterwards(op, "RemoveSiteV0") {
wallet.add_site(o.clone());
}
}
- WalletOperationV0::RemoveSiteV0(_) => {}
- WalletOperationV0::AddBrokerServerV0(o) => {
+ WalletOperation::RemoveSiteV0(_) => {}
+ WalletOperation::AddBrokerServerV0(o) => {
if self.is_last_and_not_deleted_afterwards(op, "RemoveBrokerServerV0") {
wallet.add_brokers(vec![BrokerInfoV0::ServerV0(o.clone())]);
}
}
- WalletOperationV0::RemoveBrokerServerV0(_) => {}
- WalletOperationV0::SetSaveToNGOneV0(o) => {
+ WalletOperation::RemoveBrokerServerV0(_) => {}
+ WalletOperation::SetSaveToNGOneV0(o) => {
if self.is_last_occurrence(op.0, &op.1) != 0 {
wallet.save_to_ng_one = o.clone();
}
}
- WalletOperationV0::SetBrokerCoreV0(o) => {
+ WalletOperation::SetBrokerCoreV0(o) => {
if self.is_last_occurrence(op.0, &op.1) != 0 {
wallet.add_brokers(vec![BrokerInfoV0::CoreV0(o.clone())]);
}
}
- WalletOperationV0::SetClientV0(o) => {
+ WalletOperation::SetClientV0(o) => {
if self.is_last_occurrence(op.0, &op.1) != 0 {
wallet.add_client(o.clone());
}
}
- WalletOperationV0::AddOverlayCoreOverrideV0((overlay, cores)) => {
+ WalletOperation::AddOverlayCoreOverrideV0((overlay, cores)) => {
if self
.is_last_and_not_deleted_afterwards(op, "RemoveOverlayCoreOverrideV0")
{
wallet.add_overlay_core_overrides(overlay, cores);
}
}
- WalletOperationV0::RemoveOverlayCoreOverrideV0(_) => {}
- WalletOperationV0::AddSiteCoreV0((site, core)) => {
+ WalletOperation::RemoveOverlayCoreOverrideV0(_) => {}
+ WalletOperation::AddSiteCoreV0((site, core)) => {
if self.is_first_and_not_deleted_afterwards(op, "RemoveSiteCoreV0") {
let _ = wallet.sites.get_mut(&site).and_then(|site| {
site.cores.push(*core);
@@ -294,8 +297,8 @@ impl WalletLogV0 {
});
}
}
- WalletOperationV0::RemoveSiteCoreV0(_) => {}
- WalletOperationV0::AddSiteBootstrapV0((site, server)) => {
+ WalletOperation::RemoveSiteCoreV0(_) => {}
+ WalletOperation::AddSiteBootstrapV0((site, server)) => {
if self.is_first_and_not_deleted_afterwards(op, "RemoveSiteBootstrapV0") {
let _ = wallet.sites.get_mut(&site).and_then(|site| {
site.bootstraps.push(*server);
@@ -303,14 +306,14 @@ impl WalletLogV0 {
});
}
}
- WalletOperationV0::RemoveSiteBootstrapV0(_) => {}
- WalletOperationV0::AddThirdPartyDataV0((key, value)) => {
+ WalletOperation::RemoveSiteBootstrapV0(_) => {}
+ WalletOperation::AddThirdPartyDataV0((key, value)) => {
if self.is_last_and_not_deleted_afterwards(op, "RemoveThirdPartyDataV0") {
let _ = wallet.third_parties.insert(key.to_string(), value.to_vec());
}
}
- WalletOperationV0::RemoveThirdPartyDataV0(_) => {}
- WalletOperationV0::SetSiteRBDRefV0((site, store_type, rbdr)) => {
+ WalletOperation::RemoveThirdPartyDataV0(_) => {}
+ WalletOperation::SetSiteRBDRefV0((site, store_type, rbdr)) => {
if self.is_last_occurrence(op.0, &op.1) != 0 {
let _ = wallet.sites.get_mut(&site).and_then(|site| {
match store_type {
@@ -328,7 +331,7 @@ impl WalletLogV0 {
});
}
}
- WalletOperationV0::SetSiteRepoSecretV0((site, store_type, secret)) => {
+ WalletOperation::SetSiteRepoSecretV0((site, store_type, secret)) => {
if self.is_last_occurrence(op.0, &op.1) != 0 {
let _ = wallet.sites.get_mut(&site).and_then(|site| {
match store_type {
@@ -357,7 +360,7 @@ impl WalletLogV0 {
pub fn is_first_and_not_deleted_afterwards(
&self,
- item: &(u128, WalletOperationV0),
+ item: &(u128, WalletOperation),
delete_type: &str,
) -> bool {
let hash = self.is_first_occurrence(item.0, &item.1);
@@ -375,7 +378,7 @@ impl WalletLogV0 {
pub fn is_last_and_not_deleted_afterwards(
&self,
- item: &(u128, WalletOperationV0),
+ item: &(u128, WalletOperation),
delete_type: &str,
) -> bool {
let hash = self.is_last_occurrence(item.0, &item.1);
@@ -391,7 +394,7 @@ impl WalletLogV0 {
false
}
- pub fn is_first_occurrence(&self, timestamp: u128, searched_op: &WalletOperationV0) -> u64 {
+ pub fn is_first_occurrence(&self, timestamp: u128, searched_op: &WalletOperation) -> u64 {
let searched_hash = searched_op.hash();
//let mut timestamp = u128::MAX;
//let mut found = searched_op;
@@ -406,7 +409,7 @@ impl WalletLogV0 {
searched_hash.0
}
- pub fn is_last_occurrence(&self, timestamp: u128, searched_op: &WalletOperationV0) -> u64 {
+ pub fn is_last_occurrence(&self, timestamp: u128, searched_op: &WalletOperation) -> u64 {
let searched_hash = searched_op.hash();
//let mut timestamp = 0u128;
//let mut found = searched_op;
@@ -426,7 +429,7 @@ impl WalletLogV0 {
searched_type: &str,
searched_hash: u64,
after: u128,
- ) -> Option<(u128, &WalletOperationV0)> {
+ ) -> Option<(u128, &WalletOperation)> {
let mut timestamp = u128::MAX;
let mut found = None;
for op in &self.log {
@@ -446,7 +449,7 @@ impl WalletLogV0 {
/// WalletOperation
#[derive(Clone, Debug, Serialize, Deserialize)]
-pub enum WalletOperationV0 {
+pub enum WalletOperation {
CreateWalletV0(WalletOpCreateV0),
AddSiteV0(SiteV0),
RemoveSiteV0(PrivKey),
@@ -468,7 +471,7 @@ pub enum WalletOperationV0 {
}
use std::collections::hash_map::DefaultHasher;
-impl WalletOperationV0 {
+impl WalletOperation {
pub fn hash(&self) -> (u64, &str) {
let mut s = DefaultHasher::new();
match self {
@@ -579,6 +582,7 @@ impl From<&WalletOpCreateV0> for EncryptedWalletV0 {
fn from(op: &WalletOpCreateV0) -> Self {
let mut wallet = EncryptedWalletV0 {
wallet_privkey: op.wallet_privkey.clone(),
+ wallet_id: op.wallet_privkey.to_pub().to_string(),
pazzle: op.pazzle.clone(),
mnemonic: op.mnemonic.clone(),
pin: op.pin.clone(),
diff --git a/ngaccount/Cargo.toml b/ngaccount/Cargo.toml
index d2aefb3..27086ea 100644
--- a/ngaccount/Cargo.toml
+++ b/ngaccount/Cargo.toml
@@ -17,6 +17,7 @@ env_logger = "0.10"
stores-lmdb = { path = "../stores-lmdb" }
p2p-repo = { path = "../p2p-repo", features = ["server_log_output"] }
p2p-net = { path = "../p2p-net" }
+p2p-client-ws = { path = "../p2p-client-ws" }
ng-wallet = { path = "../ng-wallet" }
serde = { version = "1.0.142", features = ["derive"] }
serde_bare = "0.5.0"
diff --git a/ngaccount/src/main.rs b/ngaccount/src/main.rs
index 311d58b..0e73823 100644
--- a/ngaccount/src/main.rs
+++ b/ngaccount/src/main.rs
@@ -11,6 +11,9 @@ extern crate anyhow;
mod types;
+use p2p_client_ws::remote_ws::ConnectionWebSocket;
+use p2p_net::actors::add_user::*;
+use p2p_net::broker::BROKER;
use p2p_repo::store::StorageError;
use warp::reply::Response;
use warp::{Filter, Reply};
@@ -18,12 +21,18 @@ use warp::{Filter, Reply};
use rust_embed::RustEmbed;
use serde_bare::{from_slice, to_vec};
use serde_json::json;
+use std::convert::Infallible;
+use std::net::IpAddr;
+use std::str::FromStr;
use std::sync::Arc;
use std::{env, fs};
use crate::types::*;
use ng_wallet::types::*;
-use p2p_net::types::{CreateAccountBSP, APP_NG_ONE_URL, NG_ONE_URL};
+use p2p_net::types::{
+ BindAddress, CreateAccountBSP, Invitation, InvitationV0, APP_ACCOUNT_REGISTERED_SUFFIX,
+ APP_NG_ONE_URL, NG_ONE_URL,
+};
use p2p_repo::log::*;
use p2p_repo::types::*;
use p2p_repo::utils::{generate_keypair, sign, verify};
@@ -32,27 +41,110 @@ use p2p_repo::utils::{generate_keypair, sign, verify};
#[folder = "web/dist"]
struct Static;
-struct Server {}
+struct Server {
+ admin_key: PrivKey,
+ local_peer_key: PrivKey,
+ ip: IpAddr,
+ port: u16,
+ peer_id: PubKey,
+ domain: String,
+}
impl Server {
- fn register_(&self, ca: String) -> Result<(), NgHttpError> {
+ async fn register_(&self, ca: String) -> Result> {
log_debug!("registering {}", ca);
- let cabsp: CreateAccountBSP = ca.try_into().map_err(|_| NgHttpError::InvalidParams)?;
+ let mut cabsp: CreateAccountBSP = ca.try_into().map_err(|_| None)?;
log_debug!("{:?}", cabsp);
- Ok(())
+ // if needed, proceed with payment and verify it here. once validated, add the user
+
+ let local_peer_pubk = self.local_peer_key.to_pub();
+ let res = BROKER
+ .write()
+ .await
+ .admin(
+ Box::new(ConnectionWebSocket {}),
+ self.local_peer_key.clone(),
+ local_peer_pubk,
+ self.peer_id,
+ self.admin_key.to_pub(),
+ self.admin_key.clone(),
+ BindAddress {
+ port: self.port,
+ ip: (&self.ip).into(),
+ },
+ AddUser::V0(AddUserV0 {
+ user: cabsp.user(),
+ is_admin: false,
+ }),
+ )
+ .await;
+
+ let mut redirect_url = cabsp
+ .redirect_url()
+ .clone()
+ .unwrap_or(format!("{}{}", self.domain, APP_ACCOUNT_REGISTERED_SUFFIX));
+
+ match &res {
+ Err(e) => {
+ log_err!("error while registering: {e} {:?}", cabsp);
+ Err(Some(format!("{}?e={}", redirect_url, e)))
+ }
+ Ok(_) => {
+ log_info!("User added successfully {}", cabsp.user());
+ let mut return_invitation = if cabsp.invitation().is_some() {
+ InvitationV0 {
+ bootstrap: cabsp.invitation().as_ref().unwrap().bootstrap.clone(),
+ name: cabsp.invitation().as_ref().unwrap().name.clone(),
+ code: None,
+ url: None,
+ }
+ } else {
+ InvitationV0::empty(Some(self.domain.clone()))
+ };
+ return_invitation.append_bootstraps(cabsp.additional_bootstrap());
+ Ok(format!(
+ "{}?i={}&u={}",
+ redirect_url,
+ Invitation::V0(return_invitation),
+ cabsp.user()
+ ))
+ }
+ }
}
- pub fn register(&self, ca: String) -> Response {
- match self.register_(ca) {
- Ok(_) => warp::http::StatusCode::CREATED.into_response(),
- Err(e) => e.into_response(),
+ pub async fn register(self: Arc, ca: String) -> Result {
+ match self.register_(ca).await {
+ Ok(redirect_url) => {
+ let response = Response::new(redirect_url.into());
+ let (mut parts, body) = response.into_parts();
+ parts.status = warp::http::StatusCode::OK;
+ let response = Response::from_parts(parts, body);
+ Ok(response)
+ }
+ Err(redirect_url) => {
+ if redirect_url.is_some() {
+ let response = Response::new(redirect_url.unwrap().into());
+ let (mut parts, body) = response.into_parts();
+ parts.status = warp::http::StatusCode::BAD_REQUEST;
+ let response = Response::from_parts(parts, body);
+ Ok(response)
+ } else {
+ Ok(warp::http::StatusCode::NOT_ACCEPTABLE.into_response())
+ }
+ }
}
}
}
+fn with_server(
+ server: Arc,
+) -> impl Filter,), Error = std::convert::Infallible> + Clone {
+ warp::any().map(move || Arc::clone(&server))
+}
+
#[tokio::main]
async fn main() -> anyhow::Result<()> {
if std::env::var("RUST_LOG").is_err() {
@@ -60,13 +152,26 @@ async fn main() -> anyhow::Result<()> {
}
env_logger::init();
- let server = Arc::new(Server {});
-
let domain =
env::var("NG_ACCOUNT_DOMAIN").map_err(|_| anyhow!("NG_ACCOUNT_DOMAIN must be set"))?;
- let admin_user =
- env::var("NG_ACCOUNT_ADMIN").map_err(|_| anyhow!("NG_ACCOUNT_ADMIN must be set"))?;
+ let admin_user = env::var("NG_ACCOUNT_ADMIN")
+ .map_err(|_| anyhow!("NG_ACCOUNT_ADMIN must be set (with private key)"))?;
+
+ let admin_key: PrivKey = admin_user.as_str().try_into().map_err(|_| {
+ anyhow!(
+ "NG_ACCOUNT_ADMIN is invalid. It should be a base64-url encoded serde serialization of a [u8; 32] of the private key for an admin user. cannot start"
+ )
+ })?;
+
+ let local_peer_privkey = env::var("NG_ACCOUNT_LOCAL_PEER_KEY")
+ .map_err(|_| anyhow!("NG_ACCOUNT_LOCAL_PEER_KEY must be set"))?;
+
+ let local_peer_key: PrivKey = local_peer_privkey.as_str().try_into().map_err(|_| {
+ anyhow!(
+ "NG_ACCOUNT_LOCAL_PEER_KEY is invalid. It should be a base64-url encoded serde serialization of a [u8; 32] of the private key for the peerId. cannot start"
+ )
+ })?;
// format is IP,PORT,PEERID
let server_address =
@@ -78,15 +183,39 @@ async fn main() -> anyhow::Result<()> {
"NG_ACCOUNT_SERVER is invalid. format is IP,PORT,PEER_ID"
));
}
- let ip: IP = addr[0].into();
+ let ip = IpAddr::from_str(addr[0]).map_err(|_| {
+ anyhow!("NG_ACCOUNT_SERVER is invalid. format is IP,PORT,PEER_ID. The first part is not an IP address. cannot start")
+ })?;
- log::info!("{}", domain);
+ let port = match addr[1].parse::() {
+ Err(_) => {
+ return Err(anyhow!("NG_ACCOUNT_SERVER is invalid. format is IP,PORT,PEER_ID. The port is invalid. It should be a number. cannot start"));
+ }
+ Ok(val) => val,
+ };
+ let peer_id: PubKey = addr[2].try_into().map_err(|_| {
+ anyhow!(
+ "NG_ACCOUNT_SERVER is invalid. format is IP,PORT,PEER_ID.
+ The PEER_ID is invalid. It should be a base64-url encoded serde serialization of a [u8; 32]. cannot start"
+ )
+ })?;
+
+ log::info!("domain {}", domain);
+
+ let server = Arc::new(Server {
+ admin_key,
+ local_peer_key,
+ ip,
+ port,
+ peer_id,
+ domain,
+ });
// GET /api/v1/register/ca with the same ?ca= query param => 201 CREATED
- let server_for_move = Arc::clone(&server);
let register_api = warp::get()
+ .and(with_server(server))
.and(warp::path!("register" / String))
- .map(move |ca| server_for_move.register(ca));
+ .and_then(Server::register);
let api_v1 = warp::path!("api" / "v1" / ..).and(register_api);
diff --git a/ngaccount/web/package.json b/ngaccount/web/package.json
index 7f95437..efa7dfe 100644
--- a/ngaccount/web/package.json
+++ b/ngaccount/web/package.json
@@ -12,7 +12,8 @@
"dependencies": {
"flowbite": "^1.6.5",
"flowbite-svelte": "^0.37.1",
- "svelte-spa-router": "^3.3.0"
+ "svelte-spa-router": "^3.3.0",
+ "@tauri-apps/api": "2.0.0-alpha.4"
},
"devDependencies": {
"@sveltejs/vite-plugin-svelte": "^2.0.4",
diff --git a/ngaccount/web/pnpm-lock.yaml b/ngaccount/web/pnpm-lock.yaml
index 8a2fcde..bc4e95e 100644
--- a/ngaccount/web/pnpm-lock.yaml
+++ b/ngaccount/web/pnpm-lock.yaml
@@ -2,6 +2,7 @@ lockfileVersion: 5.4
specifiers:
'@sveltejs/vite-plugin-svelte': ^2.0.4
+ '@tauri-apps/api': 2.0.0-alpha.4
autoprefixer: ^10.4.14
cross-env: ^7.0.3
flowbite: ^1.6.5
@@ -16,6 +17,7 @@ specifiers:
vite-plugin-svelte-svg: ^2.2.1
dependencies:
+ '@tauri-apps/api': 2.0.0-alpha.4
flowbite: 1.6.6
flowbite-svelte: 0.37.5_svelte@3.59.1
svelte-spa-router: 3.3.0
@@ -332,6 +334,11 @@ packages:
- supports-color
dev: true
+ /@tauri-apps/api/2.0.0-alpha.4:
+ resolution: {integrity: sha512-gWe5fFHbwFM+dmdDPtlDvVDVtoMneGRM+S8mECevWhKpXYxId0yxznE56YGAvPSJXC3vgsXw16mOmkTnEVKnaw==}
+ engines: {node: '>= 14.6.0', npm: '>= 6.6.0', yarn: '>= 1.19.1'}
+ dev: false
+
/@trysound/sax/0.2.0:
resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==}
engines: {node: '>=10.13.0'}
diff --git a/ngaccount/web/src/routes/Create.svelte b/ngaccount/web/src/routes/Create.svelte
index de94a95..e85d03c 100644
--- a/ngaccount/web/src/routes/Create.svelte
+++ b/ngaccount/web/src/routes/Create.svelte
@@ -11,14 +11,18 @@
@@ -60,11 +91,34 @@
{/if}
{#if error}
-
-
- An error occurred while registering on this broker :
- {error}
+
+
+
+
+ An error occurred while registering on this broker: {error}
+ {#if go_back}
+
+ {/if}
{:else}
{#if ca}
@@ -218,10 +272,9 @@
Registration is free of charge. And it would be very nice of you
- if you wanted to donate a small amount for the fees we have to pay
- for the servers. Here is the donation link: https://nextgraph.org/donate
diff --git a/ngaccount/web/src/routes/Delete.svelte b/ngaccount/web/src/routes/Delete.svelte
index 8a2691d..366d015 100644
--- a/ngaccount/web/src/routes/Delete.svelte
+++ b/ngaccount/web/src/routes/Delete.svelte
@@ -17,8 +17,8 @@
import { onMount } from "svelte";
- const params = new URLSearchParams($querystring);
- let ca = params.get("ca");
+ const param = new URLSearchParams($querystring);
+ let ca = param.get("ca");
let domain = import.meta.env.NG_ACCOUNT_DOMAIN;
diff --git a/ngaccount/web/src/routes/Home.svelte b/ngaccount/web/src/routes/Home.svelte
index 3828c22..c40f102 100644
--- a/ngaccount/web/src/routes/Home.svelte
+++ b/ngaccount/web/src/routes/Home.svelte
@@ -17,7 +17,7 @@
const api_url = import.meta.env.PROD
? "api/v1/"
- : "http://localhost:3030/api/v1/";
+ : "http://localhost:3031/api/v1/";
async function bootstrap() {}
diff --git a/ngcli/src/main.rs b/ngcli/src/main.rs
index bce112c..83c0a3a 100644
--- a/ngcli/src/main.rs
+++ b/ngcli/src/main.rs
@@ -139,6 +139,10 @@ async fn main() -> Result<(), ProtocolError> {
.about("add a user to the server, so it can connect to it")
.arg(arg!([USER_ID] "userId of the user to add. should be a base64-url encoded serde serialization of its pubkey [u8; 32]").required(true))
.arg(arg!(-a --admin "make this user admin as well").required(false)))
+ .subcommand(
+ Command::new("del-user")
+ .about("removes a user from the broker")
+ .arg(arg!([USER_ID] "userId of the user to remove. should be a base64-url encoded serde serialization of its pubkey [u8; 32]").required(true)))
.subcommand(
Command::new("list-users")
.about("list all users registered in the broker")
@@ -146,7 +150,7 @@ async fn main() -> Result<(), ProtocolError> {
.subcommand(
Command::new("add-invitation")
.about("add an invitation to register on the server")
- .arg(arg!([EXPIRES] "offset (from now) of time after which the invitation should expire. Format example: 1w 1d 1m. default unit is second").conflicts_with("forever"))
+ .arg(arg!([EXPIRES] "offset (from now) of time after which the invitation should expire. Format example: 1w 1d 1m. default unit is second. see https://crates.io/crates/duration-str for format").conflicts_with("forever"))
.arg(arg!(-a --admin "user registered with this invitation will have admin permissions").required(false))
.arg(arg!(-i --multi "many users can use this invitation to register themselves, until the invitation code is deleted by an admin").required(false).conflicts_with("admin").conflicts_with("unique"))
.arg(arg!(-u --unique "this invitation can be used only once. this is the default").required(false).conflicts_with("admin"))
@@ -440,6 +444,30 @@ async fn main() -> Result<(), ProtocolError> {
}
return res.map(|_| ());
}
+ Some(("del-user", sub2_matches)) => {
+ log_debug!("add-user");
+ let res = do_admin_call(
+ keys[1],
+ config_v0,
+ DelUser::V0(DelUserV0 {
+ user: sub2_matches
+ .get_one::("USER_ID")
+ .unwrap()
+ .as_str()
+ .try_into()
+ .map_err(|_| {
+ log_err!("supplied USER_ID is invalid");
+ ProtocolError::InvalidValue
+ })?,
+ }),
+ )
+ .await;
+ match &res {
+ Err(e) => log_err!("An error occurred: {e}"),
+ Ok(_) => println!("User removed successfully"),
+ }
+ return res.map(|_| ());
+ }
Some(("list-users", sub2_matches)) => {
log_debug!("list-users");
let admins = sub2_matches.get_flag("admin");
diff --git a/ngone/web/src/routes/Home.svelte b/ngone/web/src/routes/Home.svelte
index c56d66a..bcb59cc 100644
--- a/ngone/web/src/routes/Home.svelte
+++ b/ngone/web/src/routes/Home.svelte
@@ -25,7 +25,7 @@
async function bootstrap() {
let bs;
try {
- bs = localStorage.getItem("bootstrap");
+ bs = localStorage.getItem("ng_wallets");
} catch (e) {}
if (bs) {
} else {
diff --git a/ngone/web/src/routes/WalletCreate.svelte b/ngone/web/src/routes/WalletCreate.svelte
index d5ca013..623f280 100644
--- a/ngone/web/src/routes/WalletCreate.svelte
+++ b/ngone/web/src/routes/WalletCreate.svelte
@@ -12,7 +12,9 @@