adding bootstrap and core info in wallet at creation time

Niko PLP 1 year ago
parent 58f21626a3
commit 342168a90b
  1. 1
      Cargo.lock
  2. 1
      ng-app/src/api.ts
  3. 791
      ng-app/src/routes/WalletCreate.svelte
  4. 23
      ng-sdk-js/src/lib.rs
  5. 35
      ng-wallet/src/lib.rs
  6. 24
      ng-wallet/src/types.rs
  7. 3
      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", "anyhow",
"base64-url", "base64-url",
"bytes", "bytes",
"duration-str",
"env_logger", "env_logger",
"log", "log",
"ng-wallet", "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 NG_EU_BSP_REGISTERED = "https://nextgraph.eu/#/user/registered";
export const APP_ACCOUNT_REGISTERED_SUFFIX = "/#/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 = "https://nextgraph.net";
export const NG_NET_BSP_REGISTER = "https://account.nextgraph.net/#/create"; export const NG_NET_BSP_REGISTER = "https://account.nextgraph.net/#/create";

@ -21,7 +21,7 @@
NG_NET_BSP, NG_NET_BSP,
NG_EU_BSP_REGISTER, NG_EU_BSP_REGISTER,
NG_EU_BSP_REGISTERED, NG_EU_BSP_REGISTERED,
APP_ACCOUNT_REGISTERED_SUFFIX, APP_WALLET_CREATE_SUFFIX,
default as ng, default as ng,
} from "../api"; } from "../api";
import { display_pazzle } from "../wallet_emojis"; import { display_pazzle } from "../wallet_emojis";
@ -85,6 +85,8 @@
}; };
let intro = false; let intro = false;
let registration_error;
let registration_success;
let pin = []; let pin = [];
let pin_confirm = []; let pin_confirm = [];
let security_txt = ""; let security_txt = "";
@ -129,23 +131,44 @@
: "http://localhost:3030/api/v1/"; : "http://localhost:3030/api/v1/";
async function bootstrap() { async function bootstrap() {
console.log(await ng.client_info()); //console.log(await ng.client_info());
invitation = await ng.get_local_bootstrap_with_public( if (!tauri_platform) {
location.href, if (param.get("skipintro") || param.get("rs")) {
param.get("i") intro = false;
); }
console.log(invitation); if (param.get("re")) {
// TODO: implement this error screen and link button registration_error = param.get("re");
if (!invitation && param.get("i")) { }
console.error( if (param.get("rs")) {
"got an invitation for another broker. click on the link below to be redirected to the right broker" registration_success = param.get("rs");
); invitation = await ng.decode_invitation(param.get("i"));
} else {
invitation = await ng.get_local_bootstrap_with_public(
location.href,
param.get("i")
);
}
console.log(invitation);
if (!invitation && param.get("i")) {
let redirect = await ng.get_ngone_url_of_invitation(param.get("i"));
if (redirect) {
console.error("got an invitation for another broker. redirecting");
window.location.href = redirect;
} else {
//let i = await ng.decode_invitation(param.get("i"));
console.error("invalid invitation. ignoring it");
}
}
} }
scrollToTop(); scrollToTop();
} }
function create_wallet() { function create_wallet() {
intro = false; intro = false;
if (invitation && invitation.V0.url) {
// we redirect to the TOS url of the invitation.
window.location.href = invitation.V0.url;
}
scrollToTop(); scrollToTop();
} }
@ -163,6 +186,15 @@
async function do_wallet() { async function do_wallet() {
creating = true; creating = true;
let local_invitation = await ng.get_local_bootstrap(location.href);
let additional_bootstrap;
if (local_invitation) {
additional_bootstrap = local_invitation.V0.bootstrap;
}
let core_registration;
if (invitation.V0.code) {
core_registration = invitation.V0.code.ChaCha20Key;
}
let params = { let params = {
security_img: security_img, security_img: security_img,
security_txt, security_txt,
@ -172,19 +204,25 @@
send_wallet: options.cloud, send_wallet: options.cloud,
local_save: options.trusted, // this is only used for tauri apps local_save: options.trusted, // this is only used for tauri apps
result_with_wallet_file: false, // this will be automatically changed to true for browser app result_with_wallet_file: false, // this will be automatically changed to true for browser app
core_bootstrap: invitation.V0.bootstrap,
core_registration,
additional_bootstrap,
}; };
console.log(params); console.log(params);
try { try {
let res = await ng.wallet_create_wallet(params); let res = await ng.wallet_create_wallet(params);
console.log(res); console.log(res);
wallets.set(Object.fromEntries(await ng.get_wallets_from_localstorage())); wallets.set(
set_active_session(res[1]); Object.fromEntries((await ng.get_wallets_from_localstorage()) || [])
);
if (res[1]) {
set_active_session(res[1]);
}
ready = res[0]; ready = res[0];
console.log(display_pazzle(ready.pazzle)); console.log(display_pazzle(ready.pazzle));
download_name = "wallet-" + ready.wallet_name + ".ngw"; download_name = "wallet-" + ready.wallet_name + ".ngw";
if (options.cloud) { if (options.cloud) {
cloud_link = "https://nextgraph.one/#/w/" + res.wallet_name; cloud_link = "https://nextgraph.one/#/w/" + ready.wallet_name;
} }
if (ready.wallet_file.length) { if (ready.wallet_file.length) {
const blob = new Blob([ready.wallet_file]); const blob = new Blob([ready.wallet_file]);
@ -240,18 +278,10 @@
const selectEU = async (event) => { const selectEU = async (event) => {
if (!tauri_platform) { if (!tauri_platform) {
let local_invitation = await ng.get_local_bootstrap(location.href);
let additional_bootstrap;
if (local_invitation) {
additional_bootstrap = local_invitation.V0.bootstrap;
}
let local_url = await ng.get_local_url(location.href); let local_url = await ng.get_local_url(location.href);
let create = { let create = {
V0: { V0: {
additional_bootstrap, redirect_url: local_url + APP_WALLET_CREATE_SUFFIX,
invitation: undefined,
user: ready.user,
redirect_url: local_url + APP_ACCOUNT_REGISTERED_SUFFIX,
}, },
}; };
let ca = await ng.encode_create_account(create); let ca = await ng.encode_create_account(create);
@ -260,9 +290,6 @@
} else { } else {
let create = { let create = {
V0: { V0: {
additional_bootstrap: undefined,
invitation: undefined,
user: ready.user,
redirect_url: undefined, redirect_url: undefined,
}, },
}; };
@ -283,7 +310,51 @@
<Logo class="logo block h-40" alt="NextGraph Logo" /> <Logo class="logo block h-40" alt="NextGraph Logo" />
</a> </a>
</div> </div>
{#if intro} {#if registration_error}
<div class=" max-w-6xl lg:px-8 mx-auto px-4 text-red-800">
<svg
class="animate-bounce mt-10 h-16 w-16 mx-auto"
fill="none"
stroke="currentColor"
stroke-width="1.5"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z"
/>
</svg>
{#if registration_error == "AlreadyExists"}
<p class="max-w-xl md:mx-auto lg:max-w-2xl mb-5">
The user is already registered with the selected broker.<br /> Try logging
in instead
</p>
<a use:link href="/wallet/login">
<button
tabindex="-1"
class="text-white bg-primary-700 hover:bg-primary-700/90 focus:ring-4 focus:outline-none focus:ring-primary-700/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-700/55 mb-2"
>
Login
</button>
</a>
{:else}
<p class="max-w-xl md:mx-auto lg:max-w-2xl mb-5">
An error occurred during registration:<br />{registration_error}
</p>
<a use:link href="/">
<button
tabindex="-1"
class="text-white bg-primary-700 hover:bg-primary-700/90 focus:ring-4 focus:outline-none focus:ring-primary-700/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-700/55 mb-2"
>
Go back to homepage
</button>
</a>
{/if}
</div>
{:else if intro}
<div class=" max-w-6xl lg:px-8 mx-auto px-4"> <div class=" max-w-6xl lg:px-8 mx-auto px-4">
<p class="max-w-xl md:mx-auto lg:max-w-2xl"> <p class="max-w-xl md:mx-auto lg:max-w-2xl">
A <b>NextGraph Wallet</b> is unique to each person. It stores your A <b>NextGraph Wallet</b> is unique to each person. It stores your
@ -526,10 +597,340 @@
Ok, I create my wallet now ! Ok, I create my wallet now !
</button> </button>
</div> </div>
{:else if !invitation}
<div class=" max-w-6xl lg:px-8 mx-auto px-4">
<p class="max-w-xl md:mx-auto lg:max-w-2xl">
NextGraph is based on an efficient decentralized P2P network, and in
order to join this network and start using the app, you need to first
select a <b>broker&nbsp;server</b>.
</p>
</div>
<div class="px-4 pt-3 mx-auto max-w-6xl lg:px-8 lg:pt-10 dark:bg-slate-800">
<div class="max-w-xl md:mx-auto sm:text-center lg:max-w-2xl">
<h2 class="pb-5 text-xl">
What is a broker? <span class="text-sm">Please read</span>
</h2>
<ul class="mb-8 space-y-4 text-left text-gray-500 dark:text-gray-400">
<li class="flex space-x-3">
<svg
fill="none"
stroke="currentColor"
stroke-width="1.5"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
class="flex-shrink-0 w-5 h-5 text-green-500 dark:text-green-400"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0l3.181 3.183a8.25 8.25 0 0013.803-3.7M4.031 9.865a8.25 8.25 0 0113.803-3.7l3.181 3.182m0-4.991v4.99"
/>
</svg>
<span>
The broker helps you keep all your data in <b>sync</b>, as it is
connected to the internet 24/7 and keeps a copy of the updates for
you. This way, even if the devices of the other participants are
offline, you can still see their changes</span
>
</li>
<li class="flex space-x-3">
<svg
class="flex-shrink-0 w-5 h-5 text-green-500 dark:text-green-400"
fill="none"
stroke="currentColor"
stroke-width="1.5"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M15.75 5.25a3 3 0 013 3m3 0a6 6 0 01-7.029 5.912c-.563-.097-1.159.026-1.563.43L10.5 17.25H8.25v2.25H6v2.25H2.25v-2.818c0-.597.237-1.17.659-1.591l6.499-6.499c.404-.404.527-1 .43-1.563A6 6 0 1121.75 8.25z"
/>
</svg>
<span>
All your data is secure and <b>end-to-end encrypted</b>, and the
broker cannot see the content of the documents as it does not have
the keys to decrypt them.</span
>
</li>
<li class="flex space-x-3">
<svg
class="flex-shrink-0 w-5 h-5 text-green-500 dark:text-green-400"
fill="none"
stroke="currentColor"
stroke-width="1.5"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M3.98 8.223A10.477 10.477 0 001.934 12C3.226 16.338 7.244 19.5 12 19.5c.993 0 1.953-.138 2.863-.395M6.228 6.228A10.45 10.45 0 0112 4.5c4.756 0 8.773 3.162 10.065 7.498a10.523 10.523 0 01-4.293 5.774M6.228 6.228L3 3m3.228 3.228l3.65 3.65m7.894 7.894L21 21m-3.228-3.228l-3.65-3.65m0 0a3 3 0 10-4.243-4.243m4.242 4.242L9.88 9.88"
/>
</svg>
<span>
The broker helps you enforce your <b>privacy</b> as it hides your internet
address (IP) from other users you share documents with.</span
>
</li>
<li class="flex space-x-3">
<svg
class="flex-shrink-0 w-5 h-5 text-green-500 dark:text-green-400"
fill="none"
stroke="currentColor"
stroke-width="1.5"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M9.75 9.75l4.5 4.5m0-4.5l-4.5 4.5M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
<span>
It will be possible in the future to use NextGraph without any
broker and to have direct connections between peers, but this will
imply a less smooth experience.</span
>
</li>
<li class="flex space-x-3">
<svg
class="flex-shrink-0 w-5 h-5 text-green-500 dark:text-green-400"
fill="none"
stroke="currentColor"
stroke-width="1.5"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M7.5 21L3 16.5m0 0L7.5 12M3 16.5h13.5m0-13.5L21 7.5m0 0L16.5 12M21 7.5H7.5"
/>
</svg>
<span>
At anytime you can decide to switch to another broker service
provider or host it yourself. Your data is totally <b>portable</b>
and can freely move to another broker.</span
>
</li>
<li class="flex space-x-3">
<svg
class="flex-shrink-0 w-5 h-5 text-green-500 dark:text-green-400"
fill="none"
stroke="currentColor"
stroke-width="1.5"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M2.25 12l8.954-8.955c.44-.439 1.152-.439 1.591 0L21.75 12M4.5 9.75v10.125c0 .621.504 1.125 1.125 1.125H9.75v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21h4.125c.621 0 1.125-.504 1.125-1.125V9.75M8.25 21h8.25"
/>
</svg>
<span>
Soon we will offer you the opportunity to host your own broker at <b
>home</b
>
or <b>office</b>. Instead of using a "broker service provider",
you will own a small device that you connect behind your internet
router. It is called <b>NG Box</b> and will be available soon.</span
>
</li>
<li class="flex space-x-3">
<svg
class="flex-shrink-0 w-5 h-5 text-green-500 dark:text-green-400"
fill="none"
stroke="currentColor"
stroke-width="1.5"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M5.25 14.25h13.5m-13.5 0a3 3 0 01-3-3m3 3a3 3 0 100 6h13.5a3 3 0 100-6m-16.5-3a3 3 0 013-3h13.5a3 3 0 013 3m-19.5 0a4.5 4.5 0 01.9-2.7L5.737 5.1a3.375 3.375 0 012.7-1.35h7.126c1.062 0 2.062.5 2.7 1.35l2.587 3.45a4.5 4.5 0 01.9 2.7m0 0a3 3 0 01-3 3m0 3h.008v.008h-.008v-.008zm0-6h.008v.008h-.008v-.008zm-3 6h.008v.008h-.008v-.008zm0-6h.008v.008h-.008v-.008z"
/>
</svg>
<span>
Organizations and companies have the opportunity to host a broker <b
>on-premise</b
>
or in the <b>cloud</b>, as the software is open source.
Individuals can also
<b>self-host</b> a broker on any VPS server or at home, on their dedicated
hardware.</span
>
</li>
</ul>
<h2 class="mt-3 text-xl">Please choose one broker among the list</h2>
</div>
</div>
<div class="row mt-5">
<button
on:click|once={selectEU}
class="choice-button text-primary-700 bg-primary-100 hover:bg-primary-100/90 focus:ring-4 focus:outline-none focus:ring-primary-100/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-100/55 mb-2"
>
<EULogo class="mr-4 block h-10 w-10" alt="European Union flag" />
For European Union citizens
</button>
</div>
<div class="row mt-5">
<button
on:click|once={selectNET}
class="choice-button text-primary-700 bg-primary-100 hover:bg-primary-100/90 focus:ring-4 focus:outline-none focus:ring-primary-100/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-100/55 mb-2"
>
<svg
fill="none"
stroke="currentColor"
stroke-width="1.5"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
class="mr-4 block h-10 w-10"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M12 21a9.004 9.004 0 008.716-6.747M12 21a9.004 9.004 0 01-8.716-6.747M12 21c2.485 0 4.5-4.03 4.5-9S14.485 3 12 3m0 18c-2.485 0-4.5-4.03-4.5-9S9.515 3 12 3m0 0a8.997 8.997 0 017.843 4.582M12 3a8.997 8.997 0 00-7.843 4.582m15.686 0A11.953 11.953 0 0112 10.5c-2.998 0-5.74-1.1-7.843-2.918m15.686 0A8.959 8.959 0 0121 12c0 .778-.099 1.533-.284 2.253m0 0A17.919 17.919 0 0112 16.5c-3.162 0-6.133-.815-8.716-2.247m0 0A9.015 9.015 0 013 12c0-1.605.42-3.113 1.157-4.418"
/>
</svg>
For the rest of the world
</button>
</div>
<div class="row mt-5">
<button
on:click|once={enterINVITE}
class="choice-button text-primary-700 bg-primary-100 hover:bg-primary-100/90 focus:ring-4 focus:outline-none focus:ring-primary-100/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-100/55 mb-2"
>
<svg
fill="none"
stroke="currentColor"
stroke-width="1.5"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
class="mr-4 block h-10 w-10"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M13.19 8.688a4.5 4.5 0 011.242 7.244l-4.5 4.5a4.5 4.5 0 01-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5 4.5 0 00-6.364-6.364l-4.5 4.5a4.5 4.5 0 001.242 7.244"
/>
</svg>
Enter an invitation link
</button>
</div>
{#if mobile}
<div class="row mt-5">
<button
on:click|once={enterQRcode}
class="choice-button text-primary-700 bg-primary-100 hover:bg-primary-100/90 focus:ring-4 focus:outline-none focus:ring-primary-100/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-100/55 mb-2"
><svg
class="mr-4 block h-10 w-10"
fill="none"
stroke="currentColor"
stroke-width="1.5"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M3.75 4.875c0-.621.504-1.125 1.125-1.125h4.5c.621 0 1.125.504 1.125 1.125v4.5c0 .621-.504 1.125-1.125 1.125h-4.5A1.125 1.125 0 013.75 9.375v-4.5zM3.75 14.625c0-.621.504-1.125 1.125-1.125h4.5c.621 0 1.125.504 1.125 1.125v4.5c0 .621-.504 1.125-1.125 1.125h-4.5a1.125 1.125 0 01-1.125-1.125v-4.5zM13.5 4.875c0-.621.504-1.125 1.125-1.125h4.5c.621 0 1.125.504 1.125 1.125v4.5c0 .621-.504 1.125-1.125 1.125h-4.5A1.125 1.125 0 0113.5 9.375v-4.5z"
/>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M6.75 6.75h.75v.75h-.75v-.75zM6.75 16.5h.75v.75h-.75v-.75zM16.5 6.75h.75v.75h-.75v-.75zM13.5 13.5h.75v.75h-.75v-.75zM13.5 19.5h.75v.75h-.75v-.75zM19.5 13.5h.75v.75h-.75v-.75zM19.5 19.5h.75v.75h-.75v-.75zM16.5 16.5h.75v.75h-.75v-.75z"
/>
</svg>
Scan an invitation QRcode
</button>
</div>
{/if}
<div class="row mt-5">
<button
on:click|once={displaySelfHost}
class="choice-button text-primary-700 bg-primary-100 hover:bg-primary-100/90 focus:ring-4 focus:outline-none focus:ring-primary-100/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-100/55 mb-2"
>
<svg
fill="none"
stroke="currentColor"
stroke-width="1.5"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
class="mr-4 block h-10 w-10"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M5.25 14.25h13.5m-13.5 0a3 3 0 01-3-3m3 3a3 3 0 100 6h13.5a3 3 0 100-6m-16.5-3a3 3 0 013-3h13.5a3 3 0 013 3m-19.5 0a4.5 4.5 0 01.9-2.7L5.737 5.1a3.375 3.375 0 012.7-1.35h7.126c1.062 0 2.062.5 2.7 1.35l2.587 3.45a4.5 4.5 0 01.9 2.7m0 0a3 3 0 01-3 3m0 3h.008v.008h-.008v-.008zm0-6h.008v.008h-.008v-.008zm-3 6h.008v.008h-.008v-.008zm0-6h.008v.008h-.008v-.008z"
/>
</svg>
Self-hosted broker
</button>
</div>
<div class="row mt-5 mb-12">
<button
on:click|once={displayNGbox}
class="choice-button text-primary-700 bg-primary-100 hover:bg-primary-100/90 focus:ring-4 focus:outline-none focus:ring-primary-100/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-100/55 mb-2"
>
<svg
xmlns="http://www.w3.org/2000/svg"
version="1.1"
viewBox="0 0 225 225"
class="mr-4 block h-10 w-10"
stroke="currentColor"
stroke-width="12"
fill="none"
>
<path
d="M 88.332599,179.77884 C 72.858008,177.42608 59.581081,170.564 48.8817,159.38898 36.800075,146.77026 30.396139,130.74266 30.396139,113.12381 c 0,-8.81477 1.466462,-16.772273 4.503812,-24.439156 3.697755,-9.333883 8.658122,-16.726264 15.988284,-23.827148 4.07992,-3.952299 5.699054,-5.267377 9.730928,-7.903581 10.263753,-6.710853 20.852276,-10.247623 32.861256,-10.976317 17.083161,-1.036581 33.737521,4.410501 47.100151,15.404873 1.30009,1.069669 2.35446,2.035155 2.34305,2.145524 -0.0114,0.110369 -3.32807,3.135042 -7.37038,6.721489 -4.04229,3.586437 -8.6667,7.731233 -10.27646,9.210635 -1.60975,1.479412 -3.05439,2.689839 -3.21032,2.689839 -0.15591,0 -1.2075,-0.642795 -2.33686,-1.428431 -6.49544,-4.518567 -13.79659,-6.747116 -22.104843,-6.747116 -10.982241,0 -20.054641,3.741852 -27.727158,11.435891 -5.517107,5.532575 -9.233107,12.555305 -10.782595,20.377588 -0.596045,3.00901 -0.594915,11.67153 0.0017,14.67182 3.195984,16.0665 15.801761,28.55358 31.607491,31.30987 3.592183,0.62643 10.334745,0.61437 13.792675,-0.0247 12.10383,-2.2368 22.30712,-9.80603 27.83192,-20.64689 0.66747,-1.30971 1.08703,-2.48825 0.93235,-2.61898 -0.1547,-0.13073 -5.9299,-1.01605 -12.83381,-1.96739 -8.43575,-1.16241 -12.87296,-1.9096 -13.52955,-2.27826 -1.31171,-0.73647 -2.44642,-2.49122 -2.44642,-3.78325 0,-1.012 1.74837,-13.68832 2.1486,-15.57814 0.25598,-1.20873 2.0923,-3.01339 3.3151,-3.25795 0.53677,-0.10735 7.61424,0.73799 15.7688,1.88346 8.13723,1.14303 14.89071,1.97925 15.00772,1.85826 0.11702,-0.12098 0.96445,-5.648553 1.88315,-12.283473 0.95557,-6.900944 1.90122,-12.59548 2.20977,-13.306594 0.29667,-0.683692 0.95765,-1.595052 1.46889,-2.025218 1.77972,-1.497534 2.7114,-1.539742 10.52745,-0.476938 8.31229,1.130266 9.2373,1.347581 10.59333,2.488613 1.41776,1.192951 1.96085,2.424677 1.94866,4.419342 -0.006,0.950347 -0.79507,7.156475 -1.75393,13.791395 -0.95885,6.634933 -1.70069,12.111623 -1.64854,12.170443 0.0522,0.0588 6.18174,0.95872 13.62132,1.99978 9.57969,1.34053 13.80866,2.0595 14.49353,2.46406 1.3199,0.77969 2.13943,2.28402 2.1135,3.87957 -0.0399,2.45278 -2.08103,15.63263 -2.5664,16.57122 -0.57073,1.10369 -2.24485,2.197 -3.38232,2.20889 -0.44831,0.004 -6.79249,-0.82755 -14.09817,-1.84941 -7.3057,-1.02186 -13.34942,-1.79161 -13.43049,-1.71053 -0.0811,0.0811 -1.02469,6.33285 -2.09694,13.89286 -1.24218,8.75802 -2.1547,14.1778 -2.51495,14.93697 -0.62565,1.31846 -2.38302,2.64205 -3.91461,2.94836 -0.8254,0.16509 -9.4024,-0.80047 -11.73007,-1.32049 -0.47193,-0.10544 -1.63157,0.58011 -3.8898,2.29957 -9.71515,7.39729 -20.99725,11.99799 -33.08692,13.49241 -3.79574,0.46921 -13.565667,0.37348 -17.125664,-0.16779 z"
/>
<rect
ry="37.596001"
y="10.583322"
x="14.363095"
height="204.86308"
width="195.79167"
/>
</svg>
NG Box (owned or invited)
</button>
</div>
{:else if pin.length < 4} {:else if pin.length < 4}
<div class=" max-w-6xl lg:px-8 mx-auto px-4"> <div class=" max-w-6xl lg:px-8 mx-auto px-4">
{#if registration_success}
<Alert color="green" class="mt-5 mb-5">
<span class="font-bold text-xl"
>You have been successfully registered to {registration_success}</span
>
</Alert>
{/if}
<p class="max-w-xl md:mx-auto lg:max-w-2xl"> <p class="max-w-xl md:mx-auto lg:max-w-2xl">
<span class="text-xl">Let's start by choosing a PIN code</span> <span class="text-xl"
>Let's start creating your wallet by choosing a PIN code</span
>
<Alert color="yellow" class="mt-5"> <Alert color="yellow" class="mt-5">
We recommend you to choose a PIN code that you already know very well. We recommend you to choose a PIN code that you already know very well.
<br /> <br />
@ -779,7 +1180,7 @@
here. {#if !tauri_platform}By selecting this option, you agree to save here. {#if !tauri_platform}By selecting this option, you agree to save
some cookies on your browser.{/if}<br /> some cookies on your browser.{/if}<br />
<Toggle class="mt-3" bind:checked={options.trusted} <Toggle class="mt-3" bind:checked={options.trusted}
>Save your wallet on this device?</Toggle >Save my wallet on this device?</Toggle
> >
</p> </p>
<p class="max-w-xl md:mx-auto mt-10 lg:max-w-2xl text-left"> <p class="max-w-xl md:mx-auto mt-10 lg:max-w-2xl text-left">
@ -794,7 +1195,7 @@
>. >.
<br /> <br />
<Toggle class="mt-3" bind:checked={options.cloud} <Toggle class="mt-3" bind:checked={options.cloud}
>Save your wallet in the cloud?</Toggle >Save my wallet in the cloud?</Toggle
> >
</p> </p>
<p class="max-w-xl md:mx-auto mt-10 lg:max-w-2xl text-left"> <p class="max-w-xl md:mx-auto mt-10 lg:max-w-2xl text-left">
@ -855,7 +1256,7 @@
{:else if !error} {:else if !error}
{#if !ready} {#if !ready}
<div class=" max-w-6xl lg:px-8 mx-auto px-4 text-primary-700"> <div class=" max-w-6xl lg:px-8 mx-auto px-4 text-primary-700">
We are creating your wallet... Your wallet is being created...
<svg <svg
class="animate-spin mt-10 h-6 w-6 mx-auto" class="animate-spin mt-10 h-6 w-6 mx-auto"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
@ -878,330 +1279,6 @@
/> />
</svg> </svg>
</div> </div>
{:else if !invitation}
<div class=" max-w-6xl lg:px-8 mx-auto px-4">
<p class="max-w-xl md:mx-auto lg:max-w-2xl">
NextGraph is based on an efficient decentralized P2P network, and in
order to join this network and start using the app, you need to first
select a <b>broker&nbsp;server</b>.
</p>
</div>
<div
class="px-4 pt-3 mx-auto max-w-6xl lg:px-8 lg:pt-10 dark:bg-slate-800"
>
<div class="max-w-xl md:mx-auto sm:text-center lg:max-w-2xl">
<h2 class="pb-5 text-xl">
What is a broker? <span class="text-sm">Please read</span>
</h2>
<ul class="mb-8 space-y-4 text-left text-gray-500 dark:text-gray-400">
<li class="flex space-x-3">
<svg
fill="none"
stroke="currentColor"
stroke-width="1.5"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
class="flex-shrink-0 w-5 h-5 text-green-500 dark:text-green-400"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0l3.181 3.183a8.25 8.25 0 0013.803-3.7M4.031 9.865a8.25 8.25 0 0113.803-3.7l3.181 3.182m0-4.991v4.99"
/>
</svg>
<span>
The broker helps you keep all your data in <b>sync</b>, as it is
connected to the internet 24/7 and keeps a copy of the updates
for you. This way, even if the devices of the other participants
are offline, you can still see their changes</span
>
</li>
<li class="flex space-x-3">
<svg
class="flex-shrink-0 w-5 h-5 text-green-500 dark:text-green-400"
fill="none"
stroke="currentColor"
stroke-width="1.5"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M15.75 5.25a3 3 0 013 3m3 0a6 6 0 01-7.029 5.912c-.563-.097-1.159.026-1.563.43L10.5 17.25H8.25v2.25H6v2.25H2.25v-2.818c0-.597.237-1.17.659-1.591l6.499-6.499c.404-.404.527-1 .43-1.563A6 6 0 1121.75 8.25z"
/>
</svg>
<span>
All your data is secure and <b>end-to-end encrypted</b>, and the
broker cannot see the content of the documents as it does not
have the keys to decrypt them.</span
>
</li>
<li class="flex space-x-3">
<svg
class="flex-shrink-0 w-5 h-5 text-green-500 dark:text-green-400"
fill="none"
stroke="currentColor"
stroke-width="1.5"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M3.98 8.223A10.477 10.477 0 001.934 12C3.226 16.338 7.244 19.5 12 19.5c.993 0 1.953-.138 2.863-.395M6.228 6.228A10.45 10.45 0 0112 4.5c4.756 0 8.773 3.162 10.065 7.498a10.523 10.523 0 01-4.293 5.774M6.228 6.228L3 3m3.228 3.228l3.65 3.65m7.894 7.894L21 21m-3.228-3.228l-3.65-3.65m0 0a3 3 0 10-4.243-4.243m4.242 4.242L9.88 9.88"
/>
</svg>
<span>
The broker helps you enforce your <b>privacy</b> as it hides your
internet address (IP) from other users you share documents with.</span
>
</li>
<li class="flex space-x-3">
<svg
class="flex-shrink-0 w-5 h-5 text-green-500 dark:text-green-400"
fill="none"
stroke="currentColor"
stroke-width="1.5"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M9.75 9.75l4.5 4.5m0-4.5l-4.5 4.5M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
<span>
It will be possible in the future to use NextGraph without any
broker and to have direct connections between peers, but this
will imply a less smooth experience.</span
>
</li>
<li class="flex space-x-3">
<svg
class="flex-shrink-0 w-5 h-5 text-green-500 dark:text-green-400"
fill="none"
stroke="currentColor"
stroke-width="1.5"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M7.5 21L3 16.5m0 0L7.5 12M3 16.5h13.5m0-13.5L21 7.5m0 0L16.5 12M21 7.5H7.5"
/>
</svg>
<span>
At anytime you can decide to switch to another broker service
provider or host it yourself. Your data is totally <b
>portable</b
>
and can freely move to another broker.</span
>
</li>
<li class="flex space-x-3">
<svg
class="flex-shrink-0 w-5 h-5 text-green-500 dark:text-green-400"
fill="none"
stroke="currentColor"
stroke-width="1.5"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M2.25 12l8.954-8.955c.44-.439 1.152-.439 1.591 0L21.75 12M4.5 9.75v10.125c0 .621.504 1.125 1.125 1.125H9.75v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21h4.125c.621 0 1.125-.504 1.125-1.125V9.75M8.25 21h8.25"
/>
</svg>
<span>
Soon we will offer you the opportunity to host your own broker
at <b>home</b>
or <b>office</b>. Instead of using a "broker service provider",
you will own a small device that you connect behind your
internet router. It is called <b>NG Box</b> and will be available
soon.</span
>
</li>
<li class="flex space-x-3">
<svg
class="flex-shrink-0 w-5 h-5 text-green-500 dark:text-green-400"
fill="none"
stroke="currentColor"
stroke-width="1.5"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M5.25 14.25h13.5m-13.5 0a3 3 0 01-3-3m3 3a3 3 0 100 6h13.5a3 3 0 100-6m-16.5-3a3 3 0 013-3h13.5a3 3 0 013 3m-19.5 0a4.5 4.5 0 01.9-2.7L5.737 5.1a3.375 3.375 0 012.7-1.35h7.126c1.062 0 2.062.5 2.7 1.35l2.587 3.45a4.5 4.5 0 01.9 2.7m0 0a3 3 0 01-3 3m0 3h.008v.008h-.008v-.008zm0-6h.008v.008h-.008v-.008zm-3 6h.008v.008h-.008v-.008zm0-6h.008v.008h-.008v-.008z"
/>
</svg>
<span>
Organizations and companies have the opportunity to host a
broker <b>on-premise</b>
or in the <b>cloud</b>, as the software is open source.
Individuals can also
<b>self-host</b> a broker on any VPS server or at home, on their
dedicated hardware.</span
>
</li>
</ul>
<h2 class="mt-3 text-xl">Please choose one broker among the list</h2>
</div>
</div>
<div class="row mt-5">
<button
on:click|once={selectEU}
class="choice-button text-primary-700 bg-primary-100 hover:bg-primary-100/90 focus:ring-4 focus:outline-none focus:ring-primary-100/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-100/55 mb-2"
>
<EULogo class="mr-4 block h-10 w-10" alt="European Union flag" />
For European Union citizens
</button>
</div>
<div class="row mt-5">
<button
on:click|once={selectNET}
class="choice-button text-primary-700 bg-primary-100 hover:bg-primary-100/90 focus:ring-4 focus:outline-none focus:ring-primary-100/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-100/55 mb-2"
>
<svg
fill="none"
stroke="currentColor"
stroke-width="1.5"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
class="mr-4 block h-10 w-10"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M12 21a9.004 9.004 0 008.716-6.747M12 21a9.004 9.004 0 01-8.716-6.747M12 21c2.485 0 4.5-4.03 4.5-9S14.485 3 12 3m0 18c-2.485 0-4.5-4.03-4.5-9S9.515 3 12 3m0 0a8.997 8.997 0 017.843 4.582M12 3a8.997 8.997 0 00-7.843 4.582m15.686 0A11.953 11.953 0 0112 10.5c-2.998 0-5.74-1.1-7.843-2.918m15.686 0A8.959 8.959 0 0121 12c0 .778-.099 1.533-.284 2.253m0 0A17.919 17.919 0 0112 16.5c-3.162 0-6.133-.815-8.716-2.247m0 0A9.015 9.015 0 013 12c0-1.605.42-3.113 1.157-4.418"
/>
</svg>
For the rest of the world
</button>
</div>
<div class="row mt-5">
<button
on:click|once={enterINVITE}
class="choice-button text-primary-700 bg-primary-100 hover:bg-primary-100/90 focus:ring-4 focus:outline-none focus:ring-primary-100/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-100/55 mb-2"
>
<svg
fill="none"
stroke="currentColor"
stroke-width="1.5"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
class="mr-4 block h-10 w-10"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M13.19 8.688a4.5 4.5 0 011.242 7.244l-4.5 4.5a4.5 4.5 0 01-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5 4.5 0 00-6.364-6.364l-4.5 4.5a4.5 4.5 0 001.242 7.244"
/>
</svg>
Enter an invitation link
</button>
</div>
{#if mobile}
<div class="row mt-5">
<button
on:click|once={enterQRcode}
class="choice-button text-primary-700 bg-primary-100 hover:bg-primary-100/90 focus:ring-4 focus:outline-none focus:ring-primary-100/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-100/55 mb-2"
><svg
class="mr-4 block h-10 w-10"
fill="none"
stroke="currentColor"
stroke-width="1.5"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M3.75 4.875c0-.621.504-1.125 1.125-1.125h4.5c.621 0 1.125.504 1.125 1.125v4.5c0 .621-.504 1.125-1.125 1.125h-4.5A1.125 1.125 0 013.75 9.375v-4.5zM3.75 14.625c0-.621.504-1.125 1.125-1.125h4.5c.621 0 1.125.504 1.125 1.125v4.5c0 .621-.504 1.125-1.125 1.125h-4.5a1.125 1.125 0 01-1.125-1.125v-4.5zM13.5 4.875c0-.621.504-1.125 1.125-1.125h4.5c.621 0 1.125.504 1.125 1.125v4.5c0 .621-.504 1.125-1.125 1.125h-4.5A1.125 1.125 0 0113.5 9.375v-4.5z"
/>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M6.75 6.75h.75v.75h-.75v-.75zM6.75 16.5h.75v.75h-.75v-.75zM16.5 6.75h.75v.75h-.75v-.75zM13.5 13.5h.75v.75h-.75v-.75zM13.5 19.5h.75v.75h-.75v-.75zM19.5 13.5h.75v.75h-.75v-.75zM19.5 19.5h.75v.75h-.75v-.75zM16.5 16.5h.75v.75h-.75v-.75z"
/>
</svg>
Scan an invitation QRcode
</button>
</div>
{/if}
<div class="row mt-5">
<button
on:click|once={displaySelfHost}
class="choice-button text-primary-700 bg-primary-100 hover:bg-primary-100/90 focus:ring-4 focus:outline-none focus:ring-primary-100/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-100/55 mb-2"
>
<svg
fill="none"
stroke="currentColor"
stroke-width="1.5"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
class="mr-4 block h-10 w-10"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M5.25 14.25h13.5m-13.5 0a3 3 0 01-3-3m3 3a3 3 0 100 6h13.5a3 3 0 100-6m-16.5-3a3 3 0 013-3h13.5a3 3 0 013 3m-19.5 0a4.5 4.5 0 01.9-2.7L5.737 5.1a3.375 3.375 0 012.7-1.35h7.126c1.062 0 2.062.5 2.7 1.35l2.587 3.45a4.5 4.5 0 01.9 2.7m0 0a3 3 0 01-3 3m0 3h.008v.008h-.008v-.008zm0-6h.008v.008h-.008v-.008zm-3 6h.008v.008h-.008v-.008zm0-6h.008v.008h-.008v-.008z"
/>
</svg>
Self-hosted broker
</button>
</div>
<div class="row mt-5 mb-12">
<button
on:click|once={displayNGbox}
class="choice-button text-primary-700 bg-primary-100 hover:bg-primary-100/90 focus:ring-4 focus:outline-none focus:ring-primary-100/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-100/55 mb-2"
>
<svg
xmlns="http://www.w3.org/2000/svg"
version="1.1"
viewBox="0 0 225 225"
class="mr-4 block h-10 w-10"
stroke="currentColor"
stroke-width="12"
fill="none"
>
<path
d="M 88.332599,179.77884 C 72.858008,177.42608 59.581081,170.564 48.8817,159.38898 36.800075,146.77026 30.396139,130.74266 30.396139,113.12381 c 0,-8.81477 1.466462,-16.772273 4.503812,-24.439156 3.697755,-9.333883 8.658122,-16.726264 15.988284,-23.827148 4.07992,-3.952299 5.699054,-5.267377 9.730928,-7.903581 10.263753,-6.710853 20.852276,-10.247623 32.861256,-10.976317 17.083161,-1.036581 33.737521,4.410501 47.100151,15.404873 1.30009,1.069669 2.35446,2.035155 2.34305,2.145524 -0.0114,0.110369 -3.32807,3.135042 -7.37038,6.721489 -4.04229,3.586437 -8.6667,7.731233 -10.27646,9.210635 -1.60975,1.479412 -3.05439,2.689839 -3.21032,2.689839 -0.15591,0 -1.2075,-0.642795 -2.33686,-1.428431 -6.49544,-4.518567 -13.79659,-6.747116 -22.104843,-6.747116 -10.982241,0 -20.054641,3.741852 -27.727158,11.435891 -5.517107,5.532575 -9.233107,12.555305 -10.782595,20.377588 -0.596045,3.00901 -0.594915,11.67153 0.0017,14.67182 3.195984,16.0665 15.801761,28.55358 31.607491,31.30987 3.592183,0.62643 10.334745,0.61437 13.792675,-0.0247 12.10383,-2.2368 22.30712,-9.80603 27.83192,-20.64689 0.66747,-1.30971 1.08703,-2.48825 0.93235,-2.61898 -0.1547,-0.13073 -5.9299,-1.01605 -12.83381,-1.96739 -8.43575,-1.16241 -12.87296,-1.9096 -13.52955,-2.27826 -1.31171,-0.73647 -2.44642,-2.49122 -2.44642,-3.78325 0,-1.012 1.74837,-13.68832 2.1486,-15.57814 0.25598,-1.20873 2.0923,-3.01339 3.3151,-3.25795 0.53677,-0.10735 7.61424,0.73799 15.7688,1.88346 8.13723,1.14303 14.89071,1.97925 15.00772,1.85826 0.11702,-0.12098 0.96445,-5.648553 1.88315,-12.283473 0.95557,-6.900944 1.90122,-12.59548 2.20977,-13.306594 0.29667,-0.683692 0.95765,-1.595052 1.46889,-2.025218 1.77972,-1.497534 2.7114,-1.539742 10.52745,-0.476938 8.31229,1.130266 9.2373,1.347581 10.59333,2.488613 1.41776,1.192951 1.96085,2.424677 1.94866,4.419342 -0.006,0.950347 -0.79507,7.156475 -1.75393,13.791395 -0.95885,6.634933 -1.70069,12.111623 -1.64854,12.170443 0.0522,0.0588 6.18174,0.95872 13.62132,1.99978 9.57969,1.34053 13.80866,2.0595 14.49353,2.46406 1.3199,0.77969 2.13943,2.28402 2.1135,3.87957 -0.0399,2.45278 -2.08103,15.63263 -2.5664,16.57122 -0.57073,1.10369 -2.24485,2.197 -3.38232,2.20889 -0.44831,0.004 -6.79249,-0.82755 -14.09817,-1.84941 -7.3057,-1.02186 -13.34942,-1.79161 -13.43049,-1.71053 -0.0811,0.0811 -1.02469,6.33285 -2.09694,13.89286 -1.24218,8.75802 -2.1547,14.1778 -2.51495,14.93697 -0.62565,1.31846 -2.38302,2.64205 -3.91461,2.94836 -0.8254,0.16509 -9.4024,-0.80047 -11.73007,-1.32049 -0.47193,-0.10544 -1.63157,0.58011 -3.8898,2.29957 -9.71515,7.39729 -20.99725,11.99799 -33.08692,13.49241 -3.79574,0.46921 -13.565667,0.37348 -17.125664,-0.16779 z"
/>
<rect
ry="37.596001"
y="10.583322"
x="14.363095"
height="204.86308"
width="195.79167"
/>
</svg>
NG Box (owned or invited)
</button>
</div>
{:else} {:else}
<div class=" max-w-6xl lg:px-8 mx-auto px-4 text-green-800"> <div class=" max-w-6xl lg:px-8 mx-auto px-4 text-green-800">
Your wallet is ready! Your wallet is ready!

@ -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")] #[cfg(target_arch = "wasm32")]
#[wasm_bindgen] #[wasm_bindgen]
pub async fn get_local_bootstrap_with_public(location: String, invite: JsValue) -> JsValue { 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) let mut params = serde_wasm_bindgen::from_value::<CreateWalletV0>(js_params)
.map_err(|_| "Deserialization error of args")?; .map_err(|_| "Deserialization error of args")?;
params.result_with_wallet_file = true; params.result_with_wallet_file = true;
let local_save = params.local_save;
let res = create_wallet_v0(params).await; let res = create_wallet_v0(params).await;
match res { match res {
Ok(r) => { Ok(r) => {
let session = save_wallet_locally(&r)?; if local_save {
Ok(serde_wasm_bindgen::to_value(&(r, session)).unwrap()) 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()), Err(e) => Err(e.to_string().into()),
} }
@ -329,6 +345,9 @@ pub fn test_create_wallet() -> JsValue {
9, 9,
false, false,
false, false,
BootstrapContentV0::new(),
None,
None,
); );
serde_wasm_bindgen::to_value(&r).unwrap() 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) /// 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 /// and returns the Wallet, the pazzle and the mnemonic
pub async fn create_wallet_v0( pub async fn create_wallet_v0(
params: CreateWalletV0, mut params: CreateWalletV0,
) -> Result<CreateWalletResultV0, NgWalletError> { ) -> Result<CreateWalletResultV0, NgWalletError> {
// TODO : use some automatically zeroed variable for the 2 first arguments, and for the returned values
let creating_pazzle = Instant::now(); let creating_pazzle = Instant::now();
// pazzle_length can only be 9, 12, or 15 // 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 //Creating a new peerId for this Client and User
let peer = generate_keypair(); 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]; let mut master_key = [0u8; 32];
getrandom::getrandom(&mut master_key).map_err(|e| NgWalletError::InternalError)?; getrandom::getrandom(&mut master_key).map_err(|e| NgWalletError::InternalError)?;
@ -632,6 +656,9 @@ mod test {
9, 9,
false, false,
false, false,
BootstrapContentV0::new(),
None,
None,
)) ))
.await .await
.expect("create_wallet_v0"); .expect("create_wallet_v0");

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

@ -26,4 +26,5 @@ serde-big-array = "0.5.1"
base64-url = "2.0.0" base64-url = "2.0.0"
serde_json = "1.0.96" serde_json = "1.0.96"
bytes = "1.0" bytes = "1.0"
anyhow = "1.0.71" anyhow = "1.0.71"
duration-str = "0.5.1"

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

@ -95,7 +95,7 @@ async fn main() -> Result<(), ProtocolError> {
.arg(arg!( .arg(arg!(
-v --verbose ... "Increase the logging output. once : info, twice : debug, 3 times : trace" -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) .required(false)
.value_parser(value_parser!(PathBuf)) .value_parser(value_parser!(PathBuf))
.default_value(".ng")) .default_value(".ng"))
@ -137,12 +137,12 @@ async fn main() -> Result<(), ProtocolError> {
.subcommand( .subcommand(
Command::new("add-user") Command::new("add-user")
.about("add a user to the server, so it can connect to it") .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))) .arg(arg!(-a --admin "make this user admin as well").required(false)))
.subcommand( .subcommand(
Command::new("del-user") Command::new("del-user")
.about("removes a user from the broker") .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( .subcommand(
Command::new("list-users") Command::new("list-users")
.about("list all users registered in the broker") .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!(-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!(-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!(-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( .subcommand(
Command::new("list-invitations") Command::new("list-invitations")
.about("list all invitations") .about("list all invitations")
@ -521,6 +522,7 @@ async fn main() -> Result<(), ProtocolError> {
invite_code, invite_code,
expiry, expiry,
memo: sub2_matches.get_one::<String>("memo").map(|s| s.clone()), memo: sub2_matches.get_one::<String>("memo").map(|s| s.clone()),
tos_url: !sub2_matches.get_flag("notos"),
}), }),
) )
.await; .await;

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

@ -145,7 +145,7 @@ pub struct SiteV0 {
// Identity::OrgPrivate or Identity::IndividualPrivate // Identity::OrgPrivate or Identity::IndividualPrivate
pub private: SiteStore, pub private: SiteStore,
pub cores: Vec<PubKey>, pub cores: Vec<(PubKey, Option<[u8; 32]>)>,
pub bootstraps: Vec<PubKey>, pub bootstraps: Vec<PubKey>,
} }
@ -529,6 +529,25 @@ pub struct BootstrapContentV0 {
pub servers: Vec<BrokerServerV0>, 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)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub enum BootstrapContent { pub enum BootstrapContent {
V0(BootstrapContentV0), 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 /// first URL in the list is the ngone one
pub fn get_urls(&self) -> Vec<String> { pub fn get_urls(&self) -> Vec<String> {
match self { match self {
@ -773,37 +800,36 @@ impl CreateAccountBSP {
} }
Some(base64_url::encode(&payload_ser.unwrap())) Some(base64_url::encode(&payload_ser.unwrap()))
} }
pub fn user(&self) -> PubKey { // pub fn user(&self) -> PubKey {
match self { // match self {
Self::V0(v0) => v0.user, // Self::V0(v0) => v0.user,
} // }
} // }
pub fn redirect_url(&self) -> &Option<String> { pub fn redirect_url(&self) -> &Option<String> {
match self { match self {
Self::V0(v0) => &v0.redirect_url, Self::V0(v0) => &v0.redirect_url,
} }
} }
pub fn invitation(&self) -> &Option<InvitationV0> { // pub fn invitation(&self) -> &Option<InvitationV0> {
match self { // match self {
Self::V0(v0) => &v0.invitation, // Self::V0(v0) => &v0.invitation,
} // }
} // }
pub fn additional_bootstrap(&mut self) -> &mut Option<BootstrapContentV0> { // pub fn additional_bootstrap(&mut self) -> &mut Option<BootstrapContentV0> {
match self { // match self {
Self::V0(v0) => &mut v0.additional_bootstrap, // Self::V0(v0) => &mut v0.additional_bootstrap,
} // }
} // }
} }
/// Create an account at a Broker Service Provider (BSP). Version 0 /// Create an account at a Broker Service Provider (BSP). Version 0
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct CreateAccountBSPV0 { pub struct CreateAccountBSPV0 {
pub invitation: Option<InvitationV0>, //pub invitation: Option<InvitationV0>,
pub additional_bootstrap: Option<BootstrapContentV0>,
//pub additional_bootstrap: Option<BootstrapContentV0>,
/// the user asking to create an account /// the user asking to create an account
pub user: PubKey, //pub user: PubKey,
/// signature over serialized invitation code, with user key /// signature over serialized invitation code, with user key
// pub sig: Sig, // pub sig: Sig,

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

Loading…
Cancel
Save