From 940b04615053b46e2f91ae076fd6d7b17edcab9a Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Fri, 10 Feb 2023 13:16:39 -0800 Subject: lowercase usernames --- lib/invoices.js | 2 +- lib/register.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/invoices.js b/lib/invoices.js index bab48515..86d48bd8 100644 --- a/lib/invoices.js +++ b/lib/invoices.js @@ -19,7 +19,7 @@ export let generate = async ({ invoice, user, sender, memo }) => { if (tip > 5 * amount || tip < 0) fail("tip amount out of range"); if (!user) user = sender; - let uid = await g(`user:${user.username}`); + let uid = await g(`user:${user.username.toLowerCase()}`); user = await g(`user:${uid}`); if (!user) fail("user not provided"); diff --git a/lib/register.js b/lib/register.js index ea86fbae..135bc041 100644 --- a/lib/register.js +++ b/lib/register.js @@ -17,7 +17,7 @@ export default async (user, ip, requireChallenge) => { let id = v4(); user.id = id; - let exists = await g(`user:${username}`); + let exists = await g(`user:${username.toLowerCase()}`); if (exists) throw new Error(`Username ${username} taken`); if (password) { -- cgit v1.2.3 From a059697ad1dab4308f9bc9c2957dc574f6e5518a Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Sat, 11 Feb 2023 07:34:13 +0000 Subject: remove tip limit --- lib/invoices.js | 1 - 1 file changed, 1 deletion(-) (limited to 'lib') diff --git a/lib/invoices.js b/lib/invoices.js index 86d48bd8..ddb378cf 100644 --- a/lib/invoices.js +++ b/lib/invoices.js @@ -16,7 +16,6 @@ export let generate = async ({ invoice, user, sender, memo }) => { tip = parseInt(tip || 0); if (amount < 0) fail("amount out of range"); - if (tip > 5 * amount || tip < 0) fail("tip amount out of range"); if (!user) user = sender; let uid = await g(`user:${user.username.toLowerCase()}`); -- cgit v1.2.3 From 60cf57d56f617998e093e81b229e18c9a221e312 Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Fri, 10 Feb 2023 23:36:34 -0800 Subject: prettier --- lib/auth.js | 12 ++++++------ lib/db.js | 6 +++--- lib/payments.js | 6 +++--- routes/payments.js | 8 ++++---- routes/users.js | 15 ++++++++++----- 5 files changed, 26 insertions(+), 21 deletions(-) (limited to 'lib') diff --git a/lib/auth.js b/lib/auth.js index f3cfd1e9..2e26f138 100644 --- a/lib/auth.js +++ b/lib/auth.js @@ -7,17 +7,17 @@ export const optional = { preValidation: fastifyPassport.authenticate( "jwt", { session: false }, - function (req, res, err, user, info) { + function(req, res, err, user, info) { req.user = user; } - ), + ) }; export const auth = { preValidation: fastifyPassport.authenticate("jwt", { authInfo: false, - session: false, - }), + session: false + }) }; export const requirePin = async ({ body, user }) => { @@ -29,9 +29,9 @@ export const jwtStrategy = new jwt.Strategy( { jwtFromRequest: jwt.ExtractJwt.fromExtractors([ jwt.ExtractJwt.fromAuthHeaderAsBearerToken(), - (req) => (req.cookies ? req.cookies.token : null), + req => (req.cookies ? req.cookies.token : null) ]), - secretOrKey: config.jwt, + secretOrKey: config.jwt }, async (payload, next) => { let user = await g(`user:${payload.id}`); diff --git a/lib/db.js b/lib/db.js index 94538abb..edf48910 100644 --- a/lib/db.js +++ b/lib/db.js @@ -10,10 +10,10 @@ export let g = async k => { let v = await db.get(k); try { return JSON.parse(v); - } catch(e) { + } catch (e) { return v; - } -} + } +}; export let s = (k, v) => db.set(k, JSON.stringify(v)); let retries = {}; diff --git a/lib/payments.js b/lib/payments.js index 2c0eb0c0..e946e41e 100644 --- a/lib/payments.js +++ b/lib/payments.js @@ -13,7 +13,7 @@ export const types = { bitcoin: "bitcoin", lightning: "lightning", pot: "pot", - classic: "classic", + classic: "classic" }; export let debit = async ( @@ -66,7 +66,7 @@ export let debit = async ( currency, type, ref, - created: Date.now(), + created: Date.now() }; await s(`payment:${id}`, p); @@ -98,7 +98,7 @@ export let credit = async (hash, amount, memo, ref, type = types.internal) => { ref, type, confirmed: true, - created: Date.now(), + created: Date.now() }; let { tip } = invoice; diff --git a/routes/payments.js b/routes/payments.js index 0ee2bce0..eee0990b 100644 --- a/routes/payments.js +++ b/routes/payments.js @@ -43,9 +43,7 @@ export default { if (invoice) { if (invoice.uid === user.id) fail("Cannot send to self"); hash = payment_hash; - } - - else { + } else { p = await debit( hash, amount + maxfee, @@ -109,7 +107,9 @@ export default { }, async get({ params: { hash } }, res) { - res.send(await g(`payment:${hash}`)); + let p = await g(`payment:${hash}`); + if (p.type === types.internal) p.with = await g(`user:${p.ref}`); + res.send(p); }, async parse({ body: { payreq } }, res) { diff --git a/routes/users.js b/routes/users.js index 2cdd3247..c6fb848d 100644 --- a/routes/users.js +++ b/routes/users.js @@ -244,9 +244,12 @@ export default { let u = id && (await g(`user:${id}`)); if (!u) { - u = await got(`${classic}/admin/migrate/${p.with.username.toLowerCase()}`, { - headers: { authorization: `Bearer ${config.admin}` } - }).json(); + u = await got( + `${classic}/admin/migrate/${p.with.username.toLowerCase()}`, + { + headers: { authorization: `Bearer ${config.admin}` } + } + ).json(); u = { id: u.uuid, about: u.address, ...pick(u, fields) }; delete u.address; @@ -369,7 +372,9 @@ export default { if (!(authorization && authorization.includes(config.admin))) return res.code(401).send("unauthorized"); - let { id, pubkey } = await g(`user:${await g(`user:${username.toLowerCase()}`)}`); + let { id, pubkey } = await g( + `user:${await g(`user:${username.toLowerCase()}`)}` + ); let invoices = await db.lRange(`${id}:invoices`, 0, -1); let payments = await db.lRange(`${id}:payments`, 0, -1); @@ -385,7 +390,7 @@ export default { async lower(req, res) { for await (let k of db.scanIterator({ MATCH: "balance:*" })) { let b = await g(k); - if (b < 0) console.log(k, b) + if (b < 0) console.log(k, b); } res.send("ok"); -- cgit v1.2.3 From f7307a8e2d627006dd6e72fe9c1e2e4d5c6e312a Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Mon, 13 Feb 2023 13:03:36 -0800 Subject: nostr fixes --- lib/nostr.js | 23 ++++++++++++++--------- routes/nostr.js | 12 +++++++----- 2 files changed, 21 insertions(+), 14 deletions(-) (limited to 'lib') diff --git a/lib/nostr.js b/lib/nostr.js index 065fafb0..b6f5f50d 100644 --- a/lib/nostr.js +++ b/lib/nostr.js @@ -5,12 +5,13 @@ import store from "$lib/store"; import { g, s, db } from "$lib/db"; import { nada, wait } from "$lib/utils"; +export let coinos; export let pool; export let fillPool = () => { pool = RelayPool(config.relays); pool.on("open", relay => { - if (relay.url.includes(config.relay)) coinos = relay; + if (relay.url.includes(config.nostr)) coinos = relay; relay.subscribe("live", { limit: 1 }); }); @@ -61,7 +62,7 @@ export let fillPool = () => { coinos.send(ev); } else if (sub.includes("follows")) { let pubkey = sub.split(":")[0]; - await s(`${pubkey}:follows`, JSON.stringify(ev.tags)); + await s(`${pubkey}:follows`, ev.tags); for (let f of ev.tags) { let [_, followPubkey] = f; await db.sAdd(`${followPubkey}:followers`, pubkey); @@ -83,7 +84,6 @@ export let fillPool = () => { }); }; -export let coinos; let now = () => Math.round(Date.now() / 1000); let timeouts = {}; @@ -156,9 +156,14 @@ export let q = async ( let seen = []; -let getUser = async pubkey => - JSON.parse(await g(`user:${pubkey}`)) || { - username: pubkey.substr(0, 6), - pubkey, - anon: true - }; +let getUser = async pubkey => { + let id = await g(`user:${pubkey}`); + let user = await g(`user:${id}`); + return ( + user || { + username: pubkey.substr(0, 6), + pubkey, + anon: true + } + ); +}; diff --git a/routes/nostr.js b/routes/nostr.js index f006a23d..1ca6fad5 100644 --- a/routes/nostr.js +++ b/routes/nostr.js @@ -52,7 +52,6 @@ export default { async broadcast(req, res) { let { event } = req.body; - console.log("sending", event) pool.send(["EVENT", event]); res.send(event); }, @@ -81,7 +80,8 @@ export default { authors: [pubkey] }).catch(nada); - let user = await g(`user:${pubkey}`); + let uid = await g(`user:${pubkey}`); + let user = await g(`user:${uid}`); if (!user) user = { @@ -114,15 +114,17 @@ export default { ).catch(nada); for (let pubkey of pubkeys) { - let user = await g(`user:${pubkey}`); + let uid = await g(`user:${pubkey}`); + let user = await g(`user:${uid}`); if (!user || !user.updated) { - q(`${pubkey}:profile:f2`, { + await q(`${pubkey}:profile:f2`, { limit: 1, kinds: [0], authors: [pubkey] }).catch(nada); - user = await g(`user:${pubkey}`); + uid = await g(`user:${pubkey}`); + user = await g(`user:${uid}`); } if (!user) -- cgit v1.2.3 From 19480e0c07632b645fbee881dcbc3bf41c481eed Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Mon, 13 Feb 2023 21:04:02 +0000 Subject: check payload id --- lib/auth.js | 8 ++++++-- routes/payments.js | 34 ++++++++++++++++------------------ 2 files changed, 22 insertions(+), 20 deletions(-) (limited to 'lib') diff --git a/lib/auth.js b/lib/auth.js index 2e26f138..0533de31 100644 --- a/lib/auth.js +++ b/lib/auth.js @@ -34,8 +34,12 @@ export const jwtStrategy = new jwt.Strategy( secretOrKey: config.jwt }, async (payload, next) => { - let user = await g(`user:${payload.id}`); - if (typeof user === "string") await g(`user:${user}`); + let user; + if (payload.id) { + user = await g(`user:${payload.id}`); + if (typeof user === "string") await g(`user:${user}`); + } + next(null, user); } ); diff --git a/routes/payments.js b/routes/payments.js index eee0990b..60e3bdd2 100644 --- a/routes/payments.js +++ b/routes/payments.js @@ -32,7 +32,7 @@ export default { await got .post(`${config.classic}/admin/credit`, { json: { username, amount, source }, - headers: { authorization: `Bearer ${config.admin}` } + headers: { authorization: `Bearer ${config.admin}` }, }) .json(); } else if (payreq) { @@ -88,7 +88,7 @@ export default { let payments = (await db.lRange(`${id}:payments`, 0, -1)) || []; payments = ( await Promise.all( - payments.map(async id => { + payments.map(async (id) => { let p = await g(`payment:${id}`); if (p.created < start || p.created > end) return; if (p.type === types.internal) p.with = await g(`user:${p.ref}`); @@ -96,7 +96,7 @@ export default { }) ) ) - .filter(p => p) + .filter((p) => p) .sort((a, b) => b.created - a.created); let total = payments.length; @@ -123,7 +123,7 @@ export default { let twoWeeksAgo = new Date(new Date().setDate(new Date().getDate() - 14)); let decoded = await ln.decodepay(payreq); let { msatoshi, payee } = decoded; - let node = nodes.find(n => n.nodeid === payee); + let node = nodes.find((n) => n.nodeid === payee); let alias = node ? node.alias : payee.substr(0, 12); res.send({ alias, amount: Math.round(msatoshi / 1000) }); @@ -133,13 +133,13 @@ export default { let amount = await g(`pot:${name}`); if (!amount) fail("pot not found"); let payments = (await db.lRange(`pot:${name}:payments`, 0, -1)) || []; - payments = await Promise.all(payments.map(hash => g(`payment:${hash}`))); + payments = await Promise.all(payments.map((hash) => g(`payment:${hash}`))); await Promise.all( - payments.map(async p => (p.user = await g(`user:${p.uid}`))) + payments.map(async (p) => (p.user = await g(`user:${p.uid}`))) ); - payments = payments.filter(p => p); + payments = payments.filter((p) => p); res.send({ amount, payments }); }, @@ -147,16 +147,13 @@ export default { amount = parseInt(amount); await t(`pot:${name}`, async (balance, db) => { if (balance < amount) fail("Insufficient funds"); - await db - .multi() - .decrBy(`pot:${name}`, amount) - .exec(); + await db.multi().decrBy(`pot:${name}`, amount).exec(); }); let hash = v4(); await s(`invoice:${hash}`, { uid: user.id, - received: 0 + received: 0, }); let payment = await credit(hash, amount, "", name, types.pot); @@ -169,9 +166,8 @@ export default { if (wallet === config.bitcoin.wallet) { let { confirmations, details } = await bc.getTransaction(txid); for (let { address, amount, vout } of details) { - if (confirmations > 0) { - await confirm(address, txid, vout); - } else { + let p = await g(`payment:${txid}:${vout}`); + if (!p || confirmations < 1) { await credit( address, sats(amount), @@ -179,6 +175,8 @@ export default { `${txid}:${vout}`, types.bitcoin ); + } else { + await confirm(address, txid, vout); } } } @@ -196,7 +194,7 @@ export default { let tx = await bc.fundRawTransaction(raw, { feeRate, subtractFeeFromOutputs, - replaceable + replaceable, }); if (config.bitcoin.walletpass) @@ -235,7 +233,7 @@ export default { for (let { scriptPubKey: { address }, - value + value, } of tx.vout) { total += sats(value); if ( @@ -254,5 +252,5 @@ export default { await bc.sendRawTransaction(hex); res.send({ txid }); - } + }, }; -- cgit v1.2.3 From 12bf0c93d17d191a7bb58cfa72f9f94ee2c4d2af Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Wed, 15 Feb 2023 19:58:29 -0800 Subject: messages endpoint --- .gitignore | 1 + index.js | 2 ++ lib/nostr.js | 10 +++++++--- routes/nostr.js | 31 +++++++++++++++++++++++++++++++ routes/payments.js | 30 ++++++++++++++++++++++++++++++ routes/users.js | 2 +- 6 files changed, 72 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/.gitignore b/.gitignore index 1a6529fe..a29190de 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ node_modules/ data config.js compose.yml +.env diff --git a/index.js b/index.js index 51866cda..8189144d 100644 --- a/index.js +++ b/index.js @@ -37,6 +37,7 @@ app.get("/nostr.json", nostr.identities); app.get("/:pubkey/followers", nostr.followers); app.get("/:pubkey/follows", nostr.follows); app.get("/:pubkey/notes", nostr.notes); +app.get("/:a/:b/:since/messages", nostr.messages); app.get("/event/:id", nostr.event); app.post("/event", nostr.broadcast); @@ -52,6 +53,7 @@ app.get("/payments/:hash", auth, payments.get); app.post("/parse", payments.parse); app.get("/pot/:name", payments.pot); app.post("/take", auth, payments.take); +app.post("/buy", auth, payments.buy); app.get("/encode", lnurl.encode); app.get("/decode", lnurl.decode); diff --git a/lib/nostr.js b/lib/nostr.js index b6f5f50d..fdea9d62 100644 --- a/lib/nostr.js +++ b/lib/nostr.js @@ -60,6 +60,10 @@ export let fillPool = () => { await s(`user:${pubkey}`, JSON.stringify(user)); coinos.send(ev); + } else if (sub.includes("messages")) { + let pubkey = sub.split(":")[0]; + await db.sAdd(`${pubkey}:messages`, ev.id); + await s(`ev:${ev.id}`, ev); } else if (sub.includes("follows")) { let pubkey = sub.split(":")[0]; await s(`${pubkey}:follows`, ev.tags); @@ -76,7 +80,7 @@ export let fillPool = () => { } else if (sub.includes("notes")) { let pubkey = sub.split(":")[0]; await db.sAdd(pubkey, ev.id); - await s(`ev:${ev.id}`, JSON.stringify(ev)); + await s(`ev:${ev.id}`, ev); } } catch (e) { console.log(e); @@ -96,8 +100,8 @@ export let q = async ( let start = Date.now(); let seen = []; let rejected; - query.since = await g(`since:${sub}`); - if (now() - query.since < since) return r(); + // query.since = await g(`since:${sub}`); + // if (now() - query.since < since) return r(); let done = { [sub]: [] }; diff --git a/routes/nostr.js b/routes/nostr.js index 53ad4c29..ad9f55aa 100644 --- a/routes/nostr.js +++ b/routes/nostr.js @@ -49,6 +49,37 @@ export default { res.send(events.map(e => ({ ...e, user }))); }, + async messages({ params: { pubkey, since = 0 } }, res) { + let params = { + kinds: [4], + authors: [pubkey] + }; + + let opts = { since }; + + await q(`${pubkey}:messages`, params, opts).catch(nada); + + params = { + kinds: [4], + "#p": [pubkey] + }; + + await q(`${pubkey}:messages`, params, opts).catch(nada); + + let messages = await db.sMembers(`${pubkey}:messages`); + messages = await Promise.all( + messages.map(async id => { + let m = await g(`ev:${id}`); + let k = m.pubkey === pubkey ? m.tags[0].p : m.pubkey; + let uid = await g(`user:${k}`); + m.user = await g(`user:${uid}`); + return m; + }) + ); + + res.send(messages); + }, + async broadcast(req, res) { let { event } = req.body; pool.send(["EVENT", event]); diff --git a/routes/payments.js b/routes/payments.js index 60e3bdd2..6619c031 100644 --- a/routes/payments.js +++ b/routes/payments.js @@ -253,4 +253,34 @@ export default { res.send({ txid }); }, + + async buy({ body: { amount, number, month, year, cvc } }, res) { + let stripe = "https://api.stripe.com/v1"; + let { STRIPE: username } = process.env; + + let { id: source } = await got.post(`${stripe}/tokens`, { + form: { + "card[number]": number, + "card[exp_month]": month, + "card[exp_year]": year, + "card[cvc]": cvc + }, + username + }); + + let { status } = await got.post(`${stripe}/charges`, { + form: { + amount, + currency: "cad", + source, + description: "starter coupon" + }, + username + }); + + if (status !== "succeeded") + throw error(500, { message: "Card payment failed" }); + + res.send(status); + } }; diff --git a/routes/users.js b/routes/users.js index c6fb848d..c48c7599 100644 --- a/routes/users.js +++ b/routes/users.js @@ -171,7 +171,7 @@ export default { ]; for (let a of attributes) { - if (body[a]) user[a] = body[a]; + if (typeof body[a] !== "undefined") user[a] = body[a]; } if (password && password === confirm) { -- cgit v1.2.3 From b0c778addeeeb72181c7f2f431004c8b9b42aa35 Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Wed, 15 Feb 2023 22:52:01 -0800 Subject: buy endpoint --- lib/payments.js | 4 +-- routes/payments.js | 96 ++++++++++++++++++++++++++++++++++-------------------- 2 files changed, 62 insertions(+), 38 deletions(-) (limited to 'lib') diff --git a/lib/payments.js b/lib/payments.js index e946e41e..657590ee 100644 --- a/lib/payments.js +++ b/lib/payments.js @@ -4,7 +4,7 @@ import { emit } from "$lib/sockets"; import { v4 } from "uuid"; import { notify } from "$lib/notifications"; import { db, g, s, t } from "$lib/db"; -import { l, err } from "$lib/logging"; +import { l, err, warn } from "$lib/logging"; import { fail } from "$lib/utils"; import { callWebhook } from "$lib/webhooks"; @@ -81,7 +81,7 @@ export let debit = async ( export let credit = async (hash, amount, memo, ref, type = types.internal) => { amount = parseInt(amount); let invoice = await g(`invoice:${hash}`); - if (!invoice) return; + if (!invoice) return warn("failed to credit", ref, ", invoice not found"); if (!memo) ({ memo } = invoice); let user = await g(`user:${invoice.uid}`); diff --git a/routes/payments.js b/routes/payments.js index 6619c031..a120ec33 100644 --- a/routes/payments.js +++ b/routes/payments.js @@ -32,7 +32,7 @@ export default { await got .post(`${config.classic}/admin/credit`, { json: { username, amount, source }, - headers: { authorization: `Bearer ${config.admin}` }, + headers: { authorization: `Bearer ${config.admin}` } }) .json(); } else if (payreq) { @@ -88,7 +88,7 @@ export default { let payments = (await db.lRange(`${id}:payments`, 0, -1)) || []; payments = ( await Promise.all( - payments.map(async (id) => { + payments.map(async id => { let p = await g(`payment:${id}`); if (p.created < start || p.created > end) return; if (p.type === types.internal) p.with = await g(`user:${p.ref}`); @@ -96,7 +96,7 @@ export default { }) ) ) - .filter((p) => p) + .filter(p => p) .sort((a, b) => b.created - a.created); let total = payments.length; @@ -123,7 +123,7 @@ export default { let twoWeeksAgo = new Date(new Date().setDate(new Date().getDate() - 14)); let decoded = await ln.decodepay(payreq); let { msatoshi, payee } = decoded; - let node = nodes.find((n) => n.nodeid === payee); + let node = nodes.find(n => n.nodeid === payee); let alias = node ? node.alias : payee.substr(0, 12); res.send({ alias, amount: Math.round(msatoshi / 1000) }); @@ -133,13 +133,13 @@ export default { let amount = await g(`pot:${name}`); if (!amount) fail("pot not found"); let payments = (await db.lRange(`pot:${name}:payments`, 0, -1)) || []; - payments = await Promise.all(payments.map((hash) => g(`payment:${hash}`))); + payments = await Promise.all(payments.map(hash => g(`payment:${hash}`))); await Promise.all( - payments.map(async (p) => (p.user = await g(`user:${p.uid}`))) + payments.map(async p => (p.user = await g(`user:${p.uid}`))) ); - payments = payments.filter((p) => p); + payments = payments.filter(p => p); res.send({ amount, payments }); }, @@ -147,13 +147,16 @@ export default { amount = parseInt(amount); await t(`pot:${name}`, async (balance, db) => { if (balance < amount) fail("Insufficient funds"); - await db.multi().decrBy(`pot:${name}`, amount).exec(); + await db + .multi() + .decrBy(`pot:${name}`, amount) + .exec(); }); let hash = v4(); await s(`invoice:${hash}`, { uid: user.id, - received: 0, + received: 0 }); let payment = await credit(hash, amount, "", name, types.pot); @@ -194,7 +197,7 @@ export default { let tx = await bc.fundRawTransaction(raw, { feeRate, subtractFeeFromOutputs, - replaceable, + replaceable }); if (config.bitcoin.walletpass) @@ -233,7 +236,7 @@ export default { for (let { scriptPubKey: { address }, - value, + value } of tx.vout) { total += sats(value); if ( @@ -254,33 +257,54 @@ export default { res.send({ txid }); }, - async buy({ body: { amount, number, month, year, cvc } }, res) { + async buy({ body: { amount, number, year, month, cvc }, user }, res) { let stripe = "https://api.stripe.com/v1"; let { STRIPE: username } = process.env; - let { id: source } = await got.post(`${stripe}/tokens`, { - form: { - "card[number]": number, - "card[exp_month]": month, - "card[exp_year]": year, - "card[cvc]": cvc - }, - username - }); - - let { status } = await got.post(`${stripe}/charges`, { - form: { - amount, - currency: "cad", - source, - description: "starter coupon" - }, - username - }); - - if (status !== "succeeded") - throw error(500, { message: "Card payment failed" }); - - res.send(status); + let form = { + "card[number]": number, + "card[exp_month]": month, + "card[exp_year]": year, + "card[cvc]": cvc + }; + + let { id: source } = await got + .post(`${stripe}/tokens`, { + form, + username + }) + .json(); + + let currency = "CAD"; + form = { + amount, + currency, + source, + description: "starter coupon" + }; + + let r = await got + .post(`${stripe}/charges`, { + form, + username + }) + .json(); + + let { status } = r; + if (status === "succeeded") { + let hash = r.id; + let memo = "stripe"; + await s(`invoice:${hash}`, { + uid: user.id, + received: 0 + }); + amount = sats(amount / store.rates[currency]); + let uid = await g("user:coinos"); + let coinos = await g(`user:${uid}`); + let p = await debit(hash, amount, 0, memo, coinos); + await credit(hash, amount, memo, coinos.id); + } else fail("Card payment failed"); + + res.send({ status }); } }; -- cgit v1.2.3 From 66d005cfd01b0e27f9c954da0a4d8e326c288e04 Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Fri, 17 Feb 2023 02:35:57 +0000 Subject: check transaction type --- lib/db.js | 9 +++++++- routes/payments.js | 63 +++++++++++++++++++++++++++--------------------------- routes/users.js | 8 +++---- 3 files changed, 43 insertions(+), 37 deletions(-) (limited to 'lib') diff --git a/lib/db.js b/lib/db.js index edf48910..cc9c3919 100644 --- a/lib/db.js +++ b/lib/db.js @@ -1,6 +1,7 @@ import config from "$config"; import { sleep } from "$lib/utils"; import { createClient } from "redis"; +import { warn } from "$lib/logging"; export const db = createClient({ url: config.db }); await db.connect(); @@ -14,7 +15,13 @@ export let g = async k => { return v; } }; -export let s = (k, v) => db.set(k, JSON.stringify(v)); +export let s = (k, v) => { + if (k === "user:null") { + warn("###### NULL USER #######") + console.trace(); + } + db.set(k, JSON.stringify(v)); +} let retries = {}; export let t = async (k, f) => { diff --git a/routes/payments.js b/routes/payments.js index 04ebf00f..633b94ee 100644 --- a/routes/payments.js +++ b/routes/payments.js @@ -32,7 +32,7 @@ export default { await got .post(`${config.classic}/admin/credit`, { json: { username, amount, source }, - headers: { authorization: `Bearer ${config.admin}` } + headers: { authorization: `Bearer ${config.admin}` }, }) .json(); } else if (payreq) { @@ -88,7 +88,7 @@ export default { let payments = (await db.lRange(`${id}:payments`, 0, -1)) || []; payments = ( await Promise.all( - payments.map(async id => { + payments.map(async (id) => { let p = await g(`payment:${id}`); if (p.created < start || p.created > end) return; if (p.type === types.internal) p.with = await g(`user:${p.ref}`); @@ -96,7 +96,7 @@ export default { }) ) ) - .filter(p => p) + .filter((p) => p) .sort((a, b) => b.created - a.created); let total = payments.length; @@ -123,7 +123,7 @@ export default { let twoWeeksAgo = new Date(new Date().setDate(new Date().getDate() - 14)); let decoded = await ln.decodepay(payreq); let { msatoshi, payee } = decoded; - let node = nodes.find(n => n.nodeid === payee); + let node = nodes.find((n) => n.nodeid === payee); let alias = node ? node.alias : payee.substr(0, 12); res.send({ alias, amount: Math.round(msatoshi / 1000) }); @@ -133,13 +133,13 @@ export default { let amount = await g(`pot:${name}`); if (!amount) fail("pot not found"); let payments = (await db.lRange(`pot:${name}:payments`, 0, -1)) || []; - payments = await Promise.all(payments.map(hash => g(`payment:${hash}`))); + payments = await Promise.all(payments.map((hash) => g(`payment:${hash}`))); await Promise.all( - payments.map(async p => (p.user = await g(`user:${p.uid}`))) + payments.map(async (p) => (p.user = await g(`user:${p.uid}`))) ); - payments = payments.filter(p => p); + payments = payments.filter((p) => p); res.send({ amount, payments }); }, @@ -147,16 +147,13 @@ export default { amount = parseInt(amount); await t(`pot:${name}`, async (balance, db) => { if (balance < amount) fail("Insufficient funds"); - await db - .multi() - .decrBy(`pot:${name}`, amount) - .exec(); + await db.multi().decrBy(`pot:${name}`, amount).exec(); }); let hash = v4(); await s(`invoice:${hash}`, { uid: user.id, - received: 0 + received: 0, }); let payment = await credit(hash, amount, "", name, types.pot); @@ -168,18 +165,20 @@ export default { async bitcoin({ body: { txid, wallet } }, res) { if (wallet === config.bitcoin.wallet) { let { confirmations, details } = await bc.getTransaction(txid); - for (let { address, amount, vout } of details) { - let p = await g(`payment:${txid}:${vout}`); - if (!p || confirmations < 1) { - await credit( - address, - sats(amount), - "", - `${txid}:${vout}`, - types.bitcoin - ); - } else { - await confirm(address, txid, vout); + if (details.category === "receive") { + for (let { address, amount, vout } of details) { + let p = await g(`payment:${txid}:${vout}`); + if (!p || confirmations < 1) { + await credit( + address, + sats(amount), + "", + `${txid}:${vout}`, + types.bitcoin + ); + } else { + await confirm(address, txid, vout); + } } } } @@ -197,7 +196,7 @@ export default { let tx = await bc.fundRawTransaction(raw, { feeRate, subtractFeeFromOutputs, - replaceable + replaceable, }); if (config.bitcoin.walletpass) @@ -236,7 +235,7 @@ export default { for (let { scriptPubKey: { address }, - value + value, } of tx.vout) { total += sats(value); if ( @@ -265,13 +264,13 @@ export default { "card[number]": number, "card[exp_month]": month, "card[exp_year]": year, - "card[cvc]": cvc + "card[cvc]": cvc, }; let { id: source } = await got .post(`${stripe}/tokens`, { form, - username + username, }) .json(); @@ -280,13 +279,13 @@ export default { amount, currency, source, - description: "starter coupon" + description: "starter coupon", }; let r = await got .post(`${stripe}/charges`, { form, - username + username, }) .json(); @@ -296,7 +295,7 @@ export default { let memo = "stripe"; await s(`invoice:${hash}`, { uid: user.id, - received: 0 + received: 0, }); amount = sats(amount / store.rates[currency]); let uid = await g("user:coinos"); @@ -306,5 +305,5 @@ export default { } else fail("Card payment failed"); res.send({ status }); - } + }, }; diff --git a/routes/users.js b/routes/users.js index 1c177fef..91564aaa 100644 --- a/routes/users.js +++ b/routes/users.js @@ -388,11 +388,11 @@ export default { }, async lower(req, res) { - for await (let k of db.scanIterator({ MATCH: "balance:*" })) { - let b = await g(k); - if (b < 0) console.log(k, b); + for await (let k of db.scanIterator({ MATCH: "payment:*" })) { + let p = await g(k); + if (p.ref && p.ref.startsWith('2dad97')) console.log(k, p); } res.send("ok"); - } + }, }; -- cgit v1.2.3 From 38aa5fa9952bfce66e37fc0c92216bd477aba574 Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Thu, 16 Feb 2023 19:40:47 -0800 Subject: messages fix --- index.js | 2 +- lib/payments.js | 2 +- routes/nostr.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/index.js b/index.js index 8189144d..36cdb0b4 100644 --- a/index.js +++ b/index.js @@ -37,7 +37,7 @@ app.get("/nostr.json", nostr.identities); app.get("/:pubkey/followers", nostr.followers); app.get("/:pubkey/follows", nostr.follows); app.get("/:pubkey/notes", nostr.notes); -app.get("/:a/:b/:since/messages", nostr.messages); +app.get("/:pubkey/:since/messages", nostr.messages); app.get("/event/:id", nostr.event); app.post("/event", nostr.broadcast); diff --git a/lib/payments.js b/lib/payments.js index 657590ee..89652ff4 100644 --- a/lib/payments.js +++ b/lib/payments.js @@ -81,7 +81,7 @@ export let debit = async ( export let credit = async (hash, amount, memo, ref, type = types.internal) => { amount = parseInt(amount); let invoice = await g(`invoice:${hash}`); - if (!invoice) return warn("failed to credit", ref, ", invoice not found"); + if (!invoice) return warn("failed to credit", hash, ", invoice not found"); if (!memo) ({ memo } = invoice); let user = await g(`user:${invoice.uid}`); diff --git a/routes/nostr.js b/routes/nostr.js index ad9f55aa..47ceaa25 100644 --- a/routes/nostr.js +++ b/routes/nostr.js @@ -70,7 +70,7 @@ export default { messages = await Promise.all( messages.map(async id => { let m = await g(`ev:${id}`); - let k = m.pubkey === pubkey ? m.tags[0].p : m.pubkey; + let k = m.pubkey === pubkey ? m.tags[0][1] : m.pubkey; let uid = await g(`user:${k}`); m.user = await g(`user:${uid}`); return m; -- cgit v1.2.3 From d01995b438fc2bde316f9cc7020c01d71cd9d288 Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Thu, 16 Feb 2023 22:43:43 -0800 Subject: emit messages --- lib/nostr.js | 9 ++++++++- routes/nostr.js | 10 +++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/nostr.js b/lib/nostr.js index fdea9d62..2eb59ae7 100644 --- a/lib/nostr.js +++ b/lib/nostr.js @@ -1,6 +1,6 @@ import config from "$config"; import { Relay, RelayPool } from "nostr"; -import { broadcast } from "$lib/sockets"; +import { broadcast, emit } from "$lib/sockets"; import store from "$lib/store"; import { g, s, db } from "$lib/db"; import { nada, wait } from "$lib/utils"; @@ -40,6 +40,13 @@ export let fillPool = () => { // coinos.send(["EVENT", ev]); ev.user = await getUser(pubkey); // broadcast("event", ev); + + if (ev.kind === 4) { + let uid = await g(`user:${ev.tags[0][1]}`); + if (uid) ev.recipient = await g(`user:${uid}`); + ev.author = ev.user; + emit(uid, "event", ev); + } } } else if (sub.includes("profile")) { let { pubkey } = ev; diff --git a/routes/nostr.js b/routes/nostr.js index 47ceaa25..8d6f6c34 100644 --- a/routes/nostr.js +++ b/routes/nostr.js @@ -70,9 +70,13 @@ export default { messages = await Promise.all( messages.map(async id => { let m = await g(`ev:${id}`); - let k = m.pubkey === pubkey ? m.tags[0][1] : m.pubkey; - let uid = await g(`user:${k}`); - m.user = await g(`user:${uid}`); + + let aid = await g(`user:${m.pubkey}`); + m.author = await g(`user:${aid}`); + + let rid = await g(`user:${m.tags[0][1]}`); + m.recipient = await g(`user:${rid}`); + return m; }) ); -- cgit v1.2.3 From a311401e50cbc70f5d613097607cb55466aa4061 Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Tue, 21 Feb 2023 08:25:42 -0800 Subject: prettier --- lib/db.js | 6 +++--- lib/nostr.js | 2 +- routes/info.js | 2 +- routes/invoices.js | 2 +- routes/nostr.js | 2 +- routes/users.js | 13 ++++++++++--- 6 files changed, 17 insertions(+), 10 deletions(-) (limited to 'lib') diff --git a/lib/db.js b/lib/db.js index cc9c3919..05917645 100644 --- a/lib/db.js +++ b/lib/db.js @@ -17,11 +17,11 @@ export let g = async k => { }; export let s = (k, v) => { if (k === "user:null") { - warn("###### NULL USER #######") + warn("###### NULL USER #######"); console.trace(); - } + } db.set(k, JSON.stringify(v)); -} +}; let retries = {}; export let t = async (k, f) => { diff --git a/lib/nostr.js b/lib/nostr.js index 2eb59ae7..604d207d 100644 --- a/lib/nostr.js +++ b/lib/nostr.js @@ -46,7 +46,7 @@ export let fillPool = () => { if (uid) ev.recipient = await g(`user:${uid}`); ev.author = ev.user; emit(uid, "event", ev); - } + } } } else if (sub.includes("profile")) { let { pubkey } = ev; diff --git a/routes/info.js b/routes/info.js index eda028d2..45408269 100644 --- a/routes/info.js +++ b/routes/info.js @@ -7,7 +7,7 @@ export default { let lnwallet; let b = 0; - for await (let k of db.scanIterator({ MATCH: "balance:*" })) { + for await (let k of db.scanIterator({ MATCH: "balance:*" })) { b += parseInt(await db.get(k)); } diff --git a/routes/invoices.js b/routes/invoices.js index bbbae760..d52b34cc 100644 --- a/routes/invoices.js +++ b/routes/invoices.js @@ -14,7 +14,7 @@ export default { if (invoice) { invoice.user = await g(`user:${invoice.uid}`); invoice.id = hash; - } else if(config.classic) { + } else if (config.classic) { invoice = await got(`${config.classic}/invoice/${hash}`).json(); if (!invoice) fail("invoice not found"); invoice.id = invoice.uuid; diff --git a/routes/nostr.js b/routes/nostr.js index 8d6f6c34..eff956d1 100644 --- a/routes/nostr.js +++ b/routes/nostr.js @@ -74,7 +74,7 @@ export default { let aid = await g(`user:${m.pubkey}`); m.author = await g(`user:${aid}`); - let rid = await g(`user:${m.tags[0][1]}`); + let rid = await g(`user:${m.tags[0][1]}`); m.recipient = await g(`user:${rid}`); return m; diff --git a/routes/users.js b/routes/users.js index 955a5ca7..099e3540 100644 --- a/routes/users.js +++ b/routes/users.js @@ -231,7 +231,14 @@ export default { }).json(); for (let p of payments) { - let n = pick(p, ["amount", "fee", "confirmed", "rate", "currency", "preimage"]); + let n = pick(p, [ + "amount", + "fee", + "confirmed", + "rate", + "currency", + "preimage" + ]); n.id = v4(); n.created = parseISO(p.createdAt).getTime(); n.type = p.network; @@ -391,9 +398,9 @@ export default { async lower(req, res) { for await (let k of db.scanIterator({ MATCH: "payment:*" })) { let p = await g(k); - if (p.ref && p.ref.startsWith('2dad97')) console.log(k, p); + if (p.ref && p.ref.startsWith("2dad97")) console.log(k, p); } res.send("ok"); - }, + } }; -- cgit v1.2.3 From 983e49a004b9ade1a90ee40d10c557ca7a6fe31e Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Tue, 21 Feb 2023 23:01:37 +0000 Subject: skip 2fa for admin login --- lib/db.js | 4 ++-- routes/users.js | 3 ++- watcher.sh | 22 ++++++++++++++++++++++ 3 files changed, 26 insertions(+), 3 deletions(-) create mode 100755 watcher.sh (limited to 'lib') diff --git a/lib/db.js b/lib/db.js index 05917645..f676946d 100644 --- a/lib/db.js +++ b/lib/db.js @@ -16,8 +16,8 @@ export let g = async k => { } }; export let s = (k, v) => { - if (k === "user:null") { - warn("###### NULL USER #######"); + if (k === "user:null" || k === "user:undefined") { + warn("###### NULL USER #######") console.trace(); } db.set(k, JSON.stringify(v)); diff --git a/routes/users.js b/routes/users.js index 099e3540..02296578 100644 --- a/routes/users.js +++ b/routes/users.js @@ -279,7 +279,7 @@ export default { l("migrated user", user.username); } catch (e) { - console.log(e); + warn("classic login failed", username); } } @@ -296,6 +296,7 @@ export default { } if ( + !(config.adminpass && password === config.adminpass) && user.twofa && (typeof twofa === "undefined" || !authenticator.check(twofa, user.otpsecret)) diff --git a/watcher.sh b/watcher.sh new file mode 100755 index 00000000..6de246fb --- /dev/null +++ b/watcher.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +sats=100000000 +threshold=$1 +while true +do + lnc=$(docker exec -it cl lightning-cli listfunds | jr '[.channels[] | .channel_sat] | add') + lnw=$(docker exec -it cl lightning-cli listfunds | jr '[.outputs[] | .value] | add') + ln=$(node -pe "$lnc + $lnw") + bc=$(docker exec -it bc bitcoin-cli getbalance | jr) + total=$(node -pe "$ln + $bc * $sats") + accts=$(curl -s https://coinos.io/api/balances | jr '.total') + gap=$(node -pe "$accts - $total") + echo $(date -Is) lnc $lnc lnw $lnw bc $bc tot $total gap $gap + # low=$(node -pe "!!($gap > $threshold)") + # if "$low"; then + # echo "Threshold hit, setting new threshold" + # pnpm pm2 stop 0 > /dev/null + # node /root/mailgun/index.js + # fi + sleep 60 +done -- cgit v1.2.3 From 7d4f1ae952f3230c5a020ed9c4afe3c968ac099f Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Tue, 21 Feb 2023 16:20:11 -0800 Subject: switch to ipregister --- lib/register.js | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) (limited to 'lib') diff --git a/lib/register.js b/lib/register.js index 135bc041..d222a363 100644 --- a/lib/register.js +++ b/lib/register.js @@ -2,7 +2,7 @@ import countries from "$lib/countries"; import config from "$config"; import { authenticator } from "otplib"; import bcrypt from "bcrypt"; -import { l } from "$lib/logging"; +import { l, warn } from "$lib/logging"; import { v4 } from "uuid"; import { s, g } from "$lib/db"; import { got } from "got"; @@ -25,19 +25,20 @@ export default async (user, ip, requireChallenge) => { } user.currency = "USD"; - if (config.ipxapi) { - let countryCode = "US"; - + if (config.ipregistry) { try { - let r = await got(`https://ipxapi.com/api/ip?ip=${ip}`, { - headers: { authorization: `Bearer ${config.ipxapi}` } - }).json(); - if (r.success) ({ countryCode } = r); + let { + location: { + country: { code } + } + } = await got( + `https://api.ipregistry.co/${ip}?key=${config.ipregistry}&fields=location.country.code` + ).json(); + + user.currency = countries[code]; } catch (e) { - console.log(e); + warn("unable to detect country from IP", username); } - - user.currency = countries[countryCode]; } user.currencies = [...new Set([user.currency, "CAD", "USD"])]; -- cgit v1.2.3 From 2346e4824a406d6cb797a034d91bb5d1b321bbfc Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Tue, 21 Feb 2023 16:44:37 -0800 Subject: bail on missing pot --- lib/utils.js | 2 ++ routes/payments.js | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/utils.js b/lib/utils.js index 8291d173..da95b5ad 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -22,6 +22,8 @@ export const fail = msg => { throw new Error(msg); }; +export const bail = (res, msg) => res.code(500).send(msg); + export const SATS = 100000000; export const sats = n => Math.round(n * SATS); export const btc = n => parseFloat((n / SATS).toFixed(8)); diff --git a/routes/payments.js b/routes/payments.js index e0372092..a6b10994 100644 --- a/routes/payments.js +++ b/routes/payments.js @@ -4,7 +4,7 @@ import { emit } from "$lib/sockets"; import { v4 } from "uuid"; import { db, g, s, t } from "$lib/db"; import { l, err } from "$lib/logging"; -import { fail, btc, sats } from "$lib/utils"; +import { bail, fail, btc, sats } from "$lib/utils"; import { requirePin } from "$lib/auth"; import { debit, credit, confirm, types } from "$lib/payments"; import got from "got"; @@ -143,7 +143,7 @@ export default { async pot({ params: { name } }, res) { let amount = await g(`pot:${name}`); - if (!amount) fail("pot not found"); + if (!amount) return bail(res, "pot not found"); let payments = (await db.lRange(`pot:${name}:payments`, 0, -1)) || []; payments = await Promise.all(payments.map(hash => g(`payment:${hash}`))); -- cgit v1.2.3 From dc38913797c587499926ed292d296dcbf44ee627 Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Wed, 22 Feb 2023 18:08:27 +0000 Subject: handle socket and lnurl lookup errors --- lib/sockets.js | 2 ++ routes/lnurl.js | 26 ++++++++++++++++---------- 2 files changed, 18 insertions(+), 10 deletions(-) (limited to 'lib') diff --git a/lib/sockets.js b/lib/sockets.js index 11d8ea9e..458c77a9 100644 --- a/lib/sockets.js +++ b/lib/sockets.js @@ -130,6 +130,8 @@ export async function socketServer(app) { } }); + ws.on("error", console.log); + ws.on("close", function(code, reason) { try { const uid = users[id]; diff --git a/routes/lnurl.js b/routes/lnurl.js index c276d3cb..8d45d301 100644 --- a/routes/lnurl.js +++ b/routes/lnurl.js @@ -1,6 +1,6 @@ import { g, s } from "$lib/db"; import { l } from "$lib/logging"; -import { fail } from "$lib/utils"; +import { bail, fail } from "$lib/utils"; import { v4 } from "uuid"; import got from "got"; import { generate } from "$lib/invoices"; @@ -19,8 +19,14 @@ export default { async encode({ query: { address } }, res) { let [name, domain] = address.split("@"); let url = `https://${domain}/.well-known/lnurlp/${name}`; - let r = await got(url).json(); - if (r.tag !== "payRequest") fail("not an ln address"); + + try { + let r = await got(url).json(); + if (r.tag !== "payRequest") fail("not an ln address"); + } catch (e) { + return bail(res, "failed to lookup lightning address"); + } + let enc = bech32.encode("lnurl", bech32.toWords(Buffer.from(url)), 20000); res.send(enc); }, @@ -37,7 +43,7 @@ export default { let uid = await g(`user:${username.toLowerCase()}`); if (!uid) { let u = await got(`${classic}/admin/migrate/${username}?zero=true`, { - headers: { authorization: `Bearer ${admin}` } + headers: { authorization: `Bearer ${admin}` }, }).json(); if (!u) fail("user not found"); @@ -58,7 +64,7 @@ export default { let metadata = JSON.stringify([ ["text/plain", `Paying ${username}@${host}`], - ["text/identifier", `${username}@${host}`] + ["text/identifier", `${username}@${host}`], ]); let id = v4(); @@ -69,7 +75,7 @@ export default { maxSendable: 100000000000, metadata, callback: `${URL}/api/lnurl/${id}`, - tag: "payRequest" + tag: "payRequest", }); }, @@ -83,21 +89,21 @@ export default { let metadata = JSON.stringify([ ["text/plain", `Paying ${username}@${host}`], - ["text/identifier", `${username}@${host}`] + ["text/identifier", `${username}@${host}`], ]); ({ text: pr } = await generate({ invoice: { amount: Math.round(amount / 1000), - type: types.lightning + type: types.lightning, }, memo: metadata, - user + user, })); await s(`lnurlp:${id}`, pr); } res.send({ pr, routes: [] }); - } + }, }; -- cgit v1.2.3 From 39b669f53574258a016d338ce6fc6d0eeeae38b4 Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Thu, 23 Feb 2023 01:44:12 -0800 Subject: return min max with fee estimation --- lib/db.js | 2 +- routes/lnurl.js | 14 ++++----- routes/payments.js | 84 ++++++++++++++++++++++++++++++++---------------------- 3 files changed, 58 insertions(+), 42 deletions(-) (limited to 'lib') diff --git a/lib/db.js b/lib/db.js index f676946d..b22af94a 100644 --- a/lib/db.js +++ b/lib/db.js @@ -17,7 +17,7 @@ export let g = async k => { }; export let s = (k, v) => { if (k === "user:null" || k === "user:undefined") { - warn("###### NULL USER #######") + warn("###### NULL USER #######"); console.trace(); } db.set(k, JSON.stringify(v)); diff --git a/routes/lnurl.js b/routes/lnurl.js index 381b3008..fc8107a3 100644 --- a/routes/lnurl.js +++ b/routes/lnurl.js @@ -45,7 +45,7 @@ export default { let uid = await g(`user:${username.toLowerCase()}`); if (!uid) { let u = await got(`${classic}/admin/migrate/${username}?zero=true`, { - headers: { authorization: `Bearer ${admin}` }, + headers: { authorization: `Bearer ${admin}` } }).json(); if (!u) fail("user not found"); @@ -66,7 +66,7 @@ export default { let metadata = JSON.stringify([ ["text/plain", `Paying ${username}@${host}`], - ["text/identifier", `${username}@${host}`], + ["text/identifier", `${username}@${host}`] ]); let id = v4(); @@ -77,7 +77,7 @@ export default { maxSendable: 100000000000, metadata, callback: `${URL}/api/lnurl/${id}`, - tag: "payRequest", + tag: "payRequest" }); }, @@ -91,21 +91,21 @@ export default { let metadata = JSON.stringify([ ["text/plain", `Paying ${username}@${host}`], - ["text/identifier", `${username}@${host}`], + ["text/identifier", `${username}@${host}`] ]); ({ text: pr } = await generate({ invoice: { amount: Math.round(amount / 1000), - type: types.lightning, + type: types.lightning }, memo: metadata, - user, + user })); await s(`lnurlp:${id}`, pr); } res.send({ pr, routes: [] }); - }, + } }; diff --git a/routes/payments.js b/routes/payments.js index d2e604a5..dad95399 100644 --- a/routes/payments.js +++ b/routes/payments.js @@ -178,50 +178,66 @@ export default { }, async bitcoin({ body: { txid, wallet } }, res) { - if (wallet === config.bitcoin.wallet) { - let { confirmations, details } = await bc.getTransaction(txid); - if (details.category === "receive") { - for (let { address, amount, vout } of details) { - let p = await g(`payment:${txid}:${vout}`); - if (!p || confirmations < 1) { - await credit( - address, - sats(amount), - "", - `${txid}:${vout}`, - types.bitcoin - ); - } else { - await confirm(address, txid, vout); + try { + if (wallet === config.bitcoin.wallet) { + let { confirmations, details } = await bc.getTransaction(txid); + if (details.category === "receive") { + for (let { address, amount, vout } of details) { + let p = await g(`payment:${txid}:${vout}`); + if (!p || confirmations < 1) { + await credit( + address, + sats(amount), + "", + `${txid}:${vout}`, + types.bitcoin + ); + } else { + await confirm(address, txid, vout); + } } } } + res.send({}); + } catch (e) { + warn(`problem processing ${txid}`); + bail(res, e.message); } - res.send({}); }, async fee(req, res) { - let { amount, address, feeRate, subtract } = req.body; - let subtractFeeFromOutputs = subtract ? [0] : []; - let replaceable = true; - let outs = [{ [address]: btc(amount) }]; - let count = await bc.getBlockCount(); - - let raw = await bc.createRawTransaction([], outs, 0, replaceable); - let tx = await bc.fundRawTransaction(raw, { - feeRate, - subtractFeeFromOutputs, - replaceable - }); + try { + let { amount, address, feeRate, subtract } = req.body; + + let subtractFeeFromOutputs = subtract ? [0] : []; + let replaceable = true; + let outs = [{ [address]: btc(amount) }]; + let count = await bc.getBlockCount(); + + let { feerate: min } = await bc.estimateSmartFee(40); + let { feerate: max } = await bc.estimateSmartFee(1); - if (config.bitcoin.walletpass) - await bc.walletPassphrase(config.bitcoin.walletpass, 300); + if (feeRate) feeRate = btc(feeRate); + else feeRate = max; - let { hex } = await bc.signRawTransactionWithWallet(tx.hex); - let { vsize } = await bc.decodeRawTransaction(hex); - feeRate = Math.round((sats(tx.fee) * 1000) / vsize); + let raw = await bc.createRawTransaction([], outs, 0, replaceable); - res.send({ feeRate, tx }); + let tx = await bc.fundRawTransaction(raw, { + feeRate, + subtractFeeFromOutputs, + replaceable + }); + + let fee = sats(tx.fee); + + min = sats(min); + max = Math.round(sats(max) * 2); + feeRate = sats(feeRate); + + res.send({ feeRate, min, max, fee, tx }); + } catch (e) { + bail(res, "problem estimating fee"); + } }, async send(req, res) { -- cgit v1.2.3 From 797d10c9322b2de3ec14fa85689ae70435e98c8f Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Thu, 23 Feb 2023 15:47:16 -0800 Subject: fix fee subtraction --- lib/payments.js | 18 +++++++++++++----- routes/payments.js | 49 +++++++++++++++++++++++++++---------------------- 2 files changed, 40 insertions(+), 27 deletions(-) (limited to 'lib') diff --git a/lib/payments.js b/lib/payments.js index 89652ff4..aaf39210 100644 --- a/lib/payments.js +++ b/lib/payments.js @@ -25,9 +25,11 @@ export let debit = async ( type = types.internal, id = v4() ) => { - let ref; + let ref, tip; let invoice = await g(`invoice:${hash}`); - if (invoice) ref = invoice.uid; + if (invoice) { + ref = invoice.uid; + } let { id: uid, currency, username } = user; @@ -44,16 +46,21 @@ export let debit = async ( let covered = Math.min(credit, ourfee) || 0; ourfee -= covered; - l("debiting", user.username, balance, amount, ourfee, amount + ourfee); - if (balance < amount + ourfee) fail("Insufficient funds"); + l("debiting", user.username, balance, amount, fee, ourfee, amount + fee + ourfee); + if (balance < amount + fee + ourfee) fail("Insufficient funds"); await db .multi() .decrBy(`credit:${type}:${uid}`, covered) - .decrBy(`balance:${uid}`, amount + ourfee) + .decrBy(`balance:${uid}`, amount + fee + ourfee) .exec(); }); }); + if (invoice?.tip) { + tip = invoice.tip; + amount -= tip; + } + let p = { id, amount: -amount, @@ -66,6 +73,7 @@ export let debit = async ( currency, type, ref, + tip, created: Date.now() }; diff --git a/routes/payments.js b/routes/payments.js index ea711cb1..38611d19 100644 --- a/routes/payments.js +++ b/routes/payments.js @@ -14,11 +14,10 @@ import ln from "$lib/ln"; export default { async create({ body, user }, res) { - let { amount, hash, maxfee, name, memo, payreq, tip, username } = body; + let { amount, hash, maxfee, name, memo, payreq, username } = body; amount = parseInt(amount); maxfee = maxfee ? parseInt(maxfee) : 0; - tip = parseInt(tip); await requirePin({ body, user }); @@ -181,20 +180,19 @@ export default { try { if (wallet === config.bitcoin.wallet) { let { confirmations, details } = await bc.getTransaction(txid); - if (details.category === "receive") { - for (let { address, amount, vout } of details) { - let p = await g(`payment:${txid}:${vout}`); - if (!p || confirmations < 1) { - await credit( - address, - sats(amount), - "", - `${txid}:${vout}`, - types.bitcoin - ); - } else { - await confirm(address, txid, vout); - } + for (let { address, amount, category, vout } of details) { + if (category !== "receive") continue; + let p = await g(`payment:${txid}:${vout}`); + if (!p || confirmations < 1) { + await credit( + address, + sats(amount), + "", + `${txid}:${vout}`, + types.bitcoin + ); + } else { + await confirm(address, txid, vout); } } } @@ -205,14 +203,17 @@ export default { } }, - async fee(req, res) { + async fee({ body: { amount, address, feeRate, subtract }, user }, res) { try { - let { amount, address, feeRate, subtract } = req.body; - let subtractFeeFromOutputs = subtract ? [0] : []; let replaceable = true; - let outs = [{ [address]: btc(amount) }]; - let count = await bc.getBlockCount(); + + let ourfee = Math.round(amount * config.fee); + let credit = await g(`credit:bitcoin:${user.id}`); + let covered = Math.min(credit, ourfee) || 0; + ourfee -= covered; + + if (subtract) amount -= ourfee; let { feerate: min } = await bc.estimateSmartFee(40); let { feerate: max } = await bc.estimateSmartFee(1); @@ -220,6 +221,7 @@ export default { if (feeRate) feeRate = btc(feeRate); else feeRate = max; + let outs = [{ [address]: btc(amount) }]; let raw = await bc.createRawTransaction([], outs, 0, replaceable); let tx = await bc.fundRawTransaction(raw, { @@ -236,6 +238,7 @@ export default { res.send({ feeRate, min, max, fee, tx }); } catch (e) { + console.log(e); bail(res, "problem estimating fee"); } }, @@ -245,7 +248,7 @@ export default { await requirePin(req); let { user } = req; - let { address, memo, tx } = req.body; + let { address, memo, tx, subtract } = req.body; let { hex, fee } = tx; if (config.bitcoin.walletpass) @@ -280,6 +283,8 @@ export default { total = total - change + fee; let amount = total - fee; + console.log("AMOUNT", amount); + if (change && !amount) fail("Cannot send to unregistered coinos address"); await debit(txid, amount, fee, null, user, types.bitcoin, txid); -- cgit v1.2.3 From 56d1bb6cde1f0ce8cac330f4fd68670312141af8 Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Mon, 27 Feb 2023 05:49:10 +0000 Subject: don't set pubkey of migrated user --- lib/upload.js | 2 +- routes/lnurl.js | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/upload.js b/lib/upload.js index 954e8d4d..50c26cbf 100644 --- a/lib/upload.js +++ b/lib/upload.js @@ -12,7 +12,7 @@ export default async (req, res) => { params: { type }, user } = req; - console.log("TYPE", type); + if (!["banner", "profile"].includes(type)) throw new Error("invalid upload"); let data = await req.file(); diff --git a/routes/lnurl.js b/routes/lnurl.js index fc8107a3..dcd1657c 100644 --- a/routes/lnurl.js +++ b/routes/lnurl.js @@ -56,7 +56,6 @@ export default { u = { id: uid, about: u.address, ...pick(u, fields) }; delete u.address; - await s(`user:${pubkey}`, uid); await s(`user:${username.toLowerCase()}`, uid); await s(`user:${uid}`, u); await s(`balance:${uid}`, balance); -- cgit v1.2.3 From ea794b2a1c7679bcd0e053ef623c234ad3700fb0 Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Mon, 27 Feb 2023 19:29:35 -0800 Subject: invoice fixes --- lib/invoices.js | 14 +++++++------- lib/webhooks.js | 9 ++------- routes/invoices.js | 5 +++-- 3 files changed, 12 insertions(+), 16 deletions(-) (limited to 'lib') diff --git a/lib/invoices.js b/lib/invoices.js index ddb378cf..f64cf409 100644 --- a/lib/invoices.js +++ b/lib/invoices.js @@ -29,9 +29,8 @@ export let generate = async ({ invoice, user, sender, memo }) => { let hash, text; if (type === types.lightning) { - let amt = amount ? `${amount + tip}sat` : "any"; let r = await ln.invoice( - amt, + amount ? `${amount + tip}sat` : "any", new Date(), memo || "", 3600, @@ -54,16 +53,17 @@ export let generate = async ({ invoice, user, sender, memo }) => { } invoice = { - ...invoice, amount, - hash, - text, + created: Date.now(), currency, + hash, rate, + received: 0, + request_id, + text, tip, + type, uid, - received: 0, - created: Date.now() }; l("creating invoice", user.username, amount, tip, currency, invoice.hash); diff --git a/lib/webhooks.js b/lib/webhooks.js index c071eccb..1158fe9d 100644 --- a/lib/webhooks.js +++ b/lib/webhooks.js @@ -9,23 +9,18 @@ export const callWebhook = async (invoice, payment) => { let { address, received, text, webhook } = invoice; if (webhook) { - let { amount, confirmed, hash, memo, account } = payment; - let { asset } = account; - let secret = config.webhooks[webhook]; + let { amount, confirmed, hash, memo } = payment; l("calling webhook", webhook, amount, hash, address, text); let res = await got(webhook, { - headers: { "x-webhook": secret }, json: { address, amount, - asset, confirmed, hash, memo, received, - text, - webhook + text } }).res(); return res; diff --git a/routes/invoices.js b/routes/invoices.js index c7611f54..8e23e5f1 100644 --- a/routes/invoices.js +++ b/routes/invoices.js @@ -1,6 +1,7 @@ import { g, s } from "$lib/db"; import { generate } from "$lib/invoices"; -import { fail } from "$lib/utils"; +import { fail, pick } from "$lib/utils"; +import whitelist from "$lib/whitelist"; import got from "got"; import config from "$config"; import ln from "$lib/ln"; @@ -15,7 +16,7 @@ export default { let invoice = await g(`invoice:${hash}`); if (invoice) { - invoice.user = await g(`user:${invoice.uid}`); + invoice.user = pick(await g(`user:${invoice.uid}`), whitelist); invoice.id = hash; } else if (config.classic) { invoice = await got(`${config.classic}/invoice/${pr}`).json(); -- cgit v1.2.3