Example #1
0
        private async Task WaitServersAreUp(string name, ILightningClient client)
        {
            var rpc = Tester.CreateRPC();
            await rpc.ScanRPCCapabilitiesAsync();

            await rpc.GenerateAsync(1);

            Exception realException = null;

            using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(Timeout - 5)))
            {
retry:
                try
                {
                    if (realException != null)
                    {
                        await Task.Delay(1000, cts.Token);
                    }
                    await client.GetInfo(cts.Token);

                    Logs.Tester.LogInformation($"{name}: Server is up");
                }
                catch (Exception ex) when(!cts.IsCancellationRequested)
                {
                    realException = ex;
                    goto retry;
                }
                catch (Exception)
                {
                    Logs.Tester.LogInformation(realException.ToString());
                    Assert.False(true, $"{name}: The server could not be started");
                }
            }
        }
        public async Task CanCreateInvoiceUsingConnectionString()
        {
            ILightningClientFactory factory = new LightningClientFactory(Tester.Network);

            var connectionStrings = Docker
                ? new[]
            {
                "type=charge;server=http://api-token:foiewnccewuify@charge:9112",
                "type=lnd-rest;server=https://lnd_dest:8080;allowinsecure=true",
                "type=clightning;server=tcp://lightningd:9835",
                "type=eclair;server=http://eclair:8080;password=bukkake;bitcoin-host=bitcoind:43782;bitcoin-auth=ceiwHEbqWI83:DwubwWsoo3"
            }
                : new[]
            {
                "type=charge;server=http://api-token:[email protected]:37462",
                "type=lnd-rest;server=https://127.0.0.1:42802;allowinsecure=true",
                "type=clightning;server=tcp://127.0.0.1:48532",
                "type=eclair;server=http://127.0.0.1:4570;password=bukkake;bitcoin-host=127.0.0.1:37393;bitcoin-auth=ceiwHEbqWI83:DwubwWsoo3"
            };

            foreach (var connectionString in connectionStrings)
            {
                ILightningClient client = factory.Create(connectionString);
                var createdInvoice      = await client.CreateInvoice(10000, "CanCreateInvoice", TimeSpan.FromMinutes(5));

                var retrievedInvoice = await client.GetInvoice(createdInvoice.Id);

                AssertUnpaid(createdInvoice);
                AssertUnpaid(retrievedInvoice);
            }
        }
        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)
            {
                var result = await sender.Pay(destInvoice.BOLT11);

                if (result.Result == PayResult.CouldNotFindRoute)
                {
                    var openChannel = await sender.OpenChannel(new OpenChannelRequest()
                    {
                        NodeInfo      = destInfo.NodeInfoList[0],
                        ChannelAmount = Money.Satoshis(16777215),
                        FeeRate       = new FeeRate(1UL, 1)
                    });

                    if (openChannel.Result == OpenChannelResult.CannotAffordFunding)
                    {
                        var address = await sender.GetDepositAddress();

                        try
                        {
                            await cashCow.SendToAddressAsync(address, Money.Coins(1.0m), null, null, 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), null, null, 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);
                    }
                }
                else if (result.Result == PayResult.Ok)
                {
                    break;
                }
            }
        }
Example #4
0
    //we group per store and init the transfers by each
    async Task <bool> TrypayBolt(ILightningClient lightningClient, PayoutBlob payoutBlob, PayoutData payoutData,
                                 BOLT11PaymentRequest bolt11PaymentRequest)
    {
        var boltAmount = bolt11PaymentRequest.MinimumAmount.ToDecimal(LightMoneyUnit.BTC);

        if (boltAmount != payoutBlob.CryptoAmount)
        {
            return(false);
        }

        var result = await lightningClient.Pay(bolt11PaymentRequest.ToString());

        return(result.Result == PayResult.Ok);
    }
Example #5
0
        public LightningClientProvider(IOptions <LightningHubOptions> options)
        {
            if (!LightningConnectionString.TryParse(options.Value.LightningConnectionString, out var connectionString))
            {
                throw new Exception("Connection string was invalid");
            }

            var networkSets = NBitcoin.Network.GetNetworks().Select(network => network.NetworkSet).Distinct();

            var networkSet = networkSets.Single(set =>
                                                set.CryptoCode.Equals(options.Value.CryptoCode, StringComparison.InvariantCultureIgnoreCase));

            _network         = networkSet.GetNetwork(options.Value.NetworkType);
            _lightningClient = LightningClientFactory.CreateClient(connectionString, _network);
        }
        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");
Example #7
0
        private static async Task <PayResponse> Pay(ILightningClient sender, string payreq)
        {
            using (var cts = new CancellationTokenSource(5000))
            {
retry:
                try
                {
                    return(await sender.Pay(payreq));
                }
                catch (CLightning.LightningRPCException ex) when(ex.Message.Contains("WIRE_INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS") &&
                                                                 !cts.IsCancellationRequested)
                {
                    await Task.Delay(500);

                    goto retry;
                }
            }
        }
Example #8
0
        public async Task CanCreateInvoiceUsingConnectionString()
        {
            ILightningClientFactory factory = new LightningClientFactory(Tester.Network);

            foreach (var connectionString in new[]
            {
                "type=charge;server=http://api-token:[email protected]:37462",
                "type=lnd-rest;server=https://127.0.0.1:42802;allowinsecure=true",
                "type=clightning;server=tcp://127.0.0.1:48532"
            })
            {
                ILightningClient client = factory.Create(connectionString);
                var createdInvoice      = await client.CreateInvoice(10000, "CanCreateInvoice", TimeSpan.FromMinutes(5));

                var retrievedInvoice = await client.GetInvoice(createdInvoice.Id);

                AssertUnpaid(createdInvoice);
                AssertUnpaid(retrievedInvoice);
            }
        }
Example #9
0
        public static async Task <ResultVM> TrypayBolt(ILightningClient lightningClient, PayoutBlob payoutBlob, PayoutData payoutData, BOLT11PaymentRequest bolt11PaymentRequest, PaymentMethodId pmi)
        {
            var boltAmount = bolt11PaymentRequest.MinimumAmount.ToDecimal(LightMoneyUnit.BTC);

            if (boltAmount != payoutBlob.CryptoAmount)
            {
                return(new ResultVM
                {
                    PayoutId = payoutData.Id,
                    Result = PayResult.Error,
                    Message = $"The BOLT11 invoice amount ({boltAmount} {pmi.CryptoCode}) did not match the payout's amount ({payoutBlob.CryptoAmount.GetValueOrDefault()} {pmi.CryptoCode})",
                    Destination = payoutBlob.Destination
                });
            }
            var result = await lightningClient.Pay(bolt11PaymentRequest.ToString(), new PayInvoiceParams());

            if (result.Result == PayResult.Ok)
            {
                var message = result.Details?.TotalAmount != null
                    ? $"Paid out {result.Details.TotalAmount.ToDecimal(LightMoneyUnit.BTC)}"
                    : null;
                payoutData.State = PayoutState.Completed;
                return(new ResultVM
                {
                    PayoutId = payoutData.Id,
                    Result = result.Result,
                    Destination = payoutBlob.Destination,
                    Message = message
                });
            }

            return(new ResultVM
            {
                PayoutId = payoutData.Id,
                Result = result.Result,
                Destination = payoutBlob.Destination,
                Message = result.ErrorDetail
            });
        }
Example #10
0
        public async Task CanCreateInvoiceUsingConnectionString()
        {
            ILightningClientFactory factory = new LightningClientFactory(Tester.Network);

            var connectionStrings = Docker
                                ? new[]
            {
                "type=charge;server=http://api-token:foiewnccewuify@charge:9112;allowinsecure=true",
                "type=lnd-rest;server=http://lnd_dest:8080;allowinsecure=true",
                "type=clightning;server=tcp://lightningd:9835",
                "type=eclair;server=http://eclair:8080;password=bukkake"
            }
                                : new[]
            {
                "type=charge;server=http://api-token:[email protected]:37462;allowinsecure=true",
                "type=lnd-rest;server=http://127.0.0.1:42802;allowinsecure=true",
                "type=clightning;server=tcp://127.0.0.1:48532",
                "type=eclair;server=http://127.0.0.1:4570;password=bukkake"
            };

            var clientTypes = Tester.GetLightningClients().Select(l => l.Client.GetType()).ToArray();

            foreach (var connectionString in connectionStrings)
            {
                ILightningClient client = factory.Create(connectionString);
                if (!clientTypes.Contains(client.GetType()))
                {
                    continue;
                }
                var createdInvoice = await client.CreateInvoice(10000, "CanCreateInvoice", TimeSpan.FromMinutes(5));

                var retrievedInvoice = await client.GetInvoice(createdInvoice.Id);

                AssertUnpaid(createdInvoice);
                AssertUnpaid(retrievedInvoice);
            }
        }
Example #11
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);
                }
            }
        }
Example #12
0
        private static async Task <LightningNodeInformation> WaitLNSynched(RPCClient rpc, ILightningClient lightningClient)
        {
            while (true)
            {
                var merchantInfo = await lightningClient.GetInfo();

                var blockCount = await rpc.GetBlockCountAsync();

                if (merchantInfo.BlockHeight != blockCount)
                {
                    await Task.Delay(500);
                }
                else
                {
                    return(merchantInfo);
                }
            }
        }
Example #13
0
 //we group per store and init the transfers by each
 async Task <bool> TrypayBolt(ILightningClient lightningClient, PayoutBlob payoutBlob, PayoutData payoutData,
                              BOLT11PaymentRequest bolt11PaymentRequest)
 {
     return((await UILightningLikePayoutController.TrypayBolt(lightningClient, payoutBlob, payoutData, bolt11PaymentRequest,
                                                              payoutData.GetPaymentMethodId())).Result == PayResult.Ok);
 }