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; } } }
//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); }
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");
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; } } }
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); } }
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 }); }
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); } }
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); } } }
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); } } }
//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); }