[Greenfield]: Add DescriptionHashOnly to include a description hash in the BOLT11 (#4411)

* [Greenfield]: Add DescriptionHashOnly to include a description hash in the BOLT11

* Add CLN test case

* Improve description in Swagger file

Co-authored-by: Dennis Reimann <mail@dennisreimann.de>
This commit is contained in:
Nicolas Dorier 2022-12-13 18:56:33 +09:00 committed by GitHub
parent e2c5e2c7fb
commit cdac238f6d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 63 additions and 20 deletions

View File

@ -28,7 +28,7 @@
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="BTCPayServer.Lightning.Common" Version="1.3.16" />
<PackageReference Include="BTCPayServer.Lightning.Common" Version="1.3.17" />
<PackageReference Include="NBitcoin" Version="7.0.20" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
</ItemGroup>

View File

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

View File

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

View File

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

View File

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

View File

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