[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
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" /> <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
</ItemGroup> </ItemGroup>
<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="NBitcoin" Version="7.0.20" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
</ItemGroup> </ItemGroup>

View File

@@ -22,8 +22,7 @@ namespace BTCPayServer.Client.Models
[JsonConverter(typeof(JsonConverters.LightMoneyJsonConverter))] [JsonConverter(typeof(JsonConverters.LightMoneyJsonConverter))]
public LightMoney Amount { get; set; } public LightMoney Amount { get; set; }
public string Description { get; set; } public string Description { get; set; }
[JsonConverter(typeof(NBitcoin.JsonConverters.UInt256JsonConverter))] public bool DescriptionHashOnly { get; set; }
public uint256 DescriptionHash { get; set; }
[JsonConverter(typeof(JsonConverters.TimeSpanJsonConverter.Seconds))] [JsonConverter(typeof(JsonConverters.TimeSpanJsonConverter.Seconds))]
public TimeSpan Expiry { get; set; } public TimeSpan Expiry { get; set; }
public bool PrivateRouteHints { 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")); 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)] [Fact(Timeout = TestTimeout)]
[Trait("Integration", "Integration")] [Trait("Integration", "Integration")]
public async Task NotificationAPITests() 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"); 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) if (request.Expiry <= TimeSpan.Zero)
{ {
ModelState.AddModelError(nameof(request.Expiry), "Expiry should be more than 0"); ModelState.AddModelError(nameof(request.Expiry), "Expiry should be more than 0");
} }
if (!ModelState.IsValid) if (!ModelState.IsValid)
{ {
return this.CreateValidationError(ModelState); return this.CreateValidationError(ModelState);
} }
request.Description ??= "";
try try
{ {
var param = request.DescriptionHash != null var param = new CreateInvoiceParams(request.Amount, request.Description, request.Expiry)
? new CreateInvoiceParams(request.Amount, request.DescriptionHash, request.Expiry)
{ {
PrivateRouteHints = request.PrivateRouteHints, Description = request.Description PrivateRouteHints = request.PrivateRouteHints,
} DescriptionHashOnly = request.DescriptionHashOnly
: new CreateInvoiceParams(request.Amount, request.Description, request.Expiry) };
{
PrivateRouteHints = request.PrivateRouteHints, DescriptionHash = request.DescriptionHash
};
var invoice = await lightningClient.CreateInvoice(param, cancellationToken); var invoice = await lightningClient.CreateInvoice(param, cancellationToken);
return Ok(ToModel(invoice)); 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; LightningInvoice invoice;
try try
{ {
var expiry = i.ExpirationTime.ToUniversalTime() - DateTimeOffset.UtcNow; 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); invoice = await client.CreateInvoice(param);
if (!BOLT11PaymentRequest.Parse(invoice.BOLT11, network.NBitcoinNetwork) if (!BOLT11PaymentRequest.Parse(invoice.BOLT11, network.NBitcoinNetwork)

View File

@@ -25,11 +25,11 @@
"nullable": true, "nullable": true,
"description": "Description of the invoice in the BOLT11" "description": "Description of the invoice in the BOLT11"
}, },
"descriptionHash": { "descriptionHashOnly": {
"type": "string", "type": "boolean",
"format": "hex",
"nullable": true, "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": { "expiry": {
"description": "Expiration time in seconds", "description": "Expiration time in seconds",