Skip to content

Commit bb10b6e

Browse files
committed
implement context maze
1 parent 5cd447b commit bb10b6e

23 files changed

+125
-120
lines changed

api/lib/bolt/bolt11.js

+8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/* eslint-disable camelcase */
22
import { payViaPaymentRequest, parsePaymentRequest } from 'ln-service'
33
import { isBolt11 } from '@/lib/bolt/bolt11-tags'
4+
import { estimateRouteFee } from '@/api/lnd'
45
export { isBolt11 }
56

67
export async function parseBolt11 ({ request }) {
@@ -9,6 +10,7 @@ export async function parseBolt11 ({ request }) {
910
}
1011

1112
export async function payBolt11 ({ lnd, request, max_fee, max_fee_mtokens, ...args }) {
13+
if (!lnd) throw new Error('lnd required') // check if forgot to pass lnd
1214
if (!isBolt11(request)) throw new Error('not a bolt11 invoice')
1315
return payViaPaymentRequest({
1416
lnd,
@@ -18,3 +20,9 @@ export async function payBolt11 ({ lnd, request, max_fee, max_fee_mtokens, ...ar
1820
...args
1921
})
2022
}
23+
24+
export async function estimateBolt11RouteFee ({ lnd, destination, tokens, mtokens, request, timeout }) {
25+
if (!lnd) throw new Error('lnd required') // check if forgot to pass lnd
26+
if (request && !isBolt11(request)) throw new Error('not a bolt11 request')
27+
return await estimateRouteFee({ lnd, destination, tokens, mtokens, request, timeout })
28+
}

api/lib/bolt/bolt12.js

+24-4
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,39 @@
33
import { payViaBolt12PaymentRequest, decodeBolt12Invoice } from '@/api/lib/lndk'
44
import { isBolt12Invoice, isBolt12Offer, isBolt12 } from '@/lib/bolt/bolt12-info'
55
import { toPositiveNumber } from '@/lib/format'
6+
import { estimateRouteFee } from '@/api/lnd'
67
export { isBolt12Invoice, isBolt12Offer, isBolt12 }
78

8-
export async function payBolt12 ({ lnd, request: invoice, max_fee, max_fee_mtokens }) {
9+
export async function payBolt12 ({ lndk, request: invoice, max_fee, max_fee_mtokens }) {
10+
if (!lndk) throw new Error('lndk required') // check if forgot to pass lndk
911
if (!isBolt12Invoice(invoice)) throw new Error('not a bolt12 invoice')
10-
return await payViaBolt12PaymentRequest({ lnd, request: invoice, max_fee, max_fee_mtokens })
12+
return await payViaBolt12PaymentRequest({ lndk, request: invoice, max_fee, max_fee_mtokens })
1113
}
1214

13-
export async function parseBolt12 ({ lnd, request: invoice }) {
15+
export async function parseBolt12 ({ lndk, request: invoice }) {
16+
if (!lndk) throw new Error('lndk required') // check if forgot to pass lndk
1417
if (!isBolt12Invoice(invoice)) throw new Error('not a bolt12 request')
15-
const decodedInvoice = await decodeBolt12Invoice({ lnd, request: invoice })
18+
const decodedInvoice = await decodeBolt12Invoice({ lndk, request: invoice })
1619
return convertBolt12RequestToLNRequest(decodedInvoice)
1720
}
1821

22+
export async function estimateBolt12RouteFee ({ lnd, lndk, destination, tokens, mtokens, request, timeout }) {
23+
if (!lndk) throw new Error('lndk required') // check if forgot to pass lndk
24+
if (!lnd) throw new Error('lnd required') // check if forgot to pass lnd
25+
if (request && !isBolt12Invoice(request)) throw new Error('not a bolt12 request')
26+
27+
const { amount_msats, node_id } = request ? await decodeBolt12Invoice({ lndk, request }) : {}
28+
29+
// extract mtokens and destination from invoice if they are not provided
30+
if (!tokens && !mtokens) mtokens = toPositiveNumber(amount_msats)
31+
destination ??= Buffer.from(node_id.key).toString('hex')
32+
33+
if (!destination) throw new Error('no destination provided')
34+
if (!tokens && !mtokens) throw new Error('no tokens amount provided')
35+
36+
return await estimateRouteFee({ lnd, destination, tokens, mtokens, timeout })
37+
}
38+
1939
const featureBitTypes = {
2040
0: 'DATALOSS_PROTECT_REQ',
2141
1: 'DATALOSS_PROTECT_OPT',

api/lib/bolt/index.js

+8-9
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
/* eslint-disable camelcase */
22
import { payBolt12, parseBolt12, isBolt12Invoice, isBolt12Offer } from '@/api/lib/bolt/bolt12'
3-
import { payBolt11, parseBolt11, isBolt11 } from '@/api/lib/bolt/bolt11'
3+
import { payBolt11, parseBolt11, isBolt11, estimateBolt11RouteFee } from '@/api/lib/bolt/bolt11'
44
import { estimateBolt12RouteFee } from '@/api/lib/lndk'
5-
import { estimateRouteFee } from '@/api/lnd'
65

7-
export async function payInvoice ({ lnd, request: invoice, max_fee, max_fee_mtokens, ...args }) {
6+
export async function payInvoice ({ lnd, lndk, request: invoice, max_fee, max_fee_mtokens, ...args }) {
87
if (isBolt12Invoice(invoice)) {
9-
return await payBolt12({ lnd, request: invoice, max_fee, max_fee_mtokens, ...args })
8+
return await payBolt12({ lndk, request: invoice, max_fee, max_fee_mtokens, ...args })
109
} else if (isBolt11(invoice)) {
1110
return await payBolt11({ lnd, request: invoice, max_fee, max_fee_mtokens, ...args })
1211
} else if (isBolt12Offer(invoice)) {
@@ -16,9 +15,9 @@ export async function payInvoice ({ lnd, request: invoice, max_fee, max_fee_mtok
1615
}
1716
}
1817

19-
export async function parseInvoice ({ lnd, request }) {
18+
export async function parseInvoice ({ lndk, request }) {
2019
if (isBolt12Invoice(request)) {
21-
return await parseBolt12({ lnd, request })
20+
return await parseBolt12({ lndk, request })
2221
} else if (isBolt11(request)) {
2322
return await parseBolt11({ request })
2423
} else if (isBolt12Offer(request)) {
@@ -28,11 +27,11 @@ export async function parseInvoice ({ lnd, request }) {
2827
}
2928
}
3029

31-
export async function estimateFees ({ lnd, destination, tokens, mtokens, request, timeout }) {
30+
export async function estimateFees ({ lnd, lndk, destination, tokens, mtokens, request, timeout }) {
3231
if (isBolt12Invoice(request)) {
33-
return await estimateBolt12RouteFee({ lnd, destination, tokens, mtokens, request, timeout })
32+
return await estimateBolt12RouteFee({ lnd, lndk, destination, tokens, mtokens, request, timeout })
3433
} else if (isBolt11(request)) {
35-
return await estimateRouteFee({ lnd, destination, tokens, request, mtokens, timeout })
34+
return await estimateBolt11RouteFee({ lnd, destination, tokens, request, mtokens, timeout })
3635
} else if (isBolt12Offer(request)) {
3736
throw new Error('bolt12 offer instead of invoice, please fetch a bolt12 invoice from the offer first')
3837
} else {

api/lib/lndk.js

+7-40
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,12 @@ import protobuf from 'protobufjs'
55
import grpcCredentials from 'lightning/lnd_grpc/grpc_credentials'
66
import { grpcSslCipherSuites } from 'lightning/grpc/index'
77
import { fromJSON } from '@grpc/proto-loader'
8-
import { estimateRouteFee } from '@/api/lnd'
98
import * as bech32b12 from '@/lib/bech32b12'
109

1110
/* eslint-disable camelcase */
1211
const { GRPC_SSL_CIPHER_SUITES } = process.env
1312

14-
const lndkInstances = new WeakMap()
15-
16-
export function enableLNDK (lnd, { cert, macaroon, socket: lndkSocket }, withProxy) {
17-
// already installed
18-
if (lndkInstances.has(lnd)) return
19-
console.log('enabling lndk', lndkSocket, 'withProxy', withProxy)
20-
13+
export function authenticatedLndkGrpc ({ cert, macaroon, socket: lndkSocket }, withProxy) {
2114
// workaround to load from string
2215
const protoArgs = { keepCase: true, longs: Number, defaults: true, oneofs: true }
2316
const proto = protobuf.parse(LNDK_RPC_PROTO, protoArgs).root
@@ -38,22 +31,13 @@ export function enableLNDK (lnd, { cert, macaroon, socket: lndkSocket }, withPro
3831
}
3932

4033
const client = new OffersService(lndkSocket, credentials, params)
41-
lndkInstances.set(lnd, client)
42-
}
43-
44-
export function getLNDK (lnd) {
45-
if (!lndkInstances.has(lnd)) {
46-
throw new Error('lndk not available, please use enableLNDK first')
47-
}
48-
return lndkInstances.get(lnd)
34+
return client
4935
}
5036

5137
export async function decodeBolt12Invoice ({
52-
lnd,
38+
lndk,
5339
request
5440
}) {
55-
const lndk = getLNDK(lnd)
56-
5741
// decode bech32 bolt12 invoice to hex string
5842
if (!request.startsWith('lni1')) throw new Error('not a valid bech32 encoded bolt12 invoice')
5943
const invoice_hex_str = bech32b12.decode(request.slice(4)).toString('hex')
@@ -70,9 +54,7 @@ export async function decodeBolt12Invoice ({
7054
return { ...decodedRequest, invoice_hex_str }
7155
}
7256

73-
export async function fetchBolt12InvoiceFromOffer ({ lnd, offer, msats, description, timeout = 10_000 }) {
74-
const lndk = getLNDK(lnd)
75-
57+
export async function fetchBolt12InvoiceFromOffer ({ lndk, offer, msats, description, timeout = 10_000 }) {
7658
return new Promise((resolve, reject) => {
7759
lndk.GetInvoice({
7860
offer,
@@ -87,7 +69,7 @@ export async function fetchBolt12InvoiceFromOffer ({ lnd, offer, msats, descript
8769
const bech32invoice = 'lni1' + bech32b12.encode(Buffer.from(response.invoice_hex_str, 'hex'))
8870

8971
// sanity check
90-
const { amount_msats } = await decodeBolt12Invoice({ lnd, request: bech32invoice })
72+
const { amount_msats } = await decodeBolt12Invoice({ lndk, request: bech32invoice })
9173
if (toPositiveNumber(amount_msats) !== toPositiveNumber(msats)) {
9274
return reject(new Error('invalid invoice response'))
9375
}
@@ -101,14 +83,12 @@ export async function fetchBolt12InvoiceFromOffer ({ lnd, offer, msats, descript
10183
}
10284

10385
export async function payViaBolt12PaymentRequest ({
104-
lnd,
86+
lndk,
10587
request: invoiceBech32,
10688
max_fee,
10789
max_fee_mtokens
10890
}) {
109-
const lndk = getLNDK(lnd)
110-
111-
const { amount_msats, invoice_hex_str } = await decodeBolt12Invoice({ lnd, request: invoiceBech32 })
91+
const { amount_msats, invoice_hex_str } = await decodeBolt12Invoice({ lndk, request: invoiceBech32 })
11292

11393
if (!max_fee_mtokens && max_fee) {
11494
max_fee_mtokens = toPositiveNumber(satsToMsats(max_fee))
@@ -130,16 +110,3 @@ export async function payViaBolt12PaymentRequest ({
130110
})
131111
})
132112
}
133-
134-
export async function estimateBolt12RouteFee ({ lnd, destination, tokens, mtokens, request, timeout }) {
135-
const { amount_msats, node_id } = request ? await decodeBolt12Invoice({ lnd, request }) : {}
136-
137-
// extract mtokens and destination from invoice if they are not provided
138-
if (!tokens && !mtokens) mtokens = toPositiveNumber(amount_msats)
139-
destination ??= Buffer.from(node_id.key).toString('hex')
140-
141-
if (!destination) throw new Error('no destination provided')
142-
if (!tokens && !mtokens) throw new Error('no tokens amount provided')
143-
144-
return await estimateRouteFee({ lnd, destination, tokens, mtokens, timeout })
145-
}

api/lnd/index.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { cachedFetcher } from '@/lib/fetch'
22
import { toPositiveNumber } from '@/lib/format'
33
import { authenticatedLndGrpc } from '@/lib/lnd'
4-
import { enableLNDK } from '@/api/lib/lndk'
4+
import { authenticatedLndkGrpc } from '@/api/lib/lndk'
55
import { getIdentity, getHeight, getWalletInfo, getNode, getPayment } from 'ln-service'
66
import { datePivot } from '@/lib/time'
77
import { LND_PATHFINDING_TIMEOUT_MS } from '@/lib/constants'
@@ -11,7 +11,7 @@ const lnd = global.lnd || authenticatedLndGrpc({
1111
macaroon: process.env.LND_MACAROON,
1212
socket: process.env.LND_SOCKET
1313
}).lnd
14-
enableLNDK(lnd, {
14+
export const lndk = authenticatedLndkGrpc({
1515
cert: process.env.LNDK_CERT,
1616
macaroon: process.env.LNDK_MACAROON,
1717
socket: process.env.LNDK_SOCKET

api/paidAction/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,7 @@ All functions have the following signature: `function(args: Object, context: Obj
193193
- `tx`: the current transaction (for anything that needs to be done atomically with the payment)
194194
- `models`: the current prisma client (for anything that doesn't need to be done atomically with the payment)
195195
- `lnd`: the current lnd client
196+
- `lndk`: the current lndk client
196197

197198
## Recording Cowboy Credits
198199

api/paidAction/index.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ async function beginPessimisticAction (actionType, args, context) {
213213
async function performP2PAction (actionType, args, incomingContext) {
214214
// if the action has an invoiceable peer, we'll create a peer invoice
215215
// wrap it, and return the wrapped invoice
216-
const { cost, sybilFeePercent, models, lnd, me } = incomingContext
216+
const { cost, sybilFeePercent, models, lnd, lndk, me } = incomingContext
217217
if (!sybilFeePercent) {
218218
throw new Error('sybil fee percent is not set for an invoiceable peer action')
219219
}
@@ -233,7 +233,7 @@ async function performP2PAction (actionType, args, incomingContext) {
233233
feePercent: sybilFeePercent,
234234
description,
235235
expiry: INVOICE_EXPIRE_SECS
236-
}, { models, me, lnd })
236+
}, { models, me, lnd, lndk })
237237

238238
context = {
239239
...incomingContext,
@@ -257,7 +257,7 @@ async function performP2PAction (actionType, args, incomingContext) {
257257
// we don't need to use the module for perform-ing outside actions
258258
// because we can't track the state of outside invoices we aren't paid/paying
259259
async function performDirectAction (actionType, args, incomingContext) {
260-
const { models, lnd, cost } = incomingContext
260+
const { models, lnd, cost, lndk } = incomingContext
261261
const { comment, lud18Data, noteStr, description: actionDescription } = args
262262

263263
const userId = await paidActions[actionType]?.getInvoiceablePeer?.(args, incomingContext)
@@ -276,7 +276,7 @@ async function performDirectAction (actionType, args, incomingContext) {
276276
description,
277277
expiry: INVOICE_EXPIRE_SECS,
278278
supportBolt12: false // direct payment is not supported to bolt12 for compatibility reasons
279-
}, { models, lnd })
279+
}, { models, lnd, lndk })
280280
} catch (e) {
281281
console.error('failed to create outside invoice', e)
282282
throw new NonInvoiceablePeerError()

api/payingAction/index.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,15 @@ import { payInvoice, parseInvoice } from '@/api/lib/bolt'
66
// paying actions are completely distinct from paid actions
77
// and there's only one paying action: send
88
// ... still we want the api to at least be similar
9-
export default async function performPayingAction ({ bolt11, maxFee, walletId }, { me, models, lnd }) {
9+
export default async function performPayingAction ({ bolt11, maxFee, walletId }, { me, models, lnd, lndk }) {
1010
try {
1111
console.group('performPayingAction', `${bolt11.slice(0, 10)}...`, maxFee, walletId)
1212

1313
if (!me) {
1414
throw new Error('You must be logged in to perform this action')
1515
}
1616

17-
const decoded = await parseInvoice({ request: bolt11, lnd })
17+
const decoded = await parseInvoice({ request: bolt11, lnd, lndk })
1818
const cost = toPositiveBigInt(toPositiveBigInt(decoded.mtokens) + satsToMsats(maxFee))
1919

2020
console.log('cost', cost)
@@ -42,6 +42,7 @@ export default async function performPayingAction ({ bolt11, maxFee, walletId },
4242

4343
payInvoice({
4444
lnd,
45+
lndk,
4546
request: withdrawal.bolt11,
4647
max_fee: msatsToSats(withdrawal.msatsFeePaying),
4748
pathfinding_timeout: LND_PATHFINDING_TIMEOUT_MS,

api/resolvers/item.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -945,7 +945,7 @@ export default {
945945

946946
return await performPaidAction('POLL_VOTE', { id }, { me, models, lnd })
947947
},
948-
act: async (parent, { id, sats, act = 'TIP', hasSendWallet }, { me, models, lnd, headers }) => {
948+
act: async (parent, { id, sats, act = 'TIP', hasSendWallet }, { me, models, lnd, lndk, headers }) => {
949949
assertApiKeyNotPermitted({ me })
950950
await validateSchema(actSchema, { sats, act })
951951
await assertGofacYourself({ models, headers })
@@ -979,11 +979,11 @@ export default {
979979
}
980980

981981
if (act === 'TIP') {
982-
return await performPaidAction('ZAP', { id, sats, hasSendWallet }, { me, models, lnd })
982+
return await performPaidAction('ZAP', { id, sats, hasSendWallet }, { me, models, lnd, lndk })
983983
} else if (act === 'DONT_LIKE_THIS') {
984-
return await performPaidAction('DOWN_ZAP', { id, sats }, { me, models, lnd })
984+
return await performPaidAction('DOWN_ZAP', { id, sats }, { me, models, lnd, lndk })
985985
} else if (act === 'BOOST') {
986-
return await performPaidAction('BOOST', { id, sats }, { me, models, lnd })
986+
return await performPaidAction('BOOST', { id, sats }, { me, models, lnd, lndk })
987987
} else {
988988
throw new GqlInputError('unknown act')
989989
}

api/resolvers/paidAction.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ export default {
5050
}
5151
},
5252
Mutation: {
53-
retryPaidAction: async (parent, { invoiceId }, { models, me, lnd }) => {
53+
retryPaidAction: async (parent, { invoiceId }, { models, me, lnd, lndk }) => {
5454
if (!me) {
5555
throw new Error('You must be logged in')
5656
}
@@ -67,7 +67,7 @@ export default {
6767
throw new Error(`Invoice is not in failed state: ${invoice.actionState}`)
6868
}
6969

70-
const result = await retryPaidAction(invoice.actionType, { invoice }, { models, me, lnd })
70+
const result = await retryPaidAction(invoice.actionType, { invoice }, { models, me, lnd, lndk })
7171

7272
return {
7373
...result,

0 commit comments

Comments
 (0)