adding bootstrap and core info in wallet at creation time

Niko PLP 10 months ago
parent 58f21626a3
commit 342168a90b
  1. 1
      Cargo.lock
  2. 1
      ng-app/src/api.ts
  3. 907
      ng-app/src/routes/WalletCreate.svelte
  4. 19
      ng-sdk-js/src/lib.rs
  5. 35
      ng-wallet/src/lib.rs
  6. 24
      ng-wallet/src/types.rs
  7. 1
      ngaccount/Cargo.toml
  8. 60
      ngaccount/src/main.rs
  9. 1
      ngaccount/web/src/routes/Create.svelte
  10. 10
      ngcli/src/main.rs
  11. 12
      p2p-net/src/actors/add_invitation.rs
  12. 66
      p2p-net/src/types.rs
  13. 10
      p2p-net/src/utils.rs

1
Cargo.lock generated

@ -2838,6 +2838,7 @@ dependencies = [
"anyhow",
"base64-url",
"bytes",
"duration-str",
"env_logger",
"log",
"ng-wallet",

@ -115,6 +115,7 @@ export const NG_EU_BSP_REGISTER = "https://account.nextgraph.eu/#/create";
export const NG_EU_BSP_REGISTERED = "https://nextgraph.eu/#/user/registered";
export const APP_ACCOUNT_REGISTERED_SUFFIX = "/#/user/registered";
export const APP_WALLET_CREATE_SUFFIX = "/#/wallet/create";
export const NG_NET_BSP = "https://nextgraph.net";
export const NG_NET_BSP_REGISTER = "https://account.nextgraph.net/#/create";

File diff suppressed because it is too large Load Diff

@ -76,6 +76,17 @@ pub async fn get_local_url(location: String) -> JsValue {
}
}
#[cfg(target_arch = "wasm32")]
#[wasm_bindgen]
pub async fn get_ngone_url_of_invitation(invitation_string: String) -> JsValue {
let res = decode_invitation_string(invitation_string);
if res.is_some() {
serde_wasm_bindgen::to_value(&res.unwrap().get_urls()[0]).unwrap()
} else {
JsValue::FALSE
}
}
#[cfg(target_arch = "wasm32")]
#[wasm_bindgen]
pub async fn get_local_bootstrap_with_public(location: String, invite: JsValue) -> JsValue {
@ -308,11 +319,16 @@ pub async fn wallet_create_wallet(js_params: JsValue) -> Result<JsValue, JsValue
let mut params = serde_wasm_bindgen::from_value::<CreateWalletV0>(js_params)
.map_err(|_| "Deserialization error of args")?;
params.result_with_wallet_file = true;
let local_save = params.local_save;
let res = create_wallet_v0(params).await;
match res {
Ok(r) => {
if local_save {
let session = save_wallet_locally(&r)?;
Ok(serde_wasm_bindgen::to_value(&(r, session)).unwrap())
} else {
Ok(serde_wasm_bindgen::to_value(&(r, false)).unwrap())
}
}
Err(e) => Err(e.to_string().into()),
}
@ -329,6 +345,9 @@ pub fn test_create_wallet() -> JsValue {
9,
false,
false,
BootstrapContentV0::new(),
None,
None,
);
serde_wasm_bindgen::to_value(&r).unwrap()
}

@ -341,10 +341,8 @@ pub fn gen_shuffle_for_pin() -> Vec<u8> {
/// creates a Wallet from a pin, a security text and image (with option to send the bootstrap and wallet to nextgraph.one)
/// and returns the Wallet, the pazzle and the mnemonic
pub async fn create_wallet_v0(
params: CreateWalletV0,
mut params: CreateWalletV0,
) -> Result<CreateWalletResultV0, NgWalletError> {
// TODO : use some automatically zeroed variable for the 2 first arguments, and for the returned values
let creating_pazzle = Instant::now();
// pazzle_length can only be 9, 12, or 15
@ -481,7 +479,33 @@ pub async fn create_wallet_v0(
//Creating a new peerId for this Client and User
let peer = generate_keypair();
let wallet_log = WalletLog::new_v0(create_op);
let mut wallet_log = WalletLog::new_v0(create_op);
// adding some more operations in the log
// pub core_bootstrap: BootstrapContentV0,
// #[zeroize(skip)]
// pub core_registration: Option<[u8; 32]>,
// #[zeroize(skip)]
// pub additional_bootstrap: Option<BootstrapContentV0>,
wallet_log.add(WalletOperation::AddSiteCoreV0((
user,
params
.core_bootstrap
.get_first_peer_id()
.ok_or(NgWalletError::InvalidBootstrap)?,
params.core_registration,
)));
if let Some(additional) = &params.additional_bootstrap {
params.core_bootstrap.merge(additional);
}
for server in &params.core_bootstrap.servers {
wallet_log.add(WalletOperation::AddBrokerServerV0(server.clone()));
wallet_log.add(WalletOperation::AddSiteBootstrapV0((user, server.peer_id)));
}
let mut master_key = [0u8; 32];
getrandom::getrandom(&mut master_key).map_err(|e| NgWalletError::InternalError)?;
@ -632,6 +656,9 @@ mod test {
9,
false,
false,
BootstrapContentV0::new(),
None,
None,
))
.await
.expect("create_wallet_v0");

@ -226,6 +226,11 @@ impl WalletLog {
pub fn new_v0(create_op: WalletOpCreateV0) -> Self {
WalletLog::V0(WalletLogV0::new(create_op))
}
pub fn add(&mut self, op: WalletOperation) {
match self {
Self::V0(v0) => v0.add(op),
}
}
}
impl WalletLogV0 {
@ -289,10 +294,10 @@ impl WalletLogV0 {
}
}
WalletOperation::RemoveOverlayCoreOverrideV0(_) => {}
WalletOperation::AddSiteCoreV0((site, core)) => {
WalletOperation::AddSiteCoreV0((site, core, registration)) => {
if self.is_first_and_not_deleted_afterwards(op, "RemoveSiteCoreV0") {
let _ = wallet.sites.get_mut(&site).and_then(|site| {
site.cores.push(*core);
site.cores.push((*core, *registration));
None::<SiteV0>
});
}
@ -460,7 +465,7 @@ pub enum WalletOperation {
SetClientV0(ClientV0),
AddOverlayCoreOverrideV0((OverlayId, Vec<PubKey>)),
RemoveOverlayCoreOverrideV0(OverlayId),
AddSiteCoreV0((PubKey, PubKey)),
AddSiteCoreV0((PubKey, PubKey, Option<[u8; 32]>)),
RemoveSiteCoreV0((PubKey, PubKey)),
AddSiteBootstrapV0((PubKey, PubKey)),
RemoveSiteBootstrapV0((PubKey, PubKey)),
@ -763,6 +768,12 @@ pub struct CreateWalletV0 {
pub result_with_wallet_file: bool,
#[zeroize(skip)]
pub local_save: bool,
#[zeroize(skip)]
pub core_bootstrap: BootstrapContentV0,
#[zeroize(skip)]
pub core_registration: Option<[u8; 32]>,
#[zeroize(skip)]
pub additional_bootstrap: Option<BootstrapContentV0>,
}
impl CreateWalletV0 {
@ -773,6 +784,9 @@ impl CreateWalletV0 {
pazzle_length: u8,
send_bootstrap: bool,
send_wallet: bool,
core_bootstrap: BootstrapContentV0,
core_registration: Option<[u8; 32]>,
additional_bootstrap: Option<BootstrapContentV0>,
) -> Self {
CreateWalletV0 {
result_with_wallet_file: false,
@ -783,6 +797,9 @@ impl CreateWalletV0 {
pazzle_length,
send_bootstrap,
send_wallet,
core_bootstrap,
core_registration,
additional_bootstrap,
}
}
}
@ -822,6 +839,7 @@ pub enum NgWalletError {
DecryptionError,
InvalidSignature,
NoCreateWalletPresent,
InvalidBootstrap,
}
impl fmt::Display for NgWalletError {

@ -27,3 +27,4 @@ base64-url = "2.0.0"
serde_json = "1.0.96"
bytes = "1.0"
anyhow = "1.0.71"
duration-str = "0.5.1"

@ -11,8 +11,9 @@ extern crate anyhow;
mod types;
use duration_str::parse;
use p2p_client_ws::remote_ws::ConnectionWebSocket;
use p2p_net::actors::add_user::*;
use p2p_net::actors::add_invitation::*;
use p2p_net::broker::BROKER;
use p2p_repo::store::StorageError;
use warp::reply::Response;
@ -30,12 +31,12 @@ use std::{env, fs};
use crate::types::*;
use ng_wallet::types::*;
use p2p_net::types::{
BindAddress, CreateAccountBSP, Invitation, InvitationV0, APP_ACCOUNT_REGISTERED_SUFFIX,
APP_NG_ONE_URL, NG_ONE_URL,
AdminResponseContentV0, BindAddress, CreateAccountBSP, Invitation, InvitationCode,
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};
use p2p_repo::utils::{generate_keypair, sign, timestamp_after, verify};
#[derive(RustEmbed)]
#[folder = "web/dist"]
@ -60,6 +61,11 @@ impl Server {
// if needed, proceed with payment and verify it here. once validated, add the user
let duration = parse("5m").unwrap();
let expiry = timestamp_after(duration);
let symkey = SymKey::random();
let invite_code = InvitationCode::Unique(symkey.clone());
let local_peer_pubk = self.local_peer_key.to_pub();
let res = BROKER
.write()
@ -75,43 +81,45 @@ impl Server {
port: self.port,
ip: (&self.ip).into(),
},
AddUser::V0(AddUserV0 {
user: cabsp.user(),
is_admin: false,
AddInvitation::V0(AddInvitationV0 {
invite_code,
expiry,
memo: None,
tos_url: false,
}),
)
.await;
let mut redirect_url = cabsp
let redirect_url = cabsp
.redirect_url()
.clone()
.unwrap_or(format!("{}{}", self.domain, APP_ACCOUNT_REGISTERED_SUFFIX));
match &res {
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,
Err(Some(format!("{}?re={}", redirect_url, e)))
}
} else {
InvitationV0::empty(Some(self.domain.clone()))
};
return_invitation.append_bootstraps(cabsp.additional_bootstrap());
Ok(AdminResponseContentV0::Invitation(Invitation::V0(mut invitation))) => {
log_info!("invitation created successfully {:?}", invitation);
invitation.name = Some(self.domain.clone());
Ok(format!(
"{}?i={}&u={}",
"{}?i={}&rs={}",
redirect_url,
Invitation::V0(return_invitation),
cabsp.user()
Invitation::V0(invitation),
self.domain
))
}
_ => {
log_err!(
"error while registering: invalid response from add_invitation {:?}",
cabsp
);
Err(Some(format!(
"{}?re={}",
redirect_url, "add_invitation_failed"
)))
}
}
}

@ -45,6 +45,7 @@
error = "We are redirecting you...";
go_back = false;
} else {
//console.log(result);
success(result);
}
} catch (e) {

@ -95,7 +95,7 @@ async fn main() -> Result<(), ProtocolError> {
.arg(arg!(
-v --verbose ... "Increase the logging output. once : info, twice : debug, 3 times : trace"
))
.arg(arg!(-b --base [PATH] "Base path for client home folder containing all persistent files, config, and key")
.arg(arg!(-b --base <PATH> "Base path for client home folder containing all persistent files, config, and key")
.required(false)
.value_parser(value_parser!(PathBuf))
.default_value(".ng"))
@ -137,12 +137,12 @@ async fn main() -> Result<(), ProtocolError> {
.subcommand(
Command::new("add-user")
.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!(<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)))
.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")
@ -156,7 +156,8 @@ async fn main() -> Result<(), ProtocolError> {
.arg(arg!(-u --unique "this invitation can be used only once. this is the default").required(false).conflicts_with("admin"))
.arg(arg!(-f --forever "this invitation does not expire. it can be used forever (or until deleted by an admin). default if no EXPIRES provided").required(false))
.arg(arg!(-n --name <NAME> "optional name of this broker that will be displayed to the user when registering: You have been invited to register an account at [NAME]").required(false))
.arg(arg!(-m --memo <MEMO> "optional memo about this invitation that will be kept in the server. it will help you to remember who you invited and to manage the invitation").required(false)))
.arg(arg!(-m --memo <MEMO> "optional memo about this invitation that will be kept in the server. it will help you to remember who you invited and to manage the invitation").required(false))
.arg(arg!(--notos "the TOS have already been accepted by the user. No need to redirect to a page for TOS acceptance.").required(false)))
.subcommand(
Command::new("list-invitations")
.about("list all invitations")
@ -521,6 +522,7 @@ async fn main() -> Result<(), ProtocolError> {
invite_code,
expiry,
memo: sub2_matches.get_one::<String>("memo").map(|s| s.clone()),
tos_url: !sub2_matches.get_flag("notos"),
}),
)
.await;

@ -27,6 +27,7 @@ pub struct AddInvitationV0 {
pub invite_code: InvitationCode,
pub expiry: u32,
pub memo: Option<String>,
pub tos_url: bool,
}
/// Add invitation
@ -51,6 +52,11 @@ impl AddInvitation {
AddInvitation::V0(o) => &o.memo,
}
}
pub fn tos_url(&self) -> bool {
match self {
AddInvitation::V0(o) => o.tos_url,
}
}
pub fn get_actor(&self) -> Box<dyn EActor> {
Actor::<AddInvitation, AdminResponse>::new_responder()
}
@ -103,7 +109,11 @@ impl EActor for Actor<'_, AddInvitation, AdminResponse> {
broker.get_bootstrap()?.clone(),
Some(req.code().get_symkey()),
None,
broker.get_registration_url().map(|s| s.clone()),
if req.tos_url() {
broker.get_registration_url().map(|s| s.clone())
} else {
None
},
));
let response: AdminResponseV0 = invitation.into();
fsm.lock().await.send(response.into()).await?;

@ -145,7 +145,7 @@ pub struct SiteV0 {
// Identity::OrgPrivate or Identity::IndividualPrivate
pub private: SiteStore,
pub cores: Vec<PubKey>,
pub cores: Vec<(PubKey, Option<[u8; 32]>)>,
pub bootstraps: Vec<PubKey>,
}
@ -529,6 +529,25 @@ pub struct BootstrapContentV0 {
pub servers: Vec<BrokerServerV0>,
}
impl BootstrapContentV0 {
pub fn new() -> Self {
BootstrapContentV0 { servers: vec![] }
}
pub fn merge(&mut self, with: &BootstrapContentV0) {
'outer: for server2 in &with.servers {
for server1 in &self.servers {
if *server1 == *server2 {
continue 'outer;
}
}
self.servers.push(server2.clone());
}
}
pub fn get_first_peer_id(&self) -> Option<PubKey> {
self.servers.first().map(|s| s.peer_id)
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum BootstrapContent {
V0(BootstrapContentV0),
@ -679,6 +698,14 @@ impl Invitation {
}
}
pub fn set_url(&mut self, url: Option<&String>) {
if url.is_some() {
match self {
Invitation::V0(v0) => v0.url = Some(url.unwrap().clone()),
}
}
}
/// first URL in the list is the ngone one
pub fn get_urls(&self) -> Vec<String> {
match self {
@ -773,37 +800,36 @@ impl CreateAccountBSP {
}
Some(base64_url::encode(&payload_ser.unwrap()))
}
pub fn user(&self) -> PubKey {
match self {
Self::V0(v0) => v0.user,
}
}
// pub fn user(&self) -> PubKey {
// match self {
// Self::V0(v0) => v0.user,
// }
// }
pub fn redirect_url(&self) -> &Option<String> {
match self {
Self::V0(v0) => &v0.redirect_url,
}
}
pub fn invitation(&self) -> &Option<InvitationV0> {
match self {
Self::V0(v0) => &v0.invitation,
}
}
pub fn additional_bootstrap(&mut self) -> &mut Option<BootstrapContentV0> {
match self {
Self::V0(v0) => &mut v0.additional_bootstrap,
}
}
// pub fn invitation(&self) -> &Option<InvitationV0> {
// match self {
// Self::V0(v0) => &v0.invitation,
// }
// }
// pub fn additional_bootstrap(&mut self) -> &mut Option<BootstrapContentV0> {
// match self {
// Self::V0(v0) => &mut v0.additional_bootstrap,
// }
// }
}
/// Create an account at a Broker Service Provider (BSP). Version 0
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct CreateAccountBSPV0 {
pub invitation: Option<InvitationV0>,
pub additional_bootstrap: Option<BootstrapContentV0>,
//pub invitation: Option<InvitationV0>,
//pub additional_bootstrap: Option<BootstrapContentV0>,
/// the user asking to create an account
pub user: PubKey,
//pub user: PubKey,
/// signature over serialized invitation code, with user key
// pub sig: Sig,

@ -9,6 +9,7 @@
* according to those terms.
*/
use crate::broker::BROKER;
use crate::types::*;
use crate::NG_BOOTSTRAP_LOCAL_PATH;
use async_std::task;
@ -150,10 +151,13 @@ pub async fn retrieve_local_bootstrap(
let resp = reqwest::get(format!("{}{}", APP_PREFIX, NG_BOOTSTRAP_LOCAL_PATH)).await;
if resp.is_ok() {
let resp = resp.unwrap().json::<BootstrapContent>().await;
resp.ok().map(|v| v.into())
} else {
None
if resp.is_ok() {
let mut inv: Invitation = resp.unwrap().into();
inv.set_url(BROKER.read().await.get_registration_url());
return Some(inv);
}
}
None
};
let res = if invite1.is_none() {

Loading…
Cancel
Save