Flutter未知功能插件ldk_node的潜在使用

发布于 1周前 作者 phonegap100 来自 Flutter

Flutter未知功能插件ldk_node的潜在使用

LDK_Node 简介

LDK_Node 是一个基于 Lightning Development Kit (LDK)Bitcoin Dev Kit (BDK) 构建的Flutter库,旨在提供一个易于使用的、非托管的Lightning节点。它支持通过Esplora服务器获取链上数据、文件系统持久化、闪电网络中的八卦传播以及可配置的熵源。

使用 ldk_node 的步骤

  1. 添加依赖: 在 pubspec.yaml 文件中添加 ldk_node 依赖:

    dependencies:
      ldk_node: ^0.3.0
    

    或者使用 flutter pub add 命令:

    flutter pub add ldk_node
    
  2. 初始化和启动节点: 下面是一个完整的示例代码,展示了如何创建、启动和同步本地节点,并执行一些基本操作,如打开通道、发送和接收支付等。

完整示例 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

1 回复

更多关于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...'),
        ),
      ),
    );
  }
}

注意事项

  1. 安全性:在使用任何第三方插件时,务必确认其来源可靠,并检查其代码以确保没有恶意行为。
  2. 文档:查阅ldk_node插件的官方文档,了解其API和功能。
  3. 错误处理:在实际应用中,应添加更完善的错误处理逻辑,以应对可能的异常情况。
  4. 平台特定实现:如果ldk_node是一个包含原生代码(如Android和iOS)的插件,确保在不同平台上进行了充分的测试。

由于ldk_node的具体实现和功能未知,上述代码仅作为集成和使用自定义Flutter插件的一般指导。在实际项目中,应根据ldk_node插件的实际文档和API进行调整。

回到顶部