diff --git a/README.md b/README.md index a516673..b1bf97d 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ Or with custodial solution for lightning (without having a node of your own) : - [x] LNDHub / BlueWallet ([Documentation](documentation/client-lndhub.md)) - [x] LNBits ([Documentation](documentation/client-lnbits.md)) -## Extend +## Extensible With `LightningPay`, you can easly extend your client by add extension methods to the `ILightningClient` interface. diff --git a/documentation/client-eclair.md b/documentation/client-eclair.md index e508652..01b7a34 100644 --- a/documentation/client-eclair.md +++ b/documentation/client-eclair.md @@ -57,6 +57,7 @@ The `AddEclairLightningClient` method has optionnal pamameters to configure your | --------------------- | -------- | -------- | ------------------------------------------------------------ | | address | `Uri` | Yes | Address of your node server with port (example : http://localhost:8080/) | | password | `String` | No | Eclair api password | +| retryOnHttpError | `int` | No | Number of retry on http error | | certificateThumbprint | `String` | No | Certificate thumbprint used for your https address if the certificate is not public
Ex : "284800A04D0C046636EBE60C37A4F527B8B550F3" | | allowInsecure | `bool` | No | If you use https address, determine if you allow non secure transport (certificateThumbprint parameter will be ignored) | diff --git a/documentation/client-lnbits.md b/documentation/client-lnbits.md index a53464c..0d66066 100644 --- a/documentation/client-lnbits.md +++ b/documentation/client-lnbits.md @@ -58,6 +58,7 @@ The `AddLNBitsLightningClient` method has optionnal pamameters to configure your | --------------------- | -------- | -------- | ------------------------------------------------------------ | | address | `Uri` | Yes | Address of the LNBits api (example : https://lnbits.com/) | | apiKey | `String` | Yes | LNBits api key | +| retryOnHttpError | `int` | No | Number of retry on http error | | certificateThumbprint | `String` | No | Certificate thumbprint used for your https address if the certificate is not public
Ex : "284800A04D0C046636EBE60C37A4F527B8B550F3" | | allowInsecure | `bool` | No | If you use https address, determine if you allow non secure transport (certificateThumbprint parameter will be ignored) | diff --git a/documentation/client-lnd.md b/documentation/client-lnd.md index eddcbbf..a82fccf 100644 --- a/documentation/client-lnd.md +++ b/documentation/client-lnd.md @@ -58,6 +58,7 @@ The `AddLndLightningClient` method has optionnal pamameters to configure your cl | address | `Uri` | Yes | Address of your node server with port (example : http://localhost:8080/) | | macaroonHexString | `String` | No | Authentication assertion in hex string format
Tip : To get the hex string of your, type the command xxd -p -c2000 admin.macaroon to get the hex representation of your file. | | macaroonBytes | `byte[]` | No | Authentication assertion in Byte array (to load macaron from file with .NET code `File.ReadAllBytes(macaroonFilePath)` ) | +| retryOnHttpError | `int` | No | Number of retry on http error | | certificateThumbprint | `String` | No | Certificate thumbprint used for your https address if the certificate is not public
Ex : "284800A04D0C046636EBE60C37A4F527B8B550F3" | | allowInsecure | `bool` | No | If you use https address, determine if you allow non secure transport (certificateThumbprint parameter will be ignored) | diff --git a/documentation/client-lndhub.md b/documentation/client-lndhub.md index 08b3e13..0da6e04 100644 --- a/documentation/client-lndhub.md +++ b/documentation/client-lndhub.md @@ -61,6 +61,7 @@ The `AddLndHubLightningClient` method has optionnal pamameters to configure your | address | `Uri` | Yes | Address of the LNDHub api (example : https://lndhub.herokuapp.com/) | | login | `String` | Yes | LNDHub login | | password | `String` | Yes | LNDHub Password | +| retryOnHttpError | `int` | No | Number of retry on http error | | certificateThumbprint | `String` | No | Certificate thumbprint used for your https address if the certificate is not public
Ex : "284800A04D0C046636EBE60C37A4F527B8B550F3" | | allowInsecure | `bool` | No | If you use https address, determine if you allow non secure transport (certificateThumbprint parameter will be ignored) | diff --git a/samples/LightningPay.Samples.WebAppMvc/Startup.cs b/samples/LightningPay.Samples.WebAppMvc/Startup.cs index 0f30fb7..608b203 100644 --- a/samples/LightningPay.Samples.WebAppMvc/Startup.cs +++ b/samples/LightningPay.Samples.WebAppMvc/Startup.cs @@ -52,7 +52,7 @@ public void ConfigureServices(IServiceCollection services) */ - services.AddLndLightningClient(new Uri("tcp://127.0.0.1:9835")); + services.AddLndLightningClient(new Uri("http://localhost:32736/")); } diff --git a/src/LightningPay.DependencyInjection/Extensions/EclairExtensions.cs b/src/LightningPay.DependencyInjection/Extensions/EclairExtensions.cs index a2cb1f1..bce7abd 100644 --- a/src/LightningPay.DependencyInjection/Extensions/EclairExtensions.cs +++ b/src/LightningPay.DependencyInjection/Extensions/EclairExtensions.cs @@ -2,6 +2,9 @@ using Microsoft.Extensions.DependencyInjection; +using Polly; +using Polly.Extensions.Http; + using LightningPay.Clients.Eclair; namespace LightningPay @@ -34,14 +37,14 @@ public static IServiceCollection AddEclairLightningClient(this IServiceCollectio /// The services. /// The address of the LNBits api. /// The password. + /// Number of retry on http error /// if set to true [allow insecure]. /// The certificate thumbprint. - /// - /// ServiceCollection - /// + /// ServiceCollection public static IServiceCollection AddEclairLightningClient(this IServiceCollection services, Uri address, string password, + int retryOnHttpError = 10, bool allowInsecure = false, string certificateThumbprint = null) { @@ -57,7 +60,11 @@ public static IServiceCollection AddEclairLightningClient(this IServiceCollectio CertificateThumbprint = certificateThumbprint.HexStringToByteArray() }); services.AddSingleton(); + services.AddHttpClient() + .AddPolicyHandler(HttpPolicyExtensions + .HandleTransientHttpError() + .WaitAndRetryAsync(retryOnHttpError, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)))) .ConfigurePrimaryHttpMessageHandler(); return services; diff --git a/src/LightningPay.DependencyInjection/Extensions/LNBitsExtensions.cs b/src/LightningPay.DependencyInjection/Extensions/LNBitsExtensions.cs index 9592b85..e3ffce2 100644 --- a/src/LightningPay.DependencyInjection/Extensions/LNBitsExtensions.cs +++ b/src/LightningPay.DependencyInjection/Extensions/LNBitsExtensions.cs @@ -2,6 +2,9 @@ using Microsoft.Extensions.DependencyInjection; +using Polly; +using Polly.Extensions.Http; + using LightningPay.Clients.LNBits; namespace LightningPay @@ -34,6 +37,7 @@ public static IServiceCollection AddLNBitsLightningClient(this IServiceCollectio /// The services. /// The address of the LNBits api. /// The api key. + /// Number of retry on http error /// if set to true [allow insecure]. /// The certificate thumbprint. /// @@ -42,6 +46,7 @@ public static IServiceCollection AddLNBitsLightningClient(this IServiceCollectio public static IServiceCollection AddLNBitsLightningClient(this IServiceCollection services, Uri address, string apiKey, + int retryOnHttpError = 10, bool allowInsecure = false, string certificateThumbprint = null) { @@ -57,7 +62,11 @@ public static IServiceCollection AddLNBitsLightningClient(this IServiceCollectio CertificateThumbprint = certificateThumbprint.HexStringToByteArray() }); services.AddSingleton(); + services.AddHttpClient() + .AddPolicyHandler(HttpPolicyExtensions + .HandleTransientHttpError() + .WaitAndRetryAsync(retryOnHttpError, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)))) .ConfigurePrimaryHttpMessageHandler(); return services; diff --git a/src/LightningPay.DependencyInjection/Extensions/LndExtensions.cs b/src/LightningPay.DependencyInjection/Extensions/LndExtensions.cs index 75d910f..f035a04 100644 --- a/src/LightningPay.DependencyInjection/Extensions/LndExtensions.cs +++ b/src/LightningPay.DependencyInjection/Extensions/LndExtensions.cs @@ -1,9 +1,12 @@ using System; +using System.Net; +using System.Net.Http; using Microsoft.Extensions.DependencyInjection; -using LightningPay.Clients.Lnd; +using Polly; +using LightningPay.Clients.Lnd; namespace LightningPay { @@ -50,6 +53,7 @@ public static IServiceCollection AddLndLightningClient(this IServiceCollection s /// The services. /// The address of the LND server. /// The macaroon hexadecimal string. + /// Number of retry on http error /// if set to true [allow insecure]. /// The certificate thumbprint. /// @@ -58,20 +62,23 @@ public static IServiceCollection AddLndLightningClient(this IServiceCollection s public static IServiceCollection AddLndLightningClient(this IServiceCollection services, Uri address, string macaroonHexString = null, + int retryOnHttpError = 10, bool allowInsecure = false, string certificateThumbprint = null) { return AddLndLightningClient(services, address, macaroonHexString.HexStringToByteArray(), - allowInsecure, - certificateThumbprint); + retryOnHttpError: retryOnHttpError, + allowInsecure: allowInsecure, + certificateThumbprint: certificateThumbprint); } /// Adds the LND lightning client. /// The services. /// The address of the LND server. /// The macaroon bytes. + /// Number of retry on http error /// if set to true [allow insecure]. /// The certificate thumbprint. /// @@ -80,6 +87,7 @@ public static IServiceCollection AddLndLightningClient(this IServiceCollection s public static IServiceCollection AddLndLightningClient(this IServiceCollection services, Uri address, byte[] macaroonBytes = null, + int retryOnHttpError = 10, bool allowInsecure = false, string certificateThumbprint = null) { @@ -96,7 +104,13 @@ public static IServiceCollection AddLndLightningClient(this IServiceCollection s CertificateThumbprint = certificateThumbprint.HexStringToByteArray() }); services.AddSingleton(); + services.AddHttpClient() + .AddPolicyHandler(Policy + // Add custom handling to exclude 500 because api return 500 for bad request errors + .Handle() + .OrResult(r => (int)r.StatusCode > 500 || r.StatusCode == HttpStatusCode.RequestTimeout) + .WaitAndRetryAsync(retryOnHttpError, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)))) .ConfigurePrimaryHttpMessageHandler(); return services; diff --git a/src/LightningPay.DependencyInjection/Extensions/LndHubExtensions.cs b/src/LightningPay.DependencyInjection/Extensions/LndHubExtensions.cs index 256875b..0e4b1ca 100644 --- a/src/LightningPay.DependencyInjection/Extensions/LndHubExtensions.cs +++ b/src/LightningPay.DependencyInjection/Extensions/LndHubExtensions.cs @@ -2,6 +2,9 @@ using Microsoft.Extensions.DependencyInjection; +using Polly; +using Polly.Extensions.Http; + using LightningPay.Clients.LndHub; namespace LightningPay @@ -37,6 +40,7 @@ public static IServiceCollection AddLndHubLightningClient(this IServiceCollectio /// The address of the LNDHub. /// The login. /// The password. + /// Number of retry on http error /// if set to true [allow insecure]. /// The certificate thumbprint. /// @@ -46,6 +50,7 @@ public static IServiceCollection AddLndHubLightningClient(this IServiceCollectio Uri address, string login, string password, + int retryOnHttpError = 10, bool allowInsecure = false, string certificateThumbprint = null) { @@ -62,7 +67,11 @@ public static IServiceCollection AddLndHubLightningClient(this IServiceCollectio CertificateThumbprint = certificateThumbprint.HexStringToByteArray() }); services.AddSingleton(); + services.AddHttpClient() + .AddPolicyHandler(HttpPolicyExtensions + .HandleTransientHttpError() + .WaitAndRetryAsync(retryOnHttpError, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)))) .ConfigurePrimaryHttpMessageHandler(); return services; diff --git a/src/LightningPay.DependencyInjection/LightningPay.DependencyInjection.csproj b/src/LightningPay.DependencyInjection/LightningPay.DependencyInjection.csproj index 8d1d6ad..fbadf4f 100644 --- a/src/LightningPay.DependencyInjection/LightningPay.DependencyInjection.csproj +++ b/src/LightningPay.DependencyInjection/LightningPay.DependencyInjection.csproj @@ -19,27 +19,27 @@ - + - + - + - + - + diff --git a/tests/LightningPay.IntegrationTest/CLightningClientIntegrationTest.cs b/tests/LightningPay.IntegrationTest/CLightningClientIntegrationTest.cs index 753b784..dbbbab2 100644 --- a/tests/LightningPay.IntegrationTest/CLightningClientIntegrationTest.cs +++ b/tests/LightningPay.IntegrationTest/CLightningClientIntegrationTest.cs @@ -1,6 +1,6 @@ -using System.Threading.Tasks; +using System; -using LightningPay.Clients.CLightning; +using Microsoft.Extensions.DependencyInjection; namespace LightningPay.IntegrationTest { @@ -8,11 +8,10 @@ public class CLightningClientIntegrationTest : LightningClientIntegrationTestBas { protected override bool NeedBitcoind => true; - protected override Task GetClient() - { - ILightningClient client = CLightningClient.New("tcp://127.0.0.1:48532"); - return Task.FromResult(client); + protected override void ConfigureServices(IServiceCollection services) + { + services.AddCLightningClient(new Uri("tcp://127.0.0.1:48532")); } protected override string SelfPaymentErrorMesssage => "Error code 210"; diff --git a/tests/LightningPay.IntegrationTest/EclairClientIntegrationTest.cs b/tests/LightningPay.IntegrationTest/EclairClientIntegrationTest.cs index 6e63feb..efd1c2c 100644 --- a/tests/LightningPay.IntegrationTest/EclairClientIntegrationTest.cs +++ b/tests/LightningPay.IntegrationTest/EclairClientIntegrationTest.cs @@ -1,6 +1,6 @@ -using System.Threading.Tasks; +using System; -using LightningPay.Clients.Eclair; +using Microsoft.Extensions.DependencyInjection; namespace LightningPay.IntegrationTest { @@ -8,11 +8,9 @@ public class EclairClientIntegrationTest : LightningClientIntegrationTestBase { protected override bool NeedBitcoind => true; - protected override Task GetClient() + protected override void ConfigureServices(IServiceCollection services) { - ILightningClient client = EclairClient.New("http://localhost:4570/", "eclairpassword"); - - return Task.FromResult(client); + services.AddEclairLightningClient(new Uri("http://localhost:4570/"), "eclairpassword"); } protected override string SelfPaymentErrorMesssage => "Cannot process to the payment"; diff --git a/tests/LightningPay.IntegrationTest/LNBitsClientIntegrationTest.cs b/tests/LightningPay.IntegrationTest/LNBitsClientIntegrationTest.cs index 0903c62..138b8e2 100644 --- a/tests/LightningPay.IntegrationTest/LNBitsClientIntegrationTest.cs +++ b/tests/LightningPay.IntegrationTest/LNBitsClientIntegrationTest.cs @@ -1,6 +1,6 @@ -using System.Threading.Tasks; +using System; -using LightningPay.Clients.LNBits; +using Microsoft.Extensions.DependencyInjection; namespace LightningPay.IntegrationTest { @@ -8,12 +8,9 @@ public class LNBitsClientIntegrationTest : LightningClientIntegrationTestBase { protected override bool NeedBitcoind => false; - protected override Task GetClient() + protected override void ConfigureServices(IServiceCollection services) { - ILightningClient client = - LNBitsClient.New("https://lnbits.lndev.link/", apiKey: "0f920e085f96413fa754b73b9895abbd"); - - return Task.FromResult(client); + services.AddLNBitsLightningClient(new Uri("https://lnbits.lndev.link/"), "0f920e085f96413fa754b73b9895abbd"); } protected override string SelfPaymentErrorMesssage => "Insufficient balance"; diff --git a/tests/LightningPay.IntegrationTest/LNHubClientIntegrationTest.cs b/tests/LightningPay.IntegrationTest/LNHubClientIntegrationTest.cs index 30aeb18..ea8bb13 100644 --- a/tests/LightningPay.IntegrationTest/LNHubClientIntegrationTest.cs +++ b/tests/LightningPay.IntegrationTest/LNHubClientIntegrationTest.cs @@ -1,6 +1,6 @@ -using System.Threading.Tasks; +using System; -using LightningPay.Clients.LndHub; +using Microsoft.Extensions.DependencyInjection; namespace LightningPay.IntegrationTest { @@ -8,50 +8,12 @@ public class LNHubClientIntegrationTest : LightningClientIntegrationTestBase { protected override bool NeedBitcoind => false; - protected async override Task GetClient() + protected override void ConfigureServices(IServiceCollection services) { - ILightningClient client = LndHubClient.New("https://lndhub.herokuapp.com/"); - - var wallet = await client.CreateLndHubWallet(); - - client = LndHubClient.New("https://lndhub.herokuapp.com/", - login: wallet.Login, - password: wallet.Password); - - return client; + services.AddLndHubLightningClient(new Uri("https://lndhub.herokuapp.com/"), "2073282b83fad2955b57", "a1c4f8c30a93bf3e8cbf"); } protected override string SelfPaymentErrorMesssage => "not enough balance"; } - internal static class LNHubClientIntegrationTestExtensions - { - internal async static Task CreateLndHubWallet(this ILightningClient client) - { - return await client.ToRestClient() - .Post("/create", new CreateWalletRequest() - { - PartnerId = "bluewallet", - AccountType = "test" - }); - } - - internal class CreateWalletRequest - { - [Serializable("partnerid")] - public string PartnerId { get; set; } - - [Serializable("accounttype")] - public string AccountType { get; set; } - } - - internal class CreateWalletResponse - { - [Serializable("login")] - public string Login { get; set; } - - [Serializable("password")] - public string Password { get; set; } - } - } } diff --git a/tests/LightningPay.IntegrationTest/LightningClientIntegrationTestBase.cs b/tests/LightningPay.IntegrationTest/LightningClientIntegrationTestBase.cs index 8030534..b708ebe 100644 --- a/tests/LightningPay.IntegrationTest/LightningClientIntegrationTestBase.cs +++ b/tests/LightningPay.IntegrationTest/LightningClientIntegrationTestBase.cs @@ -1,7 +1,10 @@ -using NBitcoin; +using System.Threading.Tasks; + +using Microsoft.Extensions.DependencyInjection; + +using NBitcoin; using NBitcoin.RPC; -using System; -using System.Threading.Tasks; + using Xunit; @@ -12,7 +15,11 @@ public abstract class LightningClientIntegrationTestBase [Fact(Timeout =(2 * 60 * 1000))] public async Task Run() { - var client = await this.GetClient(); + IServiceCollection serviceCollection = new ServiceCollection(); + this.ConfigureServices(serviceCollection); + var serviceProvider = serviceCollection.BuildServiceProvider(); + + var client = serviceProvider.GetRequiredService(); await this.WaitServersAreUp(client); @@ -63,9 +70,9 @@ protected async Task WaitServersAreUp(ILightningClient client) } } - protected abstract bool NeedBitcoind { get; } + protected abstract void ConfigureServices(IServiceCollection services); - protected abstract Task GetClient(); + protected abstract bool NeedBitcoind { get; } protected abstract string SelfPaymentErrorMesssage { get; } diff --git a/tests/LightningPay.IntegrationTest/LightningPay.IntegrationTest.csproj b/tests/LightningPay.IntegrationTest/LightningPay.IntegrationTest.csproj index 5535e89..d208b51 100644 --- a/tests/LightningPay.IntegrationTest/LightningPay.IntegrationTest.csproj +++ b/tests/LightningPay.IntegrationTest/LightningPay.IntegrationTest.csproj @@ -21,7 +21,7 @@ - + diff --git a/tests/LightningPay.IntegrationTest/LndClientIntegrationTest.cs b/tests/LightningPay.IntegrationTest/LndClientIntegrationTest.cs index 77b32c2..a1e7d8b 100644 --- a/tests/LightningPay.IntegrationTest/LndClientIntegrationTest.cs +++ b/tests/LightningPay.IntegrationTest/LndClientIntegrationTest.cs @@ -1,6 +1,6 @@ -using System.Threading.Tasks; +using System; -using LightningPay.Clients.Lnd; +using Microsoft.Extensions.DependencyInjection; namespace LightningPay.IntegrationTest { @@ -8,11 +8,9 @@ public class LndClientIntegrationTest : LightningClientIntegrationTestBase { protected override bool NeedBitcoind => true; - protected override Task GetClient() + protected override void ConfigureServices(IServiceCollection services) { - ILightningClient client = LndClient.New("http://localhost:32736/"); - - return Task.FromResult(client); + services.AddLndLightningClient(new Uri("http://localhost:32736/")); } protected override string SelfPaymentErrorMesssage => "self-payments not allowed";