diff --git a/.gitignore b/.gitignore index 33d7186..f1e389f 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ node_modules */tests/*.mnemonic */ng-example/* .vscode/settings.json +.env.local diff --git a/docker/Dockerfile.alpine b/docker/Dockerfile.alpine new file mode 100644 index 0000000..c77517c --- /dev/null +++ b/docker/Dockerfile.alpine @@ -0,0 +1,36 @@ +# Use rust's latest alpine image as base image. +FROM rust:alpine + +ENV LD_LIBRARY_PATH=/lib:$LD_LIBRARY_PATH + +RUN apk add git nodejs npm llvm-static llvm-dev clang-static clang-dev openssl openssl-dev perl gtk+3.0-dev webkit2gtk-dev librsvg-dev curl wget pkgconf eudev-dev build-base zlib-static bzip2-static build-base ncursers-static && \ + # Install Rust and Node.js tools + cargo install cargo-watch && \ + cargo install wasm-pack --git https://github.com/rustwasm/wasm-pack.git --rev c2b663f25abe50631a236d57a8c6d7fd806413b2 && \ + cargo install tauri-cli --version "2.0.0-alpha.11" --locked && \ + npm install -g pnpm + +# Clone the nextgraph-rs repository +RUN git clone https://git.nextgraph.org/NextGraph/nextgraph-rs.git && \ + cd /nextgraph-rs/ng-sdk-js && \ + wasm-pack build --target bundler && npm install --no-save pkg && + # Build ng-app web version + cd /nextgraph-rs/ng-app && pnpm install && pnpm webfilebuild + +# From here the build fails due to llvm / clang linking issues... +# +# WORKDIR /nextgraph-rs +## Build the nextgraph-rs project and its subprojects +# RUN cd /nextgraph-rs && git pull && cargo update -p ng-rocksdb && \ +# cargo build -r && \ +# cargo build -r -p ngd && \ +# cargo build -r -p ngcli + +# TODO: Build the platform-specific ng-app versions +# cd /nextgraph-rs/ng-app && cargo tauri build --target x86_64-unknown-linux-gnu +# ... + +# TODO: To remove the image size, remove ~/.cargo, ~/.rustup, and the build dependencies + +# To build the image, run: +# docker build -t nextgraph-rs:alpine -f docker/Dockerfile.alpine . diff --git a/docker/Dockerfile.fedora b/docker/Dockerfile.fedora new file mode 100644 index 0000000..c8b65d8 --- /dev/null +++ b/docker/Dockerfile.fedora @@ -0,0 +1,46 @@ +# Use fedora:40 as base image +FROM fedora:40 + +# Set the environment variable to ensure cargo is available in the PATH +ENV PATH="/root/.cargo/bin:${PATH}" +SHELL ["/bin/bash", "-c"] + +# Install the required packages and Rust + +RUN dnf install -y git clang-devel webkit2gtk4.1-devel openssl openssl-devel curl wget file libappindicator-gtk3-devel librsvg2-devel perl && \ + dnf group install -y "C Development Tools and Libraries" && \ + # Rust + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | bash -s -- -y && \ + # Node.js + curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash && \ + export NVM_DIR="$HOME/.nvm" && [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" && [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" && \ + nvm install 22 && \ + npm install -g pnpm && \ + # Clear Cache + rm -rf /var/cache/dnf && \ + # Install Rust and Node.js tools + cargo install cargo-watch && \ + cargo install wasm-pack --git https://github.com/rustwasm/wasm-pack.git --rev c2b663f25abe50631a236d57a8c6d7fd806413b2 && \ + cargo install tauri-cli --version "2.0.0-alpha.11" --locked && \ + # Clone the nextgraph-rs repository (TODO: It might be better to put this into a seperate RUN command to avoid rebuilding the image if the repository changes) + git clone https://git.nextgraph.org/NextGraph/nextgraph-rs.git && \ + # Build sdk and ng-app web version + cd /nextgraph-rs/ng-sdk-js && wasm-pack build --target bundler && npm install --no-save pkg && \ + cd /nextgraph-rs/ng-app && pnpm install && pnpm webfilebuild + +# Build the nextgraph-rs project +RUN export NVM_DIR="$HOME/.nvm" && [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" && [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" && \ + cd /nextgraph-rs && git pull && cargo update -p ng-rocksdb && \ + cargo build -r && \ + cargo build -r -p ngd && \ + cargo build -r -p ngcli + + +# TODO: Build the platform-specific ng-app versions +# cd /nextgraph-rs/ng-app && cargo tauri build --target x86_64-unknown-linux-gnu +# ... + +# TODO: To remove the image size, remove ~/.cargo, ~/.rustup, and the build dependencies + +# To build the image, run: +# docker build -t nextgraph-rs:fedora -f docker/Dockerfile.fedora . diff --git a/docker/Dockerfile.ubuntu b/docker/Dockerfile.ubuntu new file mode 100644 index 0000000..60deb3e --- /dev/null +++ b/docker/Dockerfile.ubuntu @@ -0,0 +1,47 @@ +# Use ubuntu 22.04 as base image +FROM ubuntu:22.04 + +SHELL ["/bin/bash", "-c"] + +# Set the environment variable to ensure cargo is available in the PATH +ENV PATH="/root/.cargo/bin:${PATH}" + +# Install the required packages and Rust +RUN apt update && \ + apt upgrade -y && \ + apt install -y git llvm-dev libclang-dev clang libssl-dev perl libappindicator3-dev libwebkit2gtk-4.0-dev librsvg2-dev curl wget pkg-config libudev-dev build-essential && \ + rm -rf /var/cache/apt && \ + # Rust + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y && \ + # Node.js + curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash && \ + export NVM_DIR="$HOME/.nvm" && [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" && [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" && \ + nvm install 22 && \ + npm install -g pnpm && \ + # Install Rust and Node.js tools + cargo install cargo-watch && \ + cargo install wasm-pack --git https://github.com/rustwasm/wasm-pack.git --rev c2b663f25abe50631a236d57a8c6d7fd806413b2 && \ + cargo install tauri-cli --version "2.0.0-alpha.11" --locked && \ + npm install -g pnpm && \ + # Clone the nextgraph-rs repository (TODO: It might be better to put this into a seperate RUN command to avoid rebuilding the image if the repository changes) + git clone https://git.nextgraph.org/NextGraph/nextgraph-rs.git && \ + # Build sdk and ng-app web version + cd /nextgraph-rs/ng-sdk-js && wasm-pack build --target bundler && npm install --no-save pkg && \ + cd /nextgraph-rs/ng-app && \ + pnpm install && pnpm webfilebuild + +# Build the nextgraph-rs project and its subprojects +WORKDIR /nextgraph-rs +RUN cargo build -r && \ + cargo build -r -p ngd && \ + cargo build -r -p ngcli + + +# TODO: Build the platform-specific ng-app versions +# WORKDIR /nextgraph-rs/ng-app +# RUN cargo tauri build --target x86_64-unknown-linux-gnu + +# TODO: To remove the image size, remove ~/.cargo, ~/.rustup, and the build dependencies + +# To build the image, run: +# docker build -t nextgraph-rs:ubuntu -f docker/Dockerfile.ubuntu . diff --git a/nextgraph/src/local_broker.rs b/nextgraph/src/local_broker.rs index 79725c0..edf7e60 100644 --- a/nextgraph/src/local_broker.rs +++ b/nextgraph/src/local_broker.rs @@ -40,6 +40,7 @@ use ng_verifier::types::*; use ng_verifier::verifier::Verifier; use ng_wallet::emojis::encode_pazzle; +use ng_wallet::bip39::encode_mnemonic; use ng_wallet::{create_wallet_first_step_v0, create_wallet_second_step_v0, types::*}; #[cfg(not(target_family = "wasm"))] @@ -1555,6 +1556,27 @@ pub fn wallet_open_with_pazzle( Ok(opened_wallet) } +#[doc(hidden)] +/// This is a bit hard to use as the mnemonic words are encoded in u16. +/// prefer the function wallet_open_with_mnemonic_words +pub fn wallet_open_with_mnemonic( + wallet: &Wallet, + mnemonic: Vec, + pin: [u8; 4], +) -> Result { + if mnemonic.len() != 12 { + return Err(NgError::InvalidMnemonic); + } + // Convert from vec to array. + let mut mnemonic_arr = [0u16; 12]; + for (place, element) in mnemonic_arr.iter_mut().zip(mnemonic.iter()) { + *place = *element; + } + let opened_wallet = ng_wallet::open_wallet_with_mnemonic(wallet, mnemonic_arr, pin)?; + + Ok(opened_wallet) +} + /// Opens a wallet by providing an ordered list of words, and the pin. /// /// If you are opening a wallet that is already known to the LocalBroker, you must then call [wallet_was_opened]. @@ -1569,6 +1591,21 @@ pub fn wallet_open_with_pazzle_words( wallet_open_with_pazzle(wallet, encode_pazzle(pazzle_words)?, pin) } + +/// Opens a wallet by providing an ordered list of mnemonic words, and the pin. +/// +/// If you are opening a wallet that is already known to the LocalBroker, you must then call [wallet_was_opened]. +/// Otherwise, if you are importing, then you must call [wallet_import]. +pub fn wallet_open_with_mnemonic_words( + wallet: &Wallet, + mnemonic: &Vec, + pin: [u8; 4], +) -> Result { + let encoded: Vec = encode_mnemonic(mnemonic)?; + + wallet_open_with_mnemonic(wallet, encoded, pin) +} + /// Imports a wallet into the LocalBroker so the user can then access its content. /// /// the wallet should have been previous opened with [wallet_open_with_pazzle_words]. diff --git a/ng-app/src-tauri/gen/android/buildSrc/src/main/java/org/nextgraph/ng_app_native/kotlin/BuildTask.kt b/ng-app/src-tauri/gen/android/buildSrc/src/main/java/org/nextgraph/ng_app_native/kotlin/BuildTask.kt index c5a8b39..5c71d63 100644 --- a/ng-app/src-tauri/gen/android/buildSrc/src/main/java/org/nextgraph/ng_app_native/kotlin/BuildTask.kt +++ b/ng-app/src-tauri/gen/android/buildSrc/src/main/java/org/nextgraph/ng_app_native/kotlin/BuildTask.kt @@ -16,7 +16,8 @@ open class BuildTask : DefaultTask() { @TaskAction fun assemble() { - val executable = """/Users/nl/.cargo/bin/cargo-tauri"""; + val homePath = System.getProperty("user.home"); + val executable = "$homePath/.cargo/bin/cargo-tauri"; try { runTauriCli(executable) } catch (e: Exception) { diff --git a/ng-app/src-tauri/src/lib.rs b/ng-app/src-tauri/src/lib.rs index 58c1e1f..e5684af 100644 --- a/ng-app/src-tauri/src/lib.rs +++ b/ng-app/src-tauri/src/lib.rs @@ -108,6 +108,30 @@ async fn wallet_open_with_pazzle( Ok(wallet) } +#[tauri::command(rename_all = "snake_case")] +async fn wallet_open_with_mnemonic( + wallet: Wallet, + mnemonic: Vec, + pin: [u8; 4], + _app: tauri::AppHandle, +) -> Result { + let wallet = nextgraph::local_broker::wallet_open_with_mnemonic(&wallet, mnemonic, pin) + .map_err(|e| e.to_string())?; + Ok(wallet) +} + +#[tauri::command(rename_all = "snake_case")] +async fn wallet_open_with_mnemonic_words( + wallet: Wallet, + mnemonic_words: Vec, + pin: [u8; 4], + _app: tauri::AppHandle, +) -> Result { + let wallet = nextgraph::local_broker::wallet_open_with_mnemonic_words(&wallet, &mnemonic_words, pin) + .map_err(|e| e.to_string())?; + Ok(wallet) +} + #[tauri::command(rename_all = "snake_case")] async fn wallet_get_file(wallet_name: String, app: tauri::AppHandle) -> Result<(), String> { let ser = nextgraph::local_broker::wallet_get_file(&wallet_name) @@ -516,6 +540,8 @@ impl AppBuilder { wallet_gen_shuffle_for_pazzle_opening, wallet_gen_shuffle_for_pin, wallet_open_with_pazzle, + wallet_open_with_mnemonic, + wallet_open_with_mnemonic_words, wallet_was_opened, wallet_create, wallet_read_file, diff --git a/ng-app/src/App.svelte b/ng-app/src/App.svelte index 87570ac..babb772 100644 --- a/ng-app/src/App.svelte +++ b/ng-app/src/App.svelte @@ -19,6 +19,7 @@ active_session, close_active_session, disconnections_subscribe, + select_default_lang, } from "./store"; import Home from "./routes/Home.svelte"; @@ -64,7 +65,9 @@ onMount(async () => { try { await disconnections_subscribe(); + await select_default_lang(); } catch (e) { + console.error(e); //console.log("called disconnections_subscribe twice"); } let tauri_platform = import.meta.env.TAURI_PLATFORM; diff --git a/ng-app/src/api.ts b/ng-app/src/api.ts index 7891d6f..cd12c30 100644 --- a/ng-app/src/api.ts +++ b/ng-app/src/api.ts @@ -16,6 +16,7 @@ const mapping = { "wallet_gen_shuffle_for_pazzle_opening": ["pazzle_length"], "wallet_gen_shuffle_for_pin": [], "wallet_open_with_pazzle": ["wallet","pazzle","pin"], + "wallet_open_with_mnemonic_words": ["wallet","mnemonic_words","pin"], "wallet_was_opened": ["opened_wallet"], "wallet_create": ["params"], "wallet_read_file": ["file"], @@ -182,7 +183,7 @@ const handler = { return false; } else if (path[0] === "get_local_url") { return false; - } else if (path[0] === "wallet_open_with_pazzle") { + } else if (path[0] === "wallet_open_with_pazzle" || path[0] === "wallet_open_with_mnemonic_words") { let arg:any = {}; args.map((el,ix) => arg[mapping[path[0]][ix]]=el) let img = Array.from(new Uint8Array(arg.wallet.V0.content.security_img)); @@ -190,9 +191,8 @@ const handler = { arg.wallet = {V0:{id:arg.wallet.V0.id, sig:arg.wallet.V0.sig, content:{}}}; Object.assign(arg.wallet.V0.content,old_content); arg.wallet.V0.content.security_img = img; - return tauri.invoke(path[0],arg) - } - else { + return tauri.invoke(path[0],arg); + } else { let arg = {}; args.map((el,ix) => arg[mapping[path[0]][ix]]=el) return tauri.invoke(path[0],arg) diff --git a/ng-app/src/assets/nextgraph-gray.svg b/ng-app/src/assets/nextgraph-nofill.svg similarity index 96% rename from ng-app/src/assets/nextgraph-gray.svg rename to ng-app/src/assets/nextgraph-nofill.svg index a403f35..2cf436e 100644 --- a/ng-app/src/assets/nextgraph-gray.svg +++ b/ng-app/src/assets/nextgraph-nofill.svg @@ -11,6 +11,6 @@ style="fill:#ffffff;stroke:none;stroke-width:0.268375" /> + /> \ No newline at end of file diff --git a/ng-app/src/lib/CenteredLayout.svelte b/ng-app/src/lib/CenteredLayout.svelte index 4db2a7b..42d7abf 100644 --- a/ng-app/src/lib/CenteredLayout.svelte +++ b/ng-app/src/lib/CenteredLayout.svelte @@ -11,19 +11,89 @@ -
- {#each locales as loc} - {loc}  - {/each} - +
+ {#if !changingLang} +
+ +
+ {#if displayFooter} +
+
+ +
+ +
+
+ {/if} + {:else} +
+
    + {#each Object.entries(available_languages) as lang} + + {/each} +
+
+ {/if}
diff --git a/ng-app/src/lib/FullLayout.svelte b/ng-app/src/lib/FullLayout.svelte index 9bc4e00..a56fa0b 100644 --- a/ng-app/src/lib/FullLayout.svelte +++ b/ng-app/src/lib/FullLayout.svelte @@ -19,11 +19,9 @@ import { link, location } from "svelte-spa-router"; import MobileBottomBarItem from "./MobileBottomBarItem.svelte"; import MobileBottomBar from "./MobileBottomBar.svelte"; - // @ts-ignore - import Logo from "../assets/nextgraph.svg?component"; - // @ts-ignore - import LogoGray from "../assets/nextgraph-gray.svg?component"; - import { online } from "../store"; + import Logo from "./components/Logo.svelte"; + + import { connection_status } from "../store"; import { onMount, tick } from "svelte"; @@ -91,76 +89,76 @@
- + - {#if $online} - - {:else} - - {/if} + - + - + - + - + - + - + -{#if step == "load"} -
-

How to open your wallet, step by step :

-
    -
  • - For each one of the 9 categories of images, you will be presented with - the 15 possible image choices. The categories are shuffled at every - login. They will not always appear in the same order. -
  • -
  • - At each category, only one of the 15 displayed choices is the correct - image that belongs to your pazzle. Find it and tap or click on that one. - The 15 images are shuffled too, they will not appear at the same - position at each login. On a computer, you can also use the tab key on - your keyboard to move to the desired item on the screen, then press the - space bar to select each one. -
  • -
  • - Once you completed the last category, you will be presented with all the - images you have previously selected. Their order is displayed as it was - when you picked them. But this is not the correct order of the images in - your pazzle. You now have to order them correctly. -
  • -
  • - You must remember which image should be the first one in your pazzle. - Find it on the screen and click or tap on it. It will be greyed out and - the number 1 will appear on top of it. -
  • -
  • - Move on to the second image of your pazzle (that you memorized). Find it - on the screen and tap on it. Repeat this step until you reached the last - image. -
  • -
  • - Finally, your PIN code will be asked. enter it by clicking or tapping on - the digits. -
  • -
-
-
- {#if !loaded} - Loading pazzle... +
660} + class:flex={height > 660} + bind:this={top} +> + {#if step == "load"} +
+

+ How to open your wallet? You have 2 options: +

+

With your Pazzle

+
    +
  • + For each one of the 9 categories of images, you will be presented with + the 15 possible image choices. The categories are shuffled at every + login. They will not always appear in the same order. +
  • +
  • + At each category, only one of the 15 displayed choices is the correct + image that belongs to your pazzle. Find it and tap or click on that + one. The 15 images are shuffled too, they will not appear at the same + position at each login. On a computer, you can also use the tab key on + your keyboard to move to the desired item on the screen, then press + the space bar to select each one. +
  • +
  • + Once you completed the last category, you will be presented with all + the images you have previously selected. Their order is displayed as + it was when you picked them. But this is not the correct order of the + images in your pazzle. You now have to order them correctly. +
  • +
  • + You must remember which image should be the first one in your pazzle. + Find it on the screen and click or tap on it. It will be greyed out + and the number 1 will appear on top of it. +
  • +
  • + Move on to the second image of your pazzle (that you memorized). Find + it on the screen and tap on it. Repeat this step until you reached the + last image. +
  • +
  • + Finally, your PIN code will be asked. enter it by clicking or tapping + on the digits. +
  • +
+ +

+ With your 12 words Mnemonic (passphrase) +

+
    +
  • + Enter your twelve words mnemonic in the input field. The words must be + separated by spaces. +
  • +
  • Enter the PIN code that you chose when you created your wallet.
  • +
+ + + {#if for_import} +
+ Do you trust this device?
+

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

+
+ Yes, save my wallet on this device +
+
+ {/if} + +
+
+ {#if !loaded} + Loading pazzle... + + + + + {:else} + + {/if} + + + Open with Mnemonic instead + +
+
+
+ + {:else if step == "pazzle" || step == "order" || step == "pin" || step == "mnemonic"} +
660} + class:min-w-[310px]={mobile} + class:min-w-[500px]={!mobile} + class:max-w-[370px]={mobile} + class:max-w-[600px]={!mobile} + > +
+ + + {#if step == "mnemonic"} +
+ + +
+ +
+ + {:else if step == "pazzle"} +

+ + + Select your emoji of category:
{emoji_cat[ + shuffle.category_indices[pazzlePage] + ]}
+

+ {#each [0, 1, 2, 3, 4] as row} +
+ {#each emojis2[pazzlePage]?.slice(0 + row * 3, 3 + row * 3) || [] as emoji, i (pazzlePage + "-" + row + "-" + i)} +
select(row * 3 + i)} + on:keypress={() => select(row * 3 + i)} + > + +
+ {/each} +
+ {/each} + {:else if step == "order"} +

+ Select each image in the correct order +

+ {#each [0, 1, 2] as row} +
+ {#each selection.slice(0 + row * 3, 3 + row * 3) || [] as emoji, i} + {#if !emoji.sel} +
select_order(emoji)} + on:keypress={() => select_order(emoji)} + > + +
+ {:else} +
+ + {emoji.sel} +
+ {/if} + {/each} +
+ {/each} + {:else if step == "pin"} +

+ Enter your PIN code +

+ + {#each [0, 1, 2] as row} +
+ {#each shuffle_pin.slice(0 + row * 3, 3 + row * 3) as num} + + {/each} +
+ {/each} +
+
+ + +
+ {#each pin_code as pin_key}*{/each} + {/if} +
+ +
+ + +
+
+ {:else if step == "opening"} +
+ Opening your wallet...
+ Please wait - {:else} - - {/if} -
- {#if for_import} -
- Do you trust this device?
-
- Yes, save my wallet on this device -
-

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

- {/if} -{:else if step == "pazzle"} -
- {#each [0, 1, 2, 3, 4] as row} -
- {#each emojis2[display]?.slice(0 + row * 3, 3 + row * 3) || [] as emoji, i} -
select(row * 3 + i)} - on:keypress={() => select(row * 3 + i)} + {:else if step == "end"} + {#if error} +
+
+ An error occurred ! +
- {/each} + + + + {error} + +
+
+ + +
- {/each} -
-{:else if step == "order"} - -
- {#each [0, 1, 2] as row} -
- {#each selection.slice(0 + row * 3, 3 + row * 3) || [] as emoji, i} - {#if !emoji.sel} -
select_order(emoji, row * 3 + i)} - on:keypress={() => select_order(emoji, row * 3 + i)} - > - -
- {:else} -
- - {emoji.sel} -
- {/if} - {/each} + {:else} +
+ Your wallet is opened!
Please wait while the app is loading... +
- {/each} -
-{:else if step == "pin"} -
-

- Enter your PIN code -

-
- {#each [0, 1, 2] as row} -
- {#each shuffle_pin.slice(0 + row * 3, 3 + row * 3) as num} - - {/each} -
- {/each} - -
-
-{:else if step == "opening"} -
- Opening your wallet...
- Please wait - - - - -
-{:else if step == "end"} - {#if error} -
- An error occurred ! - - - {error} - - - -
- {:else} -
- Your wallet is opened!
Please wait while the app is loading... - -
+ {/if} {/if} -{/if} +
+ +