complete nip 57 support
This commit is contained in:
parent
ef5346000b
commit
93d4581360
14
package-lock.json
generated
14
package-lock.json
generated
@ -7689,6 +7689,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"noble-secp256k1": {
|
||||||
|
"version": "1.2.14",
|
||||||
|
"resolved": "https://registry.npmjs.org/noble-secp256k1/-/noble-secp256k1-1.2.14.tgz",
|
||||||
|
"integrity": "sha512-GSCXyoZBUaaPwVWdYncMEmzlSUjF9J/YeEHpklYJCyg8wPuJP3NzDx0BkiwArzINkdX2HJHvUJhL6vVWPOQQcg=="
|
||||||
|
},
|
||||||
"node-abort-controller": {
|
"node-abort-controller": {
|
||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.0.1.tgz",
|
||||||
@ -7821,6 +7826,15 @@
|
|||||||
"resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-7.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-7.2.0.tgz",
|
||||||
"integrity": "sha512-uhXOdZry0L6M2UIo9BTt7FdpBDiAGN/7oItedQwPKh8jh31ZlvC8U9Xl/EJ3aijDHaywXTW3QbZ6LuCocur1YA=="
|
"integrity": "sha512-uhXOdZry0L6M2UIo9BTt7FdpBDiAGN/7oItedQwPKh8jh31ZlvC8U9Xl/EJ3aijDHaywXTW3QbZ6LuCocur1YA=="
|
||||||
},
|
},
|
||||||
|
"nostr": {
|
||||||
|
"version": "0.2.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/nostr/-/nostr-0.2.7.tgz",
|
||||||
|
"integrity": "sha512-Yq5tkiCTJtohs7H7Fx+Ki83bR8b3xapdwYi17HunhW5EQfZ9rI0cIXFyihj2ZRkv/0YYvVBiCp/KIaLYt5LyfQ==",
|
||||||
|
"requires": {
|
||||||
|
"noble-secp256k1": "^1.2.14",
|
||||||
|
"ws": "^8.8.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"nprogress": {
|
"nprogress": {
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz",
|
||||||
|
@ -46,6 +46,7 @@
|
|||||||
"next-seo": "^4.29.0",
|
"next-seo": "^4.29.0",
|
||||||
"nextjs-progressbar": "0.0.16",
|
"nextjs-progressbar": "0.0.16",
|
||||||
"node-s3-url-encode": "^0.0.4",
|
"node-s3-url-encode": "^0.0.4",
|
||||||
|
"nostr": "^0.2.7",
|
||||||
"opentimestamps": "^0.4.9",
|
"opentimestamps": "^0.4.9",
|
||||||
"page-metadata-parser": "^1.1.4",
|
"page-metadata-parser": "^1.1.4",
|
||||||
"pageres": "^7.1.0",
|
"pageres": "^7.1.0",
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import models from '../../../../api/models'
|
import models from '../../../../api/models'
|
||||||
import lnd from '../../../../api/lnd'
|
import lnd from '../../../../api/lnd'
|
||||||
import { createInvoice } from 'ln-service'
|
import { createInvoice } from 'ln-service'
|
||||||
import { lnurlPayDescriptionHashForUser } from '../../../../lib/lnurl'
|
import { lnurlPayDescriptionHashForUser, lnurlPayMetadataString } from '../../../../lib/lnurl'
|
||||||
import serialize from '../../../../api/resolvers/serial'
|
import serialize from '../../../../api/resolvers/serial'
|
||||||
import * as secp256k1 from '@noble/secp256k1'
|
import * as secp256k1 from '@noble/secp256k1'
|
||||||
import { createHash } from 'crypto'
|
import { createHash } from 'crypto'
|
||||||
@ -21,14 +21,14 @@ export default async ({ query: { username, amount, nostr } }, res) => {
|
|||||||
const hasETag = note.tags?.filter(t => t[0] === 'e').length <= 1
|
const hasETag = note.tags?.filter(t => t[0] === 'e').length <= 1
|
||||||
if (await secp256k1.schnorr.verify(note.sig, note.id, note.pubkey) &&
|
if (await secp256k1.schnorr.verify(note.sig, note.id, note.pubkey) &&
|
||||||
hasPTag && hasETag) {
|
hasPTag && hasETag) {
|
||||||
description = user.hideInvoiceDesc ? undefined : `${amount} msats for @${user.name} on stacker.news via NIP-57`
|
description = noteStr
|
||||||
descriptionHash = createHash('sha256').update(noteStr).digest('hex')
|
descriptionHash = createHash('sha256').update(noteStr).digest('hex')
|
||||||
} else {
|
} else {
|
||||||
res.status(400).json({ status: 'ERROR', reason: 'invalid NIP-57 note' })
|
res.status(400).json({ status: 'ERROR', reason: 'invalid NIP-57 note' })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
description = user.hideInvoiceDesc ? undefined : `${amount} msats for @${user.name} on stacker.news`
|
description = user.hideInvoiceDesc ? undefined : lnurlPayMetadataString(username)
|
||||||
descriptionHash = lnurlPayDescriptionHashForUser(username)
|
descriptionHash = lnurlPayDescriptionHashForUser(username)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,7 +11,10 @@ const { ApolloClient, HttpLink, InMemoryCache } = require('@apollo/client')
|
|||||||
const { indexItem, indexAllItems } = require('./search')
|
const { indexItem, indexAllItems } = require('./search')
|
||||||
const { timestampItem } = require('./ots')
|
const { timestampItem } = require('./ots')
|
||||||
const { computeStreaks, checkStreak } = require('./streak')
|
const { computeStreaks, checkStreak } = require('./streak')
|
||||||
|
const { nip57 } = require('./nostr')
|
||||||
|
|
||||||
const fetch = require('cross-fetch')
|
const fetch = require('cross-fetch')
|
||||||
|
const { authenticatedLndGrpc } = require('ln-service')
|
||||||
|
|
||||||
async function work () {
|
async function work () {
|
||||||
const boss = new PgBoss(process.env.DATABASE_URL)
|
const boss = new PgBoss(process.env.DATABASE_URL)
|
||||||
@ -34,7 +37,13 @@ async function work () {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const args = { boss, models, apollo }
|
const { lnd } = authenticatedLndGrpc({
|
||||||
|
cert: process.env.LND_CERT,
|
||||||
|
macaroon: process.env.LND_MACAROON,
|
||||||
|
socket: process.env.LND_SOCKET
|
||||||
|
})
|
||||||
|
|
||||||
|
const args = { boss, models, apollo, lnd }
|
||||||
|
|
||||||
boss.on('error', error => console.error(error))
|
boss.on('error', error => console.error(error))
|
||||||
|
|
||||||
@ -50,6 +59,7 @@ async function work () {
|
|||||||
await boss.work('earn', earn(args))
|
await boss.work('earn', earn(args))
|
||||||
await boss.work('streak', computeStreaks(args))
|
await boss.work('streak', computeStreaks(args))
|
||||||
await boss.work('checkStreak', checkStreak(args))
|
await boss.work('checkStreak', checkStreak(args))
|
||||||
|
await boss.work('nip57', nip57(args))
|
||||||
|
|
||||||
console.log('working jobs')
|
console.log('working jobs')
|
||||||
}
|
}
|
||||||
|
72
worker/nostr.js
Normal file
72
worker/nostr.js
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
const { getInvoice } = require('ln-service')
|
||||||
|
const { Relay, signId, calculateId, getPublicKey } = require('nostr')
|
||||||
|
|
||||||
|
const nostrOptions = { startAfter: 5, retryLimit: 21, retryBackoff: true }
|
||||||
|
|
||||||
|
function nip57 ({ boss, lnd }) {
|
||||||
|
return async function ({ data: { hash } }) {
|
||||||
|
console.log('running nip57')
|
||||||
|
|
||||||
|
let inv
|
||||||
|
try {
|
||||||
|
inv = await getInvoice({ id: hash, lnd })
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err)
|
||||||
|
// on lnd related errors, we manually retry which so we don't exponentially backoff
|
||||||
|
await boss.send('nip57', { hash }, nostrOptions)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const desc = JSON.parse(inv.description)
|
||||||
|
const ptag = desc.tags.filter(t => t?.length >= 2 && t[0] === 'p')[0]
|
||||||
|
const etag = desc.tags.filter(t => t?.length >= 2 && t[0] === 'e')[0]
|
||||||
|
const relays = desc.tags.find(t => t?.length >= 2 && t[0] === 'relays').slice(1)
|
||||||
|
|
||||||
|
const tags = [ptag]
|
||||||
|
if (etag) {
|
||||||
|
tags.push(etag)
|
||||||
|
}
|
||||||
|
tags.push(['bolt11', inv.request])
|
||||||
|
tags.push(['description', inv.description])
|
||||||
|
tags.push(['preimage', inv.secret])
|
||||||
|
|
||||||
|
const e = {
|
||||||
|
kind: 9735,
|
||||||
|
pubkey: getPublicKey(process.env.NOSTR_PRIVATE_KEY),
|
||||||
|
created_at: Math.floor(new Date(inv.confirmed_at).getTime() / 1000),
|
||||||
|
content: '',
|
||||||
|
tags
|
||||||
|
}
|
||||||
|
e.id = await calculateId(e)
|
||||||
|
e.sig = await signId(process.env.NOSTR_PRIVATE_KEY, e.id)
|
||||||
|
|
||||||
|
relays.forEach(r => {
|
||||||
|
const timeout = 1000
|
||||||
|
const relay = Relay(r)
|
||||||
|
|
||||||
|
function timedout () {
|
||||||
|
relay.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
let timer = setTimeout(timedout, timeout)
|
||||||
|
|
||||||
|
relay.on('open', () => {
|
||||||
|
clearTimeout(timer)
|
||||||
|
timer = setTimeout(timedout, timeout)
|
||||||
|
relay.send(['EVENT', e])
|
||||||
|
})
|
||||||
|
|
||||||
|
relay.on('ok', () => {
|
||||||
|
clearTimeout(timer)
|
||||||
|
relay.close()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e)
|
||||||
|
}
|
||||||
|
console.log('dont running nip57')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { nip57 }
|
@ -1,14 +1,9 @@
|
|||||||
const serialize = require('../api/resolvers/serial')
|
const serialize = require('../api/resolvers/serial')
|
||||||
const { authenticatedLndGrpc, getInvoice, getPayment } = require('ln-service')
|
const { getInvoice, getPayment } = require('ln-service')
|
||||||
|
|
||||||
const { lnd } = authenticatedLndGrpc({
|
|
||||||
cert: process.env.LND_CERT,
|
|
||||||
macaroon: process.env.LND_MACAROON,
|
|
||||||
socket: process.env.LND_SOCKET
|
|
||||||
})
|
|
||||||
const walletOptions = { startAfter: 5, retryLimit: 21, retryBackoff: true }
|
const walletOptions = { startAfter: 5, retryLimit: 21, retryBackoff: true }
|
||||||
|
|
||||||
function checkInvoice ({ boss, models }) {
|
function checkInvoice ({ boss, models, lnd }) {
|
||||||
return async function ({ data: { hash } }) {
|
return async function ({ data: { hash } }) {
|
||||||
let inv
|
let inv
|
||||||
try {
|
try {
|
||||||
@ -24,6 +19,10 @@ function checkInvoice ({ boss, models }) {
|
|||||||
if (inv.is_confirmed) {
|
if (inv.is_confirmed) {
|
||||||
await serialize(models,
|
await serialize(models,
|
||||||
models.$executeRaw`SELECT confirm_invoice(${inv.id}, ${Number(inv.received_mtokens)})`)
|
models.$executeRaw`SELECT confirm_invoice(${inv.id}, ${Number(inv.received_mtokens)})`)
|
||||||
|
try {
|
||||||
|
JSON.parse(inv.description)
|
||||||
|
await boss.send('nip57', { hash })
|
||||||
|
} catch {}
|
||||||
} else if (inv.is_canceled) {
|
} else if (inv.is_canceled) {
|
||||||
// mark as cancelled
|
// mark as cancelled
|
||||||
await serialize(models,
|
await serialize(models,
|
||||||
@ -42,7 +41,7 @@ function checkInvoice ({ boss, models }) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkWithdrawal ({ boss, models }) {
|
function checkWithdrawal ({ boss, models, lnd }) {
|
||||||
return async function ({ data: { id, hash } }) {
|
return async function ({ data: { id, hash } }) {
|
||||||
let wdrwl
|
let wdrwl
|
||||||
let notFound = false
|
let notFound = false
|
||||||
|
Loading…
x
Reference in New Issue
Block a user