diff options
| author | Adam Soltys <[email protected]> | 2023-04-07 14:46:09 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2023-04-07 14:46:09 -0700 |
| commit | d145b9de59cb20181f6fe48f6aa4677d406cf16d (patch) | |
| tree | 16439dbce4ff7e48c60f57875489c16ea5532a13 /src/lib | |
| parent | d4de8dce613f5ee49068ce90b868ffda5d4771d7 (diff) | |
| parent | 648d9fb41c5302672822fd76fb1b1a90f654b05b (diff) | |
| download | coinos-ui-d145b9de59cb20181f6fe48f6aa4677d406cf16d.tar.gz coinos-ui-d145b9de59cb20181f6fe48f6aa4677d406cf16d.zip | |
Merge branch 'master' into hashme
Diffstat (limited to 'src/lib')
| -rw-r--r-- | src/lib/nostr.js | 86 | ||||
| -rw-r--r-- | src/lib/samples.js | 23 | ||||
| -rw-r--r-- | src/lib/store.js | 3 | ||||
| -rw-r--r-- | src/lib/translations/index.js | 5 | ||||
| -rw-r--r-- | src/lib/translations/lang.json | 3 | ||||
| -rw-r--r-- | src/lib/utils.js | 54 |
6 files changed, 79 insertions, 95 deletions
diff --git a/src/lib/nostr.js b/src/lib/nostr.js index d72f48ef..1924643e 100644 --- a/src/lib/nostr.js +++ b/src/lib/nostr.js @@ -1,60 +1,39 @@ import { get } from 'svelte/store'; -import * as ecc from 'tiny-secp256k1'; -import { generateMnemonic, entropyToMnemonic, mnemonicToEntropy, mnemonicToSeedSync } from 'bip39'; import { Buffer } from 'buffer'; -import { bech32m } from 'bech32'; -import { bip32, fail, wait, post, stretch } from '$lib/utils'; -import { calculateId, signId } from 'nostr'; +import { fail, wait, post, stretch } from '$lib/utils'; import { decrypted, password as pw, passwordPrompt, pin, loginRedirect } from '$lib/store'; import { goto, invalidate } from '$app/navigation'; +import { getPublicKey, signEvent, getEventHash, nip04, nip06 } from 'nostr-tools'; +import { mnemonicToEntropy, entropyToMnemonic } from '@scure/bip39'; +import { wordlist } from '@scure/bip39/wordlists/english.js'; +import { bech32m } from '@scure/base'; -const { encode, decode, fromWords, toWords } = bech32m; +const { generateSeedWords, privateKeyFromSeedWords } = nip06; +const { encode, decode, toWords, fromWords } = bech32m; export let generate = async (user) => { - let password = get(pw); - let mnemonic, key, seed, entropy, cipher, salt, child; - if (!get(pin)?.length === 6) return; + let password = get(pw); - mnemonic = generateMnemonic(); - seed = mnemonicToSeedSync(mnemonic); - entropy = mnemonicToEntropy(mnemonic); - key = bip32.fromSeed(seed); - child = key.derivePath("m/44'/1237'/0'/0/0"); - salt = crypto.getRandomValues(new Uint8Array(16)); + let salt = crypto.getRandomValues(new Uint8Array(16)); + let mnemonic = generateSeedWords(); + let sk = privateKeyFromSeedWords(mnemonic); let bytes = new Uint8Array( await crypto.subtle.encrypt( { name: 'AES-GCM', iv: new Uint8Array(16) }, await stretch(password, salt), - Uint8Array.from(Buffer.from(entropy, 'hex')) + Uint8Array.from(Buffer.from(mnemonicToEntropy(mnemonic, wordlist), 'hex')) ) ); - user.pubkey = Buffer.from(ecc.xOnlyPointFromPoint(child.publicKey)).toString('hex'); + user.pubkey = getPublicKey(sk); user.cipher = encode('en', toWords(bytes), 180); user.salt = Buffer.from(salt).toString('hex'); }; export let encrypt = async ({ message, recipient, user }) => { - let sharedPoint = Buffer.from( - ecc.pointMultiply( - Uint8Array.from(Buffer.from('02' + recipient, 'hex')), - Buffer.from(await getPrivateKey(user), 'hex') - ) - ); - - let iv = crypto.getRandomValues(new Uint8Array(16)); - let cipher = await crypto.subtle.encrypt( - { name: 'AES-CBC', iv }, - await crypto.subtle.importKey('raw', sharedPoint.slice(1), 'AES-CBC', true, [ - 'encrypt', - 'decrypt' - ]), - new TextEncoder().encode(message) - ); - - return Buffer.from(cipher).toString('base64') + '?iv=' + Buffer.from(iv).toString('base64'); + return nip04.encrypt(await getPrivateKey(user), recipient, message); }; export let decrypt = async ({ event, user }) => { @@ -63,22 +42,8 @@ export let decrypt = async ({ event, user }) => { let { content, pubkey, id } = event; if (cache[id]) return cache[id]; if (pubkey === user.pubkey) pubkey = event.tags[0][1]; - pubkey = Uint8Array.from(Buffer.from('02' + pubkey, 'hex')); - - let sharedPoint = Buffer.from( - ecc.pointMultiply(pubkey, Buffer.from(await getPrivateKey(user), 'hex')) - ); - let [cipher, iv] = content.split('?iv='); - cipher = Uint8Array.from(Buffer.from(cipher, 'base64')); - iv = Uint8Array.from(Buffer.from(iv, 'base64')); - - let key = await crypto.subtle.importKey('raw', sharedPoint.slice(1), 'AES-CBC', true, [ - 'encrypt', - 'decrypt' - ]); - - let message = await crypto.subtle.decrypt({ name: 'AES-CBC', iv }, key, cipher); + let message = await nip04.decrypt(await getPrivateKey(user), pubkey, content); cache[id] = Buffer.from(message).toString('utf8'); decrypted.set(cache); @@ -111,27 +76,22 @@ let getPrivateKey = async (user) => { password = get(pw); let mnemonic, key, seed, entropy, child, privkey; - entropy = Buffer.from( + + entropy = new Uint8Array( await crypto.subtle.decrypt( { name: 'AES-GCM', iv: new Uint8Array(16) }, await stretch(password, Buffer.from(salt, 'hex')), Uint8Array.from(fromWords(decode(cipher, 180).words)) - ), - 'hex' - ).toString('hex'); - - mnemonic = entropyToMnemonic(entropy); - seed = mnemonicToSeedSync(mnemonic); - key = bip32.fromSeed(seed); - child = key.derivePath("m/44'/1237'/0'/0/0"); + ) + ); - return child.privateKey; + mnemonic = entropyToMnemonic(entropy, wordlist); + return privateKeyFromSeedWords(mnemonic); }; export let sign = async ({ event, user }) => { - let privkey = await getPrivateKey(user); - event.id = await calculateId(event); - event.sig = await signId(privkey, event.id); + event.id = getEventHash(event); + event.sig = signEvent(event, await getPrivateKey(user)); }; export let send = (event) => { diff --git a/src/lib/samples.js b/src/lib/samples.js index 93b9aa24..14d9e4e0 100644 --- a/src/lib/samples.js +++ b/src/lib/samples.js @@ -7,6 +7,13 @@ export default { "username": "adam", "password": "hunter2" }'`, + payments: `curl "https://coinos.io/api/payments" + -H "application/json" + -H "Authorization: Bearer $token" +}'`, + rates: `curl "https://coinos.io/api/rates" + -H "application/json" +}'`, invoice: `curl "https://coinos.io/api/invoice" -H "application/json" -H "Authorization: Bearer $token" @@ -19,5 +26,19 @@ export default { }, }'`, fetchInvoice: `curl "https://coinos.io/api/invoice/bc1qmhfk9stzffhd9umzmld92vff7zg3mdlh7rvvaj" - -H "application/json"'` + -H "application/json"'`, + invoiceResponse: `{ + "amount":3141 + "tip":0 + "type":"bitcoin" + "prompt":false + "rate":31836.9702667 + "hash":"bc1qmhfk9stzffhd9umzmld92vff7zg3mdlh7rvvaj" + "text":"bitcoin:bc1qmhfk9stzffhd9umzmld92vff7zg3mdlh7rvvaj?amount=0.00003141" + "currency":"CAD" + "uid":"a9770421-3f65-11ed-9f57-0242ac2a0004" + "received":0 + "created":1677537428134 +}`, + socketAuth: `echo '{"type":"login","data":{"username":"user","password":"password"}}' | websocat -n https://coinos.io/ws` }; diff --git a/src/lib/store.js b/src/lib/store.js index 6853386c..ce984b17 100644 --- a/src/lib/store.js +++ b/src/lib/store.js @@ -32,7 +32,7 @@ const persistLocal = (key, defaultValue) => { return s; }; -export const animatedRate = writable(0); +export const animatedRate = persistLocal('rate', 0); export const avatar = writable(); export const banner = writable(); export const colorTheme = writable('from-primary to-gradient'); @@ -50,6 +50,5 @@ export const pin = persistSession('pin', undefined); export const rate = writable(); export const request = writable(); export const requestRedirect = writable(); -export const selectedRate = writable(); export const token = persistSession('token'); export const user = writable(); diff --git a/src/lib/translations/index.js b/src/lib/translations/index.js index dfd7f47f..ea92192b 100644 --- a/src/lib/translations/index.js +++ b/src/lib/translations/index.js @@ -28,6 +28,11 @@ const config = { loader: async () => (await import('../../locales/en.json')).default }, { + locale: 'fa', + key: '', + loader: async () => (await import('../../locales/fa.json')).default + }, + { locale: 'fr', key: '', loader: async () => (await import('../../locales/fr.json')).default diff --git a/src/lib/translations/lang.json b/src/lib/translations/lang.json index c607ebd0..cbf1ce4a 100644 --- a/src/lib/translations/lang.json +++ b/src/lib/translations/lang.json @@ -4,5 +4,6 @@ "es": "Español", "de": "Deutsch", "pt": "Português", - "zh": "中国人" + "zh": "中国人", + "fa": "فارسی " } diff --git a/src/lib/utils.js b/src/lib/utils.js index 7b10c638..5eef79fc 100644 --- a/src/lib/utils.js +++ b/src/lib/utils.js @@ -1,5 +1,3 @@ -import * as ecc from 'tiny-secp256k1'; -import { BIP32Factory } from 'bip32'; import { browser } from '$app/environment'; import { toast } from '@zerodevx/svelte-toast'; import { goto } from '$app/navigation'; @@ -12,11 +10,11 @@ export function scroll(section) { setTimeout(() => section.scrollIntoView({ behavior: 'smooth' }), 500); } -const base = browser ? '' : PUBLIC_COINOS_URL; +let base = browser ? '' : PUBLIC_COINOS_URL; -export const punk = (k) => Math.floor((parseInt(k.slice(-2), 16) / 256) * 64) + 1 + '.webp'; +export let punk = (k) => Math.floor((parseInt(k.slice(-2), 16) / 256) * 64) + 1 + '.webp'; -export const g = (url, fetch, headers) => +export let g = (url, fetch, headers) => fetch(base + url, { headers }) .then((r) => r.text()) .then((body) => { @@ -27,7 +25,7 @@ export const g = (url, fetch, headers) => } }); -export const get = (url, headers = {}) => +export let get = (url, headers = {}) => fetch(base + url, { headers }) .then(async (r) => { if (r.ok) return r.text(); @@ -41,7 +39,7 @@ export const get = (url, headers = {}) => } }); -export const post = (url, body, headers) => { +export let post = (url, body, headers) => { headers = { ...headers, 'content-type': 'application/json', accept: 'application/json' }; return fetch(base + url, { method: 'POST', body: JSON.stringify(body), headers }) .then(async (r) => { @@ -71,7 +69,7 @@ export const post = (url, body, headers) => { }); }; -export const copy = (text) => { +export let copy = (text) => { navigator.clipboard.writeText(text); success('Copied!'); }; @@ -87,7 +85,7 @@ export function reverseFormat(val, locale) { export let protectedRoutes = [/customers/, /settings/, /payments/]; -export const success = (m) => { +export let success = (m) => { toast.pop(); toast.push(m, { theme: { @@ -96,7 +94,7 @@ export const success = (m) => { }); }; -export const warning = (m) => { +export let warning = (m) => { toast.pop(); toast.push(m, { theme: { @@ -104,7 +102,7 @@ export const warning = (m) => { } }); }; -export const fail = (m) => { +export let fail = (m) => { toast.pop(); toast.push(m, { theme: { @@ -112,7 +110,7 @@ export const fail = (m) => { } }); }; -export const info = (m) => { +export let info = (m) => { toast.pop(); toast.push(m, { theme: { @@ -121,7 +119,7 @@ export const info = (m) => { }); }; -export const login = async (user, cookies) => { +export let login = async (user, cookies) => { let maxAge = 30 * 24 * 60 * 60; let res = await fetch(base + '/login', { @@ -145,17 +143,17 @@ export const login = async (user, cookies) => { cookies.set('token', token, opts); }; -export const auth = (cookies) => ({ authorization: `Bearer ${cookies.get('token')}` }); +export let auth = (cookies) => ({ authorization: `Bearer ${cookies.get('token')}` }); -export const fiat = (amount, rate) => (amount * rate) / sats; -export const fd = async (req) => { +export let fiat = (amount, rate) => (amount * rate) / sats; +export let fd = async (req) => { let obj = Object.fromEntries(await req.formData()); for (let k in obj) (obj[k] === 'undefined' && (obj[k] = undefined)) || (obj[k] === 'false' && (obj[k] = false)); return obj; }; -export const f = (s, currency) => +export let f = (s, currency) => new Intl.NumberFormat('en-US', { style: 'currency', currency @@ -163,8 +161,8 @@ export const f = (s, currency) => .format(s) .replace('CA', ''); -export const s = (s) => new Intl.NumberFormat('en-US', { maximumFractionDigits: 0 }).format(s); -export const sat = (s) => { +export let s = (s) => new Intl.NumberFormat('en-US', { maximumFractionDigits: 0 }).format(s); +export let sat = (s) => { s = Math.abs(s); let p = Math.floor(Math.log(s) / Math.LN10 + 0.000000001); let d = Math.floor((p + 1) / 3); @@ -179,20 +177,20 @@ export const sat = (s) => { ); }; -export const sats = 100000000; +export let sats = 100000000; -export const back = () => browser && history.go(-1); -export const focus = (el) => browser && screen.width > 1280 && setTimeout(() => el.focus(), 1); +export let back = () => browser && (history.length ? history.go(-1) : goto('/')); +export let focus = (el) => browser && screen.width > 1280 && setTimeout(() => el.focus(), 1); -export const sleep = (n) => new Promise((r) => setTimeout(r, n)); -export const wait = async (f, n = 100, s = 300) => { +export let sleep = (n) => new Promise((r) => setTimeout(r, n)); +export let wait = async (f, n = 100, s = 300) => { let i = 0; while (!(await f()) && i < s) (await sleep(n)) && i++; if (i >= s) throw new Error('timeout'); return f(); }; -export const stretch = async (password, salt) => +export let stretch = async (password, salt) => crypto.subtle.deriveKey( { name: 'PBKDF2', @@ -212,8 +210,6 @@ export const stretch = async (password, salt) => ['encrypt', 'decrypt'] ); -export const bip32 = await BIP32Factory(ecc); - function get_bigrams(string) { var s = string.toLowerCase(); var v = s.split(''); @@ -240,10 +236,12 @@ export function ss(str1, str2) { return 0.0; } -export const types = { +export let types = { bitcoin: 'bitcoin', lightning: 'lightning', internal: 'internal', pot: 'pot', classic: 'classic' }; + +export let ease = (t) => (t < 0.5 ? 8 * t * t * t * t : 1 - 8 * --t * t * t * t); |
