mirror of
https://github.com/coracle-social/coracle.git
synced 2024-10-06 11:43:30 +00:00
remove bolt11, bech32 and js-lnurl dependencies.
replace with much faster invoice amount parsing. fix lnurl encoding/decoding.
This commit is contained in:
parent
17afc35d1c
commit
0fb38749ac
BIN
package-lock.json
generated
BIN
package-lock.json
generated
Binary file not shown.
@ -29,15 +29,13 @@
|
|||||||
"@bugsnag/js": "^7.18.0",
|
"@bugsnag/js": "^7.18.0",
|
||||||
"@fortawesome/fontawesome-free": "^6.2.1",
|
"@fortawesome/fontawesome-free": "^6.2.1",
|
||||||
"@noble/secp256k1": "^1.7.0",
|
"@noble/secp256k1": "^1.7.0",
|
||||||
|
"@scure/base": "^1.1.1",
|
||||||
"@tsconfig/svelte": "^3.0.0",
|
"@tsconfig/svelte": "^3.0.0",
|
||||||
"bech32": "^2.0.0",
|
|
||||||
"bolt11": "^1.4.0",
|
|
||||||
"classnames": "^2.3.2",
|
"classnames": "^2.3.2",
|
||||||
"compressorjs": "^1.1.1",
|
"compressorjs": "^1.1.1",
|
||||||
"fuse.js": "^6.6.2",
|
"fuse.js": "^6.6.2",
|
||||||
"hurdak": "github:ConsignCloud/hurdak",
|
"hurdak": "github:ConsignCloud/hurdak",
|
||||||
"husky": "^8.0.3",
|
"husky": "^8.0.3",
|
||||||
"js-lnurl": "^0.5.1",
|
|
||||||
"localforage": "^1.10.0",
|
"localforage": "^1.10.0",
|
||||||
"localforage-memoryStorageDriver": "^0.9.2",
|
"localforage-memoryStorageDriver": "^0.9.2",
|
||||||
"nostr-tools": "^1.7.4",
|
"nostr-tools": "^1.7.4",
|
||||||
|
@ -200,4 +200,3 @@ const applyContext = (notes, context) => {
|
|||||||
export default {
|
export default {
|
||||||
listen, load, loadPeople, personKinds, loadParents, streamContext, applyContext,
|
listen, load, loadPeople, personKinds, loadParents, streamContext, applyContext,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
import {uniq, pick, identity, isEmpty} from 'ramda'
|
import {uniq, pick, identity, isEmpty} from 'ramda'
|
||||||
import {nip05} from 'nostr-tools'
|
import {nip05} from 'nostr-tools'
|
||||||
import {getParams} from 'js-lnurl'
|
|
||||||
import {noop, createMap, ensurePlural, chunk, switcherFn} from 'hurdak/lib/hurdak'
|
import {noop, createMap, ensurePlural, chunk, switcherFn} from 'hurdak/lib/hurdak'
|
||||||
import {log} from 'src/util/logger'
|
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 {Tags, roomAttrs, personKinds, isRelay, isShareableRelay, normalizeRelayUrl} from 'src/util/nostr'
|
||||||
import database from 'src/agent/database'
|
import database from 'src/agent/database'
|
||||||
|
|
||||||
@ -308,25 +307,31 @@ const verifyNip05 = (pubkey, as) =>
|
|||||||
}, noop)
|
}, noop)
|
||||||
|
|
||||||
const verifyZapper = async (pubkey, address) => {
|
const verifyZapper = async (pubkey, address) => {
|
||||||
// Try to parse it as a lud06 LNURL
|
let url
|
||||||
let zapper = await getParams(address) as any
|
if (address.startsWith('lnurl1')) {
|
||||||
let lnurl = address
|
// Try to parse it as a lud06 LNURL
|
||||||
|
url = lnurlDecode(address)
|
||||||
// If that failed, try to parse it as a lud16 address
|
const lnurl = address
|
||||||
if (zapper.status === 'ERROR' && address.includes('@')) {
|
} else if (address.includes('@')) {
|
||||||
|
// Otherwise try to parse it as a lud16 address
|
||||||
const [name, domain] = address.split('@')
|
const [name, domain] = address.split('@')
|
||||||
|
|
||||||
if (!domain || !name) {
|
if (!domain || !name) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const url = `https://${domain}/.well-known/lnurlp/${name}`
|
url = `https://${domain}/.well-known/lnurlp/${name}`
|
||||||
const res = await tryFetch(() => fetch(url))
|
} else {
|
||||||
|
// Otherwise this is not valid
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if (res) {
|
const res = await tryFetch(() => fetch(url))
|
||||||
zapper = await res.json()
|
|
||||||
lnurl = hexToBech32('lnurl', url)
|
let zapper
|
||||||
}
|
if (res) {
|
||||||
|
zapper = await res.json()
|
||||||
|
lnurl = lnurlEncode('lnurl', url)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (zapper?.allowsNostr && zapper?.nostrPubkey) {
|
if (zapper?.allowsNostr && zapper?.nostrPubkey) {
|
||||||
|
46
src/util/lightning.ts
Normal file
46
src/util/lightning.ts
Normal file
@ -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)
|
||||||
|
}
|
@ -1,5 +1,4 @@
|
|||||||
import {bech32} from 'bech32'
|
import {bech32, utf8} from '@scure/base'
|
||||||
import {Buffer} from 'buffer'
|
|
||||||
import {debounce, throttle} from 'throttle-debounce'
|
import {debounce, throttle} from 'throttle-debounce'
|
||||||
import {aperture, path as getPath, allPass, pipe, isNil, complement, equals, is, pluck, sum, identity, sortBy} from "ramda"
|
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"
|
import Fuse from "fuse.js/dist/fuse.min.js"
|
||||||
@ -340,11 +339,11 @@ export const uploadFile = (url, fileObj) => {
|
|||||||
return fetchJson(url, {method: 'POST', body})
|
return fetchJson(url, {method: 'POST', body})
|
||||||
}
|
}
|
||||||
|
|
||||||
export const hexToBech32 = (prefix, hex) =>
|
export const lnurlEncode = (prefix, url) =>
|
||||||
bech32.encode(prefix, bech32.toWords(Buffer.from(hex, 'hex')))
|
bech32.encode(prefix, bech32.toWords(utf8.decode(url)))
|
||||||
|
|
||||||
export const bech32ToHex = b32 =>
|
export const lnurlDecode = b32 =>
|
||||||
Buffer.from(bech32.fromWords(bech32.decode(b32).words)).toString('hex')
|
utf8.encode(bech32.fromWords(bech32.decode(b32).words))
|
||||||
|
|
||||||
export const formatSats = sats => {
|
export const formatSats = sats => {
|
||||||
const formatter = new Intl.NumberFormat()
|
const formatter = new Intl.NumberFormat()
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import cx from 'classnames'
|
import cx from 'classnames'
|
||||||
import bolt11 from 'bolt11'
|
|
||||||
import QRCode from 'qrcode'
|
import QRCode from 'qrcode'
|
||||||
import {nip19} from 'nostr-tools'
|
import {nip19} from 'nostr-tools'
|
||||||
import {find, sum, last, whereEq, without, uniq, pluck, reject, propEq} from 'ramda'
|
import {find, sum, last, whereEq, without, uniq, pluck, reject, propEq} from 'ramda'
|
||||||
@ -9,9 +8,10 @@
|
|||||||
import {slide} from 'svelte/transition'
|
import {slide} from 'svelte/transition'
|
||||||
import {navigate} from 'svelte-routing'
|
import {navigate} from 'svelte-routing'
|
||||||
import {quantify} from 'hurdak/lib/hurdak'
|
import {quantify} from 'hurdak/lib/hurdak'
|
||||||
import {Tags, findRootId, findReplyId, displayPerson, isLike} from "src/util/nostr"
|
import {Tags, findRootId, findReplyId, displayPerson, isLike} from 'src/util/nostr'
|
||||||
import {formatTimestamp, now, tryJson, stringToColor, formatSats, fetchJson} from 'src/util/misc'
|
import {formatTimestamp, now, tryJson, stringToColor, formatSats, fetchJson} from 'src/util/misc'
|
||||||
import {extractUrls, copyToClipboard} from "src/util/html"
|
import {extractUrls, copyToClipboard} from "src/util/html"
|
||||||
|
import {invoiceAmount} from 'src/util/lightning'
|
||||||
import ImageCircle from 'src/partials/ImageCircle.svelte'
|
import ImageCircle from 'src/partials/ImageCircle.svelte'
|
||||||
import Input from 'src/partials/Input.svelte'
|
import Input from 'src/partials/Input.svelte'
|
||||||
import Textarea from 'src/partials/Textarea.svelte'
|
import Textarea from 'src/partials/Textarea.svelte'
|
||||||
@ -74,7 +74,7 @@
|
|||||||
|
|
||||||
return tryJson(() => ({
|
return tryJson(() => ({
|
||||||
...zap,
|
...zap,
|
||||||
invoice: bolt11.decode(zapMeta.bolt11),
|
invoiceAmount: invoiceAmount(zapMeta.bolt11),
|
||||||
request: JSON.parse(zapMeta.description),
|
request: JSON.parse(zapMeta.description),
|
||||||
}))
|
}))
|
||||||
})
|
})
|
||||||
@ -88,11 +88,11 @@
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
const {invoice, request} = zap
|
const {invoiceAmount, request} = zap
|
||||||
const reqMeta = Tags.from(request).asMeta()
|
const reqMeta = Tags.from(request).asMeta()
|
||||||
|
|
||||||
// Verify that the zapper actually sent the requested amount (if it was supplied)
|
// Verify that the zapper actually sent the requested amount (if it was supplied)
|
||||||
if (reqMeta.amount && reqMeta.amount !== parseInt(invoice.millisatoshis)) {
|
if (reqMeta.amount && parseInt(reqMeta.amount) !== invoiceAmount) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,7 +114,7 @@
|
|||||||
$: zapped = find(z => z.request.pubkey === $profile?.pubkey, zaps)
|
$: zapped = find(z => z.request.pubkey === $profile?.pubkey, zaps)
|
||||||
$: $likesCount = likes.length
|
$: $likesCount = likes.length
|
||||||
$: $flagsCount = flags.length
|
$: $flagsCount = flags.length
|
||||||
$: $zapsTotal = sum(zaps.map(zap => zap.invoice.satoshis))
|
$: $zapsTotal = sum(zaps.map(zap => zap.invoiceAmount)) / 1000
|
||||||
$: $repliesCount = note.replies.length
|
$: $repliesCount = note.replies.length
|
||||||
$: visibleNotes = note.replies.filter(r => showContext ? true : !r.isContext)
|
$: visibleNotes = note.replies.filter(r => showContext ? true : !r.isContext)
|
||||||
$: canZap = $person?.zapper && user.canZap()
|
$: canZap = $person?.zapper && user.canZap()
|
||||||
|
Loading…
Reference in New Issue
Block a user