If an input already used in a payjoin is reused in another, we should not attempt to broadcast the original transaction.

This commit is contained in:
nicolas.dorier 2021-03-24 13:48:33 +09:00
parent 749d26fafa
commit a128685b83
No known key found for this signature in database
GPG key ID: 6618763EF09186FE
3 changed files with 17 additions and 10 deletions

View file

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

View file

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

View file

@ -1,3 +1,4 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using BTCPayServer.Data;
using Microsoft.EntityFrameworkCore;