mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2025-02-21 22:11:48 +01:00
Prevent creation of on-chain invoices below the dust limit (#3082)
* Prevent creation of on-chain invoices below the dust limit Fixes #3071. * Apply suggestions from code review Co-authored-by: Nicolas Dorier <nicolas.dorier@gmail.com> * small fix * Fix selenium test 0.000000012 BTC (whether rounded or not) is below the dust threshold, causing this test to fail. * fix CanCreateTopupInvoices test Don't apply dust threshold conditional for topup invoices. * Fix test, and minor changes Co-authored-by: Nicolas Dorier <nicolas.dorier@gmail.com>
This commit is contained in:
parent
c5dc7475a6
commit
cbcd59c996
7 changed files with 34 additions and 8 deletions
|
@ -27,7 +27,7 @@
|
|||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="NBitcoin" Version="6.0.15" />
|
||||
<PackageReference Include="NBitcoin" Version="6.0.17" />
|
||||
<PackageReference Include="BTCPayServer.Lightning.Common" Version="1.2.7" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
</ItemGroup>
|
||||
|
|
|
@ -493,7 +493,7 @@ namespace BTCPayServer.Tests
|
|||
var client = new NBitpayClient.Bitpay(new Key(), s.ServerUri);
|
||||
await client.AuthorizeClient(new NBitpayClient.PairingCode(pairingCode));
|
||||
await client.CreateInvoiceAsync(
|
||||
new NBitpayClient.Invoice() { Price = 0.000000012m, Currency = "USD", FullNotifications = true },
|
||||
new NBitpayClient.Invoice() { Price = 1.000000012m, Currency = "USD", FullNotifications = true },
|
||||
NBitpayClient.Facade.Merchant);
|
||||
|
||||
client = new NBitpayClient.Bitpay(new Key(), s.ServerUri);
|
||||
|
@ -503,7 +503,7 @@ namespace BTCPayServer.Tests
|
|||
s.Driver.FindElement(By.Id("ApprovePairing")).Click();
|
||||
|
||||
await client.CreateInvoiceAsync(
|
||||
new NBitpayClient.Invoice() { Price = 0.000000012m, Currency = "USD", FullNotifications = true },
|
||||
new NBitpayClient.Invoice() { Price = 1.000000012m, Currency = "USD", FullNotifications = true },
|
||||
NBitpayClient.Facade.Merchant);
|
||||
|
||||
s.Driver.Navigate().GoToUrl(s.Link("/api-tokens"));
|
||||
|
|
|
@ -3004,15 +3004,31 @@ namespace BTCPayServer.Tests
|
|||
|
||||
[Fact(Timeout = LongRunningTestTimeout)]
|
||||
[Trait("Integration", "Integration")]
|
||||
[Trait("Lightning", "Lightning")]
|
||||
public async Task CanCreateStrangeInvoice()
|
||||
{
|
||||
using (var tester = ServerTester.Create())
|
||||
{
|
||||
tester.ActivateLightning();
|
||||
await tester.StartAsync();
|
||||
var user = tester.NewAccount();
|
||||
user.GrantAccess();
|
||||
user.GrantAccess(true);
|
||||
user.RegisterDerivationScheme("BTC");
|
||||
|
||||
DateTimeOffset expiration = DateTimeOffset.UtcNow + TimeSpan.FromMinutes(21);
|
||||
|
||||
// This should fail, the amount is too low to be above the dust limit of bitcoin
|
||||
var ex = Assert.Throws<BitPayException>(() => user.BitPay.CreateInvoice(
|
||||
new Invoice()
|
||||
{
|
||||
Price = 0.000000012m,
|
||||
Currency = "USD",
|
||||
FullNotifications = true,
|
||||
ExpirationTime = expiration
|
||||
}, Facade.Merchant));
|
||||
Assert.Contains("dust threshold", ex.Message);
|
||||
await user.RegisterLightningNodeAsync("BTC");
|
||||
|
||||
var invoice1 = user.BitPay.CreateInvoice(
|
||||
new Invoice()
|
||||
{
|
||||
|
@ -3021,6 +3037,7 @@ namespace BTCPayServer.Tests
|
|||
FullNotifications = true,
|
||||
ExpirationTime = expiration
|
||||
}, Facade.Merchant);
|
||||
|
||||
Assert.Equal(expiration.ToUnixTimeSeconds(), invoice1.ExpirationTime.ToUnixTimeSeconds());
|
||||
var invoice2 = user.BitPay.CreateInvoice(new Invoice() { Price = 0.000000019m, Currency = "USD" },
|
||||
Facade.Merchant);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
<Import Project="../Build/Version.csproj" Condition="Exists('../Build/Version.csproj')" />
|
||||
<Import Project="../Build/Common.csproj" />
|
||||
|
||||
|
|
|
@ -136,7 +136,7 @@ public class BitcoinLikePayoutHandler : IPayoutHandler
|
|||
claimDestination is IBitcoinLikeClaimDestination bitcoinLikeClaimDestination)
|
||||
{
|
||||
txout.ScriptPubKey = bitcoinLikeClaimDestination.Address.ScriptPubKey;
|
||||
return Task.FromResult(txout.GetDustThreshold(new FeeRate(1.0m)).ToDecimal(MoneyUnit.BTC));
|
||||
return Task.FromResult(txout.GetDustThreshold().ToDecimal(MoneyUnit.BTC));
|
||||
}
|
||||
|
||||
return Task.FromResult(0m);
|
||||
|
|
|
@ -190,6 +190,15 @@ namespace BTCPayServer.Payments.Bitcoin
|
|||
}
|
||||
|
||||
var reserved = await prepare.ReserveAddress;
|
||||
if (paymentMethod.ParentEntity.Type != InvoiceType.TopUp)
|
||||
{
|
||||
var txOut = network.NBitcoinNetwork.Consensus.ConsensusFactory.CreateTxOut();
|
||||
txOut.ScriptPubKey = reserved.Address.ScriptPubKey;
|
||||
var dust = txOut.GetDustThreshold();
|
||||
var amount = paymentMethod.Calculate().Due;
|
||||
if (amount < dust)
|
||||
throw new PaymentMethodUnavailableException("Amount below the dust threshold. For amounts of this size, it is recommended to enable an off-chain (Lightning) payment method");
|
||||
}
|
||||
onchainMethod.DepositAddress = reserved.Address.ToString();
|
||||
onchainMethod.KeyPath = reserved.KeyPath;
|
||||
onchainMethod.PayjoinEnabled = blob.PayJoinEnabled &&
|
||||
|
|
|
@ -424,7 +424,7 @@ namespace BTCPayServer.Payments.PayJoin
|
|||
{
|
||||
var outputContribution = Money.Min(additionalFee, -due);
|
||||
outputContribution = Money.Min(outputContribution,
|
||||
newTx.Outputs[i].Value - newTx.Outputs[i].GetDustThreshold(minRelayTxFee));
|
||||
newTx.Outputs[i].Value - newTx.Outputs[i].GetDustThreshold());
|
||||
newTx.Outputs[i].Value -= outputContribution;
|
||||
additionalFee -= outputContribution;
|
||||
due += outputContribution;
|
||||
|
@ -437,7 +437,7 @@ namespace BTCPayServer.Payments.PayJoin
|
|||
{
|
||||
var outputContribution = Money.Min(additionalFee, feeOutput.Value);
|
||||
outputContribution = Money.Min(outputContribution,
|
||||
feeOutput.Value - feeOutput.GetDustThreshold(minRelayTxFee));
|
||||
feeOutput.Value - feeOutput.GetDustThreshold());
|
||||
outputContribution = Money.Min(outputContribution, allowedSenderFeeContribution);
|
||||
feeOutput.Value -= outputContribution;
|
||||
additionalFee -= outputContribution;
|
||||
|
|
Loading…
Add table
Reference in a new issue