public async Task OutBoundChannelOpenRoundtrip(Clients clients, ILightningClient remoteClient)
        {
            var walletInfo = await clients.NRustLightningHttpClient.GetWalletInfoAsync();

            var info = await remoteClient.GetInfo();

            Assert.Single(info.NodeInfoList);
            var theirNodeKey = info.NodeInfoList.FirstOrDefault()?.NodeId;

            Assert.NotNull(theirNodeKey);
            await clients.NRustLightningHttpClient.OpenChannelAsync(new Infrastructure.Models.Request.OpenChannelRequest
            {
                TheirNetworkKey = theirNodeKey, ChannelValueSatoshis = 1000000, PushMSat = 100000
            });

            // wait until nbx detects unconfirmed funding tx.
            await Support.Utils.Retry(30, TimeSpan.FromSeconds(1.5), async() =>
            {
                var e = (await clients.NBXClient.GetTransactionsAsync(walletInfo.DerivationStrategy));
                return(e.UnconfirmedTransactions.Transactions.Count > 0);
            });

            var addr = await clients.NRustLightningHttpClient.GetNewDepositAddressAsync();

            await clients.BitcoinRPCClient.GenerateToAddressAsync(10, addr.Address);

            var localId = (await clients.NRustLightningHttpClient.GetInfoAsync()).ConnectionString.NodeId;
            await Support.Utils.Retry(20, TimeSpan.FromSeconds(1.2), async() =>
            {
                var maybeLocalChannel = (await clients.NRustLightningHttpClient.GetChannelDetailsAsync()).Details.FirstOrDefault(cd => cd.RemoteNetworkId.Equals(theirNodeKey) && cd.IsLive);
                if (maybeLocalChannel is null)
                {
                    return(false);
                }
                // CLightning client throws null reference exception when it is used as ILightningClient.
                // So we do dirty fallback here.
                if (remoteClient is CLightningClient cli)
                {
                    var maybeRemoteChannel = (await cli.ListChannelsAsync()).Where(c => c.Active).FirstOrDefault(c => new PubKey(c.Destination).Equals(localId));
                    return(maybeRemoteChannel != null);
                }
                else
                {
                    var maybeRemoteChannel = (await remoteClient.ListChannels()).Where(c => c.IsActive).FirstOrDefault(c => c.RemoteNode.Equals(localId));
                    return(maybeRemoteChannel != null);
                }
            }, "Failed to create outbound channel");
Beispiel #2
0
        public static async Task CreateChannel(RPCClient cashCow, ILightningClient sender, ILightningClient dest)
        {
            var destInfo = await dest.GetInfo();

            var destInvoice = await dest.CreateInvoice(1000, "EnsureConnectedToDestination", TimeSpan.FromSeconds(5000));

            while (true)
            {
                int payErrors = 0;
                var result    = await Pay(sender, destInvoice.BOLT11);

                Logs.LogInformation($"Pay Result: {result.Result} {result.ErrorDetail}");
                if (result.Result == PayResult.Ok)
                {
                    break;
                }
                else if (result.Result == PayResult.CouldNotFindRoute)
                {
                    // check channels that are in process of opening, to prevent double channel open
                    await Task.Delay(100);

                    var pendingChannels = await sender.ListChannels();

                    if (pendingChannels.Any(a => a.RemoteNode == destInfo.NodeInfoList[0].NodeId))
                    {
                        Logs.LogInformation($"Channel to {destInfo.NodeInfoList[0]} is already open(ing)");

                        Logs.LogInformation($"Attempting to reconnect Result: {await sender.ConnectTo(destInfo.NodeInfoList.First())}");

                        await cashCow.GenerateAsync(1);
                        await WaitLNSynched(cashCow, sender);
                        await WaitLNSynched(cashCow, dest);

                        continue;
                    }

                    Logs.LogInformation($"Opening channel to {destInfo.NodeInfoList[0]}");
                    var openChannel = await sender.OpenChannel(new OpenChannelRequest()
                    {
                        NodeInfo      = destInfo.NodeInfoList[0],
                        ChannelAmount = Money.Satoshis(16777215),
                        FeeRate       = new FeeRate(1UL, 1)
                    });

                    Logs.LogInformation($"Channel opening result: {openChannel.Result}");
                    if (openChannel.Result == OpenChannelResult.CannotAffordFunding)
                    {
                        var address = await sender.GetDepositAddress();

                        try
                        {
                            await cashCow.SendToAddressAsync(address, Money.Coins(1.0m), new SendToAddressParameters()
                            {
                                Replaceable = true
                            });
                        }
                        catch (RPCException ex) when(ex.RPCCode == RPCErrorCode.RPC_WALLET_INSUFFICIENT_FUNDS || ex.RPCCode == RPCErrorCode.RPC_WALLET_ERROR)
                        {
                            await cashCow.GenerateAsync(1);

                            await cashCow.SendToAddressAsync(address, Money.Coins(1.0m), new SendToAddressParameters()
                            {
                                Replaceable = true
                            });
                        }
                        await cashCow.GenerateAsync(10);
                        await WaitLNSynched(cashCow, sender);
                        await WaitLNSynched(cashCow, dest);
                    }
                    if (openChannel.Result == OpenChannelResult.PeerNotConnected)
                    {
                        await sender.ConnectTo(destInfo.NodeInfoList[0]);
                    }
                    if (openChannel.Result == OpenChannelResult.NeedMoreConf)
                    {
                        await cashCow.GenerateAsync(6);
                        await WaitLNSynched(cashCow, sender);
                        await WaitLNSynched(cashCow, dest);
                    }
                    if (openChannel.Result == OpenChannelResult.AlreadyExists)
                    {
                        await Task.Delay(1000);
                    }
                    if (openChannel.Result == OpenChannelResult.Ok)
                    {
                        // generate one block and a bit more time to confirm channel opening
                        await cashCow.GenerateAsync(1);
                        await WaitLNSynched(cashCow, sender);
                        await WaitLNSynched(cashCow, dest);

                        await Task.Delay(500);
                    }
                }
                else
                {
                    if (payErrors++ > 10)
                    {
                        throw new Exception($"Couldn't establish payment channel after {payErrors} repeated tries");
                    }

                    await Task.Delay(1000);
                }
            }
        }