diff --git a/BTCPayServer.Client/BTCPayServer.Client.csproj b/BTCPayServer.Client/BTCPayServer.Client.csproj index 88579cfab..4f8b94d29 100644 --- a/BTCPayServer.Client/BTCPayServer.Client.csproj +++ b/BTCPayServer.Client/BTCPayServer.Client.csproj @@ -28,7 +28,7 @@ - + diff --git a/BTCPayServer.Client/Models/CreateLightningInvoiceRequest.cs b/BTCPayServer.Client/Models/CreateLightningInvoiceRequest.cs index 9e373eea7..d80a119b3 100644 --- a/BTCPayServer.Client/Models/CreateLightningInvoiceRequest.cs +++ b/BTCPayServer.Client/Models/CreateLightningInvoiceRequest.cs @@ -22,8 +22,7 @@ namespace BTCPayServer.Client.Models [JsonConverter(typeof(JsonConverters.LightMoneyJsonConverter))] public LightMoney Amount { get; set; } public string Description { get; set; } - [JsonConverter(typeof(NBitcoin.JsonConverters.UInt256JsonConverter))] - public uint256 DescriptionHash { get; set; } + public bool DescriptionHashOnly { get; set; } [JsonConverter(typeof(JsonConverters.TimeSpanJsonConverter.Seconds))] public TimeSpan Expiry { get; set; } public bool PrivateRouteHints { get; set; } diff --git a/BTCPayServer.Tests/GreenfieldAPITests.cs b/BTCPayServer.Tests/GreenfieldAPITests.cs index f09e1b8b3..df1a72ed4 100644 --- a/BTCPayServer.Tests/GreenfieldAPITests.cs +++ b/BTCPayServer.Tests/GreenfieldAPITests.cs @@ -2197,6 +2197,49 @@ namespace BTCPayServer.Tests await AssertPermissionError("btcpay.store.canuselightningnode", () => client.GetLightningNodeInfo(user.StoreId, "BTC")); } + [Fact(Timeout = 60 * 20 * 1000)] + [Trait("Integration", "Integration")] + [Trait("Lightning", "Lightning")] + public async Task CanUseLightningAPI2() + { + using var tester = CreateServerTester(); + tester.ActivateLightning(); + await tester.StartAsync(); + await tester.EnsureChannelsSetup(); + var user = tester.NewAccount(); + await user.GrantAccessAsync(true); + + var types = new[] { LightningConnectionType.LndREST, LightningConnectionType.CLightning }; + foreach (var type in types) + { + user.RegisterLightningNode("BTC", type); + var client = await user.CreateClient("btcpay.store.cancreatelightninginvoice"); + var amount = LightMoney.Satoshis(1000); + var expiry = TimeSpan.FromSeconds(600); + + var invoice = await client.CreateLightningInvoice(user.StoreId, "BTC", new CreateLightningInvoiceRequest + { + Amount = amount, + Expiry = expiry, + Description = "Hashed description", + DescriptionHashOnly = true + }); + var bolt11 = BOLT11PaymentRequest.Parse(invoice.BOLT11, Network.RegTest); + Assert.NotNull(bolt11.DescriptionHash); + Assert.Null(bolt11.ShortDescription); + + invoice = await client.CreateLightningInvoice(user.StoreId, "BTC", new CreateLightningInvoiceRequest + { + Amount = amount, + Expiry = expiry, + Description = "Standard description", + }); + bolt11 = BOLT11PaymentRequest.Parse(invoice.BOLT11, Network.RegTest); + Assert.Null(bolt11.DescriptionHash); + Assert.NotNull(bolt11.ShortDescription); + } + } + [Fact(Timeout = TestTimeout)] [Trait("Integration", "Integration")] public async Task NotificationAPITests() diff --git a/BTCPayServer/Controllers/GreenField/GreenfieldLightningNodeApiController.cs b/BTCPayServer/Controllers/GreenField/GreenfieldLightningNodeApiController.cs index 0291b9254..2f829d14d 100644 --- a/BTCPayServer/Controllers/GreenField/GreenfieldLightningNodeApiController.cs +++ b/BTCPayServer/Controllers/GreenField/GreenfieldLightningNodeApiController.cs @@ -295,27 +295,28 @@ namespace BTCPayServer.Controllers.Greenfield ModelState.AddModelError(nameof(request.Amount), "Amount should be more or equals to 0"); } + if (request.Description is null && request.DescriptionHashOnly) + { + ModelState.AddModelError(nameof(request.Description), "Description is required when `descriptionHashOnly` is true"); + } + if (request.Expiry <= TimeSpan.Zero) { ModelState.AddModelError(nameof(request.Expiry), "Expiry should be more than 0"); } - if (!ModelState.IsValid) { return this.CreateValidationError(ModelState); } - + + request.Description ??= ""; try { - var param = request.DescriptionHash != null - ? new CreateInvoiceParams(request.Amount, request.DescriptionHash, request.Expiry) + var param = new CreateInvoiceParams(request.Amount, request.Description, request.Expiry) { - PrivateRouteHints = request.PrivateRouteHints, Description = request.Description - } - : new CreateInvoiceParams(request.Amount, request.Description, request.Expiry) - { - PrivateRouteHints = request.PrivateRouteHints, DescriptionHash = request.DescriptionHash - }; + PrivateRouteHints = request.PrivateRouteHints, + DescriptionHashOnly = request.DescriptionHashOnly + }; var invoice = await lightningClient.CreateInvoice(param, cancellationToken); return Ok(ToModel(invoice)); } diff --git a/BTCPayServer/Controllers/UILNURLController.cs b/BTCPayServer/Controllers/UILNURLController.cs index c24d90fb9..96e5cb692 100644 --- a/BTCPayServer/Controllers/UILNURLController.cs +++ b/BTCPayServer/Controllers/UILNURLController.cs @@ -565,14 +565,14 @@ namespace BTCPayServer } } - var descriptionHash = new uint256(Hashes.SHA256(Encoding.UTF8.GetBytes(metadata)), false); LightningInvoice invoice; try { var expiry = i.ExpirationTime.ToUniversalTime() - DateTimeOffset.UtcNow; - var param = new CreateInvoiceParams(amount.Value, descriptionHash, expiry) + var param = new CreateInvoiceParams(amount.Value, metadata, expiry) { - PrivateRouteHints = blob.LightningPrivateRouteHints + PrivateRouteHints = blob.LightningPrivateRouteHints, + DescriptionHashOnly = true }; invoice = await client.CreateInvoice(param); if (!BOLT11PaymentRequest.Parse(invoice.BOLT11, network.NBitcoinNetwork) diff --git a/BTCPayServer/wwwroot/swagger/v1/swagger.template.lightning.common.json b/BTCPayServer/wwwroot/swagger/v1/swagger.template.lightning.common.json index 7418d99fb..0d875a154 100644 --- a/BTCPayServer/wwwroot/swagger/v1/swagger.template.lightning.common.json +++ b/BTCPayServer/wwwroot/swagger/v1/swagger.template.lightning.common.json @@ -25,11 +25,11 @@ "nullable": true, "description": "Description of the invoice in the BOLT11" }, - "descriptionHash": { - "type": "string", - "format": "hex", + "descriptionHashOnly": { + "type": "boolean", "nullable": true, - "description": "Description hash of the invoice in the BOLT11" + "default": false, + "description": "If `descriptionHashOnly` is `true` (default is `false`), then the BOLT11 returned contains a hash of the `description`, rather than the `description`, itself. This allows for much longer descriptions, but they must be communicated via some other mechanism." }, "expiry": { "description": "Expiration time in seconds",