parent
9511dde4f0
commit
be1b4627d1
File diff suppressed because it is too large
Load Diff
@ -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), |
||||||
|
} |
After Width: | Height: | Size: 29 KiB |
@ -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.
@ -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 |
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; |
||||||
|
} |
||||||
|
} |
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()], |
||||||
|
}) |
@ -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, |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
@ -1,5 +1,5 @@ |
|||||||
[package] |
[package] |
||||||
name = "p2p-stores-lmdb" |
name = "stores-lmdb" |
||||||
version = "0.1.0" |
version = "0.1.0" |
||||||
edition = "2021" |
edition = "2021" |
||||||
license = "MIT/Apache-2.0" |
license = "MIT/Apache-2.0" |
Loading…
Reference in new issue