Skip to content

Commit f927fc5

Browse files
committed
code cleanup, add bolt12info (bolt11 tags equivalent)
1 parent 332d1e1 commit f927fc5

File tree

11 files changed

+131
-85
lines changed

11 files changed

+131
-85
lines changed

api/paidAction/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { createHmac } from '@/api/resolvers/wallet'
55
import { Prisma } from '@prisma/client'
66
import { createWrappedInvoice, createInvoice as createUserInvoice } from '@/wallets/server'
77
import { assertBelowMaxPendingInvoices, assertBelowMaxPendingDirectPayments } from './lib/assert'
8-
import { parseBolt11 } from '@/lib/invoices'
8+
import { parseBolt11 } from '@/lib/bolt11'
99

1010
import * as ITEM_CREATE from './itemCreate'
1111
import * as ITEM_UPDATE from './itemUpdate'

api/resolvers/wallet.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ import {
1212
import { amountSchema, validateSchema, withdrawlSchema, lnAddrSchema } from '@/lib/validate'
1313
import assertGofacYourself from './ofac'
1414
import assertApiKeyNotPermitted from './apiKey'
15-
import { bolt11Tags } from '@/lib/bolt11'
15+
import { bolt11Info, isBolt11 } from '@/lib/bolt11-info'
16+
import { bolt12Info } from '@/lib/bolt12-info'
1617
import { finalizeHodlInvoice } from '@/worker/wallet'
1718
import walletDefs from '@/wallets/server'
1819
import { generateResolverName, generateTypeDefName } from '@/wallets/graphql'
@@ -368,7 +369,7 @@ const resolvers = {
368369
f = { ...f, ...f.other }
369370

370371
if (f.bolt11) {
371-
f.description = bolt11Tags(f.bolt11).description
372+
f.description = isBolt11(f.bolt11) ? bolt11Info(f.bolt11).description : bolt12Info(f.bolt11).description
372373
}
373374

374375
switch (f.type) {

components/bolt11-info.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import AccordianItem from './accordian-item'
22
import { CopyInput } from './form'
3-
import { bolt11Tags } from '@/lib/bolt11'
3+
import { bolt11Info, isBolt11 } from '@/lib/bolt11-info'
4+
import { bolt12Info } from '@/lib/bolt12-info'
45

56
export default ({ bolt11, preimage, children }) => {
67
let description, paymentHash
78
if (bolt11) {
8-
({ description, payment_hash: paymentHash } = bolt11Tags(bolt11))
9+
({ description, payment_hash: paymentHash } = isBolt11(bolt11) ? bolt11Info(bolt11) : bolt12Info(bolt11))
910
}
1011

1112
return (

lib/bolt11-info.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { decode } from 'bolt11'
2+
3+
export function isBolt11 (request) {
4+
return request.startsWith('lnbc') || request.startsWith('lntb') || request.startsWith('lntbs') || request.startsWith('lnbcrt')
5+
}
6+
7+
export function bolt11Info (bolt11) {
8+
if (!isBolt11(bolt11)) throw new Error('not a bolt11 invoice')
9+
return decode(bolt11).tagsObject
10+
}

lib/bolt11.js

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,21 @@
1-
import { decode } from 'bolt11'
1+
/* eslint-disable camelcase */
2+
import { payViaPaymentRequest, parsePaymentRequest } from 'ln-service'
23

3-
export function bolt11Tags (bolt11) {
4-
return decode(bolt11).tagsObject
4+
export function isBolt11 (request) {
5+
return request.startsWith('lnbc') || request.startsWith('lntb') || request.startsWith('lntbs') || request.startsWith('lnbcrt')
6+
}
7+
8+
export async function parseBolt11 ({ request }) {
9+
if (!isBolt11(request)) throw new Error('not a bolt11 invoice')
10+
return parsePaymentRequest({ request })
11+
}
12+
13+
export async function payBolt11 ({ lnd, request, max_fee, ...args }) {
14+
if (!isBolt11(request)) throw new Error('not a bolt11 invoice')
15+
return payViaPaymentRequest({
16+
lnd,
17+
request,
18+
max_fee,
19+
...args
20+
})
521
}

lib/bolt12-info.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { deserializeTLVStream } from './tlv'
2+
import * as bech32b12 from '@/lib/bech32b12'
3+
4+
export function isBolt12 (invoice) {
5+
return invoice.startsWith('lni1') || invoice.startsWith('lno1')
6+
}
7+
8+
export function bolt12Info (bolt12) {
9+
if (!isBolt12(bolt12)) throw new Error('not a bolt12 invoice or offer')
10+
const buf = bech32b12.decode(bolt12.substring(4)/* remove lni1 or lno1 prefix */)
11+
const tlv = deserializeTLVStream(buf)
12+
const INFO_TYPES = {
13+
description: 10n,
14+
payment_hash: 168n
15+
}
16+
const info = {
17+
description: '',
18+
payment_hash: ''
19+
}
20+
for (const { type, value } of tlv) {
21+
if (type === INFO_TYPES.description) {
22+
info.description = value.toString()
23+
} else if (type === INFO_TYPES.payment_hash) {
24+
info.payment_hash = value.toString('hex')
25+
}
26+
}
27+
return info
28+
}

lib/bolt12.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/* eslint-disable camelcase */
2+
3+
import { payViaBolt12PaymentRequest, parseBolt12Request } from '@/lib/lndk'
4+
5+
export function isBolt12Offer (invoice) {
6+
return invoice.startsWith('lno1')
7+
}
8+
9+
export function isBolt12Invoice (invoice) {
10+
return invoice.startsWith('lni1')
11+
}
12+
13+
export function isBolt12 (invoice) {
14+
return isBolt12Offer(invoice) || isBolt12Invoice(invoice)
15+
}
16+
17+
export async function payBolt12 ({ lnd, request: invoice, max_fee }) {
18+
if (!isBolt12Invoice(invoice)) throw new Error('not a bolt12 invoice')
19+
return await payViaBolt12PaymentRequest({ lnd, request: invoice, max_fee })
20+
}
21+
22+
export function parseBolt12 ({ lnd, request: invoice }) {
23+
if (!isBolt12Invoice(invoice)) throw new Error('not a bolt12 request')
24+
return parseBolt12Request({ lnd, request: invoice })
25+
}

lib/invoices.js

Lines changed: 3 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,9 @@
11
/* eslint-disable camelcase */
2-
3-
import { payViaPaymentRequest, parsePaymentRequest } from 'ln-service'
2+
import { payBolt12, parseBolt12, isBolt12Invoice } from './bolt12'
3+
import { payBolt11, parseBolt11 } from './bolt11'
4+
import { estimateBolt12RouteFee } from '@/lib/lndk'
45
import { estimateRouteFee } from '@/api/lnd'
56

6-
import { payViaBolt12PaymentRequest, parseBolt12Request, estimateBolt12RouteFee } from '@/lib/lndk'
7-
8-
export function isBolt11 (request) {
9-
return request.startsWith('lnbc') || request.startsWith('lntb') || request.startsWith('lntbs') || request.startsWith('lnbcrt')
10-
}
11-
12-
export function parseBolt11 ({ request }) {
13-
if (!isBolt11(request)) throw new Error('not a bolt11 invoice')
14-
return parsePaymentRequest({ request })
15-
}
16-
17-
export function payBolt11 ({ lnd, request, max_fee, ...args }) {
18-
if (!isBolt11(request)) throw new Error('not a bolt11 invoice')
19-
20-
return payViaPaymentRequest({
21-
lnd,
22-
request,
23-
max_fee,
24-
...args
25-
})
26-
}
27-
28-
export function isBolt12Offer (invoice) {
29-
return invoice.startsWith('lno1')
30-
}
31-
32-
export function isBolt12Invoice (invoice) {
33-
console.log('isBolt12Invoice', invoice)
34-
console.trace()
35-
return invoice.startsWith('lni1')
36-
}
37-
38-
export async function payBolt12 ({ lnd, request: invoice, max_fee }) {
39-
if (!isBolt12Invoice(invoice)) throw new Error('not a bolt12 invoice')
40-
41-
if (!invoice) throw new Error('No invoice in bolt12, please use prefetchBolt12Invoice')
42-
return await payViaBolt12PaymentRequest({ lnd, request: invoice, max_fee })
43-
}
44-
45-
export function parseBolt12 ({ lnd, request: invoice }) {
46-
if (!isBolt12Invoice(invoice)) throw new Error('not a bolt12 request')
47-
return parseBolt12Request({ lnd, request: invoice })
48-
}
49-
507
export async function payInvoice ({ lnd, request: invoice, max_fee, ...args }) {
518
if (isBolt12Invoice(invoice)) {
529
return await payBolt12({ lnd, request: invoice, max_fee, ...args })

lib/lndk.js

Lines changed: 1 addition & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -121,36 +121,7 @@ const chainsMap = {
121121
'43497fd7f826957108f4a30fd9cec3aeba79972084e90ead01ea330900000000': 'testnet',
122122
'6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000': 'mainnet'
123123
}
124-
// @returns
125-
// {
126-
// [chain_addresses]: [<Chain Address String>]
127-
// cltv_delta: <CLTV Delta Number>
128-
// created_at: <Invoice Creation Date ISO 8601 String>
129-
// [description]: <Description String>
130-
// [description_hash]: <Description Hash Hex String>
131-
// destination: <Public Key String>
132-
// expires_at: <ISO 8601 Date String>
133-
// features: [{
134-
// bit: <BOLT 09 Feature Bit Number>
135-
// is_required: <Feature Support is Required To Pay Bool>
136-
// type: <Feature Type String>
137-
// }]
138-
// id: <Payment Request Hash String>
139-
// is_expired: <Invoice is Expired Bool>
140-
// [metadata]: <Payment Metadata Hex String>
141-
// [mtokens]: <Requested Milli-Tokens Value String> (can exceed Number limit)
142-
// network: <Network Name String>
143-
// [payment]: <Payment Identifier Hex Encoded String>
144-
// [routes]: [[{
145-
// [base_fee_mtokens]: <Base Fee Millitokens String>
146-
// [channel]: <Standard Format Channel Id String>
147-
// [cltv_delta]: <Final CLTV Expiration Blocks Delta Number>
148-
// [fee_rate]: <Fee Rate Millitokens Per Million Number>
149-
// public_key: <Forward Edge Public Key Hex String>
150-
// }]]
151-
// [safe_tokens]: <Requested Tokens Rounded Up Number>
152-
// [tokens]: <Requested Chain Tokens Number> (note: can differ from mtokens)
153-
// }
124+
154125
export async function parseBolt12Request ({
155126
lnd,
156127
request

lib/tlv.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
export function deserializeTLVStream (buff) {
2+
const tlvs = []
3+
let bytePos = 0
4+
while (bytePos < buff.length) {
5+
const [type, typeLength] = readBigSize(buff, bytePos)
6+
bytePos += typeLength
7+
8+
let [length, lengthLength] = readBigSize(buff, bytePos)
9+
length = Number(length)
10+
bytePos += lengthLength
11+
12+
if (bytePos + length > buff.length) {
13+
throw new Error('invalid tlv stream')
14+
}
15+
16+
const value = buff.subarray(bytePos, bytePos + length)
17+
bytePos += length
18+
19+
tlvs.push({ type, length, value })
20+
}
21+
return tlvs
22+
}
23+
24+
function readBigSize (buf, offset) {
25+
if (buf[offset] <= 252) {
26+
return [BigInt(buf[offset]), 1]
27+
} else if (buf[offset] === 253) {
28+
return [BigInt(buf.readUInt16BE(offset + 1)), 3]
29+
} else if (buf[offset] === 254) {
30+
return [BigInt(buf.readUInt32BE(offset + 1)), 5]
31+
} else if (buf[offset] === 255) {
32+
return [buf.readBigUInt64BE(offset + 1), 9]
33+
} else {
34+
throw new Error('Invalid bigsize')
35+
}
36+
}

0 commit comments

Comments
 (0)