Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: not fetching lightning when there is a : in the bip21 #319

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ module.exports = {
"jsx-quotes": "error",
"linebreak-style": ["error", "unix"],
"max-depth": ["error", { max: 3 }],
"max-lines-per-function": ["error", { max: 200 }],
"max-lines": ["error", { max: 500 }],
"max-nested-callbacks": ["error", { max: 5 }],
"max-params": ["error", { max: 2 }],
Expand Down
58 changes: 53 additions & 5 deletions src/parsing/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,6 @@ import {
PaymentType,
} from "."

beforeAll(() => {
jest.setSystemTime(1598110996000) // Aug 22 2020 10:43
})

const p2pkh = "1KP2uzAZYoNF6U8BkMBRdivLNujwSjtAQV"
const p2sh = "3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy"
const bech32 = "bc1qdx09anw82zhujxzzsn56mruv8qvd33czzy9apt"
Expand Down Expand Up @@ -46,6 +42,12 @@ const lnbcrtInvoice =
const expiredLNInvoice =
"LNBC11245410N1P05Z2LTPP52W2GX57TZVLM09SWZ8M0CAWGQPVTL3KUWZA836H5LG6HK2N2PRYQDPHXYSV89EQYVMJQSNFW3PXCMMRDDZXJMNWV4EZQST4VA6HXAPQXGU8G6QCQZPGXQRRSSVS7S2WT4GX90MQC9CVMA8UYDSTX5P0FA68V03U96HQDPFCT9DGDQQSENNAAGAXND6664CTKV88GMQ689LS0J7FFAD4DRN6SPLXAXZ0CQYZAU9Q"

const cashappInvoiceQR =
"bitcoin:BC1Q5J58AAKAJGNZ78W2924WAJKGQLPS5CT6QXGR5E?amount=0.00025177&lightning=LNBC251770N1PJKUCFDDQDGDSHX6PQG9C8QPP5T9ZKQFZW905W3A2VZEL4VCZHQG40Q9FM0VZGCJ3DWC203KT9V4MQSP5DEYZWTU6HSSSLKDEWFH3FYR8K2YD3L7XA8KLNSVC4JP4XFK6LTCS9QRSGQCQPCXQY8AYQRZJQV06K0M23T593PNGL0JT7N9WZNP64FQNGVCTZ7VTS8NQ4TUKVTLJQZG0K5QQT0QQQSQQQQQQQQQQQQQQ9GRZJQTSJY9P55GDCEEVP36FVDMRKXQVZFHY8AK2TGC5ZGTJTRA9XLAZ97Z965UQQTQCQQQQQQQQQQQQQQQQQ9G6LA8G90SULF7X0XAF7XUMATK57KF733X04S8027XKZZJK945DZYKZWTQY63FARFWYSNKDGHHMU74KXVE9KUR3624ETHDKT60CGCJL2SQZ9V7LT"

const btcpayServerQR =
"bitcoin:BC1QUZ8NQRJ3UX86QEHT4F6QQNC75XMNRGCE06ASCH?amount=0.00025885&pj=https://pay.pavlenex.com/BTC/pj&lightning=LNBC258850N1PJKUC3KSP5DKXUDY39Z3PAG74FGE6LGCWA6UHAN084MUU3SRPLXML4NY8FSLPSPP58JJ3Q9A6C9EQ2CTPRCUNGF5RNPQ6A9TRCLSD6GFSQ0JRWZWAX3PQDRQ2PSKJEPQW3HJQ5RPWEKX2MN90QSYGMMWV96XJMMWYQ5Y7UNYV4EZQJ2Y8GS9V5RTXVCNY7PKVEV8Q6262DPN2DJPD5UNXJEFXQR3J0CQPJRZJQTTFTVQU0F5SNECKEP3LKWDUT7MMHHPCYJMLMNJN4HZE8ED7PQ88XRRTWYQQ9USQQYQQQZNDQQQQQXCQ2Q9QXPQYSGQKYWUNGZ5TNRDXKLRHW968GMW3TUECTSSU4STH3MRWLJDUEG9JNVRNFYMJJZN8J5GAWEURCFRW6477MZTVZQAAQX5678NP8TPGPM6EPCQ9NWAGG"

const checkOnChain = (address: string, network: Network) => {
const destination = parsePaymentDestination({
destination: address,
Expand Down Expand Up @@ -450,7 +452,9 @@ describe("parsePaymentDestination Lightning", () => {
)
})

it("detects a lightning param in an onchain address", () => {
it("detects a lightning param in an onchain address 1", () => {
jest.setSystemTime(1598110996000) // Aug 22 2020 10:43

const address =
"bitcoin:bc1qylh3u67j673h6y6alv70m0pl2yz53tzhvxgg7u?amount=0.00001&label=sbddesign%3A%20For%20lunch%20Tuesday&message=For%20lunch%20Tuesday&lightning=lnbc10u1p3pj257pp5yztkwjcz5ftl5laxkav23zmzekaw37zk6kmv80pk4xaev5qhtz7qdpdwd3xger9wd5kwm36yprx7u3qd36kucmgyp282etnv3shjcqzpgxqyz5vqsp5usyc4lk9chsfp53kvcnvq456ganh60d89reykdngsmtj6yw3nhvq9qyyssqjcewm5cjwz4a6rfjx77c490yced6pemk0upkxhy89cmm7sct66k8gneanwykzgdrwrfje69h9u5u0w57rrcsysas7gadwmzxc8c6t0spjazup6"
const paymentDestination = parsePaymentDestination({
Expand All @@ -469,6 +473,46 @@ describe("parsePaymentDestination Lightning", () => {
)
})

it("detects a lightning param in an onchain address 2", () => {
jest.setSystemTime(1701753935000) // 2023-12-04 23:25:35 UTC

const address = cashappInvoiceQR
const paymentDestination = parsePaymentDestination({
destination: address,
network: "mainnet",
lnAddressDomains: [],
})

expect(paymentDestination).toEqual(
expect.objectContaining({
valid: true,
paymentRequest:
"lnbc251770n1pjkucfddqdgdshx6pqg9c8qpp5t9zkqfzw905w3a2vzel4vczhqg40q9fm0vzgcj3dwc203kt9v4mqsp5deyzwtu6hssslkdewfh3fyr8k2yd3l7xa8klnsvc4jp4xfk6ltcs9qrsgqcqpcxqy8ayqrzjqv06k0m23t593pngl0jt7n9wznp64fqngvctz7vts8nq4tukvtljqzg0k5qqt0qqqsqqqqqqqqqqqqqq9grzjqtsjy9p55gdceevp36fvdmrkxqvzfhy8ak2tgc5zgtjtra9xlaz97z965uqqtqcqqqqqqqqqqqqqqqqq9g6la8g90sulf7x0xaf7xumatk57kf733x04s8027xkzzjk945dzykzwtqy63farfwysnkdghhmu74kxve9kur3624ethdkt60cgcjl2sqz9v7lt",
paymentType: PaymentType.Lightning,
}),
)
})

it("detects a lightning param in an onchain address 3", () => {
jest.setSystemTime(1701750135000)

const address = btcpayServerQR
const paymentDestination = parsePaymentDestination({
destination: address,
network: "mainnet",
lnAddressDomains: [],
})

expect(paymentDestination).toEqual(
expect.objectContaining({
valid: true,
paymentRequest:
"lnbc258850n1pjkuc3ksp5dkxudy39z3pag74fge6lgcwa6uhan084muu3srplxml4ny8fslpspp58jj3q9a6c9eq2ctprcungf5rnpq6a9trclsd6gfsq0jrwzwax3pqdrq2pskjepqw3hjq5rpwekx2mn90qsygmmwv96xjmmwyq5y7unyv4ezqj2y8gs9v5rtxvcny7pkvev8q6262dpn2djpd5unxjefxqr3j0cqpjrzjqttftvqu0f5sneckep3lkwdut7mmhhpcyjmlmnjn4hze8ed7pq88xrrtwyqq9usqqyqqqzndqqqqqxcq2q9qxpqysgqkywungz5tnrdxklrhw968gmw3tuectssu4sth3mrwljdueg9jnvrnfymjjzn8j5gaweurcfrw6477mztvzqaaqx5678np8tpgpm6epcq9nwagg",
paymentType: PaymentType.Lightning,
}),
)
})

it("fallbacks to on chain address if the lightning param has expired", () => {
const address = `bitcoin:${bech32}?amount=0.00001&label=sbddesign%3A%20For%20lunch%20Tuesday&message=For%20lunch%20Tuesday&lightning=${expiredLNInvoice}`
const paymentDestination = parsePaymentDestination({
Expand All @@ -487,6 +531,8 @@ describe("parsePaymentDestination Lightning", () => {
})

it("validates an opennode invoice", () => {
jest.setSystemTime(1598110996000) // Aug 22 2020 10:43

const paymentDestination = parsePaymentDestination({
destination: lnInvoice,
network: "mainnet",
Expand Down Expand Up @@ -518,6 +564,8 @@ describe("parsePaymentDestination Lightning", () => {
})

it("validates a lightning invoice with prefix", () => {
jest.setSystemTime(1598110996000) // Aug 22 2020 10:43

const address = `LIGHTNING:${lnInvoice}`

const paymentDestination = parsePaymentDestination({
Expand Down
34 changes: 22 additions & 12 deletions src/parsing/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -238,11 +238,24 @@ const getLNParam = (data: string): string | null => {
const getProtocolAndData = (
destination: string,
): { protocol: string; destinationWithoutProtocol: string } => {
// input might start with 'lightning:', 'bitcoin:'
const split = destination.split(":")
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this was the culprit

const protocol = split[1] ? split[0].toLocaleLowerCase() : ""
const destinationWithoutProtocol = split[1] ?? split[0]
return { protocol, destinationWithoutProtocol }
if (destination.toLocaleLowerCase().startsWith("lightning:")) {
return {
protocol: "lightning",
destinationWithoutProtocol: destination.slice(10),
}
}

if (destination.toLocaleLowerCase().startsWith("bitcoin:")) {
return {
protocol: "bitcoin",
destinationWithoutProtocol: destination.slice(8),
}
}

return {
protocol: "",
destinationWithoutProtocol: destination,
}
}

const getPaymentType = ({
Expand Down Expand Up @@ -286,7 +299,7 @@ const getPaymentType = ({
return PaymentType.Onchain
}

const handle = protocol.match(/^(http|\/\/)/iu)
const handle = destinationWithoutProtocol.match(/^(http|\/\/)/iu)
? destinationWithoutProtocol.split("/")[
destinationWithoutProtocol.split("/").length - 1
]
Expand All @@ -300,25 +313,23 @@ const getPaymentType = ({
}

const getIntraLedgerPayResponse = ({
protocol,
destinationWithoutProtocol,
destination,
lnAddressDomains,
}: {
protocol: string
destinationWithoutProtocol: string
destination: string
lnAddressDomains: string[]
}): IntraledgerPaymentDestination | UnknownPaymentDestination => {
const paymentType = PaymentType.Intraledger

const handle = protocol.match(/^(http|\/\/)/iu)
const handle = destinationWithoutProtocol.match(/^(http|\/\/)/iu)
? destinationWithoutProtocol.split("/")[
destinationWithoutProtocol.split("/").length - 1
]
: destinationWithoutProtocol

if (protocol.match(/^(http|\/\/)/iu)) {
if (destinationWithoutProtocol.match(/^(http|\/\/)/iu)) {
const domain = new URL(destination).hostname
if (!lnAddressDomains.find((lnAddressDomain) => lnAddressDomain === domain)) {
return {
Expand Down Expand Up @@ -362,7 +373,6 @@ const getLNURLPayResponse = ({

if (lnAddressDomains.find((lnAddressDomain) => lnAddressDomain === domain)) {
return getIntraLedgerPayResponse({
protocol: "",
destinationWithoutProtocol: username,
lnAddressDomains,
destination,
Expand Down Expand Up @@ -412,6 +422,7 @@ const getLightningPayResponse = ({
}): LightningPaymentDestination => {
const paymentType = PaymentType.Lightning
const { destinationWithoutProtocol } = getProtocolAndData(destination)

const lnProtocol =
getLNParam(destination)?.toLowerCase() || destinationWithoutProtocol.toLowerCase()

Expand Down Expand Up @@ -566,7 +577,6 @@ export const parsePaymentDestination = ({
return getOnChainPayResponse({ destinationWithoutProtocol, network })
case PaymentType.Intraledger:
return getIntraLedgerPayResponse({
protocol,
destinationWithoutProtocol,
destination,
lnAddressDomains,
Expand Down
Loading