Niko PLP 1 year ago
parent 2e4f44a838
commit 58f21626a3
  1. 55
      Cargo.lock
  2. 112
      ng-app/src/App.svelte
  3. 4
      ng-app/src/api.ts
  4. 19
      ng-app/src/lib/Home.svelte
  5. 421
      ng-app/src/lib/Login.svelte
  6. 287
      ng-app/src/routes/Grid.svelte
  7. 45
      ng-app/src/routes/Home.svelte
  8. 13
      ng-app/src/routes/Install.svelte
  9. 125
      ng-app/src/routes/UserRegistered.svelte
  10. 715
      ng-app/src/routes/WalletCreate.svelte
  11. 270
      ng-app/src/routes/WalletLogin.svelte
  12. 116
      ng-app/src/store.ts
  13. 2
      ng-app/src/styles.css
  14. 3
      ng-sdk-js/Cargo.toml
  15. 44
      ng-sdk-js/js/browser.js
  16. 18
      ng-sdk-js/js/node.js
  17. 239
      ng-sdk-js/src/lib.rs
  18. 62
      ng-wallet/src/types.rs
  19. 1
      ngaccount/Cargo.toml
  20. 163
      ngaccount/src/main.rs
  21. 3
      ngaccount/web/package.json
  22. 7
      ngaccount/web/pnpm-lock.yaml
  23. 81
      ngaccount/web/src/routes/Create.svelte
  24. 4
      ngaccount/web/src/routes/Delete.svelte
  25. 2
      ngaccount/web/src/routes/Home.svelte
  26. 30
      ngcli/src/main.rs
  27. 2
      ngone/web/src/routes/Home.svelte
  28. 12
      ngone/web/src/routes/WalletCreate.svelte
  29. 39
      p2p-broker/src/broker_store/invitation.rs
  30. 17
      p2p-broker/src/storage.rs
  31. 1
      p2p-client-ws/src/remote_ws.rs
  32. 19
      p2p-net/src/actors/del_user.rs
  33. 82
      p2p-net/src/broker.rs
  34. 3
      p2p-net/src/broker_storage.rs
  35. 2
      p2p-net/src/connection.rs
  36. 2
      p2p-net/src/errors.rs
  37. 77
      p2p-net/src/types.rs
  38. 84
      p2p-net/src/utils.rs
  39. 1
      p2p-repo/src/utils.rs
  40. 14
      stores-lmdb/src/kcv_store.rs

55
Cargo.lock generated

@ -997,6 +997,22 @@ dependencies = [
"typenum",
]
[[package]]
name = "crypto_box"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd26c32de5307fd08aac445a75c43472b14559d5dccdfba8022dbcd075838ebc"
dependencies = [
"aead",
"blake2",
"chacha20",
"chacha20poly1305",
"salsa20",
"x25519-dalek 1.1.1",
"xsalsa20poly1305",
"zeroize",
]
[[package]]
name = "cssparser"
version = "0.27.2"
@ -2768,6 +2784,8 @@ name = "ng-sdk-js"
version = "0.1.0"
dependencies = [
"async-std",
"base64-url",
"crypto_box",
"futures",
"getrandom 0.1.16",
"gloo-timers",
@ -2777,6 +2795,7 @@ dependencies = [
"p2p-net",
"p2p-repo",
"pharos",
"rand 0.7.3",
"serde",
"serde-wasm-bindgen",
"serde_bare",
@ -2822,6 +2841,7 @@ dependencies = [
"env_logger",
"log",
"ng-wallet",
"p2p-client-ws",
"p2p-net",
"p2p-repo",
"rust-embed",
@ -2937,7 +2957,7 @@ dependencies = [
"chacha20poly1305",
"noise-protocol",
"sha2 0.10.7",
"x25519-dalek",
"x25519-dalek 2.0.0-rc.2",
"zeroize",
]
@ -4019,6 +4039,15 @@ version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072"
[[package]]
name = "salsa20"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213"
dependencies = [
"cipher",
]
[[package]]
name = "same-file"
version = "1.0.6"
@ -5982,6 +6011,17 @@ dependencies = [
"pkg-config",
]
[[package]]
name = "x25519-dalek"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a0c105152107e3b96f6a00a65e86ce82d9b125230e1c4302940eca58ff71f4f"
dependencies = [
"curve25519-dalek 3.2.0",
"rand_core 0.5.1",
"zeroize",
]
[[package]]
name = "x25519-dalek"
version = "2.0.0-rc.2"
@ -6000,6 +6040,19 @@ version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47f9da296a88b6bc150b896d17770a62d4dc6f63ecf0ed10a9c08a1cb3d12f24"
[[package]]
name = "xsalsa20poly1305"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02a6dad357567f81cd78ee75f7c61f1b30bb2fe4390be8fb7c69e2ac8dffb6c7"
dependencies = [
"aead",
"poly1305",
"salsa20",
"subtle",
"zeroize",
]
[[package]]
name = "xxhash-rust"
version = "0.8.6"

@ -12,31 +12,131 @@
<script lang="ts">
// this line is needed to have the SDK working when compiling for a single file bundle (pnpm filebuild)
// import * as api from "ng-sdk-js";
import Router from "svelte-spa-router";
import { onMount, tick } from "svelte";
import { push, default as Router } from "svelte-spa-router";
import { onMount, tick, onDestroy } from "svelte";
import {
wallets,
active_wallet,
opened_wallets,
active_session,
} from "./store";
import Home from "./routes/Home.svelte";
import Test from "./routes/Test.svelte";
import Grid from "./routes/Grid.svelte";
import URI from "./routes/URI.svelte";
import NotFound from "./routes/NotFound.svelte";
import WalletCreate from "./routes/WalletCreate.svelte";
import WalletLogin from "./routes/WalletLogin.svelte";
import UserRegistered from "./routes/UserRegistered.svelte";
import Install from "./routes/Install.svelte";
import ng from "./api";
ng.test();
const routes = new Map();
routes.set("/", Home);
routes.set("/test", Test);
routes.set("/grid", Grid);
routes.set("/wallet/login", WalletLogin);
routes.set("/wallet/create", WalletCreate);
routes.set("/user/registered", UserRegistered);
if (import.meta.env.NG_APP_WEB) routes.set("/install", Install);
routes.set(/^\/ng(.*)/i, URI);
routes.set("*", NotFound);
let unsubscribe = () => {};
let wallet_channel;
onMount(async () => {
let tauri_platform = import.meta.env.TAURI_PLATFORM;
if (!tauri_platform) {
window.addEventListener("storage", async (event) => {
if (event.storageArea != localStorage) return;
if (event.key === "ng_wallets") {
wallets.set(
Object.fromEntries(await ng.get_wallets_from_localstorage())
);
}
});
wallets.set(
Object.fromEntries((await ng.get_wallets_from_localstorage()) || [])
);
wallet_channel = new BroadcastChannel("ng_wallet");
wallet_channel.postMessage({ cmd: "is_opened" }, location.href);
wallet_channel.onmessage = (event) => {
console.log(event);
if (!location.href.startsWith(event.origin)) return;
console.log("ng_wallet", event.data);
switch (event.data.cmd) {
case "is_opened":
console.log($active_wallet);
if ($active_wallet && $active_wallet.wallet) {
wallet_channel.postMessage(
{ cmd: "opened", wallet: $active_wallet },
location.href
);
}
for (let opened of Object.keys($opened_wallets)) {
wallet_channel.postMessage(
{
cmd: "opened",
wallet: { wallet: $opened_wallets[opened], id: opened },
},
location.href
);
}
break;
case "opened":
if (!$opened_wallets[event.data.wallet.id]) {
opened_wallets.update((w) => {
w[event.data.wallet.id] = event.data.wallet.wallet;
return w;
});
}
break;
case "closed":
opened_wallets.update((w) => {
delete w[event.data.walletid];
return w;
});
if ($active_wallet && $active_wallet.id == event.data.walletid) {
active_session.set(undefined);
active_wallet.set(undefined);
}
break;
}
};
unsubscribe = active_wallet.subscribe((value) => {
if (value) {
if (value.wallet) {
wallet_channel.postMessage(
{ cmd: "opened", wallet: value },
location.href
);
} else {
wallet_channel.postMessage(
{ cmd: "closed", walletid: value.id },
location.href
);
active_wallet.set(undefined);
active_session.set(undefined);
}
} else {
//push("#/wallet/login");
}
});
}
});
onDestroy(unsubscribe);
</script>
<main class="">
<!-- <p>
{JSON.stringify(Object.keys($wallets))}
{JSON.stringify($active_wallet)}
{JSON.stringify(Object.keys($opened_wallets))}
{JSON.stringify($active_session)}
</p> -->
<Router {routes} />
</main>

@ -96,6 +96,8 @@ const handler = {
return await tauri.invoke(path[0],{params})
} else if (path[0].starts_with("get_local_bootstrap")) {
return false;
} else if (path[0].starts_with("get_local_url")) {
return false;
}
else {
let arg = {};
@ -112,6 +114,8 @@ export const NG_EU_BSP = "https://nextgraph.eu";
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 NG_NET_BSP = "https://nextgraph.net";
export const NG_NET_BSP_REGISTER = "https://account.nextgraph.net/#/create";
export const NG_NET_BSP_REGISTERED = "https://nextgraph.net/#/user/registered";

@ -12,20 +12,21 @@
<script>
import { Button } from "flowbite-svelte";
import { link } from "svelte-spa-router";
// @ts-ignore
import Logo from "../assets/nextgraph.svg?component";
import { has_wallets } from "../store";
import { onMount } from "svelte";
export let display_login_create = false;
</script>
<main class="container3">
<div class="row">
<Logo class="logo block h-40" alt="NextGraph Logo" />
</div>
<h1 class="text-2xl mb-10">Welcome to NextGraph</h1>
{#if !$has_wallets || display_login_create}
<main class="container3">
<div class="row">
<Logo class="logo block h-40" alt="NextGraph Logo" />
</div>
<h1 class="text-2xl mb-10">Welcome to NextGraph</h1>
{#if display_login_create}
<p class="max-w-sm">
We could not find a wallet saved on this device.<br /> If you already have
a wallet, select "Log in", otherwise, select "Create Wallet" here below
@ -80,5 +81,5 @@
</button>
</a>
</div>
{/if}
</main>
</main>
{/if}

@ -0,0 +1,421 @@
<!--
// Copyright (c) 2022-2023 Niko Bonnieure, Par le Peuple, NextGraph.org developers
// All rights reserved.
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0>
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.
-->
<script lang="ts">
import { Alert } from "flowbite-svelte";
import { onMount, createEventDispatcher, tick } from "svelte";
import ng from "../api";
import { emoji_cat, emojis, load_svg } from "../wallet_emojis";
export let wallet;
const dispatch = createEventDispatcher();
onMount(async () => {
await load_svg();
await init();
//console.log(JSON.stringify(await ng.test_create_wallet()));
//console.log(await ng.test_create_wallet());
// let ref = {
// id: {
// Blake3Digest32: [
// 228, 228, 181, 117, 36, 206, 41, 223, 130, 96, 85, 195, 104, 137, 78,
// 145, 42, 176, 58, 244, 111, 97, 246, 39, 11, 76, 135, 150, 188, 111,
// 66, 33,
// ],
// },
// key: {
// ChaCha20Key: [
// 100, 243, 39, 242, 203, 131, 102, 50, 9, 54, 248, 113, 4, 160, 28, 45,
// 73, 56, 217, 112, 95, 150, 144, 137, 9, 57, 106, 5, 39, 202, 146, 94,
// ],
// },
// };
// let img = await ng.doc_get_file_from_store_with_object_ref("ng:", ref);
// let c = {
// security_img: img["File"].V0.content,
// security_txt: " know yourself ",
// pin: [5, 2, 9, 1],
// pazzle_length: 9,
// send_bootstrap: false,
// send_wallet: false,
// result_with_wallet_file: true,
// local_save: false,
// };
// try {
// let res = await ng.wallet_create_wallet(c);
// console.log(res);
// wallet = res.wallet;
// for (const emoji of res.pazzle) {
// let cat = (emoji & 240) >> 4;
// let idx = emoji & 15;
// console.log(emoji_cat[cat], emojis[emoji_cat[cat]][idx].code);
// }
// } catch (e) {
// console.error(e);
// }
//await start_pin();
});
async function init() {
shuffle = await ng.wallet_gen_shuffle_for_pazzle_opening(pazzle_length);
emojis2 = [];
for (const [idx, cat_idx] of shuffle.category_indices.entries()) {
let cat = emojis[emoji_cat[cat_idx]];
let items = [];
for (const id of shuffle.emoji_indices[idx]) {
items.push(cat[id]);
}
emojis2.push(items);
}
emojis2 = emojis2;
display = 0;
selection = [];
error = undefined;
step = "pazzle";
}
let emojis2 = [];
let shuffle;
let step = "load";
let pazzle_length = 9;
let display = 0;
let selection = [];
let pin_code = [];
let ordered = [];
let last_one = {};
let shuffle_pin;
let error;
function order() {
step = "order";
ordered = [];
last_one = {};
for (let i = 0; i < pazzle_length; i++) {
last_one[i] = true;
}
}
async function start_pin() {
pin_code = [];
console.log(ordered);
shuffle_pin = await ng.wallet_gen_shuffle_for_pin();
step = "pin";
console.log(shuffle_pin);
}
function select(val) {
//console.log(emojis2[display][val]);
let cat_idx = shuffle.category_indices[display];
let cat = emojis[emoji_cat[cat_idx]];
let idx = shuffle.emoji_indices[display][val];
console.log(cat_idx, emoji_cat[cat_idx], idx, cat[idx].code);
selection.push({ cat: cat_idx, index: idx });
console.log(selection);
if (display == pazzle_length - 1) {
order();
} else {
display = display + 1;
}
}
async function finish() {
step = "opening";
let pazzle = [];
for (const emoji of ordered) {
pazzle.push((emoji.cat << 4) + emoji.index);
}
console.log(pazzle);
// open the wallet
try {
let secret_wallet = await ng.wallet_open_wallet_with_pazzle(
wallet,
pazzle,
pin_code
);
step = "end";
console.log(secret_wallet);
dispatch("opened", {
wallet: secret_wallet,
id: secret_wallet.V0.wallet_id,
});
} catch (e) {
console.error(e);
error = e;
step = "end";
dispatch("error", { error: e });
}
// display the result
}
function cancel() {
dispatch("cancel");
}
async function pin(val) {
console.log(val);
pin_code.push(val);
if (pin_code.length == 4) {
await finish();
}
}
async function select_order(val, pos) {
delete last_one[pos];
console.log(last_one);
console.log(val);
ordered.push(val);
val.sel = ordered.length;
selection = selection;
if (ordered.length == pazzle_length - 1) {
let last = selection[Object.keys(last_one)[0]];
ordered.push(last);
last.sel = ordered.length;
selection = selection;
console.log(last);
await start_pin();
}
}
</script>
{#if step == "load"}
<div class=" max-w-6xl lg:px-8 mx-auto px-4 text-primary-700">
Loading...
<svg
class="animate-spin mt-10 h-14 w-14 mx-auto"
xmlns="http://www.w3.org/2000/svg"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<circle
class="opacity-25"
cx="12"
cy="12"
r="10"
stroke="currentColor"
stroke-width="4"
/>
<path
class="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
/>
</svg>
</div>
{:else if step == "pazzle"}
<div class="h-screen aspect-[3/5] pazzleline max-w-[500px] min-w-[200px]">
{#each [0, 1, 2, 3, 4] as row}
<div class="columns-3 gap-0">
{#each emojis2[display]?.slice(0 + row * 3, 3 + row * 3) || [] as emoji, i}
<div
role="button"
tabindex="0"
class="w-full aspect-square emoji"
on:click={() => select(row * 3 + i)}
on:keypress={() => select(row * 3 + i)}
>
<svelte:component this={emoji.svg?.default} />
</div>
{/each}
</div>
{/each}
</div>
{:else if step == "order"}
<!-- console.log(cat_idx, emoji_cat[cat_idx], idx, cat[idx].code); -->
<div class="h-screen aspect-[3/3] pazzleline max-w-[500px] min-w-[200px]">
{#each [0, 1, 2] as row}
<div class="columns-3 gap-0">
{#each selection.slice(0 + row * 3, 3 + row * 3) || [] as emoji, i}
{#if !emoji.sel}
<div
role="button"
tabindex="0"
class="w-full aspect-square emoji"
on:click={() => select_order(emoji, row * 3 + i)}
on:keypress={() => select_order(emoji, row * 3 + i)}
>
<svelte:component
this={emojis[emoji_cat[emoji.cat]][emoji.index].svg?.default}
/>
</div>
{:else}
<div class="w-full aspect-square opacity-25 select-none sel-emoji">
<svelte:component
this={emojis[emoji_cat[emoji.cat]][emoji.index].svg?.default}
/>
<span class="sel drop-shadow-[2px_2px_2px_rgba(255,255,255,1)]"
>{emoji.sel}</span
>
</div>
{/if}
{/each}
</div>
{/each}
</div>
{:else if step == "pin"}
<div class=" max-w-6xl lg:px-8 mx-auto px-4">
<p class="max-w-xl md:mx-auto lg:max-w-2xl">
<span class="text-xl">Enter your PIN code</span>
</p>
<div class="w-[325px] mx-auto">
{#each [0, 1, 2] as row}
<div class="">
{#each shuffle_pin.slice(0 + row * 3, 3 + row * 3) as num}
<button
tabindex="0"
class="m-1 select-none align-bottom text-7xl w-[100px] h-[100px] p-0"
on:click={async () => await pin(num)}
>
<span>{num}</span>
</button>
{/each}
</div>
{/each}
<button
tabindex="0"
class="m-1 select-none mx-auto align-bottom text-7xl w-[100px] h-[100px] p-0"
on:click={async () => await pin(shuffle_pin[9])}
>
<span>{shuffle_pin[9]}</span>
</button>
</div>
</div>
{:else if step == "opening"}
<div class=" max-w-6xl lg:px-8 mx-auto px-4 text-primary-700">
Opening your wallet...<br />
Please wait
<svg
class="animate-spin mt-10 h-14 w-14 mx-auto"
xmlns="http://www.w3.org/2000/svg"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<circle
class="opacity-25"
cx="12"
cy="12"
r="10"
stroke="currentColor"
stroke-width="4"
/>
<path
class="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
/>
</svg>
</div>
{:else if step == "end"}
{#if error}
<div class=" max-w-6xl lg:px-8 mx-auto px-4 text-red-800">
An error occurred !
<svg
fill="none"
class="animate-bounce mt-10 h-10 w-10 mx-auto"
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>
<Alert color="red" class="mt-5">
{error}
</Alert>
<button class="mt-10 select-none" on:click={init}> Try again </button>
<button class="mt-10 ml-5 select-none" on:click={cancel}> Cancel </button>
</div>
{:else}
<div class=" max-w-6xl lg:px-8 mx-auto px-4 text-green-800">
Your wallet is opened!
<svg
class="my-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="M9 12.75L11.25 15 15 9.75M21 12c0 1.268-.63 2.39-1.593 3.068a3.745 3.745 0 01-1.043 3.296 3.745 3.745 0 01-3.296 1.043A3.745 3.745 0 0112 21c-1.268 0-2.39-.63-3.068-1.593a3.746 3.746 0 01-3.296-1.043 3.745 3.745 0 01-1.043-3.296A3.745 3.745 0 013 12c0-1.268.63-2.39 1.593-3.068a3.745 3.745 0 011.043-3.296 3.746 3.746 0 013.296-1.043A3.746 3.746 0 0112 3c1.268 0 2.39.63 3.068 1.593a3.746 3.746 0 013.296 1.043 3.746 3.746 0 011.043 3.296A3.745 3.745 0 0121 12z"
/>
</svg>
</div>
{/if}
{/if}
<style>
.pazzleline {
margin-right: auto;
margin-left: auto;
}
.pin {
cursor: pointer;
text-align: center;
}
.sel {
position: relative;
top: -56%;
font-size: 100px;
font-weight: 700;
}
.sel-emoji {
overflow: hidden;
}
.emoji {
cursor: pointer;
/* padding: 0;
margin: 0;
border: 0;
box-shadow: none; */
}
</style>

@ -1,287 +0,0 @@
<!--
// Copyright (c) 2022-2023 Niko Bonnieure, Par le Peuple, NextGraph.org developers
// All rights reserved.
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0>
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.
-->
<script lang="ts">
import { onMount } from "svelte";
import ng from "../api";
import { emoji_cat, emojis, load_svg } from "../wallet_emojis";
onMount(async () => {
await load_svg();
shuffle = await ng.wallet_gen_shuffle_for_pazzle_opening(pazzle_length);
for (const [idx, cat_idx] of shuffle.category_indices.entries()) {
let cat = emojis[emoji_cat[cat_idx]];
let items = [];
for (const id of shuffle.emoji_indices[idx]) {
items.push(cat[id]);
}
emojis2.push(items);
}
emojis2 = emojis2;
//console.log(JSON.stringify(await ng.test_create_wallet()));
//console.log(await ng.test_create_wallet());
let ref = {
id: {
Blake3Digest32: [
228, 228, 181, 117, 36, 206, 41, 223, 130, 96, 85, 195, 104, 137, 78,
145, 42, 176, 58, 244, 111, 97, 246, 39, 11, 76, 135, 150, 188, 111,
66, 33,
],
},
key: {
ChaCha20Key: [
100, 243, 39, 242, 203, 131, 102, 50, 9, 54, 248, 113, 4, 160, 28, 45,
73, 56, 217, 112, 95, 150, 144, 137, 9, 57, 106, 5, 39, 202, 146, 94,
],
},
};
let img = await ng.doc_get_file_from_store_with_object_ref("ng:", ref);
let c = {
security_img: img["File"].V0.content,
security_txt: " know yourself ",
pin: [5, 2, 9, 1],
pazzle_length: 9,
send_bootstrap: false,
send_wallet: false,
result_with_wallet_file: true,
local_save: false,
};
try {
let res = await ng.wallet_create_wallet(c);
console.log(res);
wallet = res.wallet;
for (const emoji of res.pazzle) {
let cat = (emoji & 240) >> 4;
let idx = emoji & 15;
console.log(emoji_cat[cat], emojis[emoji_cat[cat]][idx].code);
}
} catch (e) {
console.error(e);
}
//await start_pin();
});
let wallet;
let emojis2 = [];
let shuffle;
let step = "pazzle";
let pazzle_length = 9;
let display = 0;
let selection = [];
let pin_code = [];
let ordered = [];
let last_one = {};
let shuffle_pin;
function order() {
step = "order";
last_one = {};
for (let i = 0; i < pazzle_length; i++) {
last_one[i] = true;
}
}
async function start_pin() {
console.log(ordered);
shuffle_pin = await ng.wallet_gen_shuffle_for_pin();
step = "pin";
console.log(shuffle_pin);
}
function select(val) {
//console.log(emojis2[display][val]);
let cat_idx = shuffle.category_indices[display];
let cat = emojis[emoji_cat[cat_idx]];
let idx = shuffle.emoji_indices[display][val];
console.log(cat_idx, emoji_cat[cat_idx], idx, cat[idx].code);
selection.push({ cat: cat_idx, index: idx });
console.log(selection);
if (display == pazzle_length - 1) {
order();
} else {
display = display + 1;
}
}
async function finish() {
step = "end";
let pazzle = [];
for (const emoji of ordered) {
pazzle.push((emoji.cat << 4) + emoji.index);
}
console.log(pazzle);
// open the wallet
try {
let secret_wallet = await ng.wallet_open_wallet_with_pazzle(
wallet,
pazzle,
pin_code
);
console.log(secret_wallet);
} catch (e) {
console.error(e);
}
// display the result
}
async function pin(val) {
console.log(val);
pin_code.push(val);
if (pin_code.length == 4) {
await finish();
}
}
async function select_order(val, pos) {
delete last_one[pos];
console.log(last_one);
console.log(val);
ordered.push(val);
val.sel = ordered.length;
selection = selection;
if (ordered.length == pazzle_length - 1) {
let last = selection[Object.keys(last_one)[0]];
ordered.push(last);
last.sel = ordered.length;
selection = selection;
console.log(last);
await start_pin();
}
}
</script>
{#if step == "pazzle"}
<div class="h-screen aspect-[3/5] pazzleline max-w-[500px] min-w-[200px]">
{#each [0, 1, 2, 3, 4] as row}
<div class="columns-3 gap-0">
{#each emojis2[display]?.slice(0 + row * 3, 3 + row * 3) || [] as emoji, i}
<div
role="button"
tabindex="0"
class="w-full aspect-square emoji"
on:click={() => select(row * 3 + i)}
on:keypress={() => select(row * 3 + i)}
>
<svelte:component this={emoji.svg?.default} />
</div>
{/each}
</div>
{/each}
</div>
{:else if step == "order"}
<!-- console.log(cat_idx, emoji_cat[cat_idx], idx, cat[idx].code); -->
<div class="h-screen aspect-[3/3] pazzleline max-w-[500px] min-w-[200px]">
{#each [0, 1, 2] as row}
<div class="columns-3 gap-0">
{#each selection.slice(0 + row * 3, 3 + row * 3) || [] as emoji, i}
{#if !emoji.sel}
<div
role="button"
tabindex="0"
class="w-full aspect-square emoji"
on:click={() => select_order(emoji, row * 3 + i)}
on:keypress={() => select_order(emoji, row * 3 + i)}
>
<svelte:component
this={emojis[emoji_cat[emoji.cat]][emoji.index].svg?.default}
/>
</div>
{:else}
<div class="w-full aspect-square opacity-25 select-none sel-emoji">
<svelte:component
this={emojis[emoji_cat[emoji.cat]][emoji.index].svg?.default}
/>
<span class="sel drop-shadow-[2px_2px_2px_rgba(255,255,255,1)]"
>{emoji.sel}</span
>
</div>
{/if}
{/each}
</div>
{/each}
</div>
{:else if step == "pin"}
<div class="aspect-[5/2] pazzleline max-w-[800px] min-w-[200px] mt-20">
{#each [0, 1] as row}
<div class="columns-5 gap-0">
{#each shuffle_pin.slice(0 + row * 5, 5 + row * 5) as num, i}
<div
role="button"
tabindex="0"
class="w-full aspect-square pin align-bottom text-9xl"
on:click={async () => await pin(num)}
on:keypress={async () => await pin(num)}
>
<span>{num}</span>
</div>
{/each}
</div>
{/each}
</div>
{:else if step == "end"}{/if}
<style>
.pazzleline {
margin-right: auto;
margin-left: auto;
}
.pin {
cursor: pointer;
text-align: center;
}
.sel {
position: relative;
top: -56%;
font-size: 100px;
left: 30%;
font-weight: 700;
}
.sel-emoji {
overflow: hidden;
}
.emoji {
cursor: pointer;
/* padding: 0;
margin: 0;
border: 0;
box-shadow: none; */
}
</style>

@ -13,25 +13,34 @@
import { Button } from "flowbite-svelte";
import { link } from "svelte-spa-router";
import Home from "../lib/Home.svelte";
import { onMount } from "svelte";
import { push } from "svelte-spa-router";
import { onMount, onDestroy } from "svelte";
import {
wallets,
active_wallet,
opened_wallets,
active_session,
has_wallets,
derived,
} from "../store";
let display_login_create = false;
let unsubscribe;
onMount(() => {
const combined = derived([active_wallet, has_wallets], ([$s1, $s2]) => [
$s1,
$s2,
]);
unsubscribe = combined.subscribe((value) => {
console.log(value);
if (!value[0] && value[1]) {
push("#/wallet/login");
}
});
});
async function bootstrap() {
let bs;
try {
bs = localStorage.getItem("bootstrap");
} catch (e) {}
if (bs) {
} else {
// probe localhost and LAN
// if nothing found, displays login/create account
console.log("no wallet found");
display_login_create = true;
}
}
onMount(() => bootstrap());
onDestroy(() => {
unsubscribe();
});
</script>
<Home {display_login_create} />
<Home />

@ -12,23 +12,16 @@
<script>
import { Button, Alert } from "flowbite-svelte";
import { link } from "svelte-spa-router";
import { has_wallets } from "../store";
// @ts-ignore
import Logo from "../assets/nextgraph.svg?component";
import { onMount } from "svelte";
let top;
let display_note_on_local_wallets = false;
async function bootstrap() {
scrollToTop();
let bs;
try {
bs = localStorage.getItem("bootstrap");
} catch (e) {}
if (bs) {
display_note_on_local_wallets = true;
}
}
function scrollToTop() {
@ -50,7 +43,7 @@
mobile, tablet, laptop and desktop.<br /> The app supports iOS, Android, Linux,
macOS, Windows and all other platforms that can run a modern web browser.
</p>
{#if display_note_on_local_wallets}
{#if $has_wallets}
<Alert color="yellow" class="mt-5 block">
A wallet is saved in this browser. If it is yours,<br /> once the
installation of the app will be finished,<br /> choose the option "Login"

@ -0,0 +1,125 @@
<!--
// Copyright (c) 2022-2023 Niko Bonnieure, Par le Peuple, NextGraph.org developers
// All rights reserved.
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0>
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.
-->
<script>
import { Button, Alert, Dropzone, Toggle } from "flowbite-svelte";
import { link, querystring } from "svelte-spa-router";
// @ts-ignore
import Logo from "../assets/nextgraph.svg?component";
import { onMount, tick } from "svelte";
import {
NG_EU_BSP,
NG_NET_BSP,
NG_EU_BSP_REGISTER,
NG_EU_BSP_REGISTERED,
APP_ACCOUNT_REGISTERED_SUFFIX,
default as ng,
} from "../api";
const param = new URLSearchParams($querystring);
let tauri_platform = import.meta.env.TAURI_PLATFORM;
let mobile = tauri_platform == "android" || tauri_platform == "ios";
let error = param.get("e");
let invite = param.get("i");
let invitation;
let user = param.get("u");
onMount(async () => {
if (invite) {
invitation = await ng.decode_invitation(invite);
}
});
</script>
<main class="container3">
<div class="row">
<a href="#/">
<Logo class="logo block h-40" alt="NextGraph Logo" />
</a>
</div>
{#if 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 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="/">
<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:<br />{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 invite && user}
<div class=" max-w-6xl lg:px-8 mx-auto px-4 text-green-800">
<svg
class="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="M9 12.75L11.25 15 15 9.75M21 12c0 1.268-.63 2.39-1.593 3.068a3.745 3.745 0 01-1.043 3.296 3.745 3.745 0 01-3.296 1.043A3.745 3.745 0 0112 21c-1.268 0-2.39-.63-3.068-1.593a3.746 3.746 0 01-3.296-1.043 3.745 3.745 0 01-1.043-3.296A3.745 3.745 0 013 12c0-1.268.63-2.39 1.593-3.068a3.745 3.745 0 011.043-3.296 3.746 3.746 0 013.296-1.043A3.746 3.746 0 0112 3c1.268 0 2.39.63 3.068 1.593a3.746 3.746 0 013.296 1.043 3.746 3.746 0 011.043 3.296A3.745 3.745 0 0121 12z"
/>
</svg>
<p class="max-w-xl md:mx-auto lg:max-w-2xl">
You have been successfully <br />registered {#if invitation?.V0?.name}
to {invitation?.V0?.name}{/if}
</p>
</div>
{/if}
</main>
<style>
</style>

@ -12,20 +12,24 @@
<script>
import { Button, Alert, Dropzone, Toggle } from "flowbite-svelte";
import { link, querystring } from "svelte-spa-router";
// @ts-ignore
import EULogo from "../assets/EU.svg?component";
// @ts-ignore
import Logo from "../assets/nextgraph.svg?component";
import {
NG_EU_BSP,
NG_NET_BSP,
NG_EU_BSP_REGISTER,
NG_EU_BSP_REGISTERED,
APP_ACCOUNT_REGISTERED_SUFFIX,
default as ng,
} from "../api";
import { display_pazzle } from "../wallet_emojis";
import { onMount, tick } from "svelte";
import { wallets, set_active_session, has_wallets } from "../store";
const params = new URLSearchParams($querystring);
const param = new URLSearchParams($querystring);
let tauri_platform = import.meta.env.TAURI_PLATFORM;
@ -124,29 +128,20 @@
? "api/v1/"
: "http://localhost:3030/api/v1/";
let display_note_on_local_wallets = false;
async function bootstrap() {
console.log(await ng.client_info());
invitation = await ng.get_local_bootstrap_with_public(
location.href,
params.get("i")
param.get("i")
);
console.log(invitation);
// TODO: implement this error screen and link button
if (!invitation && params.get("i")) {
if (!invitation && param.get("i")) {
console.error(
"got an invitation for another broker. click on the link below to be redirected to the right broker"
);
}
scrollToTop();
let bs;
try {
bs = localStorage.getItem("bootstrap");
} catch (e) {}
if (bs) {
display_note_on_local_wallets = true;
}
}
function create_wallet() {
@ -182,14 +177,17 @@
try {
let res = await ng.wallet_create_wallet(params);
console.log(res);
console.log(display_pazzle(res.pazzle));
ready = res;
download_name = "wallet-" + res.wallet_name + ".ngw";
wallets.set(Object.fromEntries(await ng.get_wallets_from_localstorage()));
set_active_session(res[1]);
ready = res[0];
console.log(display_pazzle(ready.pazzle));
download_name = "wallet-" + ready.wallet_name + ".ngw";
if (options.cloud) {
cloud_link = "https://nextgraph.one/#/w/" + res.wallet_name;
}
if (res.wallet_file.length) {
const blob = new Blob([res.wallet_file]);
if (ready.wallet_file.length) {
const blob = new Blob([ready.wallet_file]);
download_link = URL.createObjectURL(blob);
// we also save the wallet to localStorage here, and only if options.trusted is true
@ -201,6 +199,16 @@
}
} catch (e) {
console.log(e);
if (
e == "The operation is insecure." ||
e ==
"Failed to read the 'sessionStorage' property from 'Window': Access is denied for this document." ||
e ==
"Failed to read the 'localStorage' property from 'Window': Access is denied for this document."
) {
e =
"Please allow this website to store cookies, session and local storage.";
}
error = e;
}
}
@ -219,15 +227,16 @@
onMount(async () => await bootstrap());
ready = {
user: {
Ed25519PubKey: [
141, 114, 111, 29, 59, 133, 182, 172, 177, 211, 238, 224, 62, 208, 206,
18, 226, 219, 118, 229, 184, 76, 204, 29, 194, 228, 248, 186, 15, 113,
125, 119,
],
},
};
ready = false;
// {
// user: {
// Ed25519PubKey: [
// 141, 114, 111, 29, 59, 133, 182, 172, 177, 211, 238, 224, 62, 208, 206,
// 18, 226, 219, 118, 229, 184, 76, 204, 29, 194, 228, 248, 186, 15, 113,
// 125, 119,
// ],
// },
// };
const selectEU = async (event) => {
if (!tauri_platform) {
@ -236,12 +245,13 @@
if (local_invitation) {
additional_bootstrap = local_invitation.V0.bootstrap;
}
let local_url = await ng.get_local_url(location.href);
let create = {
V0: {
additional_bootstrap,
invitation: undefined,
user: ready.user,
redirect_url: NG_EU_BSP_REGISTERED,
redirect_url: local_url + APP_ACCOUNT_REGISTERED_SUFFIX,
},
};
let ca = await ng.encode_create_account(create);
@ -253,7 +263,7 @@
additional_bootstrap: undefined,
invitation: undefined,
user: ready.user,
redirect_url: NG_EU_BSP_REGISTERED,
redirect_url: undefined,
},
};
let ca = await ng.encode_create_account(create);
@ -286,7 +296,7 @@
below in order to create your unique personal wallet.
</p>
</div>
{#if display_note_on_local_wallets}
{#if $has_wallets}
<Alert color="yellow" class="mt-5">
Some wallets are saved on this device,<br /> to log in with one of them,
<a href="/wallet/login" use:link>click here.</a>
@ -516,327 +526,6 @@
Ok, I create my wallet now !
</button>
</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"