Skip to content
Draft
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
58 changes: 54 additions & 4 deletions integration_test/payment_request_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ import 'package:flutter_test/flutter_test.dart';

Future<void> main({bool isInitialized = false}) async {
TestWidgetsFlutterBinding.ensureInitialized();
if (!isInitialized) await Bull.init();
if (!isInitialized) {
await Bull.initLogs();
await Bull.initFlutterRustBridgeDependencies();
}

const btcAddress = 'bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq';

Expand All @@ -14,10 +17,14 @@ Future<void> main({bool isInitialized = false}) async {
'mkvv34x5ekzd3ev56nyd3hxqurzepexejxxepnxscrvwfnv9nxzcn9xq6xyefhvgcxxcmyxy'
'mnserxfq5fns';

// This decoder-compatible vector includes payment-secret metadata. Parsing
// extracts its expiry timestamp but deliberately does not enforce wall time.
const bolt11Invoice =
'lnbc2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypq'
'dq5xysxxatsyp3k7enxv4jsxqzpuaztrnwngzn3kdzw5hydlzf03qdgm2hdq27cqv3agm2aw'
'hz5se903vruatfhq77w3ls4evs3ch9zw97j25emudupq63nyw24cg27h2rspfj9srp';
'lnbc10u1p59tufasp53yuqahahgct058zglxvhezp9nyz5fvt2kn2lsl6mg9qgsts8c72s'
'pp56hym2dpcyy0878h7q5h4t30cwclp9vd0tqpn4dns0a3mmspzkh9qdqqxqyp2xqcqz95r'
'zjqg2n4jluz7ty6mn96krzje43zm7ylttjvcxcccg99tmm30s6lm4d6zzxeyqq28qqqqqqq'
'qqqqqqqq9gq2y9qyysgqpmw883kkclyxpr2u8mg9pl47909yhtt83pjvt3qz3s9puyf39v'
'dp4ar7zu47r4mfawkc2s99vm292udx9n3s5fnrjyngnsxkxn2l60qpn65s0t';

group('PaymentRequest.parse', () {
test('BIP21 with LNURL lightning param, label, and message', () async {
Expand All @@ -43,6 +50,49 @@ Future<void> main({bool isInitialized = false}) async {
expect(bip21.network, Network.bitcoinMainnet);
});

test('Lightning URI with uppercase LNURL', () async {
final result = await PaymentRequest.parse(
'lightning:${lnurlStr.toUpperCase()}',
);

expect(result, isA<LnAddressPaymentRequest>());
expect(
(result as LnAddressPaymentRequest).address,
lnurlStr.toUpperCase(),
);
});

test('Uppercase Lightning URI with uppercase LNURL', () async {
final result = await PaymentRequest.parse(
'LIGHTNING:${lnurlStr.toUpperCase()}',
);

expect(result, isA<LnAddressPaymentRequest>());
expect(
(result as LnAddressPaymentRequest).address,
lnurlStr.toUpperCase(),
);
});

test('Lightning URI with Lightning Address', () async {
final result = await PaymentRequest.parse(
'lightning:ishi@walletofsatoshi.com',
);

expect(result, isA<LnAddressPaymentRequest>());
expect(
(result as LnAddressPaymentRequest).address,
'ishi@walletofsatoshi.com',
);
});

test('Lightning URI with Bolt11 invoice', () async {
final result = await PaymentRequest.parse('lightning:$bolt11Invoice');

expect(result, isA<Bolt11PaymentRequest>());
expect((result as Bolt11PaymentRequest).invoice, bolt11Invoice);
});

test(
'HTTPS URL with percent-encoded LNAddress in lightning param',
() async {
Expand Down
50 changes: 19 additions & 31 deletions lib/core/utils/payment_request.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import 'package:bip21_uri/bip21_uri.dart';
import 'package:bull_sdk/boltz.dart' as boltz;
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:bull_sdk/lwk.dart' as lwk;
import 'package:satoshifier/satoshifier.dart' as satoshifier;

part 'payment_request.freezed.dart';

Expand Down Expand Up @@ -78,6 +79,18 @@ sealed class PaymentRequest with _$PaymentRequest {
if (result != null) return result;
}

if (trimmed.toLowerCase().startsWith('lightning:')) {
final lightningPayload = trimmed.substring('lightning:'.length);
if (lightningPayload.toLowerCase().startsWith('lnurl') ||
lightningPayload.contains('@')) {
final lnurl = await _tryParseLnAddress(trimmed);
if (lnurl != null) return lnurl;
} else {
final bolt11 = await _tryParseBolt11(lightningPayload.toLowerCase());
if (bolt11 != null) return bolt11;
}
}

final re = RegExp(r'lnurl[0-9a-z]+', caseSensitive: false);
final m = re.firstMatch(trimmed);

Expand All @@ -98,24 +111,9 @@ sealed class PaymentRequest with _$PaymentRequest {
}

if (trimmed.toLowerCase().startsWith('lnbc') ||
trimmed.toLowerCase().startsWith('lntb') ||
trimmed.toLowerCase().startsWith('lightning:')) {
if (trimmed.toLowerCase().startsWith('lightning:')) {
final withoutPrefix = trimmed
.replaceAll("lightning:", "")
.replaceAll("LIGHTNING:", "");
if (withoutPrefix.toLowerCase().startsWith('lnurl') ||
withoutPrefix.contains('@')) {
final result = await _tryParseLnAddress(withoutPrefix);
if (result != null) return result;
} else {
final result = await _tryParseBolt11(withoutPrefix.toLowerCase());
if (result != null) return result;
}
} else {
final result = await _tryParseBolt11(trimmed.toLowerCase());
if (result != null) return result;
}
trimmed.toLowerCase().startsWith('lntb')) {
final result = await _tryParseBolt11(trimmed.toLowerCase());
if (result != null) return result;
}

if (trimmed.toLowerCase().startsWith('lnurl') || trimmed.contains('@')) {
Expand Down Expand Up @@ -300,21 +298,11 @@ sealed class PaymentRequest with _$PaymentRequest {
}

static Future<PaymentRequest?> _tryParseLnAddress(String data) async {
final bool isEmailStyle = data.contains('@');
final bool isLnurlPrefix = data.toLowerCase().startsWith('lnurl');

if (!isEmailStyle && !isLnurlPrefix) {
return null;
}

try {
final lnurl = boltz.Lnurl(value: data);
final valid = await lnurl.validate();
if (!valid) {
throw 'Invalid lnurl';
final result = await satoshifier.LnurlParser.parse(data);
if (result case satoshifier.Lnurl(address: final address)) {
return PaymentRequest.lnAddress(address: address);
}

return PaymentRequest.lnAddress(address: data);
} catch (e) {
log.warning(e.toString());
}
Expand Down
20 changes: 10 additions & 10 deletions pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -158,8 +158,8 @@ packages:
dependency: "direct main"
description:
path: "packages/bitbox-transport"
ref: "3dcd008d1ca5233a65f2f354c03b3975e2ab33f7"
resolved-ref: "3dcd008d1ca5233a65f2f354c03b3975e2ab33f7"
ref: "2140170ee109c96c56e944c7fec1997216d9fc30"
resolved-ref: "2140170ee109c96c56e944c7fec1997216d9fc30"
url: "https://github.com/SatoshiPortal/bull_sdk"
source: git
version: "0.1.0"
Expand Down Expand Up @@ -216,8 +216,8 @@ packages:
dependency: "direct main"
description:
path: "packages/boltz-stream"
ref: "3dcd008d1ca5233a65f2f354c03b3975e2ab33f7"
resolved-ref: "3dcd008d1ca5233a65f2f354c03b3975e2ab33f7"
ref: "2140170ee109c96c56e944c7fec1997216d9fc30"
resolved-ref: "2140170ee109c96c56e944c7fec1997216d9fc30"
url: "https://github.com/SatoshiPortal/bull_sdk"
source: git
version: "0.1.0"
Expand Down Expand Up @@ -305,8 +305,8 @@ packages:
dependency: "direct main"
description:
path: "packages/bull_sdk"
ref: "3dcd008d1ca5233a65f2f354c03b3975e2ab33f7"
resolved-ref: "3dcd008d1ca5233a65f2f354c03b3975e2ab33f7"
ref: "2140170ee109c96c56e944c7fec1997216d9fc30"
resolved-ref: "2140170ee109c96c56e944c7fec1997216d9fc30"
url: "https://github.com/SatoshiPortal/bull_sdk"
source: git
version: "1.0.0+1"
Expand Down Expand Up @@ -1746,8 +1746,8 @@ packages:
dependency: transitive
description:
path: "packages/bull_sdk/rust_builder"
ref: "3dcd008d1ca5233a65f2f354c03b3975e2ab33f7"
resolved-ref: "3dcd008d1ca5233a65f2f354c03b3975e2ab33f7"
ref: "2140170ee109c96c56e944c7fec1997216d9fc30"
resolved-ref: "2140170ee109c96c56e944c7fec1997216d9fc30"
url: "https://github.com/SatoshiPortal/bull_sdk"
source: git
version: "0.0.1"
Expand All @@ -1763,8 +1763,8 @@ packages:
dependency: "direct main"
description:
path: "packages/satoshifier"
ref: "3dcd008d1ca5233a65f2f354c03b3975e2ab33f7"
resolved-ref: "3dcd008d1ca5233a65f2f354c03b3975e2ab33f7"
ref: "2140170ee109c96c56e944c7fec1997216d9fc30"
resolved-ref: "2140170ee109c96c56e944c7fec1997216d9fc30"
url: "https://github.com/SatoshiPortal/bull_sdk"
source: git
version: "0.0.1"
Expand Down
8 changes: 4 additions & 4 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -47,17 +47,17 @@ dependencies:
bull_sdk:
git:
url: https://github.com/SatoshiPortal/bull_sdk
ref: 3dcd008d1ca5233a65f2f354c03b3975e2ab33f7
ref: 2140170ee109c96c56e944c7fec1997216d9fc30
path: packages/bull_sdk
boltz_stream:
git:
url: https://github.com/SatoshiPortal/bull_sdk
ref: 3dcd008d1ca5233a65f2f354c03b3975e2ab33f7
ref: 2140170ee109c96c56e944c7fec1997216d9fc30
path: packages/boltz-stream
bitbox_transport:
git:
url: https://github.com/SatoshiPortal/bull_sdk
ref: 3dcd008d1ca5233a65f2f354c03b3975e2ab33f7
ref: 2140170ee109c96c56e944c7fec1997216d9fc30
path: packages/bitbox-transport
universal_ble: ^1.2.0
hex: ^0.2.0
Expand Down Expand Up @@ -112,7 +112,7 @@ dependencies:
satoshifier:
git:
url: https://github.com/SatoshiPortal/bull_sdk
ref: 3dcd008d1ca5233a65f2f354c03b3975e2ab33f7
ref: 2140170ee109c96c56e944c7fec1997216d9fc30
path: packages/satoshifier
flutter_nfc_kit: ^3.6.1
ndef: ^0.4.0
Expand Down
Loading