mirror of
https://github.com/aljazceru/btcpayserver.git
synced 2025-12-17 14:04:26 +01:00
[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:
@@ -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>
|
||||||
|
|||||||
@@ -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; }
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
Reference in New Issue
Block a user