Remove dependency on Eclair for tests

This commit is contained in:
nicolas.dorier
2018-03-17 15:45:44 +09:00
parent b28b3ef4ff
commit 0d8affc68d
13 changed files with 277 additions and 421 deletions

View File

@@ -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<NodeInfo> 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();
}
}
}

View File

@@ -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; }
}
}

View File

@@ -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
/// <summary>
/// This will setup a channel going from customer to merchant
/// Connect a customer LN node to the merchant LN node
/// </summary>
public void PrepareLightning()
{
PrepareLightningAsync().GetAwaiter().GetResult();
}
/// <summary>
/// Connect a customer LN node to the merchant LN node
/// </summary>
/// <returns></returns>
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<Payments.Lightning.CLightning.GetInfoResponse> 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)

View File

@@ -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;

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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<GetInfoResponse> GetInfoAsync()
{
return SendCommandAsync<GetInfoResponse>("getinfo");
}
public Task SendAsync(string bolt11)
{
return SendCommandAsync<object>("pay", new[] { bolt11 }, true);
}
public async Task<PeerInfo[]> ListPeersAsync()
{
var peers = await SendCommandAsync<PeerInfo[]>("listpeers", isArray: true);
foreach(var peer in peers)
{
peer.Channels = peer.Channels ?? Array.Empty<ChannelInfo>();
}
return peers;
}
public Task FundChannelAsync(NodeInfo nodeInfo, Money money)
{
return SendCommandAsync<object>("fundchannel", new object[] { nodeInfo.NodeId, money.Satoshi }, true);
}
public Task ConnectAsync(NodeInfo nodeInfo)
{
return SendCommandAsync<object>("connect", new[] { $"{nodeInfo.NodeId}@{nodeInfo.Host}:{nodeInfo.Port}" }, true);
}
static Encoding UTF8 = new UTF8Encoding(false);
private async Task<T> SendCommandAsync<T>(string command, object[] parameters = null, bool noReturn = false, bool isArray = false)
{
parameters = parameters ?? Array.Empty<string>();
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<T>();
}
return result["result"].ToObject<T>();
}
}
}
}
public async Task<BitcoinAddress> NewAddressAsync()
{
var obj = await SendCommandAsync<JObject>("newaddr");
return BitcoinAddress.Create(obj.Property("address").Value.Value<string>(), Network);
}
}
}

View File

@@ -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
{

View File

@@ -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; }
}
}

View File

@@ -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; }
}
}

View File

@@ -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";
}
}

View File

@@ -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<GetInfoResponse> GetInfoAsync()
{
return SendCommandAsync<GetInfoResponse>(new RPCRequest("getinfo", Array.Empty<object>()));
}
public async Task<T> SendCommandAsync<T>(RPCRequest request, bool throwIfRPCError = true)
{
var response = await SendCommandAsync(request, throwIfRPCError);
return Serializer.ToObject<T>(response.ResultString, Network);
}
public async Task<RPCResponse> 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<AllChannelResponse[]> AllChannelsAsync()
{
return await SendCommandAsync<AllChannelResponse[]>(new RPCRequest("allchannels", Array.Empty<object>())).ConfigureAwait(false);
}
public ChannelInfo[] Channels()
{
return ChannelsAsync().GetAwaiter().GetResult();
}
public async Task<ChannelInfo[]> ChannelsAsync()
{
return await SendCommandAsync<ChannelInfo[]>(new RPCRequest("channels", Array.Empty<object>())).ConfigureAwait(false);
}
public void Close(string channelId)
{
CloseAsync(channelId).GetAwaiter().GetResult();
}
public async Task SendAsync(string paymentRequest)
{
await SendCommandAsync<SendResponse>(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<ChannelResponse> ChannelAsync(string channelId)
{
if (channelId == null)
throw new ArgumentNullException(nameof(channelId));
return await SendCommandAsync<ChannelResponse>(new RPCRequest("channel", new object[] { channelId })).ConfigureAwait(false);
}
public string[] AllNodes()
{
return AllNodesAsync().GetAwaiter().GetResult();
}
public async Task<string[]> AllNodesAsync()
{
return await SendCommandAsync<string[]>(new RPCRequest("allnodes", Array.Empty<object>())).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<Stream> 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<string> 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<string> ReceiveAsync(LightMoney amount, string description = null)
{
if (amount == null)
throw new ArgumentNullException(nameof(amount));
List<object> args = new List<object>();
args.Add(amount.MilliSatoshi);
if(description != null)
{
args.Add(description);
}
return (await SendCommandAsync(new RPCRequest("receive", args.ToArray())).ConfigureAwait(false)).ResultString;
}
public async Task<string> 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;
}
}
}

View File

@@ -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; }
}
}