wallet creation and opening

master
Niko 11 months ago
parent e1513cca51
commit 4545d3b5b9
  1. 810
      Cargo.lock
  2. 4
      Cargo.toml
  3. 5
      README.md
  4. 10
      ng-app-native/index.html
  5. 9
      ng-app-native/src-tauri/src/lib.rs
  6. 9
      ng-app-native/src-tauri/src/main.rs
  7. 9
      ng-app-native/src-tauri/src/mobile.rs
  8. 13
      ng-app-native/src/App.svelte
  9. 11
      ng-app-native/src/lib/Greet.svelte
  10. 9
      ng-app-native/src/main.ts
  11. 11
      ng-app-native/src/routes/Home.svelte
  12. 11
      ng-app-native/src/routes/NotFound.svelte
  13. 11
      ng-app-native/src/routes/Test.svelte
  14. 15
      ng-app-native/src/styles.css
  15. 3
      ng-wallet/.gitignore
  16. 27
      ng-wallet/Cargo.toml
  17. 213
      ng-wallet/src/bip39.rs
  18. 546
      ng-wallet/src/lib.rs
  19. 233
      ng-wallet/src/types.rs
  20. BIN
      ng-wallet/tests/generated_security_image.jpg.compare
  21. BIN
      ng-wallet/tests/valid_security_image.jpg
  22. 2
      ngcli/Cargo.toml
  23. 6
      ngcli/src/main.rs
  24. 29
      ngone/Cargo.toml
  25. 36
      ngone/README.md
  26. BIN
      ngone/data/data.mdb
  27. BIN
      ngone/data/lock.mdb
  28. 215
      ngone/src/main.rs
  29. 101
      ngone/src/store/dynbox.rs
  30. 3
      ngone/src/store/mod.rs
  31. 129
      ngone/src/store/wallet_record.rs
  32. 30
      ngone/src/types.rs
  33. 24
      ngone/web/.gitignore
  34. 3
      ngone/web/.vscode/extensions.json
  35. 23
      ngone/web/index.html
  36. 32
      ngone/web/jsconfig.json
  37. 16
      ngone/web/package.json
  38. 411
      ngone/web/pnpm-lock.yaml
  39. 1
      ngone/web/public/vite.svg
  40. 62
      ngone/web/src/App.svelte
  41. 25
      ngone/web/src/Greeting.svelte
  42. 91
      ngone/web/src/app.css
  43. 1
      ngone/web/src/assets/svelte.svg
  44. 10
      ngone/web/src/lib/Counter.svelte
  45. 8
      ngone/web/src/main.js
  46. 2
      ngone/web/src/vite-env.d.ts
  47. 7
      ngone/web/svelte.config.js
  48. 7
      ngone/web/vite.config.js
  49. 2
      p2p-broker/Cargo.toml
  50. 16
      p2p-broker/src/broker_store/account.rs
  51. 13
      p2p-broker/src/broker_store/config.rs
  52. 14
      p2p-broker/src/broker_store/overlay.rs
  53. 13
      p2p-broker/src/broker_store/peer.rs
  54. 15
      p2p-broker/src/broker_store/repostoreinfo.rs
  55. 12
      p2p-broker/src/broker_store/topic.rs
  56. 12
      p2p-broker/src/server.rs
  57. 8
      p2p-broker/src/server_ws.rs
  58. 4
      p2p-net/Cargo.toml
  59. 27
      p2p-repo/src/kcv_store.rs
  60. 4
      p2p-repo/src/lib.rs
  61. 79
      p2p-repo/src/site.rs
  62. 19
      p2p-repo/src/store.rs
  63. 63
      p2p-repo/src/types.rs
  64. 2
      stores-lmdb/Cargo.toml
  65. 133
      stores-lmdb/src/kcv_store.rs
  66. 2
      stores-lmdb/src/lib.rs
  67. 0
      stores-lmdb/src/repo_store.rs

810
Cargo.lock generated

File diff suppressed because it is too large Load Diff

@ -5,11 +5,13 @@ members = [
"p2p-broker",
"p2p-client-ws",
"p2p-verifier",
"p2p-stores-lmdb",
"stores-lmdb",
"ngcli",
"ngd",
"ngone",
"ng-app-js",
"ng-app-native/src-tauri",
"ng-wallet"
]
[profile.release]

@ -33,6 +33,7 @@ Read our [getting started guide](https://docs.nextgraph.org/en/getting-started/)
```
cargo install wasm-pack
cargo install cargo-watch
// optionally, if you want a Rust REPL: cargo install evcxr_repl
git clone git@git.nextgraph.org:NextGraph/nextgraph-rs.git
cd nextgraph-rs
@ -47,12 +48,14 @@ The crates are organized as follow :
- p2p-net : all the common types, traits and structs for the P2P networks
- p2p-broker : the broker code (as server and core peer)
- p2p-client-ws : the client connecting to a broker, used by the apps and verifier
- p2p-stores-lmdb : lmdb backed stores for the p2p layer
- p2p-verifier : the code of the verifier
- stores-lmdb : lmdb backed stores
- ngcli : CLI tool to manipulate the repos
- ngd : binary executable of the daemon (that can run a broker, verifier and/or Rust services)
- ngone : server for nextgraph.one (boostrap into the right app)
- ng-app-js : contains the JS SDK, the web app, react app, and some node services
- ng-app-native : all the native apps, based on Tauri
- ng-wallet : keeps the secret keys of all identities of the user in a safe wallet
### Run

@ -1,3 +1,13 @@
<!--
// 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.
-->
<!DOCTYPE html>
<html lang="en">
<head>

@ -1,3 +1,12 @@
// 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.
use tauri::App;
#[cfg(mobile)]

@ -1,3 +1,12 @@
// 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.
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
#![cfg_attr(
all(not(debug_assertions), target_os = "windows"),

@ -1,3 +1,12 @@
// 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.
#[tauri::mobile_entry_point]
fn main() {
crate::AppBuilder::new().run();

@ -1,3 +1,14 @@
<!--
// 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 Router from "svelte-spa-router";
@ -17,6 +28,6 @@
};
</script>
<main class="container2">
<main>
<Router {routes} />
</main>

@ -1,3 +1,14 @@
<!--
// 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">
let name = "";
let greetMsg = "";

@ -1,3 +1,12 @@
// 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.
import "./app.postcss";
import "./styles.css";
import App from "./App.svelte";

@ -1,3 +1,14 @@
<!--
// 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 } from "flowbite-svelte";
import { link } from "svelte-spa-router";

@ -1,3 +1,14 @@
<!--
// 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 { Alert } from "flowbite-svelte";
</script>

@ -1,3 +1,14 @@
<!--
// 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 Greet from "../lib/Greet.svelte";
</script>

@ -1,3 +1,14 @@
/*
// 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.
*/
:root {
font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
font-size: 16px;
@ -30,10 +41,6 @@
transition: 0.75s;
}
.logo.tauri:hover {
filter: drop-shadow(0 0 2em #24c8db);
}
.row {
display: flex;
justify-content: center;

@ -0,0 +1,3 @@
.DS_Store
tests/generated_security_image.jpg
tests/wallet.ngw

@ -0,0 +1,27 @@
[package]
name = "ng-wallet"
version = "0.1.0"
edition = "2021"
license = "MIT/Apache-2.0"
authors = ["Niko PLP <niko@nextgraph.org>"]
description = "keeps the secret keys of all identities of the user in a safe wallet"
repository = "https://git.nextgraph.org/NextGraph/nextgraph-rs"
[dependencies]
debug_print = "1.0.0"
serde = { version = "1.0.142", features = ["derive"] }
serde_bare = "0.5.0"
serde_bytes = "0.11.7"
serde-big-array = "0.5.1"
p2p-repo = { path = "../p2p-repo" }
p2p-net = { path = "../p2p-net" }
image = "0.24.6"
getrandom = { version = "0.1.1", features = ["wasm-bindgen"] }
rand = { version = "0.7", features = ["getrandom"] }
chacha20poly1305 = "0.10.1"
#{version = "0.10.1", features = ["heapless","getrandom"] }
# slice_as_array = "1.1.0"
argon2 = "0.5.0"
safe-transmute = "0.11.2"
aes-gcm-siv = {version = "0.11.1", features = ["aes","heapless","getrandom","std"] }
base64-url = "2.0.0"

@ -0,0 +1,213 @@
pub const bip39_wordlist: [&str; 2048] = [
"abandon", "ability", "able", "about", "above", "absent", "absorb", "abstract", "absurd",
"abuse", "access", "accident", "account", "accuse", "achieve", "acid", "acoustic", "acquire",
"across", "act", "action", "actor", "actress", "actual", "adapt", "add", "addict", "address",
"adjust", "admit", "adult", "advance", "advice", "aerobic", "affair", "afford", "afraid",
"again", "age", "agent", "agree", "ahead", "aim", "air", "airport", "aisle", "alarm", "album",
"alcohol", "alert", "alien", "all", "alley", "allow", "almost", "alone", "alpha", "already",
"also", "alter", "always", "amateur", "amazing", "among", "amount", "amused", "analyst",
"anchor", "ancient", "anger", "angle", "angry", "animal", "ankle", "announce", "annual",
"another", "answer", "antenna", "antique", "anxiety", "any", "apart", "apology", "appear",
"apple", "approve", "april", "arch", "arctic", "area", "arena", "argue", "arm", "armed",
"armor", "army", "around", "arrange", "arrest", "arrive", "arrow", "art", "artefact", "artist",
"artwork", "ask", "aspect", "assault", "asset", "assist", "assume", "asthma", "athlete",
"atom", "attack", "attend", "attitude", "attract", "auction", "audit", "august", "aunt",
"author", "auto", "autumn", "average", "avocado", "avoid", "awake", "aware", "away", "awesome",
"awful", "awkward", "axis", "baby", "bachelor", "bacon", "badge", "bag", "balance", "balcony",
"ball", "bamboo", "banana", "banner", "bar", "barely", "bargain", "barrel", "base", "basic",
"basket", "battle", "beach", "bean", "beauty", "because", "become", "beef", "before", "begin",
"behave", "behind", "believe", "below", "belt", "bench", "benefit", "best", "betray", "better",
"between", "beyond", "bicycle", "bid", "bike", "bind", "biology", "bird", "birth", "bitter",
"black", "blade", "blame", "blanket", "blast", "bleak", "bless", "blind", "blood", "blossom",
"blouse", "blue", "blur", "blush", "board", "boat", "body", "boil", "bomb", "bone", "bonus",
"book", "boost", "border", "boring", "borrow", "boss", "bottom", "bounce", "box", "boy",
"bracket", "brain", "brand", "brass", "brave", "bread", "breeze", "brick", "bridge", "brief",
"bright", "bring", "brisk", "broccoli", "broken", "bronze", "broom", "brother", "brown",
"brush", "bubble", "buddy", "budget", "buffalo", "build", "bulb", "bulk", "bullet", "bundle",
"bunker", "burden", "burger", "burst", "bus", "business", "busy", "butter", "buyer", "buzz",
"cabbage", "cabin", "cable", "cactus", "cage", "cake", "call", "calm", "camera", "camp", "can",
"canal", "cancel", "candy", "cannon", "canoe", "canvas", "canyon", "capable", "capital",
"captain", "car", "carbon", "card", "cargo", "carpet", "carry", "cart", "case", "cash",
"casino", "castle", "casual", "cat", "catalog", "catch", "category", "cattle", "caught",
"cause", "caution", "cave", "ceiling", "celery", "cement", "census", "century", "cereal",
"certain", "chair", "chalk", "champion", "change", "chaos", "chapter", "charge", "chase",
"chat", "cheap", "check", "cheese", "chef", "cherry", "chest", "chicken", "chief", "child",
"chimney", "choice", "choose", "chronic", "chuckle", "chunk", "churn", "cigar", "cinnamon",
"circle", "citizen", "city", "civil", "claim", "clap", "clarify", "claw", "clay", "clean",
"clerk", "clever", "click", "client", "cliff", "climb", "clinic", "clip", "clock", "clog",
"close", "cloth", "cloud", "clown", "club", "clump", "cluster", "clutch", "coach", "coast",
"coconut", "code", "coffee", "coil", "coin", "collect", "color", "column", "combine", "come",
"comfort", "comic", "common", "company", "concert", "conduct", "confirm", "congress",
"connect", "consider", "control", "convince", "cook", "cool", "copper", "copy", "coral",
"core", "corn", "correct", "cost", "cotton", "couch", "country", "couple", "course", "cousin",
"cover", "coyote", "crack", "cradle", "craft", "cram", "crane", "crash", "crater", "crawl",
"crazy", "cream", "credit", "creek", "crew", "cricket", "crime", "crisp", "critic", "crop",
"cross", "crouch", "crowd", "crucial", "cruel", "cruise", "crumble", "crunch", "crush", "cry",
"crystal", "cube", "culture", "cup", "cupboard", "curious", "current", "curtain", "curve",
"cushion", "custom", "cute", "cycle", "dad", "damage", "damp", "dance", "danger", "daring",
"dash", "daughter", "dawn", "day", "deal", "debate", "debris", "decade", "december", "decide",
"decline", "decorate", "decrease", "deer", "defense", "define", "defy", "degree", "delay",
"deliver", "demand", "demise", "denial", "dentist", "deny", "depart", "depend", "deposit",
"depth", "deputy", "derive", "describe", "desert", "design", "desk", "despair", "destroy",
"detail", "detect", "develop", "device", "devote", "diagram", "dial", "diamond", "diary",
"dice", "diesel", "diet", "differ", "digital", "dignity", "dilemma", "dinner", "dinosaur",
"direct", "dirt", "disagree", "discover", "disease", "dish", "dismiss", "disorder", "display",
"distance", "divert", "divide", "divorce", "dizzy", "doctor", "document", "dog", "doll",
"dolphin", "domain", "donate", "donkey", "donor", "door", "dose", "double", "dove", "draft",
"dragon", "drama", "drastic", "draw", "dream", "dress", "drift", "drill", "drink", "drip",
"drive", "drop", "drum", "dry", "duck", "dumb", "dune", "during", "dust", "dutch", "duty",
"dwarf", "dynamic", "eager", "eagle", "early", "earn", "earth", "easily", "east", "easy",
"echo", "ecology", "economy", "edge", "edit", "educate", "effort", "egg", "eight", "either",
"elbow", "elder", "electric", "elegant", "element", "elephant", "elevator", "elite", "else",
"embark", "embody", "embrace", "emerge", "emotion", "employ", "empower", "empty", "enable",
"enact", "end", "endless", "endorse", "enemy", "energy", "enforce", "engage", "engine",
"enhance", "enjoy", "enlist", "enough", "enrich", "enroll", "ensure", "enter", "entire",
"entry", "envelope", "episode", "equal", "equip", "era", "erase", "erode", "erosion", "error",
"erupt", "escape", "essay", "essence", "estate", "eternal", "ethics", "evidence", "evil",
"evoke", "evolve", "exact", "example", "excess", "exchange", "excite", "exclude", "excuse",
"execute", "exercise", "exhaust", "exhibit", "exile", "exist", "exit", "exotic", "expand",
"expect", "expire", "explain", "expose", "express", "extend", "extra", "eye", "eyebrow",
"fabric", "face", "faculty", "fade", "faint", "faith", "fall", "false", "fame", "family",
"famous", "fan", "fancy", "fantasy", "farm", "fashion", "fat", "fatal", "father", "fatigue",
"fault", "favorite", "feature", "february", "federal", "fee", "feed", "feel", "female",
"fence", "festival", "fetch", "fever", "few", "fiber", "fiction", "field", "figure", "file",
"film", "filter", "final", "find", "fine", "finger", "finish", "fire", "firm", "first",
"fiscal", "fish", "fit", "fitness", "fix", "flag", "flame", "flash", "flat", "flavor", "flee",
"flight", "flip", "float", "flock", "floor", "flower", "fluid", "flush", "fly", "foam",
"focus", "fog", "foil", "fold", "follow", "food", "foot", "force", "forest", "forget", "fork",
"fortune", "forum", "forward", "fossil", "foster", "found", "fox", "fragile", "frame",
"frequent", "fresh", "friend", "fringe", "frog", "front", "frost", "frown", "frozen", "fruit",
"fuel", "fun", "funny", "furnace", "fury", "future", "gadget", "gain", "galaxy", "gallery",
"game", "gap", "garage", "garbage", "garden", "garlic", "garment", "gas", "gasp", "gate",
"gather", "gauge", "gaze", "general", "genius", "genre", "gentle", "genuine", "gesture",
"ghost", "giant", "gift", "giggle", "ginger", "giraffe", "girl", "give", "glad", "glance",
"glare", "glass", "glide", "glimpse", "globe", "gloom", "glory", "glove", "glow", "glue",
"goat", "goddess", "gold", "good", "goose", "gorilla", "gospel", "gossip", "govern", "gown",
"grab", "grace", "grain", "grant", "grape", "grass", "gravity", "great", "green", "grid",
"grief", "grit", "grocery", "group", "grow", "grunt", "guard", "guess", "guide", "guilt",
"guitar", "gun", "gym", "habit", "hair", "half", "hammer", "hamster", "hand", "happy",
"harbor", "hard", "harsh", "harvest", "hat", "have", "hawk", "hazard", "head", "health",
"heart", "heavy", "hedgehog", "height", "hello", "helmet", "help", "hen", "hero", "hidden",
"high", "hill", "hint", "hip", "hire", "history", "hobby", "hockey", "hold", "hole", "holiday",
"hollow", "home", "honey", "hood", "hope", "horn", "horror", "horse", "hospital", "host",
"hotel", "hour", "hover", "hub", "huge", "human", "humble", "humor", "hundred", "hungry",
"hunt", "hurdle", "hurry", "hurt", "husband", "hybrid", "ice", "icon", "idea", "identify",
"idle", "ignore", "ill", "illegal", "illness", "image", "imitate", "immense", "immune",
"impact", "impose", "improve", "impulse", "inch", "include", "income", "increase", "index",
"indicate", "indoor", "industry", "infant", "inflict", "inform", "inhale", "inherit",
"initial", "inject", "injury", "inmate", "inner", "innocent", "input", "inquiry", "insane",
"insect", "inside", "inspire", "install", "intact", "interest", "into", "invest", "invite",
"involve", "iron", "island", "isolate", "issue", "item", "ivory", "jacket", "jaguar", "jar",
"jazz", "jealous", "jeans", "jelly", "jewel", "job", "join", "joke", "journey", "joy", "judge",
"juice", "jump", "jungle", "junior", "junk", "just", "kangaroo", "keen", "keep", "ketchup",
"key", "kick", "kid", "kidney", "kind", "kingdom", "kiss", "kit", "kitchen", "kite", "kitten",
"kiwi", "knee", "knife", "knock", "know", "lab", "label", "labor", "ladder", "lady", "lake",
"lamp", "language", "laptop", "large", "later", "latin", "laugh", "laundry", "lava", "law",
"lawn", "lawsuit", "layer", "lazy", "leader", "leaf", "learn", "leave", "lecture", "left",
"leg", "legal", "legend", "leisure", "lemon", "lend", "length", "lens", "leopard", "lesson",
"letter", "level", "liar", "liberty", "library", "license", "life", "lift", "light", "like",
"limb", "limit", "link", "lion", "liquid", "list", "little", "live", "lizard", "load", "loan",
"lobster", "local", "lock", "logic", "lonely", "long", "loop", "lottery", "loud", "lounge",
"love", "loyal", "lucky", "luggage", "lumber", "lunar", "lunch", "luxury", "lyrics", "machine",
"mad", "magic", "magnet", "maid", "mail", "main", "major", "make", "mammal", "man", "manage",
"mandate", "mango", "mansion", "manual", "maple", "marble", "march", "margin", "marine",
"market", "marriage", "mask", "mass", "master", "match", "material", "math", "matrix",
"matter", "maximum", "maze", "meadow", "mean", "measure", "meat", "mechanic", "medal", "media",
"melody", "melt", "member", "memory", "mention", "menu", "mercy", "merge", "merit", "merry",
"mesh", "message", "metal", "method", "middle", "midnight", "milk", "million", "mimic", "mind",
"minimum", "minor", "minute", "miracle", "mirror", "misery", "miss", "mistake", "mix", "mixed",
"mixture", "mobile", "model", "modify", "mom", "moment", "monitor", "monkey", "monster",
"month", "moon", "moral", "more", "morning", "mosquito", "mother", "motion", "motor",
"mountain", "mouse", "move", "movie", "much", "muffin", "mule", "multiply", "muscle", "museum",
"mushroom", "music", "must", "mutual", "myself", "mystery", "myth", "naive", "name", "napkin",
"narrow", "nasty", "nation", "nature", "near", "neck", "need", "negative", "neglect",
"neither", "nephew", "nerve", "nest", "net", "network", "neutral", "never", "news", "next",
"nice", "night", "noble", "noise", "nominee", "noodle", "normal", "north", "nose", "notable",
"note", "nothing", "notice", "novel", "now", "nuclear", "number", "nurse", "nut", "oak",
"obey", "object", "oblige", "obscure", "observe", "obtain", "obvious", "occur", "ocean",
"october", "odor", "off", "offer", "office", "often", "oil", "okay", "old", "olive", "olympic",
"omit", "once", "one", "onion", "online", "only", "open", "opera", "opinion", "oppose",
"option", "orange", "orbit", "orchard", "order", "ordinary", "organ", "orient", "original",
"orphan", "ostrich", "other", "outdoor", "outer", "output", "outside", "oval", "oven", "over",
"own", "owner", "oxygen", "oyster", "ozone", "pact", "paddle", "page", "pair", "palace",
"palm", "panda", "panel", "panic", "panther", "paper", "parade", "parent", "park", "parrot",
"party", "pass", "patch", "path", "patient", "patrol", "pattern", "pause", "pave", "payment",
"peace", "peanut", "pear", "peasant", "pelican", "pen", "penalty", "pencil", "people",
"pepper", "perfect", "permit", "person", "pet", "phone", "photo", "phrase", "physical",
"piano", "picnic", "picture", "piece", "pig", "pigeon", "pill", "pilot", "pink", "pioneer",
"pipe", "pistol", "pitch", "pizza", "place", "planet", "plastic", "plate", "play", "please",
"pledge", "pluck", "plug", "plunge", "poem", "poet", "point", "polar", "pole", "police",
"pond", "pony", "pool", "popular", "portion", "position", "possible", "post", "potato",
"pottery", "poverty", "powder", "power", "practice", "praise", "predict", "prefer", "prepare",
"present", "pretty", "prevent", "price", "pride", "primary", "print", "priority", "prison",
"private", "prize", "problem", "process", "produce", "profit", "program", "project", "promote",
"proof", "property", "prosper", "protect", "proud", "provide", "public", "pudding", "pull",
"pulp", "pulse", "pumpkin", "punch", "pupil", "puppy", "purchase", "purity", "purpose",
"purse", "push", "put", "puzzle", "pyramid", "quality", "quantum", "quarter", "question",
"quick", "quit", "quiz", "quote", "rabbit", "raccoon", "race", "rack", "radar", "radio",
"rail", "rain", "raise", "rally", "ramp", "ranch", "random", "range", "rapid", "rare", "rate",
"rather", "raven", "raw", "razor", "ready", "real", "reason", "rebel", "rebuild", "recall",
"receive", "recipe", "record", "recycle", "reduce", "reflect", "reform", "refuse", "region",
"regret", "regular", "reject", "relax", "release", "relief", "rely", "remain", "remember",
"remind", "remove", "render", "renew", "rent", "reopen", "repair", "repeat", "replace",
"report", "require", "rescue", "resemble", "resist", "resource", "response", "result",
"retire", "retreat", "return", "reunion", "reveal", "review", "reward", "rhythm", "rib",
"ribbon", "rice", "rich", "ride", "ridge", "rifle", "right", "rigid", "ring", "riot", "ripple",
"risk", "ritual", "rival", "river", "road", "roast", "robot", "robust", "rocket", "romance",
"roof", "rookie", "room", "rose", "rotate", "rough", "round", "route", "royal", "rubber",
"rude", "rug", "rule", "run", "runway", "rural", "sad", "saddle", "sadness", "safe", "sail",
"salad", "salmon", "salon", "salt", "salute", "same", "sample", "sand", "satisfy", "satoshi",
"sauce", "sausage", "save", "say", "scale", "scan", "scare", "scatter", "scene", "scheme",
"school", "science", "scissors", "scorpion", "scout", "scrap", "screen", "script", "scrub",
"sea", "search", "season", "seat", "second", "secret", "section", "security", "seed", "seek",
"segment", "select", "sell", "seminar", "senior", "sense", "sentence", "series", "service",
"session", "settle", "setup", "seven", "shadow", "shaft", "shallow", "share", "shed", "shell",
"sheriff", "shield", "shift", "shine", "ship", "shiver", "shock", "shoe", "shoot", "shop",
"short", "shoulder", "shove", "shrimp", "shrug", "shuffle", "shy", "sibling", "sick", "side",
"siege", "sight", "sign", "silent", "silk", "silly", "silver", "similar", "simple", "since",
"sing", "siren", "sister", "situate", "six", "size", "skate", "sketch", "ski", "skill", "skin",
"skirt", "skull", "slab", "slam", "sleep", "slender", "slice", "slide", "slight", "slim",
"slogan", "slot", "slow", "slush", "small", "smart", "smile", "smoke", "smooth", "snack",
"snake", "snap", "sniff", "snow", "soap", "soccer", "social", "sock", "soda", "soft", "solar",
"soldier", "solid", "solution", "solve", "someone", "song", "soon", "sorry", "sort", "soul",
"sound", "soup", "source", "south", "space", "spare", "spatial", "spawn", "speak", "special",
"speed", "spell", "spend", "sphere", "spice", "spider", "spike", "spin", "spirit", "split",
"spoil", "sponsor", "spoon", "sport", "spot", "spray", "spread", "spring", "spy", "square",
"squeeze", "squirrel", "stable", "stadium", "staff", "stage", "stairs", "stamp", "stand",
"start", "state", "stay", "steak", "steel", "stem", "step", "stereo", "stick", "still",
"sting", "stock", "stomach", "stone", "stool", "story", "stove", "strategy", "street",
"strike", "strong", "struggle", "student", "stuff", "stumble", "style", "subject", "submit",
"subway", "success", "such", "sudden", "suffer", "sugar", "suggest", "suit", "summer", "sun",
"sunny", "sunset", "super", "supply", "supreme", "sure", "surface", "surge", "surprise",
"surround", "survey", "suspect", "sustain", "swallow", "swamp", "swap", "swarm", "swear",
"sweet", "swift", "swim", "swing", "switch", "sword", "symbol", "symptom", "syrup", "system",
"table", "tackle", "tag", "tail", "talent", "talk", "tank", "tape", "target", "task", "taste",
"tattoo", "taxi", "teach", "team", "tell", "ten", "tenant", "tennis", "tent", "term", "test",
"text", "thank", "that", "theme", "then", "theory", "there", "they", "thing", "this",
"thought", "three", "thrive", "throw", "thumb", "thunder", "ticket", "tide", "tiger", "tilt",
"timber", "time", "tiny", "tip", "tired", "tissue", "title", "toast", "tobacco", "today",
"toddler", "toe", "together", "toilet", "token", "tomato", "tomorrow", "tone", "tongue",
"tonight", "tool", "tooth", "top", "topic", "topple", "torch", "tornado", "tortoise", "toss",
"total", "tourist", "toward", "tower", "town", "toy", "track", "trade", "traffic", "tragic",
"train", "transfer", "trap", "trash", "travel", "tray", "treat", "tree", "trend", "trial",
"tribe", "trick", "trigger", "trim", "trip", "trophy", "trouble", "truck", "true", "truly",
"trumpet", "trust", "truth", "try", "tube", "tuition", "tumble", "tuna", "tunnel", "turkey",
"turn", "turtle", "twelve", "twenty", "twice", "twin", "twist", "two", "type", "typical",
"ugly", "umbrella", "unable", "unaware", "uncle", "uncover", "under", "undo", "unfair",
"unfold", "unhappy", "uniform", "unique", "unit", "universe", "unknown", "unlock", "until",
"unusual", "unveil", "update", "upgrade", "uphold", "upon", "upper", "upset", "urban", "urge",
"usage", "use", "used", "useful", "useless", "usual", "utility", "vacant", "vacuum", "vague",
"valid", "valley", "valve", "van", "vanish", "vapor", "various", "vast", "vault", "vehicle",
"velvet", "vendor", "venture", "venue", "verb", "verify", "version", "very", "vessel",
"veteran", "viable", "vibrant", "vicious", "victory", "video", "view", "village", "vintage",
"violin", "virtual", "virus", "visa", "visit", "visual", "vital", "vivid", "vocal", "voice",
"void", "volcano", "volume", "vote", "voyage", "wage", "wagon", "wait", "walk", "wall",
"walnut", "want", "warfare", "warm", "warrior", "wash", "wasp", "waste", "water", "wave",
"way", "wealth", "weapon", "wear", "weasel", "weather", "web", "wedding", "weekend", "weird",
"welcome", "west", "wet", "whale", "what", "wheat", "wheel", "when", "where", "whip",
"whisper", "wide", "width", "wife", "wild", "will", "win", "window", "wine", "wing", "wink",
"winner", "winter", "wire", "wisdom", "wise", "wish", "witness", "wolf", "woman", "wonder",
"wood", "wool", "word", "work", "world", "worry", "worth", "wrap", "wreck", "wrestle", "wrist",
"write", "wrong", "yard", "year", "yellow", "you", "young", "youth", "zebra", "zero", "zone",
"zoo",
];

@ -0,0 +1,546 @@
// 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.
// #[macro_use]
// extern crate slice_as_array;
#[macro_use]
extern crate p2p_net;
pub mod types;
pub mod bip39;
use std::io::Cursor;
use crate::bip39::bip39_wordlist;
use crate::types::*;
use aes_gcm_siv::{
aead::{heapless::Vec as HeaplessVec, AeadInPlace, KeyInit},
Aes256GcmSiv, Nonce,
};
use argon2::{Algorithm, Argon2, AssociatedData, ParamsBuilder, Version};
use chacha20poly1305::XChaCha20Poly1305;
use image::{imageops::FilterType, io::Reader as ImageReader, ImageOutputFormat};
use safe_transmute::transmute_to_bytes;
use p2p_repo::types::{PubKey, Site, SiteType, Timestamp};
use p2p_repo::utils::{generate_keypair, now_timestamp, sign, verify};
use rand::{thread_rng, Rng};
use serde_bare::{from_slice, to_vec};
pub fn enc_master_key(
master_key: [u8; 32],
key: [u8; 32],
nonce: u8,
wallet_id: WalletId,
) -> Result<[u8; 48], NgWalletError> {
let cipher = Aes256GcmSiv::new(&key.into());
let mut nonce_buffer = [0u8; 12];
nonce_buffer[0] = nonce;
let nonce = Nonce::from_slice(&nonce_buffer);
let mut buffer: HeaplessVec<u8, 48> = HeaplessVec::new(); // Note: buffer needs 16-bytes overhead for auth tag
buffer.extend_from_slice(&master_key);
// Encrypt `buffer` in-place, replacing the plaintext contents with ciphertext
cipher
.encrypt_in_place(nonce, &to_vec(&wallet_id).unwrap(), &mut buffer)
.map_err(|e| NgWalletError::EncryptionError)?;
// `buffer` now contains the encrypted master key
// println!("cipher {:?}", buffer);
Ok(buffer.into_array::<48>().unwrap())
}
pub fn dec_master_key(
ciphertext: [u8; 48],
key: [u8; 32],
nonce: u8,
wallet_id: WalletId,
) -> Result<[u8; 32], NgWalletError> {
let cipher = Aes256GcmSiv::new(&key.into());
let mut nonce_buffer = [0u8; 12];
nonce_buffer[0] = nonce;
let nonce = Nonce::from_slice(&nonce_buffer);
let mut buffer: HeaplessVec<u8, 48> = HeaplessVec::from_slice(&ciphertext).unwrap(); // Note: buffer needs 16-bytes overhead for auth tag
// Decrypt `buffer` in-place, replacing its ciphertext context with the original plaintext
cipher
.decrypt_in_place(nonce, &to_vec(&wallet_id).unwrap(), &mut buffer)
.map_err(|e| NgWalletError::DecryptionError)?;
Ok(buffer.into_array::<32>().unwrap())
}
fn gen_nonce(peer_id: PubKey, nonce: u64) -> [u8; 24] {
let mut buffer = Vec::with_capacity(24);
buffer.extend_from_slice(&peer_id.slice()[0..16]);
buffer.extend_from_slice(&nonce.to_be_bytes());
buffer.try_into().unwrap()
}
fn gen_associated_data(timestamp: Timestamp, wallet_id: WalletId) -> Vec<u8> {
let ser_wallet = to_vec(&wallet_id).unwrap();
[ser_wallet, timestamp.to_be_bytes().to_vec()].concat()
}
pub fn enc_encrypted_block(
block: &EncryptedWalletV0,
master_key: [u8; 32],
peer_id: PubKey,
nonce: u64,
timestamp: Timestamp,
wallet_id: WalletId,
) -> Result<Vec<u8>, NgWalletError> {
let ser_encrypted_block = to_vec(block).map_err(|e| NgWalletError::InternalError)?;
let nonce_buffer: [u8; 24] = gen_nonce(peer_id, nonce);
let cipher = XChaCha20Poly1305::new(&master_key.into());
let mut buffer: Vec<u8> = Vec::with_capacity(ser_encrypted_block.len() + 16); // Note: buffer needs 16-bytes overhead for auth tag
buffer.extend_from_slice(&ser_encrypted_block);
// Encrypt `buffer` in-place, replacing the plaintext contents with ciphertext
cipher
.encrypt_in_place(
&nonce_buffer.into(),
&gen_associated_data(timestamp, wallet_id),
&mut buffer,
)
.map_err(|e| NgWalletError::EncryptionError)?;
// `buffer` now contains the message ciphertext
// println!("encrypted_block ciphertext {:?}", buffer);
Ok(buffer)
}
pub fn dec_encrypted_block(
mut ciphertext: Vec<u8>,
master_key: [u8; 32],
peer_id: PubKey,
nonce: u64,
timestamp: Timestamp,
wallet_id: WalletId,
) -> Result<EncryptedWalletV0, NgWalletError> {
let nonce_buffer: [u8; 24] = gen_nonce(peer_id, nonce);
let cipher = XChaCha20Poly1305::new(&master_key.into());
// Decrypt `ciphertext` in-place, replacing its ciphertext context with the original plaintext
cipher
.decrypt_in_place(
&nonce_buffer.into(),
&gen_associated_data(timestamp, wallet_id),
&mut ciphertext,
)
.map_err(|e| NgWalletError::DecryptionError)?;
// `ciphertext` now contains the decrypted block
//println!("decrypted_block {:?}", ciphertext);
let decrypted_block =
from_slice::<EncryptedWalletV0>(&ciphertext).map_err(|e| NgWalletError::DecryptionError)?;
Ok(decrypted_block)
}
pub fn derive_key_from_pass(pass: Vec<u8>, salt: [u8; 16], wallet_id: WalletId) -> [u8; 32] {
let params = ParamsBuilder::new()
.m_cost(50 * 1024)
.t_cost(2)
.p_cost(1)
.data(AssociatedData::new(wallet_id.slice()).unwrap())
.output_len(32)
.build()
.unwrap();
let argon = Argon2::new(Algorithm::Argon2id, Version::V0x13, params);
let mut out = [0u8; 32];
argon.hash_password_into(&pass, &salt, &mut out).unwrap();
out
}
pub fn open_wallet_with_pazzle(
wallet: Wallet,
pazzle: Vec<u8>,
pin: [u8; 4],
) -> Result<EncryptedWallet, NgWalletError> {
verify(&wallet.content_as_bytes(), wallet.sig(), wallet.id())
.map_err(|e| NgWalletError::InvalidSignature)?;
match wallet {
Wallet::V0(v0) => {
let pazzle_key = derive_key_from_pass(
[pazzle, pin.to_vec()].concat(),
v0.content.salt_pazzle,
v0.id,
);
let master_key = dec_master_key(
v0.content.enc_master_key_pazzle,
pazzle_key,
v0.content.master_nonce,
v0.id,
)?;
Ok(EncryptedWallet::V0(dec_encrypted_block(
v0.content.encrypted,
master_key,
v0.content.peer_id,
v0.content.nonce,
v0.content.timestamp,
v0.id,
)?))
}
}
}
pub fn open_wallet_with_mnemonic(
wallet: Wallet,
mnemonic: [u16; 12],
pin: [u8; 4],
) -> Result<EncryptedWallet, NgWalletError> {
verify(&wallet.content_as_bytes(), wallet.sig(), wallet.id())
.map_err(|e| NgWalletError::InvalidSignature)?;
match wallet {
Wallet::V0(v0) => {
let mnemonic_key = derive_key_from_pass(
[transmute_to_bytes(&mnemonic), &pin].concat(),
v0.content.salt_mnemonic,
v0.id,
);
let master_key = dec_master_key(
v0.content.enc_master_key_mnemonic,
mnemonic_key,
v0.content.master_nonce,
v0.id,
)?;
Ok(EncryptedWallet::V0(dec_encrypted_block(
v0.content.encrypted,
master_key,
v0.content.peer_id,
v0.content.nonce,
v0.content.timestamp,
v0.id,
)?))
}
}
}
pub fn display_mnemonic(mnemonic: &[u16; 12]) -> Vec<String> {
let res: Vec<String> = mnemonic
.into_iter()
.map(|i| String::from(bip39_wordlist[*i as usize]))
.collect();
res
}
/// creates a Wallet from a pin, a security text and image (with option to send the bootstrap and wallet to nextgraph.one)
/// and returns the Wallet, the pazzle and the mnemonic
pub fn create_wallet_v0(
security_img: Vec<u8>,
security_txt: String,
pin: [u8; 4],
pazzle_length: u8,
send_bootstrap: Option<&Bootstrap>,
send_wallet: bool,
peer_id: PubKey,
nonce: u64,
) -> Result<(Wallet, Vec<u8>, [u16; 12]), NgWalletError> {
// TODO : use some automatically zeroed variable for the 2 first arguments, and for the returned values
// pazzle_length can only be 9, 12, or 15
if (pazzle_length != 9 && pazzle_length != 12 && pazzle_length != 15) {
return Err(NgWalletError::InvalidPazzleLength);
}
// cannot submit wallet if we don't submit also the bootstrap
if send_bootstrap.is_none() && send_wallet {
return Err(NgWalletError::SubmissionError);
}
// check validity of PIN
// shouldn't start with 0
if pin[0] == 0 {
return Err(NgWalletError::InvalidPin);
}
// each digit shouldnt be greater than 9
if pin[0] > 9 || pin[1] > 9 || pin[2] > 9 || pin[3] > 9 {
return Err(NgWalletError::InvalidPin);
}
// check for uniqueness of each digit
if pin[1] == pin[0]
|| pin[1] == pin[2]
|| pin[1] == pin[3]
|| pin[2] == pin[0]
|| pin[2] == pin[3]
|| pin[3] == pin[0]
{
return Err(NgWalletError::InvalidPin);
}
// check for ascending series
if pin[1] == pin[0] + 1 && pin[2] == pin[1] + 1 && pin[3] == pin[2] + 1 {
return Err(NgWalletError::InvalidPin);
}
// check for descending series
if pin[3] >= 3 && pin[2] == pin[3] - 1 && pin[1] == pin[2] - 1 && pin[0] == pin[1] - 1 {
return Err(NgWalletError::InvalidPin);
}
// check validity of security text
let words: Vec<_> = security_txt.split_whitespace().collect();
let new_string = words.join(" ");
let count = new_string.chars().count();
if count < 10 || count > 100 {
return Err(NgWalletError::InvalidSecurityText);
}
// check validity of image
let decoded_img = ImageReader::new(Cursor::new(security_img))
.with_guessed_format()
.map_err(|e| NgWalletError::InvalidSecurityImage)?
.decode()
.map_err(|e| NgWalletError::InvalidSecurityImage)?;
if decoded_img.height() < 150 || decoded_img.width() < 150 {
return Err(NgWalletError::InvalidSecurityImage);
}
let resized_img = decoded_img.resize_to_fill(400, 400, FilterType::Triangle);
let buffer: Vec<u8> = Vec::with_capacity(100000);
let mut cursor = Cursor::new(buffer);
resized_img
.write_to(&mut cursor, ImageOutputFormat::Jpeg(72))
.map_err(|e| NgWalletError::InvalidSecurityImage)?;
// creating the wallet keys
let (wallet_key, wallet_id) = generate_keypair();
let site = Site::create(SiteType::Individual).map_err(|e| NgWalletError::InternalError)?;
// let mut pazzle_random = vec![0u8; pazzle_length.into()];
// getrandom::getrandom(&mut pazzle_random).map_err(|e| NgWalletError::InternalError)?;
let mut pazzle = vec![0u8; pazzle_length.into()];
let mut ran = thread_rng();
for i in &mut pazzle {
*i = ran.gen_range(0, 16);
}
//println!("pazzle {:?}", pazzle);
let mut mnemonic = [0u16; 12];
for i in &mut mnemonic {
*i = ran.gen_range(0, 2048);
}
//println!("mnemonic {:?}", display_mnemonic(&mnemonic));
//slice_as_array!(&mnemonic, [String; 12])
//.ok_or(NgWalletError::InternalError)?
//.clone(),
let encrypted_block = EncryptedWalletV0 {
pazzle: pazzle.clone(),
mnemonic,
pin,
sites: vec![site],
};
let mut salt_pazzle = [0u8; 16];
getrandom::getrandom(&mut salt_pazzle).map_err(|e| NgWalletError::InternalError)?;
let mut salt_mnemonic = [0u8; 16];
getrandom::getrandom(&mut salt_mnemonic).map_err(|e| NgWalletError::InternalError)?;
//println!("salt_pazzle {:?}", salt_pazzle);
//println!("salt_mnemonic {:?}", salt_mnemonic);
let pazzle_key = derive_key_from_pass(
[pazzle.clone(), pin.to_vec()].concat(),
salt_pazzle,
wallet_id,
);
let mnemonic_key = derive_key_from_pass(
[transmute_to_bytes(&mnemonic), &pin].concat(),
salt_mnemonic,
wallet_id,
);
let mut master_key = [0u8; 32];
getrandom::getrandom(&mut master_key).map_err(|e| NgWalletError::InternalError)?;
let enc_master_key_pazzle = enc_master_key(master_key, pazzle_key, 0, wallet_id)?;
let enc_master_key_mnemonic = enc_master_key(master_key, mnemonic_key, 0, wallet_id)?;
let timestamp = now_timestamp();
let encrypted = enc_encrypted_block(
&encrypted_block,
master_key,
peer_id,
nonce,
timestamp,
wallet_id,
)?;
let wallet_content = WalletContentV0 {
security_img: cursor.into_inner(),
security_txt: new_string,
salt_pazzle,
salt_mnemonic,
enc_master_key_pazzle,
enc_master_key_mnemonic,
master_nonce: 0,
timestamp,
peer_id,
nonce,
encrypted,
};
let ser_wallet = serde_bare::to_vec(&wallet_content).unwrap();
let sig = sign(wallet_key, wallet_id, &ser_wallet).unwrap();
let wallet_v0 = WalletV0 {
/// ID
id: wallet_id,
/// Content
content: wallet_content,
/// Signature over content by wallet's private key
sig,
};
// let content = BootstrapContentV0 { servers: vec![] };
// let ser = serde_bare::to_vec(&content).unwrap();
// let sig = sign(wallet_key, wallet_id, &ser).unwrap();
// let bootstrap = Bootstrap::V0(BootstrapV0 {
// id: wallet_id,
// content,
// sig,
// });
// TODO send bootstrap (if)
// TODO send wallet (if)
Ok((Wallet::V0(wallet_v0), pazzle, mnemonic))
}
#[cfg(test)]
mod tests {
use super::*;
use p2p_repo::utils::generate_keypair;
use std::fs::File;
use std::io::BufReader;
use std::io::Read;
use std::io::Write;
use std::time::Instant;
#[test]
fn create_wallet() {
// loading an image file from disk
let f = File::open("tests/valid_security_image.jpg")
.expect("open of tests/valid_security_image.jpg");
let mut reader = BufReader::new(f);
let mut img_buffer = Vec::new();
// Read file into vector.
reader
.read_to_end(&mut img_buffer)
.expect("read of valid_security_image.jpg");
let pin = [5, 2, 9, 1];
let creation = Instant::now();
let res = create_wallet_v0(
img_buffer,
" know yourself ".to_string(),
pin,
9,
None,
false,
PubKey::Ed25519PubKey([
119, 251, 253, 29, 135, 199, 254, 50, 134, 67, 1, 208, 117, 196, 167, 107, 2, 113,
98, 243, 49, 90, 7, 0, 157, 58, 14, 187, 14, 3, 116, 86,
]),
0,
)
.expect("create_wallet_v0");
log!(
"creation of wallet took: {} ms",
creation.elapsed().as_millis()
);
log!("-----------------------------");
let (wallet, pazzle, mnemonic) = res;
let mut file = File::create("tests/wallet.ngw").expect("open wallet write file");
let ser_wallet = to_vec(&NgFile::V0(NgFileV0::Wallet(wallet.clone()))).unwrap();
file.write_all(&ser_wallet);
log!("wallet id: {:?}", base64_url::encode(&wallet.id().slice()));
log!("pazzle {:?}", pazzle);
log!("mnemonic {:?}", display_mnemonic(&mnemonic));
log!("pin {:?}", pin);
if let Wallet::V0(v0) = wallet {
log!("security text: {:?}", v0.content.security_txt);
let mut file =
File::create("tests/generated_security_image.jpg").expect("open write file");
file.write_all(&v0.content.security_img);
let f = File::open("tests/generated_security_image.jpg.compare")
.expect("open of generated_security_image.jpg.compare");
let mut reader = BufReader::new(f);
let mut generated_security_image_compare = Vec::new();
// Read file into vector.
reader
.read_to_end(&mut generated_security_image_compare)
.expect("read of generated_security_image.jpg.compare");
assert_eq!(v0.content.security_img, generated_security_image_compare);
let opening_mnemonic = Instant::now();
let w = open_wallet_with_mnemonic(Wallet::V0(v0.clone()), mnemonic, pin)
.expect("open with mnemonic");
//println!("encrypted part {:?}", w);
log!(
"opening of wallet with mnemonic took: {} ms",
opening_mnemonic.elapsed().as_millis()
);
let opening_pazzle = Instant::now();
let w = open_wallet_with_pazzle(Wallet::V0(v0.clone()), pazzle, pin)
.expect("open with pazzle");
//println!("encrypted part {:?}", w);
log!(
"opening of wallet with pazzle took: {} ms",
opening_pazzle.elapsed().as_millis()
);
}
}
}

@ -0,0 +1,233 @@
// 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.
use serde::{Deserialize, Serialize};
use serde_big_array::BigArray;
use p2p_net::types::NetAddr;
use p2p_repo::types::*;
/// WalletId is a PubKey
pub type WalletId = PubKey;
/// BootstrapId is a WalletId
pub type BootstrapId = WalletId;
/// BootstrapServer type
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub enum BoostrapServerTypeV0 {
Localhost,
BoxPrivate(Vec<NetAddr>),
BoxPublic(Vec<NetAddr>),
BoxPublicDyn(Vec<NetAddr>), // can be empty
Domain(String),
}
/// BootstrapServer details Version 0
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct BootstrapServerV0 {
/// Network addresses
pub serverType: BoostrapServerTypeV0,
/// peerId of the server
pub peerId: PubKey,
}
/// Bootstrap content Version 0
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct BootstrapContentV0 {
/// list of servers, in order of preference
pub servers: Vec<BootstrapServerV0>,
}
/// Bootstrap Version 0
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct BootstrapV0 {
/// ID
pub id: BootstrapId,
/// Content
pub content: BootstrapContentV0,
/// Signature over content by wallet's private key
pub sig: Sig,
}
/// Bootstrap info
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum Bootstrap {
V0(BootstrapV0),
}
impl Bootstrap {
pub fn id(&self) -> BootstrapId {
match self {
Bootstrap::V0(v0) => v0.id,
}
}
pub fn content_as_bytes(&self) -> Vec<u8> {
match self {
Bootstrap::V0(v0) => serde_bare::to_vec(&v0.content).unwrap(),
}
}
pub fn sig(&self) -> Sig {
match self {
Bootstrap::V0(v0) => v0.sig,
}
}
}
/// EncryptedWallet block Version 0
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct EncryptedWalletV0 {
#[serde(with = "serde_bytes")]
pub pazzle: Vec<u8>,
pub mnemonic: [u16; 12],
pub pin: [u8; 4],
// first in the list is the main Site (Personal)
pub sites: Vec<Site>,
}
/// EncryptedWallet block
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum EncryptedWallet {
V0(EncryptedWalletV0),
}
/// Wallet content Version 0
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct WalletContentV0 {
#[serde(with = "serde_bytes")]
pub security_img: Vec<u8>,
pub security_txt: String,
pub salt_pazzle: [u8; 16],
pub salt_mnemonic: [u8; 16],
// encrypted master keys. first is encrypted with pazzle, second is encrypted with mnemonic
// AD = wallet_id
#[serde(with = "BigArray")]
pub enc_master_key_pazzle: [u8; 48],
#[serde(with = "BigArray")]
pub enc_master_key_mnemonic: [u8; 48],
// nonce for the encryption of masterkey
// incremented only if the masterkey changes
// be very careful with incrementing this, as a conflict would result in total loss of crypto guarantees.
pub master_nonce: u8,
pub timestamp: Timestamp,
// the peerId that update this version of the Wallet. this value is truncated by half and concatenated with the nonce
pub peer_id: PubKey,
pub nonce: u64,
// EncryptedWallet content encrypted with XChaCha20Poly1305, AD = timestamp and walletID
#[serde(with = "serde_bytes")]
pub encrypted: Vec<u8>,
}
/// Wallet Version 0
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct WalletV0 {
/// ID
pub id: WalletId,
/// Content
pub content: WalletContentV0,
/// Signature over content by wallet's private key
pub sig: Sig,
}
/// Wallet info
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum Wallet {
V0(WalletV0),
}
impl Wallet {
pub fn id(&self) -> WalletId {
match self {
Wallet::V0(v0) => v0.id,
}
}
pub fn content_as_bytes(&self) -> Vec<u8> {
match self {
Wallet::V0(v0) => serde_bare::to_vec(&v0.content).unwrap(),
}
}
pub fn sig(&self) -> Sig {
match self {
Wallet::V0(v0) => v0.sig,
}
}
}
/// Add Wallet Version 0
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct AddWalletV0 {
/// wallet. optional (for those who chose not to upload their wallet to nextgraph.one server)
pub wallet: Option<Wallet>,
/// bootstrap
pub bootstrap: Bootstrap,
}
/// Add Wallet
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum AddWallet {
V0(AddWalletV0),
}
impl AddWallet {
pub fn id(&self) -> BootstrapId {
match self {
AddWallet::V0(v0) => v0.bootstrap.id(),
}
}
pub fn bootstrap(&self) -> &Bootstrap {
match self {
AddWallet::V0(v0) => &v0.bootstrap,
}
}
pub fn wallet(&self) -> Option<&Wallet> {
match self {
AddWallet::V0(v0) => v0.wallet.as_ref(),
}
}
}
#[derive(Debug, Eq, PartialEq, Clone)]
pub enum NgWalletError {
InvalidPin,
InvalidPazzleLength,
InvalidSecurityImage,
InvalidSecurityText,
SubmissionError,
InternalError,
EncryptionError,
DecryptionError,
InvalidSignature,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum NgFileV0 {
Wallet(Wallet),
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum NgFile {
V0(NgFileV0),
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB

@ -13,7 +13,7 @@ p2p-repo = { path = "../p2p-repo" }
p2p-net = { path = "../p2p-net" }
p2p-client-ws = { path = "../p2p-client-ws" }
p2p-broker = { path = "../p2p-broker" }
p2p-stores-lmdb = { path = "../p2p-stores-lmdb" }
stores-lmdb = { path = "../stores-lmdb" }
async-std = { version = "1.12.0", features = ["attributes"] }
futures = "0.3.24"
tempfile = "3"

@ -16,8 +16,8 @@ use futures::{future, pin_mut, stream, SinkExt, StreamExt};
use p2p_broker::broker_store::config::ConfigMode;
use p2p_repo::object::Object;
use p2p_repo::store::{store_max_value_size, store_valid_value_size, HashMapRepoStore, RepoStore};
use p2p_stores_lmdb::broker_store::LmdbBrokerStore;
use p2p_stores_lmdb::repo_store::LmdbRepoStore;
use stores_lmdb::kcv_store::LmdbKCVStore;
use stores_lmdb::repo_store::LmdbRepoStore;
use rand::rngs::OsRng;
use std::collections::HashMap;
@ -557,7 +557,7 @@ async fn test_local_connection() {
let master_key: [u8; 32] = [0; 32];
std::fs::create_dir_all(root.path()).unwrap();
println!("{}", root.path().to_str().unwrap());
let store = LmdbBrokerStore::open(root.path(), master_key);
let store = LmdbKCVStore::open(root.path(), master_key);
//let mut server = BrokerServer::new(store, ConfigMode::Local).expect("starting broker");

@ -0,0 +1,29 @@
[package]
name = "ngone"
version = "0.1.0"
edition = "2021"
license = "MIT/Apache-2.0"
authors = ["Niko PLP <niko@nextgraph.org>"]
description = "nextgraph.one server. used to bootstrap the app"
repository = "https://git.nextgraph.org/NextGraph/nextgraph-rs"
[dependencies]
tokio = { version = "1.27", features = ["full"] }
warp = "0.3"
warp-embed = "0.4"
rust-embed = "6"
log = "0.4"
env_logger = "0.10"
stores-lmdb = { path = "../stores-lmdb" }
p2p-repo = { path = "../p2p-repo" }
p2p-net = { path = "../p2p-net" }
ng-wallet = { path = "../ng-wallet" }
serde = { version = "1.0.142", features = ["derive"] }
serde_bare = "0.5.0"
serde_bytes = "0.11.7"
serde-big-array = "0.5.1"
base64-url = "2.0.0"
slice_as_array = "1.1.0"
serde_json = "1.0.96"
debug_print = "1.0.0"
bytes = "1.0"

@ -0,0 +1,36 @@
# nextgraph.one server (ngone)
This server is used internally by NextGraph to redirect users to the right app server from web clients. You probably don't need this server in your infrastructure, even if you decide to self-host a broker under a domain name.
## Install
```
cd web
pnpm install
```
## Dev
```
cd web
pnpm run dev
// in another terminal
cd ../
cargo watch -c -w src -x run
// then open http://localhost:5173/
```
## Build
```
cd web
pnpm run build
cd ..
cargo build --release
```
## run
```
../target/release/ngone
```

Binary file not shown.

Binary file not shown.

@ -0,0 +1,215 @@
// 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.
#[macro_use]
extern crate slice_as_array;
mod store;
mod types;
use p2p_repo::store::StorageError;
use warp::reply::Response;
use warp::{Filter, Reply};
use debug_print::debug_println;
use rust_embed::RustEmbed;
use serde_bare::{from_slice, to_vec};
use serde_json::json;
use std::sync::Arc;
use std::{env, fs};
use crate::store::wallet_record::*;
use crate::types::*;
use ng_wallet::types::*;
use p2p_repo::types::*;
use p2p_repo::utils::{generate_keypair, sign, verify};
use stores_lmdb::kcv_store::LmdbKCVStore;
#[derive(RustEmbed)]
#[folder = "web/dist"]
struct Static;
struct Server {
store: LmdbKCVStore,
}
impl Server {
fn add_wallet(&self, bytes: Vec<u8>) -> Result<Response, NgHttpError> {
let add_wallet = from_slice::<AddWallet>(&bytes).map_err(|e| NgHttpError::InvalidParams)?;
let bootstrap = add_wallet.bootstrap();
debug_println!("ADDING wallet {}", bootstrap.id());
verify(
&bootstrap.content_as_bytes(),
bootstrap.sig(),
bootstrap.id(),
)
.map_err(|e| NgHttpError::InvalidParams)?;
match add_wallet.wallet() {
Some(wallet) => {
verify(&wallet.content_as_bytes(), wallet.sig(), wallet.id())
.map_err(|e| NgHttpError::InvalidParams)?;
}
None => {}
}
let create_wallet_res = WalletRecord::create(&bootstrap.id(), bootstrap, &self.store);
match create_wallet_res {
Ok(wallet_record) => {
match add_wallet.wallet() {
Some(wallet) => {
let _ = wallet_record.replace_wallet(wallet);
}
None => {}
}
return Ok(warp::http::StatusCode::CREATED.into_response());
}
Err(StorageError::AlreadyExists) => return Err(NgHttpError::AlreadyExists),
Err(_) => return Err(NgHttpError::InternalError),
}
}
pub fn upload_wallet(&self, bytes: Vec<u8>) -> Response {
match self.add_wallet(bytes) {
Ok(_) => warp::http::StatusCode::CREATED.into_response(),
Err(e) => e.into_response(),
}
}
fn get_wallet(&self, encoded_id: String) -> Result<Response, NgHttpError> {
debug_println!("DOWNLOAD wallet {}", encoded_id);
let id = base64_url::decode(&encoded_id).map_err(|e| NgHttpError::InvalidParams)?;
let array = slice_as_array!(&id, [u8; 32]).ok_or(NgHttpError::InvalidParams)?;
let wallet_id = PubKey::Ed25519PubKey(*array);
let wallet_record =
WalletRecord::open(&wallet_id, &self.store).map_err(|e| NgHttpError::NotFound)?;
let wallet = wallet_record.wallet().map_err(|e| NgHttpError::NotFound)?;
let data = to_vec(&wallet).map_err(|e| NgHttpError::NotFound)?;
Ok(Response::new(data.into()))
}
pub fn download_wallet(&self, encoded_id: String) -> Response {
match self.get_wallet(encoded_id) {
Ok(res) => res,
Err(e) => e.into_response(),
}
}
fn get_bootstrap(&self, encoded_id: String) -> Result<Response, NgHttpError> {
debug_println!("DOWNLOAD bootstrap {}", encoded_id);
let id = base64_url::decode(&encoded_id).map_err(|e| NgHttpError::InvalidParams)?;
let array = slice_as_array!(&id, [u8; 32]).ok_or(NgHttpError::InvalidParams)?;
let wallet_id = PubKey::Ed25519PubKey(*array);
let wallet_record =
WalletRecord::open(&wallet_id, &self.store).map_err(|e| NgHttpError::NotFound)?;
let bootstrap = wallet_record
.bootstrap()
.map_err(|e| NgHttpError::NotFound)?;
let data = json!(bootstrap).to_string();
Ok(Response::new(data.into()))
}
pub fn download_bootstrap(&self, encoded_id: String) -> Response {
match self.get_bootstrap(encoded_id) {
Ok(res) => res,
Err(e) => e.into_response(),
}
}
// pub fn create_wallet_record(&self, bootstrap: &Bootstrap) {
// let wallet = WalletRecord::create(&bootstrap.id(), bootstrap, &self.store).unwrap();
// println!(
// "wallet created {}",
// base64_url::encode(&wallet.id().slice())
// );
// }
// pub fn open_wallet_record(&self, wallet_id: &WalletId) -> WalletRecord {
// let wallet2 = WalletRecord::open(wallet_id, &self.store).unwrap();
// println!(
// "wallet opened {}",
// base64_url::encode(&wallet2.id().slice())
// );
// wallet2
// }
}
#[tokio::main]
async fn main() {
if std::env::var("RUST_LOG").is_err() {
std::env::set_var("RUST_LOG", "info"); //trace
}
env_logger::init();
let path_str = "data";
let mut dir = env::current_dir().unwrap();
dir.push(path_str);
// FIXME: use a real key for encryption at rest
let key: [u8; 32] = [0; 32];
println!("{}", dir.to_str().unwrap());
fs::create_dir_all(dir.clone()).unwrap();
let mut store = LmdbKCVStore::open(&dir, key);
let server = Arc::new(Server { store });
// let (wallet_key, wallet_id) = generate_keypair();
// let content = BootstrapContentV0 { servers: vec![] };
// let ser = serde_bare::to_vec(&content).unwrap();
// let sig = sign(wallet_key, wallet_id, &ser).unwrap();
// let bootstrap = Bootstrap::V0(BootstrapV0 {
// id: wallet_id,
// content,
// sig,
// });
// POST /api/v1/wallet with body containing a serialized AddWallet => 201 CREATED
let server_for_move = Arc::clone(&server);
let wallet_post_api = warp::post()
.and(warp::body::content_length_limit(1024 * 1024)) // 1 MB max
.and(warp::path!("wallet"))
.and(warp::body::bytes())
.map(move |bytes: bytes::Bytes| server_for_move.upload_wallet(bytes.to_vec()));
// GET /api/v1/wallet/:walletid => 200 OK with body serialized wallet
let server_for_move = Arc::clone(&server);
let wallet_get_api = warp::get()
.and(warp::path!("wallet" / String))
.map(move |id| server_for_move.download_wallet(id));
// GET /api/v1/bootstrap/:walletid => 200 OK with body serialized bootstrap
let server_for_move = Arc::clone(&server);
let bootstrap_get_api = warp::get()
.and(warp::path!("bootstrap" / String))
.map(move |id| server_for_move.download_bootstrap(id));
let api_v1 = warp::path!("api" / "v1" / ..)
.and(wallet_get_api.or(bootstrap_get_api).or(wallet_post_api));
//.with(warp::log("request"));
let static_files = warp::get().and(warp_embed::embed(&Static)).boxed();
let mut cors = warp::cors()
.allow_methods(vec!["GET", "POST"])
.allow_headers(vec!["Content-Type"]);
#[cfg(not(debug_assertions))]
{
cors = cors.allow_origin("https://nextgraph.one");
}
#[cfg(debug_assertions)]
{
println!("ANY ORIGIN");
cors = cors.allow_any_origin();
}
log::info!("Starting server on http://localhost:3030");
warp::serve(api_v1.or(static_files).with(cors))
.run(([127, 0, 0, 1], 3030))
.await;
}

@ -0,0 +1,101 @@
// 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.
//! ng-one bootstrap
use p2p_net::types::*;
use p2p_repo::kcv_store::KCVStore;
use p2p_repo::store::*;
use p2p_repo::types::PubKey;
use serde::{Deserialize, Serialize};
use serde_bare::{from_slice, to_vec};
pub struct DynBox<'a> {
/// peer ID
id: PubKey,
store: &'a dyn KCVStore,
}
impl<'a> DynBox<'a> {
const PREFIX: u8 = b"d"[0];
// properties' suffixes
const ADDRS: u8 = b"a"[0];
const ALL_PROPERTIES: [u8; 1] = [Self::ADDRS];
const SUFFIX_FOR_EXIST_CHECK: u8 = Self::ADDRS;
pub fn open(id: &PubKey, store: &'a dyn KCVStore) -> Result<DynBox<'a>, StorageError> {
let opening = DynBox {
id: id.clone(),
store,
};
if !opening.exists() {
return Err(StorageError::NotFound);
}
Ok(opening)
}
pub fn create(
id: &PubKey,
addrs: &Vec<NetAddr>,
store: &'a dyn KCVStore,
) -> Result<DynBox<'a>, StorageError> {
let acc = DynBox {
id: id.clone(),
store,
};
if acc.exists() {
return Err(StorageError::BackendError);
}
store.write_transaction(&|tx| {
tx.put(
Self::PREFIX,
&to_vec(&id)?,
Some(Self::ADDRS),
&to_vec(&addrs)?,
)?;
Ok(())
})?;
Ok(acc)
}
pub fn exists(&self) -> bool {
self.store
.get(
Self::PREFIX,
&to_vec(&self.id).unwrap(),
Some(Self::SUFFIX_FOR_EXIST_CHECK),
)
.is_ok()
}
pub fn id(&self) -> PubKey {
self.id
}
pub fn replace_addresses(&self, addrs: &Vec<NetAddr>) -> Result<(), StorageError> {
if !self.exists() {
return Err(StorageError::BackendError);
}
self.store.replace(
Self::PREFIX,
&to_vec(&self.id)?,
Some(Self::ADDRS),
to_vec(addrs)?,
)
}
pub fn remove_addresses(&self) -> Result<(), StorageError> {
self.store
.del(Self::PREFIX, &to_vec(&self.id)?, Some(Self::ADDRS))
}
pub fn del(&self) -> Result<(), StorageError> {
self.store
.del_all(Self::PREFIX, &to_vec(&self.id)?, &Self::ALL_PROPERTIES)
}
}

@ -0,0 +1,3 @@
pub mod dynbox;
pub mod wallet_record;

@ -0,0 +1,129 @@
// 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.
//! ng-wallet
use ng_wallet::types::*;
use p2p_repo::kcv_store::KCVStore;
use p2p_repo::store::*;
use p2p_repo::types::*;
use serde::{Deserialize, Serialize};
use serde_bare::{from_slice, to_vec};
pub struct WalletRecord<'a> {
/// Wallet ID
id: WalletId,
store: &'a dyn KCVStore,
}
impl<'a> WalletRecord<'a> {
const PREFIX: u8 = b"w"[0];
// properties' suffixes
const WALLET: u8 = b"w"[0];
const BOOTSTRAP: u8 = b"b"[0];
const ALL_PROPERTIES: [u8; 2] = [Self::BOOTSTRAP, Self::WALLET];
const SUFFIX_FOR_EXIST_CHECK: u8 = Self::BOOTSTRAP;
pub fn open(id: &WalletId, store: &'a dyn KCVStore) -> Result<WalletRecord<'a>, StorageError> {
let opening = WalletRecord {
id: id.clone(),
store,
};
if !opening.exists() {
return Err(StorageError::NotFound);
}
Ok(opening)
}
pub fn create(
id: &WalletId,
bootstrap: &Bootstrap,
store: &'a dyn KCVStore,
) -> Result<WalletRecord<'a>, StorageError> {
let wallet = WalletRecord {
id: id.clone(),
store,
};
if wallet.exists() {
return Err(StorageError::BackendError);
}
store.write_transaction(&|tx| {
tx.put(
Self::PREFIX,
&to_vec(&id)?,
Some(Self::BOOTSTRAP),
&to_vec(bootstrap)?,
)?;
Ok(())
})?;
Ok(wallet)
}
pub fn exists(&self) -> bool {
self.store
.get(
Self::PREFIX,
&to_vec(&self.id).unwrap(),
Some(Self::SUFFIX_FOR_EXIST_CHECK),
)
.is_ok()
}
pub fn id(&self) -> WalletId {
self.id
}
pub fn replace_wallet(&self, wallet: &Wallet) -> Result<(), StorageError> {
if !self.exists() {
return Err(StorageError::BackendError);
}
self.store.replace(
Self::PREFIX,
&to_vec(&self.id)?,
Some(Self::WALLET),
to_vec(wallet)?,
)
}
pub fn replace_bootstrap(&self, boot: &Bootstrap) -> Result<(), StorageError> {
if !self.exists() {
return Err(StorageError::BackendError);
}
self.store.replace(
Self::PREFIX,
&to_vec(&self.id)?,
Some(Self::BOOTSTRAP),
to_vec(boot)?,
)
}
pub fn wallet(&self) -> Result<Wallet, StorageError> {
match self
.store
.get(Self::PREFIX, &to_vec(&self.id)?, Some(Self::WALLET))
{
Ok(w) => Ok(from_slice::<Wallet>(&w)?),
Err(e) => Err(e),
}
}
pub fn bootstrap(&self) -> Result<Bootstrap, StorageError> {
match self
.store
.get(Self::PREFIX, &to_vec(&self.id)?, Some(Self::BOOTSTRAP))
{
Ok(meta) => Ok(from_slice::<Bootstrap>(&meta)?),
Err(e) => Err(e),
}
}
pub fn del(&self) -> Result<(), StorageError> {
self.store
.del_all(Self::PREFIX, &to_vec(&self.id)?, &Self::ALL_PROPERTIES)
}
}

@ -0,0 +1,30 @@
// 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.
use warp::{reply::Response, Reply};
pub enum NgHttpError {
InvalidParams,
NotFound,
AlreadyExists,
InternalError,
}
impl Reply for NgHttpError {
fn into_response(self) -> Response {
match (self) {
NgHttpError::NotFound => warp::http::StatusCode::NOT_FOUND.into_response(),
NgHttpError::InvalidParams => warp::http::StatusCode::BAD_REQUEST.into_response(),
NgHttpError::AlreadyExists => warp::http::StatusCode::CONFLICT.into_response(),
NgHttpError::InternalError => {
warp::http::StatusCode::INTERNAL_SERVER_ERROR.into_response()
}
}
}
}

@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

@ -0,0 +1,3 @@
{
"recommendations": ["svelte.svelte-vscode"]
}

@ -0,0 +1,23 @@
<!--
// 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.
-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + Svelte</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>

@ -0,0 +1,32 @@
{
"compilerOptions": {
"moduleResolution": "bundler",
"target": "ESNext",
"module": "ESNext",
/**
* svelte-preprocess cannot figure out whether you have
* a value or a type, so tell TypeScript to enforce using
* `import type` instead of `import` for Types.
*/
"verbatimModuleSyntax": true,
"isolatedModules": true,
"resolveJsonModule": true,
/**
* To have warnings / errors of the Svelte compiler at the
* correct position, enable source maps by default.
*/
"sourceMap": true,
"esModuleInterop": true,
"skipLibCheck": true,
/**
* Typecheck JS in `.svelte` and `.js` files by default.
* Disable this if you'd like to use dynamic types.
*/
"checkJs": true
},
/**
* Use global.d.ts instead of compilerOptions.types
* to avoid limiting type declarations.
*/
"include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.svelte"]
}

@ -0,0 +1,16 @@
{
"name": "ng-one-web",
"private": true,
"version": "0.1.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build --base=./",
"preview": "vite preview"
},
"devDependencies": {
"@sveltejs/vite-plugin-svelte": "^2.0.4",
"svelte": "^3.58.0",
"vite": "^4.3.9"
}
}

@ -0,0 +1,411 @@
lockfileVersion: 5.4
specifiers:
'@sveltejs/vite-plugin-svelte': ^2.0.4
svelte: ^3.58.0
vite: ^4.3.9
devDependencies:
'@sveltejs/vite-plugin-svelte': 2.4.1_svelte@3.59.1+vite@4.3.9
svelte: 3.59.1
vite: 4.3.9
packages:
/@esbuild/android-arm/0.17.19:
resolution: {integrity: sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==}
engines: {node: '>=12'}
cpu: [arm]
os: [android]
requiresBuild: true
dev: true
optional: true
/@esbuild/android-arm64/0.17.19:
resolution: {integrity: sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==}
engines: {node: '>=12'}
cpu: [arm64]
os: [android]
requiresBuild: true
dev: true
optional: true
/@esbuild/android-x64/0.17.19:
resolution: {integrity: sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==}
engines: {node: '>=12'}
cpu: [x64]
os: [android]
requiresBuild: true
dev: true
optional: true
/@esbuild/darwin-arm64/0.17.19:
resolution: {integrity: sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==}
engines: {node: '>=12'}
cpu: [arm64]
os: [darwin]
requiresBuild: true
dev: true
optional: true
/@esbuild/darwin-x64/0.17.19:
resolution: {integrity: sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==}
engines: {node: '>=12'}
cpu: [x64]
os: [darwin]
requiresBuild: true
dev: true
optional: true
/@esbuild/freebsd-arm64/0.17.19:
resolution: {integrity: sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==}
engines: {node: '>=12'}
cpu: [arm64]
os: [freebsd]
requiresBuild: true
dev: true
optional: true
/@esbuild/freebsd-x64/0.17.19:
resolution: {integrity: sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==}
engines: {node: '>=12'}
cpu: [x64]
os: [freebsd]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-arm/0.17.19:
resolution: {integrity: sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==}
engines: {node: '>=12'}
cpu: [arm]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-arm64/0.17.19:
resolution: {integrity: sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==}
engines: {node: '>=12'}
cpu: [arm64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-ia32/0.17.19:
resolution: {integrity: sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==}
engines: {node: '>=12'}
cpu: [ia32]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-loong64/0.17.19:
resolution: {integrity: sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==}
engines: {node: '>=12'}
cpu: [loong64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-mips64el/0.17.19:
resolution: {integrity: sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==}
engines: {node: '>=12'}
cpu: [mips64el]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-ppc64/0.17.19:
resolution: {integrity: sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==}
engines: {node: '>=12'}
cpu: [ppc64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-riscv64/0.17.19:
resolution: {integrity: sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==}
engines: {node: '>=12'}
cpu: [riscv64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-s390x/0.17.19:
resolution: {integrity: sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==}
engines: {node: '>=12'}
cpu: [s390x]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-x64/0.17.19:
resolution: {integrity: sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==}
engines: {node: '>=12'}
cpu: [x64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/netbsd-x64/0.17.19:
resolution: {integrity: sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==}
engines: {node: '>=12'}
cpu: [x64]
os: [netbsd]
requiresBuild: true
dev: true
optional: true
/@esbuild/openbsd-x64/0.17.19:
resolution: {integrity: sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==}
engines: {node: '>=12'}
cpu: [x64]
os: [openbsd]
requiresBuild: true
dev: true
optional: true
/@esbuild/sunos-x64/0.17.19:
resolution: {integrity: sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==}
engines: {node: '>=12'}
cpu: [x64]
os: [sunos]
requiresBuild: true
dev: true
optional: true
/@esbuild/win32-arm64/0.17.19:
resolution: {integrity: sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==}
engines: {node: '>=12'}
cpu: [arm64]
os: [win32]
requiresBuild: true
dev: true
optional: true
/@esbuild/win32-ia32/0.17.19:
resolution: {integrity: sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==}
engines: {node: '>=12'}
cpu: [ia32]
os: [win32]
requiresBuild: true
dev: true
optional: true
/@esbuild/win32-x64/0.17.19:
resolution: {integrity: sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==}
engines: {node: '>=12'}
cpu: [x64]
os: [win32]
requiresBuild: true
dev: true
optional: true
/@jridgewell/sourcemap-codec/1.4.15:
resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==}
dev: true
/@sveltejs/vite-plugin-svelte-inspector/1.0.2_qiij5gx4uovhfqjpd2vh63pzyq:
resolution: {integrity: sha512-Cy1dUMcYCnDVV/hPLXa43YZJ2jGKVW5rA0xuNL9dlmYhT0yoS1g7+FOFSRlgk0BXKk/Oc7grs+8BVA5Iz2fr8A==}
engines: {node: ^14.18.0 || >= 16}
peerDependencies:
'@sveltejs/vite-plugin-svelte': ^2.2.0
svelte: ^3.54.0 || ^4.0.0-next.0
vite: ^4.0.0
dependencies:
'@sveltejs/vite-plugin-svelte': 2.4.1_svelte@3.59.1+vite@4.3.9
debug: 4.3.4
svelte: 3.59.1
vite: 4.3.9
transitivePeerDependencies:
- supports-color
dev: true
/@sveltejs/vite-plugin-svelte/2.4.1_svelte@3.59.1+vite@4.3.9:
resolution: {integrity: sha512-bNNKvoRY89ptY7udeBSCmTdCVwkjmMcZ0j/z9J5MuedT8jPjq0zrknAo/jF1sToAza4NVaAgR9AkZoD9oJJmnA==}
engines: {node: ^14.18.0 || >= 16}
peerDependencies:
svelte: ^3.54.0 || ^4.0.0-next.0
vite: ^4.0.0
dependencies:
'@sveltejs/vite-plugin-svelte-inspector': 1.0.2_qiij5gx4uovhfqjpd2vh63pzyq
debug: 4.3.4
deepmerge: 4.3.1
kleur: 4.1.5
magic-string: 0.30.0
svelte: 3.59.1
svelte-hmr: 0.15.2_svelte@3.59.1
vite: 4.3.9
vitefu: 0.2.4_vite@4.3.9
transitivePeerDependencies:
- supports-color
dev: true
/debug/4.3.4:
resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
engines: {node: '>=6.0'}
peerDependencies:
supports-color: '*'
peerDependenciesMeta:
supports-color:
optional: true
dependencies:
ms: 2.1.2
dev: true
/deepmerge/4.3.1:
resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==}
engines: {node: '>=0.10.0'}
dev: true
/esbuild/0.17.19:
resolution: {integrity: sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==}
engines: {node: '>=12'}
hasBin: true
requiresBuild: true
optionalDependencies:
'@esbuild/android-arm': 0.17.19
'@esbuild/android-arm64': 0.17.19
'@esbuild/android-x64': 0.17.19
'@esbuild/darwin-arm64': 0.17.19
'@esbuild/darwin-x64': 0.17.19
'@esbuild/freebsd-arm64': 0.17.19
'@esbuild/freebsd-x64': 0.17.19
'@esbuild/linux-arm': 0.17.19
'@esbuild/linux-arm64': 0.17.19
'@esbuild/linux-ia32': 0.17.19
'@esbuild/linux-loong64': 0.17.19
'@esbuild/linux-mips64el': 0.17.19
'@esbuild/linux-ppc64': 0.17.19
'@esbuild/linux-riscv64': 0.17.19
'@esbuild/linux-s390x': 0.17.19
'@esbuild/linux-x64': 0.17.19
'@esbuild/netbsd-x64': 0.17.19
'@esbuild/openbsd-x64': 0.17.19
'@esbuild/sunos-x64': 0.17.19
'@esbuild/win32-arm64': 0.17.19
'@esbuild/win32-ia32': 0.17.19
'@esbuild/win32-x64': 0.17.19
dev: true
/fsevents/2.3.2:
resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
os: [darwin]
requiresBuild: true
dev: true
optional: true
/kleur/4.1.5:
resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==}
engines: {node: '>=6'}
dev: true
/magic-string/0.30.0:
resolution: {integrity: sha512-LA+31JYDJLs82r2ScLrlz1GjSgu66ZV518eyWT+S8VhyQn/JL0u9MeBOvQMGYiPk1DBiSN9DDMOcXvigJZaViQ==}
engines: {node: '>=12'}
dependencies:
'@jridgewell/sourcemap-codec': 1.4.15
dev: true
/ms/2.1.2:
resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
dev: true
/nanoid/3.3.6:
resolution: {integrity: sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==}
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
hasBin: true
dev: true
/picocolors/1.0.0:
resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
dev: true
/postcss/8.4.24:
resolution: {integrity: sha512-M0RzbcI0sO/XJNucsGjvWU9ERWxb/ytp1w6dKtxTKgixdtQDq4rmx/g8W1hnaheq9jgwL/oyEdH5Bc4WwJKMqg==}
engines: {node: ^10 || ^12 || >=14}
dependencies:
nanoid: 3.3.6
picocolors: 1.0.0
source-map-js: 1.0.2
dev: true
/rollup/3.23.0:
resolution: {integrity: sha512-h31UlwEi7FHihLe1zbk+3Q7z1k/84rb9BSwmBSr/XjOCEaBJ2YyedQDuM0t/kfOS0IxM+vk1/zI9XxYj9V+NJQ==}
engines: {node: '>=14.18.0', npm: '>=8.0.0'}
hasBin: true
optionalDependencies:
fsevents: 2.3.2
dev: true
/source-map-js/1.0.2:
resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==}
engines: {node: '>=0.10.0'}
dev: true
/svelte-hmr/0.15.2_svelte@3.59.1:
resolution: {integrity: sha512-q/bAruCvFLwvNbeE1x3n37TYFb3mTBJ6TrCq6p2CoFbSTNhDE9oAtEfpy+wmc9So8AG0Tja+X0/mJzX9tSfvIg==}
engines: {node: ^12.20 || ^14.13.1 || >= 16}
peerDependencies:
svelte: ^3.19.0 || ^4.0.0-next.0
dependencies:
svelte: 3.59.1
dev: true
/svelte/3.59.1:
resolution: {integrity: sha512-pKj8fEBmqf6mq3/NfrB9SLtcJcUvjYSWyePlfCqN9gujLB25RitWK8PvFzlwim6hD/We35KbPlRteuA6rnPGcQ==}
engines: {node: '>= 8'}
dev: true
/vite/4.3.9:
resolution: {integrity: sha512-qsTNZjO9NoJNW7KnOrgYwczm0WctJ8m/yqYAMAK9Lxt4SoySUfS5S8ia9K7JHpa3KEeMfyF8LoJ3c5NeBJy6pg==}
engines: {node: ^14.18.0 || >=16.0.0}
hasBin: true
peerDependencies:
'@types/node': '>= 14'
less: '*'
sass: '*'
stylus: '*'
sugarss: '*'
terser: ^5.4.0
peerDependenciesMeta:
'@types/node':
optional: true
less:
optional: true
sass:
optional: true
stylus:
optional: true
sugarss:
optional: true
terser:
optional: true
dependencies:
esbuild: 0.17.19
postcss: 8.4.24
rollup: 3.23.0
optionalDependencies:
fsevents: 2.3.2
dev: true
/vitefu/0.2.4_vite@4.3.9:
resolution: {integrity: sha512-fanAXjSaf9xXtOOeno8wZXIhgia+CZury481LsDaV++lSvcU2R9Ch2bPh3PYFyoHW+w9LqAeYRISVQjUIew14g==}
peerDependencies:
vite: ^3.0.0 || ^4.0.0
peerDependenciesMeta:
vite:
optional: true
dependencies:
vite: 4.3.9
dev: true

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

@ -0,0 +1,62 @@
<!--
// 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 svelteLogo from "./assets/svelte.svg";
import viteLogo from "/vite.svg";
import Counter from "./lib/Counter.svelte";
import Greeting from "./Greeting.svelte";
</script>
<main>
<div>
<a href="https://vitejs.dev" target="_blank" rel="noreferrer">
<img src={viteLogo} class="logo" alt="Vite Logo" />
</a>
<a href="https://svelte.dev" target="_blank" rel="noreferrer">
<img src={svelteLogo} class="logo svelte" alt="Svelte Logo" />
</a>
</div>
<h1>Vite + Svelte</h1>
<Greeting />
<div class="card">
<Counter />
</div>
<p>
Check out <a
href="https://github.com/sveltejs/kit#readme"
target="_blank"
rel="noreferrer">SvelteKit</a
>, the official Svelte app framework powered by Vite!
</p>
<p class="read-the-docs">Click on the Vite and Svelte logos to learn more</p>
</main>
<style>
.logo {
height: 6em;
padding: 1.5em;
will-change: filter;
transition: filter 300ms;
}
.logo:hover {
filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.svelte:hover {
filter: drop-shadow(0 0 2em #ff3e00aa);
}
.read-the-docs {
color: #888;
}
</style>

@ -0,0 +1,25 @@
<script>
import { onMount } from "svelte";
const api_url = import.meta.env.PROD
? "api/v1/"
: "http://localhost:3030/api/v1/";
let greeting = "";
async function getWallet() {
const opts = {
method: "get",
};
const response = await fetch(
api_url + "bootstrap/I8tuoVE-LRH1wuWQpDBPivlSX8Wle39uHSL576BTxsk",
opts
);
const result = await response.json();
console.log("Result:", result);
}
onMount(() => getWallet());
</script>
<h1>{greeting}</h1>

@ -0,0 +1,91 @@
/*
// 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.
*/
:root {
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
line-height: 1.5;
font-weight: 400;
color-scheme: light dark;
color: rgba(255, 255, 255, 0.87);
background-color: #242424;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-webkit-text-size-adjust: 100%;
}
a {
font-weight: 500;
color: #646cff;
text-decoration: inherit;
}
a:hover {
color: #535bf2;
}
body {
margin: 0;
display: flex;
place-items: center;
min-width: 320px;
min-height: 100vh;
}
h1 {
font-size: 3.2em;
line-height: 1.1;
}
.card {
padding: 2em;
}
#app {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
text-align: center;
}
button {
border-radius: 8px;
border: 1px solid transparent;
padding: 0.6em 1.2em;
font-size: 1em;
font-weight: 500;
font-family: inherit;
background-color: #1a1a1a;
cursor: pointer;
transition: border-color 0.25s;
}
button:hover {
border-color: #646cff;
}
button:focus,
button:focus-visible {
outline: 4px auto -webkit-focus-ring-color;
}
@media (prefers-color-scheme: light) {
:root {
color: #213547;
background-color: #ffffff;
}
a:hover {
color: #747bff;
}
button {
background-color: #f9f9f9;
}
}

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="26.6" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 308"><path fill="#FF3E00" d="M239.682 40.707C211.113-.182 154.69-12.301 113.895 13.69L42.247 59.356a82.198 82.198 0 0 0-37.135 55.056a86.566 86.566 0 0 0 8.536 55.576a82.425 82.425 0 0 0-12.296 30.719a87.596 87.596 0 0 0 14.964 66.244c28.574 40.893 84.997 53.007 125.787 27.016l71.648-45.664a82.182 82.182 0 0 0 37.135-55.057a86.601 86.601 0 0 0-8.53-55.577a82.409 82.409 0 0 0 12.29-30.718a87.573 87.573 0 0 0-14.963-66.244"></path><path fill="#FFF" d="M106.889 270.841c-23.102 6.007-47.497-3.036-61.103-22.648a52.685 52.685 0 0 1-9.003-39.85a49.978 49.978 0 0 1 1.713-6.693l1.35-4.115l3.671 2.697a92.447 92.447 0 0 0 28.036 14.007l2.663.808l-.245 2.659a16.067 16.067 0 0 0 2.89 10.656a17.143 17.143 0 0 0 18.397 6.828a15.786 15.786 0 0 0 4.403-1.935l71.67-45.672a14.922 14.922 0 0 0 6.734-9.977a15.923 15.923 0 0 0-2.713-12.011a17.156 17.156 0 0 0-18.404-6.832a15.78 15.78 0 0 0-4.396 1.933l-27.35 17.434a52.298 52.298 0 0 1-14.553 6.391c-23.101 6.007-47.497-3.036-61.101-22.649a52.681 52.681 0 0 1-9.004-39.849a49.428 49.428 0 0 1 22.34-33.114l71.664-45.677a52.218 52.218 0 0 1 14.563-6.398c23.101-6.007 47.497 3.036 61.101 22.648a52.685 52.685 0 0 1 9.004 39.85a50.559 50.559 0 0 1-1.713 6.692l-1.35 4.116l-3.67-2.693a92.373 92.373 0 0 0-28.037-14.013l-2.664-.809l.246-2.658a16.099 16.099 0 0 0-2.89-10.656a17.143 17.143 0 0 0-18.398-6.828a15.786 15.786 0 0 0-4.402 1.935l-71.67 45.674a14.898 14.898 0 0 0-6.73 9.975a15.9 15.9 0 0 0 2.709 12.012a17.156 17.156 0 0 0 18.404 6.832a15.841 15.841 0 0 0 4.402-1.935l27.345-17.427a52.147 52.147 0 0 1 14.552-6.397c23.101-6.006 47.497 3.037 61.102 22.65a52.681 52.681 0 0 1 9.003 39.848a49.453 49.453 0 0 1-22.34 33.12l-71.664 45.673a52.218 52.218 0 0 1-14.563 6.398"></path></svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

@ -0,0 +1,10 @@
<script>
let count = 0
const increment = () => {
count += 1
}
</script>
<button on:click={increment}>
count is {count}
</button>

@ -0,0 +1,8 @@
import './app.css'
import App from './App.svelte'
const app = new App({
target: document.getElementById('app'),
})
export default app

@ -0,0 +1,2 @@
/// <reference types="svelte" />
/// <reference types="vite/client" />

@ -0,0 +1,7 @@
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'
export default {
// Consult https://svelte.dev/docs#compile-time-svelte-preprocess
// for more information about preprocessors
preprocess: vitePreprocess(),
}

@ -0,0 +1,7 @@
import { defineConfig } from 'vite'
import { svelte } from '@sveltejs/vite-plugin-svelte'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [svelte()],
})

@ -12,7 +12,7 @@ debug_print = "1.0.0"
p2p-repo = { path = "../p2p-repo" }
p2p-net = { path = "../p2p-net" }
p2p-client-ws = { path = "../p2p-client-ws" }
p2p-stores-lmdb = { path = "../p2p-stores-lmdb" }
stores-lmdb = { path = "../stores-lmdb" }
chacha20 = "0.9.0"
serde = { version = "1.0", features = ["derive"] }
serde_bare = "0.5.0"

@ -1,7 +1,7 @@
// 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>
// <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
@ -9,16 +9,16 @@
//! User account
use p2p_repo::broker_store::BrokerStore;
use p2p_net::types::*;
use p2p_repo::kcv_store::KCVStore;
use p2p_repo::store::*;
use p2p_repo::types::*;
use p2p_net::types::*;
use serde_bare::to_vec;
pub struct Account<'a> {
/// User ID
id: UserId,
store: &'a dyn BrokerStore,
store: &'a dyn KCVStore,
}
impl<'a> Account<'a> {
@ -33,7 +33,7 @@ impl<'a> Account<'a> {
const SUFFIX_FOR_EXIST_CHECK: u8 = Self::ADMIN;
pub fn open(id: &UserId, store: &'a dyn BrokerStore) -> Result<Account<'a>, StorageError> {
pub fn open(id: &UserId, store: &'a dyn KCVStore) -> Result<Account<'a>, StorageError> {
let opening = Account {
id: id.clone(),
store,
@ -46,7 +46,7 @@ impl<'a> Account<'a> {
pub fn create(
id: &UserId,
admin: bool,
store: &'a dyn BrokerStore,
store: &'a dyn KCVStore,
) -> Result<Account<'a>, StorageError> {
let acc = Account {
id: id.clone(),
@ -161,7 +161,7 @@ mod test {
use p2p_repo::store::*;
use p2p_repo::types::*;
use p2p_repo::utils::*;
use p2p_stores_lmdb::broker_store::LmdbBrokerStore;
use stores_lmdb::kcv_store::LmdbKCVStore;
use std::fs;
use tempfile::Builder;
@ -174,7 +174,7 @@ mod test {
let key: [u8; 32] = [0; 32];
fs::create_dir_all(root.path()).unwrap();
println!("{}", root.path().to_str().unwrap());
let mut store = LmdbBrokerStore::open(root.path(), key);
let mut store = LmdbKCVStore::open(root.path(), key);
let user_id = PubKey::Ed25519PubKey([1; 32]);

@ -10,7 +10,7 @@
//! Broker Config, persisted to store
use p2p_net::types::*;
use p2p_repo::broker_store::BrokerStore;
use p2p_repo::kcv_store::KCVStore;
use p2p_repo::store::*;
use p2p_repo::types::*;
use serde::{Deserialize, Serialize};
@ -24,7 +24,7 @@ pub enum ConfigMode {
}
pub struct Config<'a> {
store: &'a dyn BrokerStore,
store: &'a dyn KCVStore,
}
impl<'a> Config<'a> {
@ -39,7 +39,7 @@ impl<'a> Config<'a> {
const SUFFIX_FOR_EXIST_CHECK: u8 = Self::MODE;
pub fn open(store: &'a dyn BrokerStore) -> Result<Config<'a>, StorageError> {
pub fn open(store: &'a dyn KCVStore) -> Result<Config<'a>, StorageError> {
let opening = Config { store };
if !opening.exists() {
return Err(StorageError::NotFound);
@ -48,7 +48,7 @@ impl<'a> Config<'a> {
}
pub fn get_or_create(
mode: &ConfigMode,
store: &'a dyn BrokerStore,
store: &'a dyn KCVStore,
) -> Result<Config<'a>, StorageError> {
match Self::open(store) {
Err(e) => {
@ -66,10 +66,7 @@ impl<'a> Config<'a> {
}
}
}
pub fn create(
mode: &ConfigMode,
store: &'a dyn BrokerStore,
) -> Result<Config<'a>, StorageError> {
pub fn create(mode: &ConfigMode, store: &'a dyn KCVStore) -> Result<Config<'a>, StorageError> {
let acc = Config { store };
if acc.exists() {
return Err(StorageError::BackendError);

@ -1,7 +1,7 @@
// 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>
// <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
@ -9,10 +9,10 @@
//! Overlay
use p2p_repo::broker_store::BrokerStore;
use p2p_net::types::*;
use p2p_repo::kcv_store::KCVStore;
use p2p_repo::store::*;
use p2p_repo::types::*;
use p2p_net::types::*;
use p2p_repo::utils::now_timestamp;
use serde::{Deserialize, Serialize};
use serde_bare::{from_slice, to_vec};
@ -27,7 +27,7 @@ pub struct OverlayMeta {
pub struct Overlay<'a> {
/// Overlay ID
id: OverlayId,
store: &'a dyn BrokerStore,
store: &'a dyn KCVStore,
}
impl<'a> Overlay<'a> {
@ -50,7 +50,7 @@ impl<'a> Overlay<'a> {
const SUFFIX_FOR_EXIST_CHECK: u8 = Self::SECRET;
pub fn open(id: &OverlayId, store: &'a dyn BrokerStore) -> Result<Overlay<'a>, StorageError> {
pub fn open(id: &OverlayId, store: &'a dyn KCVStore) -> Result<Overlay<'a>, StorageError> {
let opening = Overlay {
id: id.clone(),
store,
@ -64,7 +64,7 @@ impl<'a> Overlay<'a> {
id: &OverlayId,
secret: &SymKey,
repo: Option<PubKey>,
store: &'a dyn BrokerStore,
store: &'a dyn KCVStore,
) -> Result<Overlay<'a>, StorageError> {
let acc = Overlay {
id: id.clone(),
@ -85,7 +85,7 @@ impl<'a> Overlay<'a> {
Self::PREFIX,
&to_vec(&id)?,
Some(Self::REPO),
& to_vec(&repo.unwrap())?,
&to_vec(&repo.unwrap())?,
)?;
}
let meta = OverlayMeta {

@ -10,7 +10,7 @@
//! Peer
use p2p_net::types::*;
use p2p_repo::broker_store::BrokerStore;
use p2p_repo::kcv_store::KCVStore;
use p2p_repo::store::*;
use p2p_repo::types::*;
use serde::{Deserialize, Serialize};
@ -19,7 +19,7 @@ use serde_bare::{from_slice, to_vec};
pub struct Peer<'a> {
/// Topic ID
id: PeerId,
store: &'a dyn BrokerStore,
store: &'a dyn KCVStore,
}
impl<'a> Peer<'a> {
@ -33,7 +33,7 @@ impl<'a> Peer<'a> {
const SUFFIX_FOR_EXIST_CHECK: u8 = Self::VERSION;
pub fn open(id: &PeerId, store: &'a dyn BrokerStore) -> Result<Peer<'a>, StorageError> {
pub fn open(id: &PeerId, store: &'a dyn KCVStore) -> Result<Peer<'a>, StorageError> {
let opening = Peer {
id: id.clone(),
store,
@ -45,7 +45,7 @@ impl<'a> Peer<'a> {
}
pub fn update_or_create(
advert: &PeerAdvert,
store: &'a dyn BrokerStore,
store: &'a dyn KCVStore,
) -> Result<Peer<'a>, StorageError> {
let id = advert.peer();
match Self::open(id, store) {
@ -62,10 +62,7 @@ impl<'a> Peer<'a> {
}
}
}
pub fn create(
advert: &PeerAdvert,
store: &'a dyn BrokerStore,
) -> Result<Peer<'a>, StorageError> {
pub fn create(advert: &PeerAdvert, store: &'a dyn KCVStore) -> Result<Peer<'a>, StorageError> {
let id = advert.peer();
let acc = Peer {
id: id.clone(),

@ -1,7 +1,7 @@
// 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>
// <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
@ -12,10 +12,10 @@
//! A repoStore is identified by its repo pubkey if in local mode
//! In core mode, it is identified by the overlayid.
use p2p_repo::broker_store::BrokerStore;
use p2p_net::types::*;
use p2p_repo::kcv_store::KCVStore;
use p2p_repo::store::*;
use p2p_repo::types::*;
use p2p_net::types::*;
use serde::{Deserialize, Serialize};
use serde_bare::{from_slice, to_vec};
@ -34,7 +34,7 @@ use serde_bare::{from_slice, to_vec};
pub struct RepoStoreInfo<'a> {
/// RepoStore ID
id: RepoHash,
store: &'a dyn BrokerStore,
store: &'a dyn KCVStore,
}
impl<'a> RepoStoreInfo<'a> {
@ -47,10 +47,7 @@ impl<'a> RepoStoreInfo<'a> {
const SUFFIX_FOR_EXIST_CHECK: u8 = Self::KEY;
pub fn open(
id: &RepoHash,
store: &'a dyn BrokerStore,
) -> Result<RepoStoreInfo<'a>, StorageError> {
pub fn open(id: &RepoHash, store: &'a dyn KCVStore) -> Result<RepoStoreInfo<'a>, StorageError> {
let opening = RepoStoreInfo {
id: id.clone(),
store,
@ -63,7 +60,7 @@ impl<'a> RepoStoreInfo<'a> {
pub fn create(
id: &RepoHash,
key: &SymKey,
store: &'a dyn BrokerStore,
store: &'a dyn KCVStore,
) -> Result<RepoStoreInfo<'a>, StorageError> {
let acc = RepoStoreInfo {
id: id.clone(),

@ -1,7 +1,7 @@
// 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>
// <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
@ -9,10 +9,10 @@
//! Topic
use p2p_repo::broker_store::BrokerStore;
use p2p_net::types::*;
use p2p_repo::kcv_store::KCVStore;
use p2p_repo::store::*;
use p2p_repo::types::*;
use p2p_net::types::*;
use serde::{Deserialize, Serialize};
use serde_bare::{from_slice, to_vec};
@ -25,7 +25,7 @@ pub struct TopicMeta {
pub struct Topic<'a> {
/// Topic ID
id: TopicId,
store: &'a dyn BrokerStore,
store: &'a dyn KCVStore,
}
impl<'a> Topic<'a> {
@ -40,7 +40,7 @@ impl<'a> Topic<'a> {
const SUFFIX_FOR_EXIST_CHECK: u8 = Self::META;
pub fn open(id: &TopicId, store: &'a dyn BrokerStore) -> Result<Topic<'a>, StorageError> {
pub fn open(id: &TopicId, store: &'a dyn KCVStore) -> Result<Topic<'a>, StorageError> {
let opening = Topic {
id: id.clone(),
store,
@ -50,7 +50,7 @@ impl<'a> Topic<'a> {
}
Ok(opening)
}
pub fn create(id: &TopicId, store: &'a mut dyn BrokerStore) -> Result<Topic<'a>, StorageError> {
pub fn create(id: &TopicId, store: &'a mut dyn KCVStore) -> Result<Topic<'a>, StorageError> {
let acc = Topic {
id: id.clone(),
store,

@ -36,8 +36,8 @@ use p2p_repo::store::RepoStore;
use p2p_repo::store::StorageError;
use p2p_repo::types::*;
use p2p_repo::utils::*;
use p2p_stores_lmdb::broker_store::LmdbBrokerStore;
use p2p_stores_lmdb::repo_store::LmdbRepoStore;
use stores_lmdb::broker_store::LmdbKCVStore;
use stores_lmdb::repo_store::LmdbRepoStore;
#[derive(Debug, Eq, PartialEq, Clone)]
pub enum BrokerError {
@ -526,7 +526,7 @@ pub struct BrokerPeerInfo {
const REPO_STORES_SUBDIR: &str = "repos";
pub struct BrokerServer {
store: LmdbBrokerStore,
store: LmdbKCVStore,
mode: ConfigMode,
repo_stores: Arc<RwLock<HashMap<RepoHash, LmdbRepoStore>>>,
// only used in ConfigMode::Local
@ -560,7 +560,7 @@ impl BrokerServer {
writer.remove(&peer_id);
}
pub fn new(store: LmdbBrokerStore, mode: ConfigMode) -> Result<BrokerServer, BrokerError> {
pub fn new(store: LmdbKCVStore, mode: ConfigMode) -> Result<BrokerServer, BrokerError> {
let mut configmode: ConfigMode;
{
let config = Config::get_or_create(&mode, &store)?;
@ -579,7 +579,7 @@ impl BrokerServer {
where
F: FnOnce(&LmdbRepoStore) -> Result<R, ProtocolError>,
{
// first let's find it in the BrokerStore.repostoreinfo table in order to get the encryption key
// first let's find it in the KCVStore.repostoreinfo table in order to get the encryption key
let info = RepoStoreInfo::open(&repo_hash, &self.store)
.map_err(|e| BrokerError::OverlayNotFound)?;
let key = info.key()?;
@ -635,7 +635,7 @@ impl BrokerServer {
// }
// // we need to open/create it
// // first let's find it in the BrokerStore.overlay table to retrieve its repo_pubkey
// // first let's find it in the KCVStore.overlay table to retrieve its repo_pubkey
// debug_println!("searching for overlayId {}", overlay_id);
// let overlay = Overlay::open(overlay_id, &self.store)?;
// debug_println!("found overlayId {}", overlay_id);

@ -26,8 +26,8 @@ use p2p_net::types::IP;
use p2p_net::utils::Sensitive;
use p2p_repo::types::{PrivKey, PubKey};
use p2p_repo::utils::generate_keypair;
use p2p_stores_lmdb::broker_store::LmdbBrokerStore;
use p2p_stores_lmdb::repo_store::LmdbRepoStore;
use stores_lmdb::kcv_store::LmdbKCVStore;
use stores_lmdb::repo_store::LmdbRepoStore;
use std::fs;
use std::ops::Deref;
use std::sync::Arc;
@ -64,7 +64,7 @@ pub async fn run_server_accept_one(
let master_key: [u8; 32] = [0; 32];
std::fs::create_dir_all(root.path()).unwrap();
println!("{}", root.path().to_str().unwrap());
let store = LmdbBrokerStore::open(root.path(), master_key);
let store = LmdbKCVStore::open(root.path(), master_key);
// TODO: remove this part
// let server: BrokerServer =
@ -93,7 +93,7 @@ pub async fn run_server(
let master_key: [u8; 32] = [0; 32];
std::fs::create_dir_all(root.path()).unwrap();
println!("{}", root.path().to_str().unwrap());
let store = LmdbBrokerStore::open(root.path(), master_key);
let store = LmdbKCVStore::open(root.path(), master_key);
// TODO: remove this part
// let server: BrokerServer =

@ -22,8 +22,8 @@ async-std = { version = "1.12.0", features = ["attributes","unstable"] }
wasm-bindgen = "0.2"
unique_id = "0.1.5"
once_cell = "1.17.1"
noise-protocol = "0.1.4"
noise-rust-crypto = "0.5.0"
noise-protocol = "0.2.0-rc1"
noise-rust-crypto = "0.6.0-rc.1"
ed25519-dalek = "1.0.1"
[target.'cfg(target_arch = "wasm32")'.dependencies]

@ -1,15 +1,14 @@
// Copyright (c) 2022-2023 Niko Bonnieure, Par le Peuple, NextGraph.org developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-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.
use crate::store::{StorageError};
pub trait WriteTransaction : ReadTransaction {
use crate::store::StorageError;
pub trait WriteTransaction: ReadTransaction {
/// Save a property value to the store.
fn put(
&mut self,
@ -32,7 +31,12 @@ pub trait WriteTransaction : ReadTransaction {
fn del(&mut self, prefix: u8, key: &Vec<u8>, suffix: Option<u8>) -> Result<(), StorageError>;
/// Delete all properties of a key from the store.
fn del_all(&mut self, prefix: u8, key: &Vec<u8>, all_suffixes: &[u8]) -> Result<(), StorageError>;
fn del_all(
&mut self,
prefix: u8,
key: &Vec<u8>,
all_suffixes: &[u8],
) -> Result<(), StorageError>;
/// Delete a specific value for a property from the store.
fn del_property_value(
@ -42,11 +46,9 @@ pub trait WriteTransaction : ReadTransaction {
suffix: Option<u8>,
value: &Vec<u8>,
) -> Result<(), StorageError>;
}
pub trait ReadTransaction {
/// Load a property from the store.
fn get(&self, prefix: u8, key: &Vec<u8>, suffix: Option<u8>) -> Result<Vec<u8>, StorageError>;
@ -66,12 +68,13 @@ pub trait ReadTransaction {
suffix: Option<u8>,
value: Vec<u8>,
) -> Result<(), StorageError>;
}
pub trait BrokerStore : ReadTransaction {
fn write_transaction(&self, method: & dyn Fn(&mut dyn WriteTransaction) -> Result<(), StorageError> ) -> Result<(), StorageError> ;
pub trait KCVStore: ReadTransaction {
fn write_transaction(
&self,
method: &dyn Fn(&mut dyn WriteTransaction) -> Result<(), StorageError>,
) -> Result<(), StorageError>;
/// Save a property value to the store.
fn put(
@ -105,6 +108,4 @@ pub trait BrokerStore : ReadTransaction {
suffix: Option<u8>,
value: Vec<u8>,
) -> Result<(), StorageError>;
}

@ -16,4 +16,6 @@ pub mod utils;
pub mod errors;
pub mod broker_store;
pub mod kcv_store;
pub mod site;

@ -0,0 +1,79 @@
/*
* 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.
*/
use crate::errors::NgError;
use crate::types::{Identity, Site, SiteType};
use crate::utils::{generate_keypair, sign, verify};
impl Site {
pub fn create(site_type: SiteType) -> Result<Self, NgError> {
let (site_key, side_id) = generate_keypair();
let (public_key, public_id) = generate_keypair();
let (protected_key, protected_id) = generate_keypair();
let (private_key, private_id) = generate_keypair();
let site_identity;
let public_identity;
let protected_identity;
let private_identity;
match site_type {
SiteType::Individual => {
site_identity = Identity::IndividualSite(side_id);
public_identity = Identity::IndividualPublic(public_id);
protected_identity = Identity::IndividualProtected(protected_id);
private_identity = Identity::IndividualPrivate(private_id);
}
SiteType::Org => {
site_identity = Identity::OrgSite(side_id);
public_identity = Identity::OrgPublic(public_id);
protected_identity = Identity::OrgProtected(protected_id);
private_identity = Identity::OrgPrivate(private_id);
}
}
let public_sig = sign(
site_key,
side_id,
&serde_bare::to_vec(&public_identity).unwrap(),
)?;
let protected_sig = sign(
site_key,
side_id,
&serde_bare::to_vec(&protected_identity).unwrap(),
)?;
let private_sig = sign(
site_key,
side_id,
&serde_bare::to_vec(&private_identity).unwrap(),
)?;
Ok(Self {
site_type,
site_identity,
site_key,
public_identity,
public_key,
public_sig,
protected_identity,
protected_key,
protected_sig,
private_identity,
private_key,
private_sig,
})
}
}

@ -3,7 +3,7 @@
// This code is partly derived from work written by TG x Thoth from P2Pcollab.
// Copyright 2022 TG x Thoth
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-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
@ -13,12 +13,12 @@
use crate::types::*;
use std::sync::{Arc, RwLock};
use std::{
cmp::min,
collections::{hash_map::Iter, HashMap},
mem::size_of_val,
};
use std::sync::{Arc, RwLock};
pub trait RepoStore {
/// Load a block from the store.
@ -37,6 +37,7 @@ pub enum StorageError {
InvalidValue,
BackendError,
SerializationError,
AlreadyExists,
}
impl From<serde_bare::error::Error> for StorageError {
@ -81,7 +82,12 @@ impl HashMapRepoStore {
}
pub fn get_all(&self) -> Vec<Block> {
self.blocks.read().unwrap().values().map(|x| x.clone()).collect()
self.blocks
.read()
.unwrap()
.values()
.map(|x| x.clone())
.collect()
}
}
@ -102,7 +108,12 @@ impl RepoStore for HashMapRepoStore {
}
fn del(&self, id: &BlockId) -> Result<(Block, usize), StorageError> {
let block = self.blocks.write().unwrap().remove(id).ok_or(StorageError::NotFound)?;
let block = self
.blocks
.write()
.unwrap()
.remove(id)
.ok_or(StorageError::NotFound)?;
let size = size_of_val(&block);
Ok((block, size))
}

@ -89,7 +89,7 @@ impl fmt::Display for PubKey {
}
/// Private key
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)]
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub enum PrivKey {
Ed25519PrivKey(Ed25519PrivKey),
}
@ -106,7 +106,7 @@ impl PrivKey {
pub type Ed25519Sig = [[u8; 32]; 2];
/// Cryptographic signature
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)]
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub enum Sig {
Ed25519Sig(Ed25519Sig),
}
@ -165,19 +165,54 @@ pub enum PermissionType {
}
/// List of Identity types
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub enum Identity {
ORG_SITE(PubKey),
PERSO_SITE(PubKey),
ORG_PUBLIC(PubKey),
ORG_PROTECTED(PubKey),
ORG_PRIVATE(PubKey),
PERSO_PUBLIC(PubKey),
PERSO_PROTECTED(PubKey),
PERSO_PRIVATE(PubKey),
GROUP(RepoId),
DIALOG(RepoId),
DOCUMENT(RepoId),
DIALOG_OVERLAY(Digest),
OrgSite(PubKey),
IndividualSite(PubKey),
OrgPublic(PubKey),
OrgProtected(PubKey),
OrgPrivate(PubKey),
IndividualPublic(PubKey),
IndividualProtected(PubKey),
IndividualPrivate(PubKey),
Group(RepoId),
Dialog(RepoId),
Document(RepoId),
DialogOverlay(Digest),
}
/// Site type
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub enum SiteType {
Org,
Individual, // formerly Personal
}
/// Site
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub struct Site {
pub site_type: SiteType,
// Identity::OrgSite or Identity::IndividualSite
pub site_identity: Identity,
pub site_key: PrivKey,
// Identity::OrgPublic or Identity::IndividualPublic
pub public_identity: Identity,
pub public_key: PrivKey,
// signature of public_identity with site_key
pub public_sig: Sig,
// Identity::OrgProtected or Identity::IndividualProtected
pub protected_identity: Identity,
pub protected_key: PrivKey,
// signature of protected_identity with site_key
pub protected_sig: Sig,
// Identity::OrgPrivate or Identity::IndividualPrivate
pub private_identity: Identity,
pub private_key: PrivKey,
// signature of private_identity with site_key
pub private_sig: Sig,
}
/// RepoHash:

@ -1,5 +1,5 @@
[package]
name = "p2p-stores-lmdb"
name = "stores-lmdb"
version = "0.1.0"
edition = "2021"
license = "MIT/Apache-2.0"

@ -1,13 +1,13 @@
// 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>
// <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.
use p2p_repo::broker_store::*;
use p2p_repo::kcv_store::*;
use p2p_repo::store::*;
use p2p_repo::types::*;
use p2p_repo::utils::*;
@ -29,29 +29,25 @@ use rkv::{
use serde::{Deserialize, Serialize};
use serde_bare::error::Error;
pub struct LmdbTransaction<'a> {
store: &'a LmdbBrokerStore,
store: &'a LmdbKCVStore,
writer: Option<Writer<LmdbRwTransaction<'a>>>,
}
impl<'a> LmdbTransaction<'a> {
fn commit(&mut self) {
self.writer.take().unwrap().commit().unwrap();
}
}
impl<'a> ReadTransaction for LmdbTransaction<'a> {
/// Load a single value property from the store.
fn get(&self, prefix: u8, key: &Vec<u8>, suffix: Option<u8>) -> Result<Vec<u8>, StorageError> {
let property = LmdbBrokerStore::compute_property(prefix, key, suffix);
let property = LmdbKCVStore::compute_property(prefix, key, suffix);
let mut iter = self
.store.main_store
.store
.main_store
.get(self.writer.as_ref().unwrap(), property)
.map_err(|e| StorageError::BackendError)?;
match iter.next() {
@ -68,10 +64,11 @@ impl<'a> ReadTransaction for LmdbTransaction<'a> {
key: &Vec<u8>,
suffix: Option<u8>,
) -> Result<Vec<Vec<u8>>, StorageError> {
let property = LmdbBrokerStore::compute_property(prefix, key, suffix);
let property = LmdbKCVStore::compute_property(prefix, key, suffix);
let mut iter = self
.store.main_store
.store
.main_store
.get(self.writer.as_ref().unwrap(), property)
.map_err(|e| StorageError::BackendError)?;
let mut vector: Vec<Vec<u8>> = vec![];
@ -95,11 +92,16 @@ impl<'a> ReadTransaction for LmdbTransaction<'a> {
suffix: Option<u8>,
value: Vec<u8>,
) -> Result<(), StorageError> {
let property = LmdbBrokerStore::compute_property(prefix, key, suffix);
let property = LmdbKCVStore::compute_property(prefix, key, suffix);
let exists = self
.store.main_store
.get_key_value(self.writer.as_ref().unwrap(), property, &Value::Blob(value.as_slice()))
.store
.main_store
.get_key_value(
self.writer.as_ref().unwrap(),
property,
&Value::Blob(value.as_slice()),
)
.map_err(|e| StorageError::BackendError)?;
if exists {
Ok(())
@ -107,11 +109,9 @@ impl<'a> ReadTransaction for LmdbTransaction<'a> {
Err(StorageError::NotFound)
}
}
}
impl<'a> WriteTransaction for LmdbTransaction<'a> {
/// Save a property value to the store.
fn put(
&mut self,
@ -120,9 +120,14 @@ impl<'a> WriteTransaction for LmdbTransaction<'a> {
suffix: Option<u8>,
value: &Vec<u8>,
) -> Result<(), StorageError> {
let property = LmdbBrokerStore::compute_property(prefix, key, suffix);
self.store.main_store
.put(self.writer.as_mut().unwrap(), property, &Value::Blob(value.as_slice()))
let property = LmdbKCVStore::compute_property(prefix, key, suffix);
self.store
.main_store
.put(
self.writer.as_mut().unwrap(),
property,
&Value::Blob(value.as_slice()),
)
.map_err(|e| StorageError::BackendError)?;
Ok(())
@ -136,14 +141,20 @@ impl<'a> WriteTransaction for LmdbTransaction<'a> {
suffix: Option<u8>,
value: &Vec<u8>,
) -> Result<(), StorageError> {
let property = LmdbBrokerStore::compute_property(prefix, key, suffix);
let property = LmdbKCVStore::compute_property(prefix, key, suffix);
self.store.main_store
self.store
.main_store
.delete_all(self.writer.as_mut().unwrap(), property.clone())
.map_err(|e| StorageError::BackendError)?;
self.store.main_store
.put(self.writer.as_mut().unwrap(), property, &Value::Blob(value.as_slice()))
self.store
.main_store
.put(
self.writer.as_mut().unwrap(),
property,
&Value::Blob(value.as_slice()),
)
.map_err(|e| StorageError::BackendError)?;
Ok(())
@ -151,8 +162,9 @@ impl<'a> WriteTransaction for LmdbTransaction<'a> {
/// Delete a property from the store.
fn del(&mut self, prefix: u8, key: &Vec<u8>, suffix: Option<u8>) -> Result<(), StorageError> {
let property = LmdbBrokerStore::compute_property(prefix, key, suffix);
self.store.main_store
let property = LmdbKCVStore::compute_property(prefix, key, suffix);
self.store
.main_store
.delete_all(self.writer.as_mut().unwrap(), property)
.map_err(|e| StorageError::BackendError)?;
@ -167,16 +179,26 @@ impl<'a> WriteTransaction for LmdbTransaction<'a> {
suffix: Option<u8>,
value: &Vec<u8>,
) -> Result<(), StorageError> {
let property = LmdbBrokerStore::compute_property(prefix, key, suffix);
self.store.main_store
.delete(self.writer.as_mut().unwrap(), property, &Value::Blob(value.as_slice()))
let property = LmdbKCVStore::compute_property(prefix, key, suffix);
self.store
.main_store
.delete(
self.writer.as_mut().unwrap(),
property,
&Value::Blob(value.as_slice()),
)
.map_err(|e| StorageError::BackendError)?;
Ok(())
}
/// Delete all properties of a key from the store.
fn del_all(&mut self, prefix: u8, key: &Vec<u8>, all_suffixes: &[u8]) -> Result<(), StorageError> {
fn del_all(
&mut self,
prefix: u8,
key: &Vec<u8>,
all_suffixes: &[u8],
) -> Result<(), StorageError> {
for suffix in all_suffixes {
self.del(prefix, key, Some(*suffix))?;
}
@ -188,7 +210,7 @@ impl<'a> WriteTransaction for LmdbTransaction<'a> {
}
#[derive(Debug)]
pub struct LmdbBrokerStore {
pub struct LmdbKCVStore {
/// the main store where all the properties of keys are stored
main_store: MultiStore<LmdbDatabase>,
/// the opened environment so we can create new transactions
@ -197,8 +219,7 @@ pub struct LmdbBrokerStore {
path: String,
}
impl ReadTransaction for LmdbBrokerStore {
impl ReadTransaction for LmdbKCVStore {
/// Load a single value property from the store.
fn get(&self, prefix: u8, key: &Vec<u8>, suffix: Option<u8>) -> Result<Vec<u8>, StorageError> {
let property = Self::compute_property(prefix, key, suffix);
@ -263,28 +284,26 @@ impl ReadTransaction for LmdbBrokerStore {
Err(StorageError::NotFound)
}
}
}
impl BrokerStore for LmdbBrokerStore {
fn write_transaction(&self, method: & dyn Fn(&mut dyn WriteTransaction) -> Result<(), StorageError> )-> Result<(), StorageError> {
impl KCVStore for LmdbKCVStore {
fn write_transaction(
&self,
method: &dyn Fn(&mut dyn WriteTransaction) -> Result<(), StorageError>,
) -> Result<(), StorageError> {
let lock = self.environment.read().unwrap();
let writer = lock.write().unwrap();
let mut transaction = LmdbTransaction {
store: self,
writer: Some(writer),
};
let res = method(&mut transaction);
if res.is_ok() {
transaction.commit();
}
res
}
/// Save a property value to the store.
@ -295,10 +314,7 @@ impl BrokerStore for LmdbBrokerStore {
suffix: Option<u8>,
value: Vec<u8>,
) -> Result<(), StorageError> {
self.write_transaction(&|tx| {
tx.put(prefix,key,suffix,&value)
})
self.write_transaction(&|tx| tx.put(prefix, key, suffix, &value))
}
/// Replace the property of a key (single value) to the store.
@ -309,17 +325,12 @@ impl BrokerStore for LmdbBrokerStore {
suffix: Option<u8>,
value: Vec<u8>,
) -> Result<(), StorageError> {
self.write_transaction(&|tx| {
tx.replace(prefix,key,suffix,&value)
})
self.write_transaction(&|tx| tx.replace(prefix, key, suffix, &value))
}
/// Delete a property from the store.
fn del(&self, prefix: u8, key: &Vec<u8>, suffix: Option<u8>) -> Result<(), StorageError> {
self.write_transaction(&|tx| {
tx.del(prefix,key,suffix)
})
self.write_transaction(&|tx| tx.del(prefix, key, suffix))
}
/// Delete a specific value for a property from the store.
@ -330,9 +341,7 @@ impl BrokerStore for LmdbBrokerStore {
suffix: Option<u8>,
value: Vec<u8>,
) -> Result<(), StorageError> {
self.write_transaction(&|tx| {
tx.del_property_value(prefix,key,suffix, &value)
})
self.write_transaction(&|tx| tx.del_property_value(prefix, key, suffix, &value))
}
/// Delete all properties of a key from the store.
@ -347,7 +356,7 @@ impl BrokerStore for LmdbBrokerStore {
}
}
impl LmdbBrokerStore {
impl LmdbKCVStore {
pub fn path(&self) -> PathBuf {
PathBuf::from(&self.path)
}
@ -362,9 +371,9 @@ impl LmdbBrokerStore {
new
}
/// Opens the store and returns a BrokerStore object that should be kept and used to manipulate Accounts, Overlays, Topics and options
/// Opens the store and returns a KCVStore object that should be kept and used to manipulate the properties
/// The key is the encryption key for the data at rest.
pub fn open<'a>(path: &Path, key: [u8; 32]) -> LmdbBrokerStore {
pub fn open<'a>(path: &Path, key: [u8; 32]) -> LmdbKCVStore {
let mut manager = Manager::<LmdbEnvironment>::singleton().write().unwrap();
let shared_rkv = manager
.get_or_create(path, |path| {
@ -378,7 +387,7 @@ impl LmdbBrokerStore {
let main_store = env.open_multi("main", StoreOptions::create()).unwrap();
LmdbBrokerStore {
LmdbKCVStore {
environment: shared_rkv.clone(),
main_store,
path: path.to_str().unwrap().to_string(),

@ -2,4 +2,4 @@
pub mod repo_store;
#[cfg(not(target_arch = "wasm32"))]
pub mod broker_store;
pub mod kcv_store;
Loading…
Cancel
Save