diff --git a/BTCPayServer.Tests/PayJoinTests.cs b/BTCPayServer.Tests/PayJoinTests.cs index 8c457e48c..5678c139b 100644 --- a/BTCPayServer.Tests/PayJoinTests.cs +++ b/BTCPayServer.Tests/PayJoinTests.cs @@ -87,6 +87,19 @@ namespace BTCPayServer.Tests Assert.True(await repo.TryLock(outpoint)); Assert.True(await repo.TryUnlock(outpoint)); Assert.False(await repo.TryUnlock(outpoint)); + + // Make sure that if any can't be locked, all are not locked + var outpoint1 = RandomOutpoint(); + var outpoint2 = RandomOutpoint(); + Assert.True(await repo.TryLockInputs(new[] { outpoint1 })); + Assert.False(await repo.TryLockInputs(new[] { outpoint1, outpoint2 })); + Assert.True(await repo.TryLockInputs(new[] { outpoint2 })); + + outpoint1 = RandomOutpoint(); + outpoint2 = RandomOutpoint(); + Assert.True(await repo.TryLockInputs(new[] { outpoint1 })); + Assert.False(await repo.TryLockInputs(new[] { outpoint2, outpoint1 })); + Assert.True(await repo.TryLockInputs(new[] { outpoint2 })); } } @@ -901,10 +914,6 @@ retry: .SendEstimatedFees(new FeeRate(100m)) .BuildTransaction(true); - //Attempt 1: Send a signed tx to invoice 1 that does not pay the invoice at all - //Result: reject - // Assert.False((await tester.PayTester.HttpClient.PostAsync(endpoint, - // new StringContent(Invoice2Coin1.ToHex(), Encoding.UTF8, "text/plain"))).IsSuccessStatusCode); //Attempt 2: Create two transactions using different inputs and send them to the same invoice. //Result: Second Tx should be rejected. diff --git a/BTCPayServer/Payments/PayJoin/PayJoinEndpointController.cs b/BTCPayServer/Payments/PayJoin/PayJoinEndpointController.cs index 4e3337302..3438e8c38 100644 --- a/BTCPayServer/Payments/PayJoin/PayJoinEndpointController.cs +++ b/BTCPayServer/Payments/PayJoin/PayJoinEndpointController.cs @@ -83,35 +83,30 @@ namespace BTCPayServer.Payments.PayJoin private readonly BTCPayNetworkProvider _btcPayNetworkProvider; private readonly InvoiceRepository _invoiceRepository; private readonly ExplorerClientProvider _explorerClientProvider; - private readonly StoreRepository _storeRepository; private readonly BTCPayWalletProvider _btcPayWalletProvider; private readonly PayJoinRepository _payJoinRepository; private readonly EventAggregator _eventAggregator; private readonly NBXplorerDashboard _dashboard; private readonly DelayedTransactionBroadcaster _broadcaster; - private readonly WalletRepository _walletRepository; private readonly BTCPayServerEnvironment _env; public PayJoinEndpointController(BTCPayNetworkProvider btcPayNetworkProvider, InvoiceRepository invoiceRepository, ExplorerClientProvider explorerClientProvider, - StoreRepository storeRepository, BTCPayWalletProvider btcPayWalletProvider, + BTCPayWalletProvider btcPayWalletProvider, PayJoinRepository payJoinRepository, EventAggregator eventAggregator, NBXplorerDashboard dashboard, DelayedTransactionBroadcaster broadcaster, - WalletRepository walletRepository, BTCPayServerEnvironment env) { _btcPayNetworkProvider = btcPayNetworkProvider; _invoiceRepository = invoiceRepository; _explorerClientProvider = explorerClientProvider; - _storeRepository = storeRepository; _btcPayWalletProvider = btcPayWalletProvider; _payJoinRepository = payJoinRepository; _eventAggregator = eventAggregator; _dashboard = dashboard; _broadcaster = broadcaster; - _walletRepository = walletRepository; _env = env; } @@ -285,6 +280,8 @@ namespace BTCPayServer.Payments.PayJoin if (!await _payJoinRepository.TryLockInputs(ctx.OriginalTransaction.Inputs.Select(i => i.PrevOut).ToArray())) { + // We do not broadcast, since we might double spend a delayed transaction of a previous payjoin + ctx.DoNotBroadcast(); return CreatePayjoinErrorAndLog(503, PayjoinReceiverWellknownErrors.Unavailable, "Some of those inputs have already been used to make another payjoin transaction"); } diff --git a/BTCPayServer/Payments/PayJoin/PayJoinRepository.cs b/BTCPayServer/Payments/PayJoin/PayJoinRepository.cs index 68d331419..98d7091a8 100644 --- a/BTCPayServer/Payments/PayJoin/PayJoinRepository.cs +++ b/BTCPayServer/Payments/PayJoin/PayJoinRepository.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using System.Threading.Tasks; using BTCPayServer.Data; using Microsoft.EntityFrameworkCore;