Flutter未知功能插件ldk_node的潜在使用
Flutter未知功能插件ldk_node的潜在使用
LDK_Node 简介
LDK_Node 是一个基于 Lightning Development Kit (LDK) 和 Bitcoin Dev Kit (BDK) 构建的Flutter库,旨在提供一个易于使用的、非托管的Lightning节点。它支持通过Esplora服务器获取链上数据、文件系统持久化、闪电网络中的八卦传播以及可配置的熵源。
使用 ldk_node 的步骤
-
添加依赖: 在
pubspec.yaml
文件中添加ldk_node
依赖:dependencies: ldk_node: ^0.3.0
或者使用
flutter pub add
命令:flutter pub add ldk_node
-
初始化和启动节点: 下面是一个完整的示例代码,展示了如何创建、启动和同步本地节点,并执行一些基本操作,如打开通道、发送和接收支付等。
完整示例 Demo
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:ldk_node/ldk_node.dart' as ldk;
import 'package:path_provider/path_provider.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
[@override](/user/override)
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
late ldk.Node aliceNode;
late ldk.Node bobNode;
ldk.PublicKey? aliceNodeId;
ldk.PublicKey? bobNodeId;
int aliceBalance = 0;
String displayText = "";
ldk.SocketAddress? bobAddr;
ldk.Bolt11Invoice? invoice;
ldk.UserChannelId? userChannelId;
String esploraUrl = "https://mutinynet.ltbl.io/api";
[@override](/user/override)
void initState() {
initAliceNode();
super.initState();
}
Future<ldk.Builder> createBuilder(
String path, ldk.SocketAddress address, String mnemonic) async {
final directory = await getApplicationDocumentsDirectory();
final nodeStorageDir = "${directory.path}/ldk_cache/$path";
print(nodeStorageDir);
// For a node on the mutiny network with default config and service
ldk.Builder builder = ldk.Builder.mutinynet()
.setEntropyBip39Mnemonic(mnemonic: ldk.Mnemonic(seedPhrase: mnemonic))
.setEsploraServer(esploraUrl)
.setStorageDirPath(nodeStorageDir)
.setListeningAddresses([address]);
return builder;
}
Future initAliceNode() async {
aliceNode = await (await createBuilder(
'alice_mutinynet',
ldk.SocketAddress.hostname(addr: "0.0.0.0", port: 3003),
"cart super leaf clinic pistol plug replace close super tooth wealth usage"))
.build();
await startNode(aliceNode);
final res = await aliceNode.nodeId();
setState(() {
aliceNodeId = res;
displayText = "${aliceNodeId?.hex} started successfully";
});
}
initBobNode() async {
bobNode = await (await createBuilder(
'bob_mutinynet',
ldk.SocketAddress.hostname(addr: "0.0.0.0", port: 3004),
"puppy interest whip tonight dad never sudden response push zone pig patch"))
.build();
await startNode(bobNode);
final res = await bobNode.nodeId();
setState(() {
bobNodeId = res;
displayText = "${bobNodeId!.hex} started successfully";
});
}
startNode(ldk.Node node) async {
try {
node.start();
} on ldk.NodeException catch (e) {
print(e.toString());
}
}
totalOnchainBalanceSats() async {
final alice = await aliceNode.listBalances();
final bob = await bobNode.listBalances();
if (kDebugMode) {
print("alice's balance: ${alice.totalOnchainBalanceSats}");
print("alice's spendable balance: ${alice.spendableOnchainBalanceSats}");
print("bob's balance: ${bob.totalOnchainBalanceSats}");
print("bob's spendable balance: ${bob.spendableOnchainBalanceSats}");
}
setState(() {
aliceBalance = alice.spendableOnchainBalanceSats.toInt();
});
}
syncWallets() async {
await aliceNode.syncWallets();
await bobNode.syncWallets();
setState(() {
displayText = "aliceNode & bobNode: Sync Completed";
});
}
listChannels() async {
final res = await aliceNode.listChannels();
if (kDebugMode) {
if (res.isNotEmpty) {
print("======Channels========");
for (var e in res) {
print("nodeId: ${aliceNodeId!.hex}");
print("userChannelId: ${e.userChannelId.data}");
print("confirmations required: ${e.confirmationsRequired}");
print("isChannelReady: ${e.isChannelReady}");
print("isUsable: ${e.isUsable}");
print("outboundCapacityMsat: ${e.outboundCapacityMsat}");
}
}
}
}
listPaymentsWithFilter(bool printPayments) async {
final res = await aliceNode.listPaymentsWithFilter(
paymentDirection: ldk.PaymentDirection.outbound);
if (res.isNotEmpty) {
if (printPayments) {
if (kDebugMode) {
print("======Payments========");
for (var e in res) {
print("amountMsat: ${e.amountMsat}");
print("paymentId: ${e.id.field0}");
print("status: ${e.status.name}");
}
}
}
return res.last;
} else {
return null;
}
}
removeLastPayment() async {
final lastPayment = await listPaymentsWithFilter(false);
if (lastPayment != null) {
final _ = await aliceNode.removePayment(paymentId: lastPayment.id);
setState(() {
displayText = "${lastPayment.hash.internal} removed";
});
}
}
Future<List<String>> newOnchainAddress() async {
final alice = await (await aliceNode.onChainPayment()).newAddress();
final bob = await (await bobNode.onChainPayment()).newAddress();
if (kDebugMode) {
print("alice's address: ${alice.s}");
print("bob's address: ${bob.s}");
}
setState(() {
displayText = alice.s;
});
return [alice.s, bob.s];
}
listeningAddress() async {
final alice = await aliceNode.listeningAddresses();
final bob = await bobNode.listeningAddresses();
setState(() {
bobAddr = bob!.first;
});
if (kDebugMode) {
print("alice's listeningAddress : ${alice!.first.toString()}");
print("bob's listeningAddress: ${bob!.first.toString()}");
}
}
closeChannel() async {
await aliceNode.closeChannel(
userChannelId: userChannelId!,
counterpartyNodeId: ldk.PublicKey(
hex:
'02465ed5be53d04fde66c9418ff14a5f2267723810176c9212b722e542dc1afb1b',
));
}
connectOpenChannel() async {
final funding_amount_sat = 80000;
final push_msat = (funding_amount_sat / 2) * 1000;
userChannelId = await aliceNode.connectOpenChannel(
channelAmountSats: BigInt.from(funding_amount_sat),
announceChannel: true,
socketAddress: ldk.SocketAddress.hostname(
addr: '45.79.52.207',
port: 9735,
),
pushToCounterpartyMsat: BigInt.from(push_msat),
nodeId: ldk.PublicKey(
hex:
'02465ed5be53d04fde66c9418ff14a5f2267723810176c9212b722e542dc1afb1b',
));
}
receiveAndSendPayments() async {
final bobBolt11Handler = await bobNode.bolt11Payment();
final aliceBolt11Handler = await aliceNode.bolt11Payment();
// Bob doesn't have a channel yet, so he can't receive normal payments,
// but he can receive payments via JIT channels through an LSP configured
// in its node.
invoice = await bobBolt11Handler.receiveViaJitChannel(
amountMsat: BigInt.from(25000 * 1000),
description: 'asdf',
expirySecs: 9217);
print(invoice!.signedRawInvoice);
setState(() {
displayText = invoice!.signedRawInvoice;
});
final paymentId = await aliceBolt11Handler.send(invoice: invoice!);
final res = await aliceNode.payment(paymentId: paymentId);
setState(() {
displayText =
"Payment status: ${res?.status.name}\n PaymentId: ${res?.id.field0}";
});
}
stop() async {
await bobNode.stop();
await aliceNode.stop();
}
Future handleEvent(ldk.Node node) async {
final res = await node.nextEvent();
res?.map(paymentSuccessful: (e) {
if (kDebugMode) {
print("paymentSuccessful: ${e.paymentHash.data}");
}
}, paymentFailed: (e) {
if (kDebugMode) {
print("paymentFailed: ${e.paymentHash.data}");
}
}, paymentReceived: (e) {
if (kDebugMode) {
print("paymentReceived: ${e.paymentHash.data}");
}
}, channelReady: (e) {
if (kDebugMode) {
print(
"channelReady: ${e.channelId.data}, userChannelId: ${e.userChannelId.data}");
}
}, channelClosed: (e) {
if (kDebugMode) {
print(
"channelClosed: ${e.channelId.data}, userChannelId: ${e.userChannelId.data}");
}
}, channelPending: (e) {
if (kDebugMode) {
print(
"channelPending: ${e.channelId.data}, userChannelId: ${e.userChannelId.data}");
}
}, paymentClaimable: (e) {
if (kDebugMode) {
print(
"paymentId: ${e.paymentId.field0.toString()}, claimableAmountMsat: ${e.claimableAmountMsat}, userChannelId: ${e.claimDeadline}");
}
});
await node.eventHandled();
}
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
appBar: PreferredSize(
preferredSize: const Size(double.infinity, kToolbarHeight * 1.8),
child: Container(
padding: const EdgeInsets.only(right: 20, left: 20, top: 40),
color: Colors.blue,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Node',
style: GoogleFonts.montserrat(
fontWeight: FontWeight.w900,
fontSize: 16,
height: 2.5,
color: Colors.white)),
const SizedBox(
height: 5,
),
Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("Response:",
textAlign: TextAlign.center,
style: GoogleFonts.nunito(
color: Colors.white,
fontSize: 12,
fontWeight: FontWeight.w700)),
const SizedBox(width: 5),
Expanded(
child: Text(
displayText,
maxLines: 2,
textAlign: TextAlign.start,
style: GoogleFonts.nunito(
color: Colors.white,
fontSize: 12,
fontWeight: FontWeight.w700),
),
),
],
),
]),
),
),
body: SingleChildScrollView(
child: Container(
padding: const EdgeInsets.only(top: 40, left: 10, right: 10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
aliceBalance.toString(),
style: GoogleFonts.montserrat(
fontWeight: FontWeight.w900,
fontSize: 40,
color: Colors.blue),
),
Text(
" sats",
style: GoogleFonts.montserrat(
fontWeight: FontWeight.w900,
fontSize: 20,
color: Colors.blue),
),
],
),
TextButton(
onPressed: () async {
await initBobNode();
},
child: Text(
"Initialize Bob's node",
style: GoogleFonts.nunito(
color: Colors.indigoAccent,
fontSize: 12,
height: 1.5,
fontWeight: FontWeight.w800),
)),
TextButton(
onPressed: () async {
await syncWallets();
},
child: Text(
"Sync Alice's & Bob's node",
style: GoogleFonts.nunito(
color: Colors.indigoAccent,
fontSize: 12,
height: 1.5,
fontWeight: FontWeight.w800),
)),
TextButton(
onPressed: () async {
await listChannels();
},
child: Text(
'List Channels',
overflow: TextOverflow.clip,
textAlign: TextAlign.center,
style: GoogleFonts.nunito(
color: Colors.indigoAccent,
fontSize: 12,
height: 1.5,
fontWeight: FontWeight.w800),
)),
TextButton(
onPressed: () async {
await totalOnchainBalanceSats();
},
child: Text(
'Get total Onchain BalanceSats',
overflow: TextOverflow.clip,
textAlign: TextAlign.center,
style: GoogleFonts.nunito(
color: Colors.indigoAccent,
fontSize: 12,
height: 1.5,
fontWeight: FontWeight.w800),
)),
TextButton(
onPressed: () async {
await newOnchainAddress();
},
child: Text(
'Get new Onchain Address for Alice and Bob',
style: GoogleFonts.nunito(
color: Colors.indigoAccent,
fontSize: 12,
height: 1.5,
fontWeight: FontWeight.w800),
)),
TextButton(
onPressed: () async {
await listeningAddress();
},
child: Text(
'Get node listening addresses',
style: GoogleFonts.nunito(
color: Colors.indigoAccent,
fontSize: 12,
height: 1.5,
fontWeight: FontWeight.w800),
)),
TextButton(
onPressed: () async {
await connectOpenChannel();
},
child: Text(
'Connect Open Channel',
style: GoogleFonts.nunito(
color: Colors.indigoAccent,
fontSize: 12,
height: 1.5,
fontWeight: FontWeight.w800),
)),
TextButton(
onPressed: () async {
await handleEvent(aliceNode);
await handleEvent(bobNode);
},
child: Text('Handle event',
style: GoogleFonts.nunito(
color: Colors.indigoAccent,
fontSize: 12,
height: 1.5,
fontWeight: FontWeight.w800))),
TextButton(
onPressed: () async {
await receiveAndSendPayments();
},
child: Text(
'Send & Receive Invoice Payment',
textAlign: TextAlign.center,
style: GoogleFonts.nunito(
color: Colors.indigoAccent,
fontSize: 12,
height: 1.5,
fontWeight: FontWeight.w800),
)),
TextButton(
onPressed: () async {
await listPaymentsWithFilter(true);
},
child: Text(
'List Payments',
textAlign: TextAlign.center,
style: GoogleFonts.nunito(
color: Colors.indigoAccent,
fontSize: 12,
height: 1.5,
fontWeight: FontWeight.w800),
)),
TextButton(
onPressed: () async {
await removeLastPayment();
},
child: Text(
'Remove the last payment',
textAlign: TextAlign.center,
style: GoogleFonts.nunito(
color: Colors.indigoAccent,
fontSize: 12,
height: 1.5,
fontWeight: FontWeight.w800),
)),
TextButton(
onPressed: () async {
await closeChannel();
},
child: Text(
'Close channel',
textAlign: TextAlign.center,
style: GoogleFonts.nunito(
color: Colors.indigoAccent,
fontSize: 12,
height: 1.5,
fontWeight: FontWeight.w800),
)),
TextButton(
onPressed: () async {
await stop();
},
child: Text(
'Stop nodes',
textAlign: TextAlign.center,
style: GoogleFonts.nunito(
color: Colors.indigoAccent,
fontSize: 12,
height: 1.5,
fontWeight: FontWeight.w800),
)),
const SizedBox(height: 25),
Text(
aliceNodeId == null
? "Node not initialized"
: "@Id_:${aliceNodeId!.hex}",
maxLines: 1,
overflow: TextOverflow.ellipsis,
textAlign: TextAlign.center,
style: GoogleFonts.nunito(
color: Colors.black.withOpacity(.3),
fontSize: 12,
height: 2,
fontWeight: FontWeight.w700),
),
const SizedBox(
height: 10,
),
],
),
),
),
));
}
}
更多关于Flutter未知功能插件ldk_node的潜在使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter未知功能插件ldk_node的潜在使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
在探讨Flutter中未知功能插件ldk_node
的潜在使用时,由于ldk_node
并非一个广为人知的官方或广泛使用的插件,我们首先需要明确一点:对于非标准或未广泛记录的插件,使用时应格外小心,确保了解其来源、安全性和功能实现。
不过,基于一般插件开发的原则和假设ldk_node
提供了某些特定功能(如与区块链节点交互、数据处理等),以下是一个假设性的代码示例,展示了如何在Flutter项目中集成和使用一个自定义插件(这里以ldk_node
为代称)。请注意,这仅是一个示例,实际使用时应根据ldk_node
插件的具体文档和API进行调整。
1. 添加插件依赖
首先,假设ldk_node
插件已经发布到pub.dev或其他Flutter插件仓库,你可以在pubspec.yaml
文件中添加依赖:
dependencies:
flutter:
sdk: flutter
ldk_node: ^x.y.z # 替换为实际版本号
然后运行flutter pub get
来安装插件。
2. 导入插件并使用其功能
在Flutter项目的Dart文件中导入ldk_node
插件,并调用其提供的方法。以下是一个假设性的示例,展示了如何初始化插件并调用其某个功能(例如,获取区块链信息):
import 'package:flutter/material.dart';
import 'package:ldk_node/ldk_node.dart'; // 假设插件提供了这样的导入路径
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
String? blockchainInfo;
@override
void initState() {
super.initState();
// 假设ldk_node插件有一个名为initialize的方法用于初始化
LdkNode.initialize().then((_) {
// 调用插件的某个功能,例如获取区块链信息
LdkNode.getBlockchainInfo().then((info) {
setState(() {
blockchainInfo = info;
});
}).catchError((error) {
print('Error fetching blockchain info: $error');
});
}).catchError((error) {
print('Error initializing ldk_node: $error');
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('LDK Node Example'),
),
body: Center(
child: Text(blockchainInfo ?? 'Loading blockchain info...'),
),
),
);
}
}
注意事项
- 安全性:在使用任何第三方插件时,务必确认其来源可靠,并检查其代码以确保没有恶意行为。
- 文档:查阅
ldk_node
插件的官方文档,了解其API和功能。 - 错误处理:在实际应用中,应添加更完善的错误处理逻辑,以应对可能的异常情况。
- 平台特定实现:如果
ldk_node
是一个包含原生代码(如Android和iOS)的插件,确保在不同平台上进行了充分的测试。
由于ldk_node
的具体实现和功能未知,上述代码仅作为集成和使用自定义Flutter插件的一般指导。在实际项目中,应根据ldk_node
插件的实际文档和API进行调整。