aboutsummaryrefslogtreecommitdiffstats
path: root/src/lib
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2023-04-07 14:46:09 -0700
committerAdam Soltys <[email protected]>2023-04-07 14:46:09 -0700
commitd145b9de59cb20181f6fe48f6aa4677d406cf16d (patch)
tree16439dbce4ff7e48c60f57875489c16ea5532a13 /src/lib
parentd4de8dce613f5ee49068ce90b868ffda5d4771d7 (diff)
parent648d9fb41c5302672822fd76fb1b1a90f654b05b (diff)
downloadcoinos-ui-d145b9de59cb20181f6fe48f6aa4677d406cf16d.tar.gz
coinos-ui-d145b9de59cb20181f6fe48f6aa4677d406cf16d.zip
Merge branch 'master' into hashme
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/nostr.js86
-rw-r--r--src/lib/samples.js23
-rw-r--r--src/lib/store.js3
-rw-r--r--src/lib/translations/index.js5
-rw-r--r--src/lib/translations/lang.json3
-rw-r--r--src/lib/utils.js54
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);