Ban UTXOs  in depth in non-RBF spend-time instead of confirmtime (but also confirmtime just to be sure)
MODIFY
WalletWasabi.Tests/RegTests.cs
WalletWasabi.Tests/RegTests.cs
@@ -1977,10 +1977,7 @@ namespace WalletWasabi.Tests
Assert.NotNull(await utxos.TryGetBannedAsync(bannedCoin.ToOutPoint(), false));
spendingTx.Inputs.Add(new TxIn(bannedCoin.ToOutPoint()));
spendingTx.Outputs.Add(new TxOut(Money.Coins(1), new Key().PubKey.GetSegwitAddress(network)));
- var fakeBlockWithSpendingBannedCoins = network.Consensus.ConsensusFactory.CreateBlock();
- fakeBlockWithSpendingBannedCoins.Transactions.Add(spendingTx);
-
- await coordinator.ProcessBlockAsync(fakeBlockWithSpendingBannedCoins);
+ await coordinator.ProcessTransactionAsync(spendingTx, false);
Assert.NotNull(await utxos.TryGetBannedAsync(new OutPoint(spendingTx.GetHash(), 0), false));
Assert.Null(await utxos.TryGetBannedAsync(bannedCoin.ToOutPoint(), false));

MODIFY
WalletWasabi/Services/CcjCoordinator.cs
WalletWasabi/Services/CcjCoordinator.cs
@@ -115,14 +115,30 @@ namespace WalletWasabi.Services
Logger.LogDebug<CcjCoordinator>(ex);
}
+ TrustedNodeNotifyingBehavior.Transaction += TrustedNodeNotifyingBehavior_TransactionAsync;
TrustedNodeNotifyingBehavior.Block += TrustedNodeNotifyingBehavior_BlockAsync;
}
+ private async void TrustedNodeNotifyingBehavior_TransactionAsync(object sender, Transaction tx)
+ {
+ try
+ {
+ await ProcessTransactionAsync(tx, isInBlock: false);
+ }
+ catch (Exception ex)
+ {
+ Logger.LogWarning<CcjCoordinator>(ex);
+ }
+ }
+
private async void TrustedNodeNotifyingBehavior_BlockAsync(object sender, Block block)
{
try
{
- await ProcessBlockAsync(block);
+ foreach (Transaction tx in block.Transactions)
+ {
+ await ProcessTransactionAsync(tx, isInBlock: true);
+ }
}
catch (Exception ex)
{
@@ -130,17 +146,20 @@ namespace WalletWasabi.Services
}
}
- public async Task ProcessBlockAsync(Block block)
+ public async Task ProcessTransactionAsync(Transaction tx, bool isInBlock)
{
+ // This should not be needed until we would only accept unconfirmed CJ outputs an no other unconf outs. But it'll be more bulletproof for future extensions.
+ // Turns out you shouldn't accept RBF at all never. (See below.)
+
// https://github.com/zkSNACKs/WalletWasabi/issues/145
- // whenever a block arrives:
- // go through all its transactions
- // if a transaction spends a banned output AND it's not CJ output
+ // if a it spends a banned output AND it's not CJ output
// ban all the outputs of the transaction
- foreach (Transaction tx in block.Transactions)
- {
- if (RoundConfig.DosSeverity <= 1) return;
+ // With RBF they could infect any utxo with just one banned utxo.
+ // We could do a whole RBF logic here in order to make sure this is the latest RBF tx (would that even be bulletproof?) but fuck that,
+ // it's hard enough to get the RBF logic right in normal situations, and then we would have to note what utxos we unbanned and stuff...
+ // With non-RBF double spend they wouldn't be able to do the infectin at scale. Maybe one UTXO with one banned, but even that's dubious. Most likely unfeasible.
+ if (RoundConfig.DosSeverity <= 1 || (!isInBlock && tx.RBF)) return;
var txId = tx.GetHash();
foreach (TxIn input in tx.Inputs)
@@ -165,7 +184,6 @@ namespace WalletWasabi.Services
}
}
}
- }
public void UpdateRoundConfig(CcjRoundConfig roundConfig)
{
@@ -389,6 +407,7 @@ namespace WalletWasabi.Services
{
if (TrustedNodeNotifyingBehavior != null)
{
+ TrustedNodeNotifyingBehavior.Transaction -= TrustedNodeNotifyingBehavior_TransactionAsync;
TrustedNodeNotifyingBehavior.Block -= TrustedNodeNotifyingBehavior_BlockAsync;
}

Ban UTXOs  in depth in non-RBF spend-time instead of confirmtime (but also confirmtime just to be sure)