Compare commits
5 Commits
266e9a892d
...
3af43d74d3
Author | SHA1 | Date |
---|---|---|
Keyan | 3af43d74d3 | |
ekzyis | 4cec369005 | |
ekzyis | d09f7c5427 | |
ekzyis | ec6124ca62 | |
ekzyis | 9f194c5d8e |
|
@ -122,3 +122,5 @@ Gudnessuche,issue,#1264,#1226,good-first-issue,,,,2k,everythingsatoshi@getalby.c
|
||||||
aniskhalfallah,pr,#1289,,easy,,,,100k,aniskhalfallah@blink.sv,2024-08-12
|
aniskhalfallah,pr,#1289,,easy,,,,100k,aniskhalfallah@blink.sv,2024-08-12
|
||||||
riccardobl,pr,#1293,#1142,medium,high,,,500k,rblb@getalby.com,2024-08-18
|
riccardobl,pr,#1293,#1142,medium,high,,,500k,rblb@getalby.com,2024-08-18
|
||||||
tsmith123,pr,#1306,#832,medium,,,,250k,stickymarch60@walletofsatoshi.com,2024-08-20
|
tsmith123,pr,#1306,#832,medium,,,,250k,stickymarch60@walletofsatoshi.com,2024-08-20
|
||||||
|
riccardobl,pr,#1311,#864,medium,high,,pending unrelated refactor,500k,rblb@getalby.com,2024-08-27
|
||||||
|
brugeman,issue,#1311,#864,medium,high,,,50k,brugeman@stacker.news,2024-08-27
|
||||||
|
|
|
|
@ -9,4 +9,6 @@ RUN apt-get update -y \
|
||||||
&& apt-get install -y cmake \
|
&& apt-get install -y cmake \
|
||||||
&& apt-get clean \
|
&& apt-get clean \
|
||||||
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
||||||
RUN cargo build --release && cargo install --path .
|
RUN cargo build --release && cargo install --path .
|
||||||
|
|
||||||
|
COPY keys.json .
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
{"server_key":"4463b828d1950885de82b518efbfbe3dd6c35278e8ee5f6389721515b4c5e021","user_key":"0d1ef06059c9b1acf8c424cfe357c5ffe2d5f3594b9081695771a363ee716b67","sent_info":true}
|
25
lib/cln.js
25
lib/cln.js
|
@ -1,30 +1,11 @@
|
||||||
import fetch from 'cross-fetch'
|
import fetch from 'cross-fetch'
|
||||||
import https from 'https'
|
|
||||||
import crypto from 'crypto'
|
import crypto from 'crypto'
|
||||||
import { HttpProxyAgent, HttpsProxyAgent } from '@/lib/proxy'
|
import { getAgent } from '@/lib/proxy'
|
||||||
import { TOR_REGEXP } from '@/lib/url'
|
|
||||||
|
|
||||||
export const createInvoice = async ({ socket, rune, cert, label, description, msats, expiry }) => {
|
export const createInvoice = async ({ socket, rune, cert, label, description, msats, expiry }) => {
|
||||||
let protocol, agent
|
const agent = getAgent({ hostname: socket, cert })
|
||||||
const httpsAgentOptions = { ca: cert ? Buffer.from(cert, 'base64') : undefined }
|
|
||||||
const isOnion = TOR_REGEXP.test(socket)
|
|
||||||
if (isOnion) {
|
|
||||||
// we support HTTP and HTTPS over Tor
|
|
||||||
protocol = cert ? 'https:' : 'http:'
|
|
||||||
// we need to use our Tor proxy to resolve onion addresses
|
|
||||||
const proxyOptions = { proxy: process.env.TOR_PROXY }
|
|
||||||
agent = protocol === 'https:'
|
|
||||||
? new HttpsProxyAgent({ ...proxyOptions, ...httpsAgentOptions, rejectUnauthorized: false })
|
|
||||||
: new HttpProxyAgent(proxyOptions)
|
|
||||||
} else if (process.env.NODE_ENV === 'development' && !cert) {
|
|
||||||
protocol = 'http:'
|
|
||||||
} else {
|
|
||||||
// we only support HTTPS over clearnet
|
|
||||||
agent = new https.Agent(httpsAgentOptions)
|
|
||||||
protocol = 'https:'
|
|
||||||
}
|
|
||||||
|
|
||||||
const url = `${protocol}//${socket}/v1/invoice`
|
const url = `${agent.protocol}//${socket}/v1/invoice`
|
||||||
const res = await fetch(url, {
|
const res = await fetch(url, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
|
|
|
@ -69,6 +69,9 @@ export class Relay {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return await withTimeout(checkPromise, timeout)
|
return await withTimeout(checkPromise, timeout)
|
||||||
|
} catch (err) {
|
||||||
|
this.close()
|
||||||
|
throw err
|
||||||
} finally {
|
} finally {
|
||||||
clearInterval(interval)
|
clearInterval(interval)
|
||||||
}
|
}
|
||||||
|
@ -187,7 +190,11 @@ export function nostrZapDetails (zap) {
|
||||||
async function publishNostrEvent (signedEvent, relayUrl) {
|
async function publishNostrEvent (signedEvent, relayUrl) {
|
||||||
const timeout = 3000
|
const timeout = 3000
|
||||||
const relay = await Relay.connect(relayUrl, { timeout })
|
const relay = await Relay.connect(relayUrl, { timeout })
|
||||||
await relay.publish(signedEvent, { timeout })
|
try {
|
||||||
|
await relay.publish(signedEvent, { timeout })
|
||||||
|
} finally {
|
||||||
|
relay.close()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function crosspost (event, relays = DEFAULT_CROSSPOSTING_RELAYS) {
|
export async function crosspost (event, relays = DEFAULT_CROSSPOSTING_RELAYS) {
|
||||||
|
|
26
lib/proxy.js
26
lib/proxy.js
|
@ -1,5 +1,6 @@
|
||||||
import http from 'http'
|
import http from 'http'
|
||||||
import https from 'https'
|
import https from 'https'
|
||||||
|
import { TOR_REGEXP } from '@/lib/url'
|
||||||
|
|
||||||
// from https://github.com/delvedor/hpagent
|
// from https://github.com/delvedor/hpagent
|
||||||
|
|
||||||
|
@ -118,3 +119,28 @@ export class HttpsProxyAgent extends https.Agent {
|
||||||
request.end()
|
request.end()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getAgent ({ hostname, cert }) {
|
||||||
|
let agent
|
||||||
|
|
||||||
|
const httpsAgentOptions = { ca: cert ? Buffer.from(cert, 'base64') : undefined }
|
||||||
|
|
||||||
|
const isOnion = TOR_REGEXP.test(hostname)
|
||||||
|
if (isOnion) {
|
||||||
|
// we support HTTP and HTTPS over Tor
|
||||||
|
const protocol = cert ? 'https:' : 'http:'
|
||||||
|
// we need to use our Tor proxy to resolve onion addresses
|
||||||
|
const proxyOptions = { proxy: process.env.TOR_PROXY }
|
||||||
|
agent = protocol === 'https:'
|
||||||
|
? new HttpsProxyAgent({ ...proxyOptions, ...httpsAgentOptions, rejectUnauthorized: false })
|
||||||
|
: new HttpProxyAgent(proxyOptions)
|
||||||
|
return agent
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV === 'development' && !cert) {
|
||||||
|
return new http.Agent()
|
||||||
|
}
|
||||||
|
|
||||||
|
// we only support HTTPS over clearnet
|
||||||
|
return new https.Agent(httpsAgentOptions)
|
||||||
|
}
|
||||||
|
|
|
@ -163,11 +163,11 @@ This function must throw an error if the configuration was found to be invalid.
|
||||||
|
|
||||||
The `context` argument is an object. It makes the wallet logger for this wallet as returned by `useWalletLogger` available under `context.logger`. See [components/wallet-logger.js](../components/wallet-logger.js).
|
The `context` argument is an object. It makes the wallet logger for this wallet as returned by `useWalletLogger` available under `context.logger`. See [components/wallet-logger.js](../components/wallet-logger.js).
|
||||||
|
|
||||||
- `sendPayment: async (bolt11: string, config, context) => Promise<{ preimage: string }>`
|
- `sendPayment: async (bolt11: string, config, context) => Promise<string>`
|
||||||
|
|
||||||
`sendPayment` will be called if a payment is required. Therefore, this function should implement the code to pay invoices from this wallet.
|
`sendPayment` will be called if a payment is required. Therefore, this function should implement the code to pay invoices from this wallet.
|
||||||
|
|
||||||
The first argument is the [BOLT11 payment request](https://github.com/lightning/bolts/blob/master/11-payment-encoding.md). The `config` argument is the current configuration of this wallet (that was validated before). The `context` argument is the same as for `testSendPayment`.
|
The first argument is the [BOLT11 payment request](https://github.com/lightning/bolts/blob/master/11-payment-encoding.md). The `config` argument is the current configuration of this wallet (that was validated before). The `context` argument is the same as for `testSendPayment`. The function should return the preimage on payment success.
|
||||||
|
|
||||||
> [!IMPORTANT]
|
> [!IMPORTANT]
|
||||||
> As mentioned above, this file must exist for every wallet and at least reexport everything in index.js so make sure that the following line is included:
|
> As mentioned above, this file must exist for every wallet and at least reexport everything in index.js so make sure that the following line is included:
|
||||||
|
|
|
@ -10,8 +10,7 @@ export async function testSendPayment ({ apiKey, currency }, { logger }) {
|
||||||
|
|
||||||
export async function sendPayment (bolt11, { apiKey, currency }) {
|
export async function sendPayment (bolt11, { apiKey, currency }) {
|
||||||
const wallet = await getWallet(apiKey, currency)
|
const wallet = await getWallet(apiKey, currency)
|
||||||
const preImage = await payInvoice(apiKey, wallet, bolt11)
|
return await payInvoice(apiKey, wallet, bolt11)
|
||||||
return { preImage }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function payInvoice (authToken, wallet, invoice) {
|
async function payInvoice (authToken, wallet, invoice) {
|
||||||
|
|
|
@ -59,7 +59,7 @@ export function useWallet (name) {
|
||||||
const hash = bolt11Tags(bolt11).payment_hash
|
const hash = bolt11Tags(bolt11).payment_hash
|
||||||
logger.info('sending payment:', `payment_hash=${hash}`)
|
logger.info('sending payment:', `payment_hash=${hash}`)
|
||||||
try {
|
try {
|
||||||
const { preimage } = await wallet.sendPayment(bolt11, config, { me, logger, status, showModal })
|
const preimage = await wallet.sendPayment(bolt11, config, { me, logger, status, showModal })
|
||||||
logger.ok('payment successful:', `payment_hash=${hash}`, `preimage=${preimage}`)
|
logger.ok('payment successful:', `payment_hash=${hash}`, `preimage=${preimage}`)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const message = err.message || err.toString?.()
|
const message = err.message || err.toString?.()
|
||||||
|
|
|
@ -19,8 +19,7 @@ export async function sendPayment (bolt11, { url, adminKey }) {
|
||||||
throw new Error('No preimage')
|
throw new Error('No preimage')
|
||||||
}
|
}
|
||||||
|
|
||||||
const preimage = checkResponse.preimage
|
return checkResponse.preimage
|
||||||
return { preimage }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getWallet ({ url, adminKey, invoiceKey }) {
|
async function getWallet ({ url, adminKey, invoiceKey }) {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { msatsToSats } from '@/lib/format'
|
import { msatsToSats } from '@/lib/format'
|
||||||
|
import { getAgent } from '@/lib/proxy'
|
||||||
|
|
||||||
export * from 'wallets/lnbits'
|
export * from 'wallets/lnbits'
|
||||||
|
|
||||||
|
@ -27,7 +28,15 @@ export async function createInvoice (
|
||||||
out: false
|
out: false
|
||||||
})
|
})
|
||||||
|
|
||||||
const res = await fetch(url + path, { method: 'POST', headers, body })
|
const hostname = url.replace(/^https?:\/\//, '')
|
||||||
|
const agent = getAgent({ hostname })
|
||||||
|
|
||||||
|
const res = await fetch(`${agent.protocol}//${hostname}${path}`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers,
|
||||||
|
agent,
|
||||||
|
body
|
||||||
|
})
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
const errBody = await res.json()
|
const errBody = await res.json()
|
||||||
throw new Error(errBody.detail)
|
throw new Error(errBody.detail)
|
||||||
|
|
|
@ -67,7 +67,7 @@ export async function sendPayment (bolt11, credentials, { logger }) {
|
||||||
if (paymentError) throw new Error(paymentError)
|
if (paymentError) throw new Error(paymentError)
|
||||||
if (!preimage) throw new Error('No preimage in response')
|
if (!preimage) throw new Error('No preimage in response')
|
||||||
|
|
||||||
return { preimage }
|
return preimage
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const msg = err.message || err.toString?.()
|
const msg = err.message || err.toString?.()
|
||||||
if (msg.includes('invoice expired')) {
|
if (msg.includes('invoice expired')) {
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
The nwc string is printed in the nwc container logs on startup ...
|
Use this NWC string to attach the wallet for payments:
|
||||||
|
|
||||||
Open the nwc container logs like this:
|
```
|
||||||
|
nostr+walletconnect://5224c44600696216595a70982ee7387a04bd66248b97fefb803f4ed6d4af1972?relay=wss%3A%2F%2Frelay.damus.io&secret=0d1ef06059c9b1acf8c424cfe357c5ffe2d5f3594b9081695771a363ee716b67
|
||||||
|
```
|
||||||
|
|
||||||
```bash
|
This won't work for receives since it allows `pay_invoice`.
|
||||||
$ sndev logs nwc
|
|
||||||
```
|
TODO: generate NWC string with only `make_invoice` as permission
|
||||||
|
|
|
@ -35,5 +35,5 @@ export async function sendPayment (bolt11, { url, primaryPassword }) {
|
||||||
throw new Error(payment.reason)
|
throw new Error(payment.reason)
|
||||||
}
|
}
|
||||||
|
|
||||||
return payment.paymentPreimage
|
return preimage
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,7 +54,11 @@ export async function nip57 ({ data: { hash }, boss, lnd, models }) {
|
||||||
relays.map(async r => {
|
relays.map(async r => {
|
||||||
const timeout = 1000
|
const timeout = 1000
|
||||||
const relay = await Relay.connect(r, { timeout })
|
const relay = await Relay.connect(r, { timeout })
|
||||||
await relay.publish(e, { timeout })
|
try {
|
||||||
|
await relay.publish(e, { timeout })
|
||||||
|
} finally {
|
||||||
|
relay.close()
|
||||||
|
}
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
Loading…
Reference in New Issue