diff options
| author | Adam Soltys <[email protected]> | 2024-09-08 20:57:06 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2024-09-08 20:57:06 -0700 |
| commit | 5c105c8f4738ab1ead3c5b92f465f899cf0a7446 (patch) | |
| tree | 9cea2d1b46cdab9869f742e3286c9fb0762f8948 | |
| parent | 92bd9085a576517e06dc000d1e1ff11d27bc34b4 (diff) | |
| download | coinos-server-5c105c8f4738ab1ead3c5b92f465f899cf0a7446.tar.gz coinos-server-5c105c8f4738ab1ead3c5b92f465f899cf0a7446.zip | |
refactor rpc helper, accounts endpoints
| -rw-r--r-- | index.ts | 3 | ||||
| -rw-r--r-- | lib/invoices.ts | 7 | ||||
| -rw-r--r-- | lib/lightning.ts | 2 | ||||
| -rw-r--r-- | lib/payments.ts | 29 | ||||
| -rw-r--r-- | lib/rpc.ts | 43 | ||||
| -rw-r--r-- | lib/utils.ts | 1 | ||||
| -rw-r--r-- | routes/ecash.ts | 2 | ||||
| -rw-r--r-- | routes/invoices.ts | 7 | ||||
| -rw-r--r-- | routes/payments.ts | 116 | ||||
| -rw-r--r-- | routes/users.ts | 39 |
10 files changed, 141 insertions, 108 deletions
@@ -79,6 +79,9 @@ app.post("/confirm", payments.confirm); app.post("/bitcoin/fee", auth, payments.fee); app.post("/bitcoin/send", auth, payments.send); +app.get("/accounts", auth, users.accounts); +app.post("/accounts", auth, users.createAccount); + app.get("/users", auth, users.list); app.get("/me", auth, users.me); app.get("/users/:key", users.get); diff --git a/lib/invoices.ts b/lib/invoices.ts index 6934a2d4..013cb975 100644 --- a/lib/invoices.ts +++ b/lib/invoices.ts @@ -5,13 +5,10 @@ import { getUser, bip21, fail, SATS } from "$lib/utils"; import { types } from "$lib/payments"; import { v4 } from "uuid"; import rpc from "$lib/rpc"; - -import bc_ from "$lib/bitcoin"; -import lq_ from "$lib/liquid"; import ln from "$lib/ln"; -let bc = bc_ as any; -let lq = lq_ as any; +let bc = rpc(config.bitcoin); +let lq = rpc(config.liquid); export let generate = async ({ invoice, user, sender = undefined }) => { let { diff --git a/lib/lightning.ts b/lib/lightning.ts index 2d44581d..f86b9a2d 100644 --- a/lib/lightning.ts +++ b/lib/lightning.ts @@ -44,7 +44,7 @@ export async function listenForLightning() { if (p) return warn("already processed", bolt11); - await credit(bolt11, received, invoice.memo, preimage, types.lightning); + await credit({ hash:bolt11, amount:received, memo:invoice.memo, ref:preimage, type:types.lightning}); } catch (e) { console.log(e); err("problem receiving lightning payment", e.message); diff --git a/lib/payments.ts b/lib/payments.ts index 5d578af9..36f18e71 100644 --- a/lib/payments.ts +++ b/lib/payments.ts @@ -133,7 +133,14 @@ export let debit = async ({ return p; }; -export let credit = async (hash, amount, memo, ref, type = types.internal) => { +export let credit = async ({ + hash, + amount, + memo = undefined, + ref = undefined, + type = types.internal, + account = undefined, +}) => { amount = parseInt(amount) || 0; let inv = await getInvoice(hash); @@ -169,6 +176,7 @@ export let credit = async (hash, amount, memo, ref, type = types.internal) => { let id = v4(); let p = { + account, id, iid: inv.id, hash, @@ -208,8 +216,8 @@ export let credit = async (hash, amount, memo, ref, type = types.internal) => { m.set(`invoice:${inv.id}`, JSON.stringify(inv)) .set(`payment:${p.id}`, JSON.stringify(p)) - .lPush(`${uid}:payments`, p.id) - .incrBy(`${balanceKey}:${uid}`, amount) + .lPush(`${account || uid}:payments`, p.id) + .incrBy(`${balanceKey}:${account || uid}`, amount) .exec(); emit(uid, "payment", p); @@ -355,7 +363,7 @@ export let sendOnchain = async (params) => { if (!hex) ({ hex } = await build(params)); let { tx, type } = await decode(hex); - let node = getNode(type); + let node = rpc(config[type]); let { txid } = tx; try { @@ -582,16 +590,6 @@ export let sendLightning = async ({ } }; -export let getNode = (type, wallet = undefined) => { - if (type === types.bitcoin) { - return bc; - } else if (type === types.liquid) { - return lq; - } else if (wallet) { - return rpc({ ...config.bitcoin, wallet }); - } else fail("unrecognized transaction type"); -}; - let getAddressType = async (a) => { try { await bc.getAddressInfo(a); @@ -608,8 +606,7 @@ let getAddressType = async (a) => { export let build = async ({ amount, address, feeRate, user, subtract }) => { let type = await getAddressType(address); - - let node = getNode(type); + let node = rpc(config[type]); amount = parseInt(amount); if (amount < 0) fail("invalid amount"); @@ -1,28 +1,19 @@ import got from "got"; +import { fail } from "$lib/utils"; +import { err } from "$lib/logging"; -export default ({ host, port, wallet, username, password }) => - new Proxy( - {}, - { - get: - (_, prop) => - (...params) => - ((method, ...params) => { - let url = `http://${host}:${port}/wallet/${wallet}`; - return got - .post(url, { - json: { - method, - params, - }, - headers: { - authorization: `Basic ${Buffer.from( - `${username}:${password}`, - ).toString("base64")}`, - }, - }) - .json() - .then(({ result }) => result); - })((prop as any).toLowerCase(), ...params), - }, - ); +export default ({ host, port, wallet, username, password }): any => { + let url = `http://${host}:${port}/wallet/${wallet}`; + let token = btoa(`${username}:${password}`); + let headers = { authorization: `Basic ${token}` }; + let p = (json) => got.post(url, { json, headers }).json(); + + let get = + (_, prop: any) => + (...params) => + p({ method: prop.toLowerCase(), params }) + .then((res: any) => res.result) + .catch((e) => (err(e.message), fail(`Failed to call ${prop}`))); + + return new Proxy({}, { get }); +}; diff --git a/lib/utils.ts b/lib/utils.ts index ada32521..b0875873 100644 --- a/lib/utils.ts +++ b/lib/utils.ts @@ -1,6 +1,5 @@ import { randomBytes, bytesToHex } from "@noble/hashes/utils"; import migrate from "$lib/migrate"; -import store from "$lib/store"; import { g, s } from "$lib/db"; import config from "$config"; diff --git a/routes/ecash.ts b/routes/ecash.ts index af7f842d..7516c05e 100644 --- a/routes/ecash.ts +++ b/routes/ecash.ts @@ -58,7 +58,7 @@ export default { received: 0, }); - await credit(hash, amount, memo, user.id, type); + await credit({hash, amount, memo, ref:user.id, type}); res.send({ ok: true }); } catch (e) { diff --git a/routes/invoices.ts b/routes/invoices.ts index 77ff2ac1..f4034408 100644 --- a/routes/invoices.ts +++ b/routes/invoices.ts @@ -1,11 +1,12 @@ import { g } from "$lib/db"; import { generate } from "$lib/invoices"; import { bail, pick } from "$lib/utils"; +import { err } from "$lib/logging"; export default { async get(req, res) { let { - params: { id }, + params: { id }, } = req; let invoice = await g(`invoice:${id}`); if (typeof invoice === "string") invoice = await g(`invoice:${invoice}`); @@ -34,8 +35,10 @@ export default { } = req; try { - res.send(await generate({ invoice, user, sender })); + res.send(await generate({ invoice, user, sender })); } catch (e) { + console.log(e); + err("problem generating invoice", e.message); bail(res, e.message); } }, diff --git a/routes/payments.ts b/routes/payments.ts index cf67e966..c0c6d947 100644 --- a/routes/payments.ts +++ b/routes/payments.ts @@ -12,7 +12,6 @@ import { debit, credit, types, - getNode, build, sendLightning, sendOnchain, @@ -20,9 +19,13 @@ import { import { mqtt1, mqtt2 } from "$lib/mqtt"; import got from "got"; import api from "$lib/api"; +import rpc from "$lib/rpc"; import ln from "$lib/ln"; +let { wallet: bcw } = config.bitcoin; +let { wallet: lqw } = config.liquid; + export default { async info(_, res) { res.send(await ln.getinfo()); @@ -53,7 +56,7 @@ export default { if (!p) { if (hash) { p = await debit({ hash, amount, memo, user }); - await credit(hash, amount, memo, user.id); + await credit({ hash, amount, memo, ref: user.id }); } else if (fund) { p = await debit({ hash, amount, memo: fund, user, type: types.fund }); await db.incrBy(`fund:${fund}`, amount); @@ -73,13 +76,13 @@ export default { async list(req, res) { let { user: { id }, - query: { start, end, limit, offset }, + query: { account, start, end, limit, offset }, } = req; if (limit) limit = parseInt(limit); offset = parseInt(offset) || 0; - let payments = (await db.lRange(`${id}:payments`, 0, -1)) || []; + let payments = (await db.lRange(`${account || id}:payments`, 0, -1)) || []; payments = ( await Promise.all( payments.map(async (id) => { @@ -237,7 +240,13 @@ export default { }); } - let payment = await credit(iid, amount, id, id, types.fund); + let payment = await credit({ + hash: iid, + amount, + memo: id, + ref: id, + type: types.fund, + }); await db.lPush(`fund:${id}:payments`, payment.id); res.send({ payment }); @@ -253,58 +262,65 @@ export default { } = req; try { - let known = await g(`${wallet}:balance`) !== null - let node = getNode(type, known ? wallet : undefined); + let node = rpc({ ...config[type], wallet }); - if (wallet === config.bitcoin.wallet || wallet === config.liquid.wallet) { - let { confirmations, details } = await node.getTransaction(txid); - for (let { address, amount, asset, category, vout } of details) { - if (!address) continue; - if (type === types.liquid && asset !== config.liquid.btc) continue; - if (category === "send") { - let p = await g(`payment:${txid}`); - if (typeof p === "string") p = await g(`payment:${p}`); - if (!p) continue; + let { confirmations, details } = await node.getTransaction(txid); - if (confirmations >= 1) p.confirmed = true; - await s(`payment:${p.id}`, p); - emit(p.uid, "payment", p); - continue; - } + for (let { address, amount, asset, category, vout } of details) { + if (!address) continue; + if (type === types.liquid && asset !== config.liquid.btc) continue; - let p = await g(`payment:${txid}:${vout}`); + if (category === "send") { + let p = await g(`payment:${txid}`); if (typeof p === "string") p = await g(`payment:${p}`); + if (!p) continue; - if (!p) { - await credit(address, sats(amount), "", `${txid}:${vout}`, type); - } else if (confirmations >= 1) { - let id = `payment:${txid}:${vout}`; - let p = await g(id); - if (typeof p === "string") p = await g(`payment:${p}`); - if (!p) return db.sAdd("missed", id); - if (p.confirmed) return; + if (confirmations >= 1) p.confirmed = true; + await s(`payment:${p.id}`, p); + emit(p.uid, "payment", p); + continue; + } - let invoice = await getInvoice(address); - let { id: iid } = invoice; + let p = await g(`payment:${txid}:${vout}`); + if (typeof p === "string") p = await g(`payment:${p}`); - p.confirmed = true; - invoice.received += parseInt(invoice.pending); - invoice.pending = 0; + if (!p) { + let account; + if ((await g(`${wallet}:balance`)) !== null) account = wallet; + await credit({ + hash: address, + amount: sats(amount), + ref: `${txid}:${vout}`, + type, + account, + }); + } else if (confirmations >= 1) { + let id = `payment:${txid}:${vout}`; + let p = await g(id); + if (typeof p === "string") p = await g(`payment:${p}`); + if (!p) return db.sAdd("missed", id); + if (p.confirmed) return; - l("confirming", id, p.id, p.amount); + let invoice = await getInvoice(address); + let { id: iid } = invoice; - await db - .multi() - .set(`invoice:${iid}`, JSON.stringify(invoice)) - .set(`payment:${p.id}`, JSON.stringify(p)) - .decrBy(`pending:${p.uid}`, p.amount) - .incrBy(`balance:${p.uid}`, p.amount) - .exec(); + p.confirmed = true; + invoice.received += parseInt(invoice.pending); + invoice.pending = 0; - emit(p.uid, "payment", p); - let user = await g(`user:${p.uid}`); - await completePayment(p, user); - } + l("confirming", id, p.id, p.amount); + + await db + .multi() + .set(`invoice:${iid}`, JSON.stringify(invoice)) + .set(`payment:${p.id}`, JSON.stringify(p)) + .decrBy(`pending:${p.uid}`, p.amount) + .incrBy(`balance:${p.uid}`, p.amount) + .exec(); + + emit(p.uid, "payment", p); + let user = await g(`user:${p.uid}`); + await completePayment(p, user); } } res.send({}); @@ -416,7 +432,7 @@ export default { if (!p) { p = await debit({ hash: pr, amount, memo, user }); - await credit(pr, amount, memo, user.id); + await credit({ hash: pr, amount, memo, ref: user.id }); } res.send(p); @@ -446,7 +462,7 @@ export default { if (p.uid !== user.id) fail("unauthorized"); let { tx, type } = await decode(p.hex); - let node = getNode(type); + let node = rpc(config[type]); let fees: any = await fetch(`${api[type]}/fees/recommended`).then((r) => r.json(), @@ -502,7 +518,7 @@ export default { let memo; let p = await debit({ hash, amount, memo, user: sender }); - await credit(hash, amount, memo, recipient.id); + await credit({ hash, amount, memo, ref: recipient.id }); res.send(p); }, diff --git a/routes/users.ts b/routes/users.ts index 3fcaad66..f49ca212 100644 --- a/routes/users.ts +++ b/routes/users.ts @@ -564,12 +564,25 @@ export default { } }, - async items(req, _) { - let { - params: { id }, - } = req; - let item = await g(`item:${id}`); - return item; + async accounts(req, res) { + let { user } = req; + + let balance = await g(`balance:${user.id}`); + let accounts: any = [ + { + balance, + type: "Cash", + mint: "https://mint.coinos.io", + }, + ]; + + for (let id of await db.lRange(`${user.id}:accounts`, 0, 1)) { + let account = await g(`account:${id}`); + account.balance = await g(`balance:${id}`); + accounts.push(account); + } + + res.send(accounts); }, async hidepay(req, res) { @@ -591,4 +604,18 @@ export default { await s(`user:${u.id}`, u); res.send({}); }, + + async createAccount(req, res) { + let { type, seed } = req.body; + let { user } = req; + + let id = v4(); + let account = { id, seed, type }; + + await s(`account:${id}`, account); + await s(`balance:${id}`, 0); + await db.lPush(`${user.id}:accounts`, id); + + res.send(account); + }, }; |
