diff --git a/BTCPayServer.Tests/EclairTester.cs b/BTCPayServer.Tests/EclairTester.cs deleted file mode 100644 index 3c886f764..000000000 --- a/BTCPayServer.Tests/EclairTester.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Threading.Tasks; -using BTCPayServer.Payments.Lightning.Eclair; -using NBitcoin; - -namespace BTCPayServer.Tests -{ - public class EclairTester - { - ServerTester parent; - public EclairTester(ServerTester parent, string environmentName, string defaultRPC, string defaultHost, Network network) - { - this.parent = parent; - RPC = new EclairRPCClient(new Uri(parent.GetEnvironment(environmentName, defaultRPC)), network); - P2PHost = parent.GetEnvironment(environmentName + "_HOST", defaultHost); - } - - public EclairRPCClient RPC { get; } - public string P2PHost { get; } - - NodeInfo _NodeInfo; - public async Task GetNodeInfoAsync() - { - if (_NodeInfo != null) - return _NodeInfo; - var info = await RPC.GetInfoAsync(); - _NodeInfo = new NodeInfo(info.NodeId, P2PHost, info.Port); - return _NodeInfo; - } - - public NodeInfo GetNodeInfo() - { - return GetNodeInfoAsync().GetAwaiter().GetResult(); - } - - } -} diff --git a/BTCPayServer.Tests/LightningDTester.cs b/BTCPayServer.Tests/LightningDTester.cs new file mode 100644 index 000000000..d014310c1 --- /dev/null +++ b/BTCPayServer.Tests/LightningDTester.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using BTCPayServer.Payments.Lightning.CLightning; +using BTCPayServer.Payments.Lightning.CLightning.RPC; +using NBitcoin; + +namespace BTCPayServer.Tests +{ + public class LightningDTester + { + ServerTester parent; + public LightningDTester(ServerTester parent, string environmentName, string defaultRPC, string defaultHost, Network network) + { + this.parent = parent; + RPC = new CLightningRPCClient(new Uri(parent.GetEnvironment(environmentName, defaultRPC)), network); + } + + public CLightningRPCClient RPC { get; } + public string P2PHost { get; } + + } +} diff --git a/BTCPayServer.Tests/ServerTester.cs b/BTCPayServer.Tests/ServerTester.cs index 7b7255ff5..dbd01b834 100644 --- a/BTCPayServer.Tests/ServerTester.cs +++ b/BTCPayServer.Tests/ServerTester.cs @@ -17,8 +17,8 @@ using System.Runtime.CompilerServices; using System.Text; using System.Threading.Tasks; using System.Threading; -using BTCPayServer.Payments.Lightning.Eclair; using System.Globalization; +using BTCPayServer.Payments.Lightning.CLightning.RPC; namespace BTCPayServer.Tests { @@ -56,8 +56,8 @@ namespace BTCPayServer.Tests LTCExplorerClient = new ExplorerClient(NetworkProvider.GetNetwork("LTC").NBXplorerNetwork, new Uri(GetEnvironment("TESTS_LTCNBXPLORERURL", "http://127.0.0.1:32838/"))); var btc = NetworkProvider.GetNetwork("BTC").NBitcoinNetwork; - CustomerEclair = new EclairTester(this, "TEST_ECLAIR", "http://eclair-cli:gpwefwmmewci@127.0.0.1:30992/", "eclair", btc); - MerchantCharge = new ChargeTester(this, "TEST_CHARGE", "http://api-token:foiewnccewuify@127.0.0.1:54938/", "lightning-charged", btc); + CustomerLightningD = new CLightningRPCClient(new Uri(GetEnvironment("TEST_CUSTOMERLIGHTNINGD", "http://127.0.0.1:30992/")), btc); + MerchantCharge = new ChargeTester(this, "TEST_MERCHANTCHARGE", "http://api-token:foiewnccewuify@127.0.0.1:54938/", "lightning-charged", btc); PayTester = new BTCPayServerTester(Path.Combine(_Directory, "pay")) { @@ -73,55 +73,62 @@ namespace BTCPayServer.Tests /// - /// This will setup a channel going from customer to merchant + /// Connect a customer LN node to the merchant LN node /// public void PrepareLightning() { PrepareLightningAsync().GetAwaiter().GetResult(); } + + /// + /// Connect a customer LN node to the merchant LN node + /// + /// public async Task PrepareLightningAsync() { - // Activate segwit - var blockCount = ExplorerNode.GetBlockCountAsync(); - // Fetch node info, but that in cache - var merchantInfo = MerchantCharge.Client.GetInfoAsync(); - var customer = CustomerEclair.GetNodeInfoAsync(); - var channels = CustomerEclair.RPC.ChannelsAsync(); - - var info = await merchantInfo; - var clightning = new NodeInfo(info.Id, MerchantCharge.P2PHost, info.Port); - var connect = CustomerEclair.RPC.ConnectAsync(clightning); - await Task.WhenAll(blockCount, customer, channels, connect); - - // If the channel is not created, let's do it - if (channels.Result.Length == 0) + while (true) { - var c = (await CustomerEclair.RPC.ChannelsAsync()); - bool generated = false; - bool createdChannel = false; - CancellationTokenSource timeout = new CancellationTokenSource(); - timeout.CancelAfter(10000); - while (c.Length == 0 || c[0].State != "NORMAL") + var channel = (await CustomerLightningD.ListPeersAsync()) + .SelectMany(p => p.Channels) + .FirstOrDefault(); + switch (channel?.State) { - if (timeout.IsCancellationRequested) - { - timeout = new CancellationTokenSource(); - timeout.CancelAfter(10000); - createdChannel = c.Length == 0; - generated = false; - } - if (!createdChannel) - { - await CustomerEclair.RPC.OpenAsync(clightning, Money.Satoshis(16777215)); - createdChannel = true; - } - if (!generated && c.Length != 0 && c[0].State == "WAIT_FOR_FUNDING_CONFIRMED") - { - ExplorerNode.Generate(6); - generated = true; - } - c = (await CustomerEclair.RPC.ChannelsAsync()); + case null: + var merchantInfo = await WaitLNSynched(); + var clightning = new NodeInfo(merchantInfo.Id, MerchantCharge.P2PHost, merchantInfo.Port); + await CustomerLightningD.ConnectAsync(clightning); + var address = await CustomerLightningD.NewAddressAsync(); + await ExplorerNode.SendToAddressAsync(address, Money.Coins(0.2m)); + ExplorerNode.Generate(1); + await WaitLNSynched(); + await CustomerLightningD.FundChannelAsync(clightning, Money.Satoshis(16777215)); + break; + case "CHANNELD_AWAITING_LOCKIN": + ExplorerNode.Generate(1); + await WaitLNSynched(); + break; + case "CHANNELD_NORMAL": + return; + default: + throw new NotSupportedException(channel?.State ?? ""); + } + } + } + + private async Task WaitLNSynched() + { + while (true) + { + var merchantInfo = await MerchantCharge.Client.GetInfoAsync(); + var blockCount = await ExplorerNode.GetBlockCountAsync(); + if (merchantInfo.BlockHeight != blockCount) + { + await Task.Delay(1000); + } + else + { + return merchantInfo; } } } @@ -135,11 +142,10 @@ namespace BTCPayServer.Tests { var bolt11 = invoice.CryptoInfo.Where(o => o.PaymentUrls.BOLT11 != null).First().PaymentUrls.BOLT11; bolt11 = bolt11.Replace("lightning:", "", StringComparison.OrdinalIgnoreCase); - await CustomerEclair.RPC.SendAsync(bolt11); + await CustomerLightningD.SendAsync(bolt11); } - public EclairTester MerchantEclair { get; set; } - public EclairTester CustomerEclair { get; set; } + public CLightningRPCClient CustomerLightningD { get; set; } public ChargeTester MerchantCharge { get; private set; } internal string GetEnvironment(string variable, string defaultValue) diff --git a/BTCPayServer.Tests/UnitTest1.cs b/BTCPayServer.Tests/UnitTest1.cs index 92c209762..39d0edbdf 100644 --- a/BTCPayServer.Tests/UnitTest1.cs +++ b/BTCPayServer.Tests/UnitTest1.cs @@ -22,7 +22,6 @@ using BTCPayServer.Data; using Microsoft.EntityFrameworkCore; using BTCPayServer.Services.Rates; using Microsoft.Extensions.Caching.Memory; -using BTCPayServer.Payments.Lightning.Eclair; using System.Collections.Generic; using BTCPayServer.Models.StoreViewModels; using System.Threading.Tasks; diff --git a/BTCPayServer.Tests/docker-compose.yml b/BTCPayServer.Tests/docker-compose.yml index f96b52438..558aa6f1f 100644 --- a/BTCPayServer.Tests/docker-compose.yml +++ b/BTCPayServer.Tests/docker-compose.yml @@ -1,7 +1,7 @@ version: "3" # Run `docker-compose up dev` for bootstrapping your development environment -# Doing so will expose eclair API, NBXplorer, Bitcoind RPC and postgres port to the host so that tests can Run, +# Doing so will expose NBXplorer, Bitcoind RPC and postgres port to the host so that tests can Run, # The Visual Studio launch setting `Docker-Regtest` is configured to use this environment. services: @@ -17,8 +17,8 @@ services: TESTS_POSTGRES: User ID=postgres;Host=postgres;Port=5432;Database=btcpayserver TESTS_PORT: 80 TESTS_HOSTNAME: tests - TEST_ECLAIR: http://eclair-cli:gpwefwmmewci@eclair:8080/ - TEST_CHARGE: http://api-token:foiewnccewuify@lightning-charged:9112/ + TEST_CUSTOMERLIGHTNINGD: http://lightningd:9835/ + TEST_MERCHANTCHARGE: http://api-token:foiewnccewuify@lightning-charged:9112/ expose: - "80" links: @@ -36,7 +36,7 @@ services: links: - nbxplorer - postgres - - eclair + - lightningd - lightning-charged nbxplorer: @@ -75,11 +75,6 @@ services: rpcport=43782 port=39388 whitelist=0.0.0.0/0 - zmqpubrawblock=tcp://0.0.0.0:29000 - zmqpubrawtx=tcp://0.0.0.0:29000 - txindex=1 - # Eclair is still using addwitnessaddress - deprecatedrpc=addwitnessaddress ports: - "43782:43782" expose: @@ -89,7 +84,7 @@ services: - "bitcoin_datadir:/data" lightning-charged: - image: shesek/lightning-charge:0.3.1 + image: shesek/lightning-charge:0.3.4 environment: NETWORK: regtest API_TOKEN: foiewnccewuify @@ -105,28 +100,23 @@ services: links: - bitcoind - eclair: - image: acinq/eclair@sha256:758eaf02683046a096ee03390d3a54df8fcfca50883f7560ab946a36ee4e81d8 - environment: - JAVA_OPTS: > - -Xmx512m - -Declair.printToConsole - -Declair.bitcoind.host=bitcoind - -Declair.bitcoind.rpcport=43782 - -Declair.bitcoind.rpcuser=ceiwHEbqWI83 - -Declair.bitcoind.rpcpassword=DwubwWsoo3 - -Declair.bitcoind.zmq=tcp://bitcoind:29000 - -Declair.api.enabled=true - -Declair.api.password=gpwefwmmewci - -Declair.chain=regtest - -Declair.api.binding-ip=0.0.0.0 - links: - - bitcoind + lightningd: + image: nicolasdorier/clightning + environment: + LIGHTNINGD_OPT: | + bitcoin-datadir=/etc/bitcoin + bitcoin-rpcconnect=bitcoind + network=regtest + log-level=debug ports: - - "30992:8080" # api port + - "30992:9835" # api port expose: - "9735" # server port - - "8080" # api port + - "9835" # api port + volumes: + - "bitcoin_datadir:/etc/bitcoin" + links: + - bitcoind litecoind: container_name: btcpayserver_dev_litecoind diff --git a/BTCPayServer/JsonConverters/LightMoneyJsonConverter.cs b/BTCPayServer/JsonConverters/LightMoneyJsonConverter.cs index 805a18c50..a0f023fb9 100644 --- a/BTCPayServer/JsonConverters/LightMoneyJsonConverter.cs +++ b/BTCPayServer/JsonConverters/LightMoneyJsonConverter.cs @@ -17,12 +17,15 @@ namespace BTCPayServer.JsonConverters return typeof(LightMoneyJsonConverter).GetTypeInfo().IsAssignableFrom(objectType.GetTypeInfo()); } + Type longType = typeof(long).GetTypeInfo(); public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { try { return reader.TokenType == JsonToken.Null ? null : - reader.TokenType == JsonToken.Integer ? new LightMoney((long)reader.Value) : + reader.TokenType == JsonToken.Integer ? + longType.IsAssignableFrom(reader.ValueType) ? new LightMoney((long)reader.Value) + : new LightMoney(long.MaxValue) : reader.TokenType == JsonToken.String ? new LightMoney(long.Parse((string)reader.Value, CultureInfo.InvariantCulture)) : null; } diff --git a/BTCPayServer/Payments/Lightning/CLightning/RPC/CLightningRPCClient.cs b/BTCPayServer/Payments/Lightning/CLightning/RPC/CLightningRPCClient.cs new file mode 100644 index 000000000..361539820 --- /dev/null +++ b/BTCPayServer/Payments/Lightning/CLightning/RPC/CLightningRPCClient.cs @@ -0,0 +1,118 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.Sockets; +using System.Text; +using System.Threading.Tasks; +using NBitcoin; +using NBitcoin.RPC; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace BTCPayServer.Payments.Lightning.CLightning.RPC +{ + public class CLightningRPCClient + { + public Network Network { get; private set; } + public Uri Address { get; private set; } + + public CLightningRPCClient(Uri address, Network network) + { + if (address == null) + throw new ArgumentNullException(nameof(address)); + if (network == null) + throw new ArgumentNullException(nameof(network)); + Address = address; + Network = network; + } + + public Task GetInfoAsync() + { + return SendCommandAsync("getinfo"); + } + + public Task SendAsync(string bolt11) + { + return SendCommandAsync("pay", new[] { bolt11 }, true); + } + + public async Task ListPeersAsync() + { + var peers = await SendCommandAsync("listpeers", isArray: true); + foreach(var peer in peers) + { + peer.Channels = peer.Channels ?? Array.Empty(); + } + return peers; + } + + public Task FundChannelAsync(NodeInfo nodeInfo, Money money) + { + return SendCommandAsync("fundchannel", new object[] { nodeInfo.NodeId, money.Satoshi }, true); + } + + public Task ConnectAsync(NodeInfo nodeInfo) + { + return SendCommandAsync("connect", new[] { $"{nodeInfo.NodeId}@{nodeInfo.Host}:{nodeInfo.Port}" }, true); + } + + static Encoding UTF8 = new UTF8Encoding(false); + private async Task SendCommandAsync(string command, object[] parameters = null, bool noReturn = false, bool isArray = false) + { + parameters = parameters ?? Array.Empty(); + var domain = Address.DnsSafeHost; + if (!IPAddress.TryParse(domain, out IPAddress address)) + { + address = (await Dns.GetHostAddressesAsync(domain)).FirstOrDefault(); + if (address == null) + throw new Exception("Host not found"); + } + Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + await socket.ConnectAsync(new IPEndPoint(address, Address.Port)); + using (var networkStream = new NetworkStream(socket)) + { + using (var textWriter = new StreamWriter(networkStream, UTF8, 1024 * 10, true)) + { + using (var jsonWriter = new JsonTextWriter(textWriter)) + { + var req = new JObject(); + req.Add("id", 0); + req.Add("method", command); + req.Add("params", new JArray(parameters)); + await req.WriteToAsync(jsonWriter); + await jsonWriter.FlushAsync(); + } + await textWriter.FlushAsync(); + } + await networkStream.FlushAsync(); + using (var textReader = new StreamReader(networkStream, UTF8, false, 1024 * 10, true)) + { + using (var jsonReader = new JsonTextReader(textReader)) + { + var result = await JObject.LoadAsync(jsonReader); + var error = result.Property("error"); + if(error != null) + { + throw new Exception(error.Value.ToString()); + } + if (noReturn) + return default(T); + if (isArray) + { + return result["result"].Children().First().Children().First().ToObject(); + } + return result["result"].ToObject(); + } + } + } + } + + public async Task NewAddressAsync() + { + var obj = await SendCommandAsync("newaddr"); + return BitcoinAddress.Create(obj.Property("address").Value.Value(), Network); + } + } +} diff --git a/BTCPayServer/Payments/Lightning/Eclair/NodeInfo.cs b/BTCPayServer/Payments/Lightning/CLightning/RPC/NodeInfo.cs similarity index 91% rename from BTCPayServer/Payments/Lightning/Eclair/NodeInfo.cs rename to BTCPayServer/Payments/Lightning/CLightning/RPC/NodeInfo.cs index e403d02b6..4f72eae99 100644 --- a/BTCPayServer/Payments/Lightning/Eclair/NodeInfo.cs +++ b/BTCPayServer/Payments/Lightning/CLightning/RPC/NodeInfo.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -namespace BTCPayServer.Payments.Lightning.Eclair +namespace BTCPayServer.Payments.Lightning.CLightning.RPC { public class NodeInfo { diff --git a/BTCPayServer/Payments/Lightning/CLightning/RPC/PeerInfo.cs b/BTCPayServer/Payments/Lightning/CLightning/RPC/PeerInfo.cs new file mode 100644 index 000000000..2c9789469 --- /dev/null +++ b/BTCPayServer/Payments/Lightning/CLightning/RPC/PeerInfo.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using NBitcoin; +using Newtonsoft.Json; + +namespace BTCPayServer.Payments.Lightning.CLightning.RPC +{ + public class ChannelInfo + { + public string State { get; set; } + public string Owner { get; set; } + + [JsonProperty("funding_txid")] + [JsonConverter(typeof(NBitcoin.JsonConverters.UInt256JsonConverter))] + public uint256 FundingTxId { get; set; } + + [JsonProperty("msatoshi_to_us")] + [JsonConverter(typeof(JsonConverters.LightMoneyJsonConverter))] + public LightMoney ToUs { get; set; } + + [JsonProperty("msatoshi_total")] + [JsonConverter(typeof(JsonConverters.LightMoneyJsonConverter))] + public LightMoney Total { get; set; } + + [JsonProperty("dust_limit_satoshis")] + [JsonConverter(typeof(NBitcoin.JsonConverters.MoneyJsonConverter))] + public Money DustLimit { get; set; } + + [JsonProperty("max_htlc_value_in_flight_msat")] + [JsonConverter(typeof(JsonConverters.LightMoneyJsonConverter))] + public LightMoney MaxHTLCValueInFlight { get; set; } + + [JsonProperty("channel_reserve_satoshis")] + [JsonConverter(typeof(NBitcoin.JsonConverters.MoneyJsonConverter))] + public Money ChannelReserve { get; set; } + + [JsonProperty("htlc_minimum_msat")] + [JsonConverter(typeof(JsonConverters.LightMoneyJsonConverter))] + public LightMoney HTLCMinimum { get; set; } + + [JsonProperty("to_self_delay")] + public int ToSelfDelay { get; set; } + [JsonProperty("max_accepted_htlcs")] + public int MaxAcceptedHTLCS { get; set; } + public string[] Status { get; set; } + } + public class PeerInfo + { + public string State { get; set; } + public string Id { get; set; } + [JsonProperty("netaddr")] + public string[] NetworkAddresses { get; set; } + public bool Connected { get; set; } + public string Owner { get; set; } + public ChannelInfo[] Channels { get; set; } + + } +} diff --git a/BTCPayServer/Payments/Lightning/Eclair/AllChannelResponse.cs b/BTCPayServer/Payments/Lightning/Eclair/AllChannelResponse.cs deleted file mode 100644 index 67c33d4a9..000000000 --- a/BTCPayServer/Payments/Lightning/Eclair/AllChannelResponse.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace BTCPayServer.Payments.Lightning.Eclair -{ - public class AllChannelResponse - { - public string ShortChannelId { get; set; } - public string NodeId1 { get; set; } - public string NodeId2 { get; set; } - } -} diff --git a/BTCPayServer/Payments/Lightning/Eclair/ChannelResponse.cs b/BTCPayServer/Payments/Lightning/Eclair/ChannelResponse.cs deleted file mode 100644 index 6e2424177..000000000 --- a/BTCPayServer/Payments/Lightning/Eclair/ChannelResponse.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace BTCPayServer.Payments.Lightning.Eclair -{ - public class ChannelResponse - { - - public string NodeId { get; set; } - public string ChannelId { get; set; } - public string State { get; set; } - } - public static class ChannelStates - { - public const string WAIT_FOR_FUNDING_CONFIRMED = "WAIT_FOR_FUNDING_CONFIRMED"; - - public const string NORMAL = "NORMAL"; - } -} diff --git a/BTCPayServer/Payments/Lightning/Eclair/EclairRPCClient.cs b/BTCPayServer/Payments/Lightning/Eclair/EclairRPCClient.cs deleted file mode 100644 index 771788b12..000000000 --- a/BTCPayServer/Payments/Lightning/Eclair/EclairRPCClient.cs +++ /dev/null @@ -1,252 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Net; -using System.Text; -using System.Threading.Tasks; -using NBitcoin; -using NBitcoin.JsonConverters; -using NBitcoin.RPC; - -namespace BTCPayServer.Payments.Lightning.Eclair -{ - public class SendResponse - { - public string PaymentHash { get; set; } - } - public class ChannelInfo - { - public string NodeId { get; set; } - public string ChannelId { get; set; } - public string State { get; set; } - } - public class EclairRPCClient - { - public EclairRPCClient(Uri address, Network network) - { - if (address == null) - throw new ArgumentNullException(nameof(address)); - if (network == null) - throw new ArgumentNullException(nameof(network)); - Address = address; - Network = network; - if (string.IsNullOrEmpty(address.UserInfo)) - throw new ArgumentException(paramName: nameof(address), message: "User info in the url should be provided"); - Password = address.UserInfo; - } - - public string Password { get; set; } - - public Network Network { get; private set; } - - - public GetInfoResponse GetInfo() - { - return GetInfoAsync().GetAwaiter().GetResult(); - } - - public Task GetInfoAsync() - { - return SendCommandAsync(new RPCRequest("getinfo", Array.Empty())); - } - - public async Task SendCommandAsync(RPCRequest request, bool throwIfRPCError = true) - { - var response = await SendCommandAsync(request, throwIfRPCError); - return Serializer.ToObject(response.ResultString, Network); - } - - public async Task SendCommandAsync(RPCRequest request, bool throwIfRPCError = true) - { - RPCResponse response = null; - HttpWebRequest webRequest = response == null ? CreateWebRequest() : null; - if (response == null) - { - var writer = new StringWriter(); - request.WriteJSON(writer); - writer.Flush(); - var json = writer.ToString(); - var bytes = Encoding.UTF8.GetBytes(json); -#if !(PORTABLE || NETCORE) - webRequest.ContentLength = bytes.Length; -#endif - var dataStream = await webRequest.GetRequestStreamAsync().ConfigureAwait(false); - await dataStream.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false); - await dataStream.FlushAsync().ConfigureAwait(false); - dataStream.Dispose(); - } - WebResponse webResponse = null; - WebResponse errorResponse = null; - try - { - webResponse = response == null ? await webRequest.GetResponseAsync().ConfigureAwait(false) : null; - response = response ?? RPCResponse.Load(await ToMemoryStreamAsync(webResponse.GetResponseStream()).ConfigureAwait(false)); - - if (throwIfRPCError) - response.ThrowIfError(); - } - catch (WebException ex) - { - if (ex.Response == null || ex.Response.ContentLength == 0 || - !ex.Response.ContentType.Equals("application/json", StringComparison.Ordinal)) - throw; - errorResponse = ex.Response; - response = RPCResponse.Load(await ToMemoryStreamAsync(errorResponse.GetResponseStream()).ConfigureAwait(false)); - if (throwIfRPCError) - response.ThrowIfError(); - } - finally - { - if (errorResponse != null) - { - errorResponse.Dispose(); - errorResponse = null; - } - if (webResponse != null) - { - webResponse.Dispose(); - webResponse = null; - } - } - return response; - } - - public AllChannelResponse[] AllChannels() - { - return AllChannelsAsync().GetAwaiter().GetResult(); - } - - public async Task AllChannelsAsync() - { - return await SendCommandAsync(new RPCRequest("allchannels", Array.Empty())).ConfigureAwait(false); - } - - public ChannelInfo[] Channels() - { - return ChannelsAsync().GetAwaiter().GetResult(); - } - - public async Task ChannelsAsync() - { - return await SendCommandAsync(new RPCRequest("channels", Array.Empty())).ConfigureAwait(false); - } - - public void Close(string channelId) - { - CloseAsync(channelId).GetAwaiter().GetResult(); - } - - public async Task SendAsync(string paymentRequest) - { - await SendCommandAsync(new RPCRequest("send", new[] { paymentRequest })).ConfigureAwait(false); - } - - public async Task CloseAsync(string channelId) - { - if (channelId == null) - throw new ArgumentNullException(nameof(channelId)); - try - { - await SendCommandAsync(new RPCRequest("close", new object[] { channelId })).ConfigureAwait(false); - } - catch (RPCException ex) when (ex.Message == "closing already in progress") - { - - } - } - - public ChannelResponse Channel(string channelId) - { - return ChannelAsync(channelId).GetAwaiter().GetResult(); - } - - public async Task ChannelAsync(string channelId) - { - if (channelId == null) - throw new ArgumentNullException(nameof(channelId)); - return await SendCommandAsync(new RPCRequest("channel", new object[] { channelId })).ConfigureAwait(false); - } - - public string[] AllNodes() - { - return AllNodesAsync().GetAwaiter().GetResult(); - } - - public async Task AllNodesAsync() - { - return await SendCommandAsync(new RPCRequest("allnodes", Array.Empty())).ConfigureAwait(false); - } - - public Uri Address { get; private set; } - - private HttpWebRequest CreateWebRequest() - { - var webRequest = (HttpWebRequest)WebRequest.Create(Address.AbsoluteUri); - webRequest.ContentType = "application/json"; - webRequest.Method = "POST"; - var auth = Convert.ToBase64String(Encoding.ASCII.GetBytes(Password)); - webRequest.Headers[HttpRequestHeader.Authorization] = $"Basic {auth}"; - return webRequest; - } - - - private async Task ToMemoryStreamAsync(Stream stream) - { - MemoryStream ms = new MemoryStream(); - await stream.CopyToAsync(ms).ConfigureAwait(false); - ms.Position = 0; - return ms; - } - - public string Open(NodeInfo node, Money fundingSatoshi, LightMoney pushAmount = null) - { - return OpenAsync(node, fundingSatoshi, pushAmount).GetAwaiter().GetResult(); - } - - public string Connect(NodeInfo node) - { - return ConnectAsync(node).GetAwaiter().GetResult(); - } - - public async Task ConnectAsync(NodeInfo node) - { - if (node == null) - throw new ArgumentNullException(nameof(node)); - return (await SendCommandAsync(new RPCRequest("connect", new object[] { node.NodeId, node.Host, node.Port })).ConfigureAwait(false)).ResultString; - } - - public string Receive(LightMoney amount, string description = null) - { - return ReceiveAsync(amount, description).GetAwaiter().GetResult(); - } - - public async Task ReceiveAsync(LightMoney amount, string description = null) - { - if (amount == null) - throw new ArgumentNullException(nameof(amount)); - List args = new List(); - args.Add(amount.MilliSatoshi); - if(description != null) - { - args.Add(description); - } - return (await SendCommandAsync(new RPCRequest("receive", args.ToArray())).ConfigureAwait(false)).ResultString; - } - - public async Task OpenAsync(NodeInfo node, Money fundingSatoshi, LightMoney pushAmount = null) - { - if (fundingSatoshi == null) - throw new ArgumentNullException(nameof(fundingSatoshi)); - if (node == null) - throw new ArgumentNullException(nameof(node)); - pushAmount = pushAmount ?? LightMoney.Zero; - - var result = await SendCommandAsync(new RPCRequest("open", new object[] { node.NodeId, fundingSatoshi.Satoshi, pushAmount.MilliSatoshi })); - - return result.ResultString; - } - - - } -} diff --git a/BTCPayServer/Payments/Lightning/Eclair/GetInfoResponse.cs b/BTCPayServer/Payments/Lightning/Eclair/GetInfoResponse.cs deleted file mode 100644 index d43b07bbe..000000000 --- a/BTCPayServer/Payments/Lightning/Eclair/GetInfoResponse.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using NBitcoin; -using Newtonsoft.Json; - -namespace BTCPayServer.Payments.Lightning.Eclair -{ - public class GetInfoResponse - { - public string NodeId { get; set; } - public string Alias { get; set; } - public int Port { get; set; } - public uint256 ChainHash { get; set; } - public int BlockHeight { get; set; } - } -}