From 0fb38749ac918f4f9fc888ec07ffbe80bf116cb1 Mon Sep 17 00:00:00 2001 From: fiatjaf Date: Fri, 3 Mar 2023 11:36:50 -0300 Subject: [PATCH 1/2] remove bolt11, bech32 and js-lnurl dependencies. replace with much faster invoice amount parsing. fix lnurl encoding/decoding. --- package-lock.json | Bin 617023 -> 597337 bytes package.json | 4 +--- src/agent/network.ts | 1 - src/agent/sync.ts | 33 +++++++++++++++----------- src/util/lightning.ts | 46 ++++++++++++++++++++++++++++++++++++ src/util/misc.ts | 11 ++++----- src/views/notes/Note.svelte | 12 +++++----- 7 files changed, 77 insertions(+), 30 deletions(-) create mode 100644 src/util/lightning.ts diff --git a/package-lock.json b/package-lock.json index 0b9816c3c697832fb975495c618d1886431bd5f3..2a7f500a830e044d796a760d2ed80e489bcb155f 100644 GIT binary patch delta 808 zcmci9+e_179LI5p4mUgf;BK#A&`&@jkUT??j-nr4R!E^$J zb;J_}dlf#g{}k8A7UCoKl79Inb(iaDDZE=0y{;@%T&5(x5lrPI5XN^=hFvYBFbY=( z$W#danj$$A4R1&*#Upbhg5u~BiSfanucUb&4rpno8$trrw!^b1AppjY69#IX6bsHM zK?7EsvK?0@XvtMvHY$Aea4DSbMs0^8nBvcSiW(1+D9_mApu0yA;F@iQFiWwmA;YLE zh_e(P$WAZL%P6TRDJsf2WIkss$f>fX*BElM?zbTy4nL=jErx z>dmI2+~~T7q{<7`*+mtm7=2>K#ki{TC*>I_9Fj}98r(z0SMo786-*B)++jXc{46g@ zewg=2(Ow2i0^=CPSV)T89_SB2ed2qV))POtb5KyB*}%-fxLU!w1e~v7F_|#jO?|*I zv!8E=d zZb#5Sc(6^>;E1niQT?YTFfpqP`x^yaQJf}p)QOWZfI*@cq-L^};m0ESqhmrmDxmJQ z7#Y9C?Ez1mE{x`2g(qI#6q7}K$)(gCm>w*3xo??!z$dM=0b^M_0$k=MD2tX{Mv;<+ KV@sSgk?;p1t1W8) delta 10152 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 PUW1&?jlp-#e^2}`>6IA4 diff --git a/package.json b/package.json index 8b069f91..51645da1 100644 --- a/package.json +++ b/package.json @@ -29,15 +29,13 @@ "@bugsnag/js": "^7.18.0", "@fortawesome/fontawesome-free": "^6.2.1", "@noble/secp256k1": "^1.7.0", + "@scure/base": "^1.1.1", "@tsconfig/svelte": "^3.0.0", - "bech32": "^2.0.0", - "bolt11": "^1.4.0", "classnames": "^2.3.2", "compressorjs": "^1.1.1", "fuse.js": "^6.6.2", "hurdak": "github:ConsignCloud/hurdak", "husky": "^8.0.3", - "js-lnurl": "^0.5.1", "localforage": "^1.10.0", "localforage-memoryStorageDriver": "^0.9.2", "nostr-tools": "^1.7.4", diff --git a/src/agent/network.ts b/src/agent/network.ts index d1cc43e6..6f953812 100644 --- a/src/agent/network.ts +++ b/src/agent/network.ts @@ -200,4 +200,3 @@ const applyContext = (notes, context) => { export default { listen, load, loadPeople, personKinds, loadParents, streamContext, applyContext, } - diff --git a/src/agent/sync.ts b/src/agent/sync.ts index 83de1558..bdfdbfc1 100644 --- a/src/agent/sync.ts +++ b/src/agent/sync.ts @@ -1,9 +1,8 @@ import {uniq, pick, identity, isEmpty} from 'ramda' import {nip05} from 'nostr-tools' -import {getParams} from 'js-lnurl' import {noop, createMap, ensurePlural, chunk, switcherFn} from 'hurdak/lib/hurdak' import {log} from 'src/util/logger' -import {hexToBech32, tryFetch, now, sleep, tryJson, timedelta, shuffle, hash} from 'src/util/misc' +import {lnurlEncode, lnurlDecode, tryFetch, now, sleep, tryJson, timedelta, shuffle, hash} from 'src/util/misc' import {Tags, roomAttrs, personKinds, isRelay, isShareableRelay, normalizeRelayUrl} from 'src/util/nostr' import database from 'src/agent/database' @@ -308,25 +307,31 @@ const verifyNip05 = (pubkey, as) => }, noop) const verifyZapper = async (pubkey, address) => { - // Try to parse it as a lud06 LNURL - let zapper = await getParams(address) as any - let lnurl = address - - // If that failed, try to parse it as a lud16 address - if (zapper.status === 'ERROR' && address.includes('@')) { + let url + if (address.startsWith('lnurl1')) { + // Try to parse it as a lud06 LNURL + url = lnurlDecode(address) + const lnurl = address + } else if (address.includes('@')) { + // Otherwise try to parse it as a lud16 address const [name, domain] = address.split('@') if (!domain || !name) { return } - const url = `https://${domain}/.well-known/lnurlp/${name}` - const res = await tryFetch(() => fetch(url)) + url = `https://${domain}/.well-known/lnurlp/${name}` + } else { + // Otherwise this is not valid + return + } - if (res) { - zapper = await res.json() - lnurl = hexToBech32('lnurl', url) - } + const res = await tryFetch(() => fetch(url)) + + let zapper + if (res) { + zapper = await res.json() + lnurl = lnurlEncode('lnurl', url) } if (zapper?.allowsNostr && zapper?.nostrPubkey) { diff --git a/src/util/lightning.ts b/src/util/lightning.ts new file mode 100644 index 00000000..e1800319 --- /dev/null +++ b/src/util/lightning.ts @@ -0,0 +1,46 @@ +const DIVISORS = { + m: BigInt(1e3), + u: BigInt(1e6), + n: BigInt(1e9), + p: BigInt(1e12) +} + +const MAX_MILLISATS = BigInt('2100000000000000000') + +const MILLISATS_PER_BTC = BigInt(1e11) + +function hrpToMillisat(hrpString: string) { + let divisor, value + if (hrpString.slice(-1).match(/^[munp]$/)) { + divisor = hrpString.slice(-1) + value = hrpString.slice(0, -1) + } else if (hrpString.slice(-1).match(/^[^munp0-9]$/)) { + throw new Error('Not a valid multiplier for the amount') + } else { + value = hrpString + } + + if (!value.match(/^\d+$/)) + throw new Error('Not a valid human readable amount') + + const valueBN = BigInt(value) + + const millisatoshisBN = divisor + ? (valueBN * MILLISATS_PER_BTC) / DIVISORS[divisor] + : valueBN * MILLISATS_PER_BTC + + if ( + (divisor === 'p' && !(valueBN % BigInt(10) === BigInt(0))) || + millisatoshisBN > MAX_MILLISATS + ) { + throw new Error('Amount is outside of valid range') + } + + return millisatoshisBN +} + +export function invoiceAmount(bolt11: string): number { + const hrp = bolt11.match(/lnbc(\d+\w)/) + const bn = hrpToMillisat(hrp[1]) + return Number(bn) +} diff --git a/src/util/misc.ts b/src/util/misc.ts index 55a51dde..3ae25b9b 100644 --- a/src/util/misc.ts +++ b/src/util/misc.ts @@ -1,5 +1,4 @@ -import {bech32} from 'bech32' -import {Buffer} from 'buffer' +import {bech32, utf8} from '@scure/base' import {debounce, throttle} from 'throttle-debounce' import {aperture, path as getPath, allPass, pipe, isNil, complement, equals, is, pluck, sum, identity, sortBy} from "ramda" import Fuse from "fuse.js/dist/fuse.min.js" @@ -340,11 +339,11 @@ export const uploadFile = (url, fileObj) => { return fetchJson(url, {method: 'POST', body}) } -export const hexToBech32 = (prefix, hex) => - bech32.encode(prefix, bech32.toWords(Buffer.from(hex, 'hex'))) +export const lnurlEncode = (prefix, url) => + bech32.encode(prefix, bech32.toWords(utf8.decode(url))) -export const bech32ToHex = b32 => - Buffer.from(bech32.fromWords(bech32.decode(b32).words)).toString('hex') +export const lnurlDecode = b32 => + utf8.encode(bech32.fromWords(bech32.decode(b32).words)) export const formatSats = sats => { const formatter = new Intl.NumberFormat() diff --git a/src/views/notes/Note.svelte b/src/views/notes/Note.svelte index 9071a8e8..20e57174 100644 --- a/src/views/notes/Note.svelte +++ b/src/views/notes/Note.svelte @@ -1,6 +1,5 @@