Add InMemoryCoinJoinIdStore
MODIFY
WalletWasabi.Tests/Helpers/WabiSabiFactory.cs
WalletWasabi.Tests/Helpers/WabiSabiFactory.cs
@@ -119,7 +119,7 @@ namespace WalletWasabi.Tests.Helpers
}
public static Alice CreateAlice(Coin coin, OwnershipProof ownershipProof, Round round)
- => new(coin, ownershipProof, round, Guid.NewGuid()) { Deadline = DateTimeOffset.UtcNow + TimeSpan.FromHours(1) };
+ => new(coin, ownershipProof, round, Guid.NewGuid(), false) { Deadline = DateTimeOffset.UtcNow + TimeSpan.FromHours(1) };
public static Alice CreateAlice(Key key, Money amount, Round round)
=> CreateAlice(CreateCoin(key, amount), CreateOwnershipProof(key), round);

MODIFY
WalletWasabi.Tests/UnitTests/WabiSabi/Backend/Rounds/Utils/CoinJoinTransactionArchiverTests.cs
WalletWasabi.Tests/UnitTests/WabiSabi/Backend/Rounds/Utils/CoinJoinTransactionArchiverTests.cs
@@ -2,9 +2,9 @@ using NBitcoin;
using System.IO;
using System.Text.Json;
using System.Threading.Tasks;
-using WalletWasabi.WabiSabi.Backend.Rounds.Utils;
+using WalletWasabi.WabiSabi.Backend.Rounds.CoinJoinStorage;
using Xunit;
-using static WalletWasabi.WabiSabi.Backend.Rounds.Utils.CoinJoinTransactionArchiver;
+using static WalletWasabi.WabiSabi.Backend.Rounds.CoinJoinStorage.CoinJoinTransactionArchiver;
namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend
{

MODIFY
WalletWasabi.Tests/UnitTests/WabiSabi/Client/ArenaClientTests.cs
WalletWasabi.Tests/UnitTests/WabiSabi/Client/ArenaClientTests.cs
@@ -173,7 +173,7 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Client
round.SetPhase(Phase.ConnectionConfirmation);
var fundingTx = BitcoinFactory.CreateSmartTransaction(ownOutputCount: 1);
var coin = fundingTx.WalletOutputs.First().Coin;
- var alice = new Alice(coin, new OwnershipProof(), round, Guid.NewGuid());
+ var alice = new Alice(coin, new OwnershipProof(), round, Guid.NewGuid(), false);
round.Alices.Add(alice);
using Arena arena = await ArenaBuilder.From(config).CreateAndStartAsync(round);

MODIFY
WalletWasabi/WabiSabi/Backend/Models/Alice.cs
WalletWasabi/WabiSabi/Backend/Models/Alice.cs
@@ -7,13 +7,14 @@ namespace WalletWasabi.WabiSabi.Backend.Models
{
public class Alice
{
- public Alice(Coin coin, OwnershipProof ownershipProof, Round round, Guid id)
+ public Alice(Coin coin, OwnershipProof ownershipProof, Round round, Guid id, bool isComingFromCoinJoin)
{
// TODO init syntax?
Round = round;
Coin = coin;
OwnershipProof = ownershipProof;
Id = id;
+ IsComingFromCoinJoin = isComingFromCoinJoin;
}
public Round Round { get; }
@@ -26,10 +27,12 @@ namespace WalletWasabi.WabiSabi.Backend.Models
public bool ConfirmedConnection { get; set; } = false;
public bool ReadyToSign { get; set; }
+ public bool IsComingFromCoinJoin { get; } = false;
public long CalculateRemainingVsizeCredentials(int maxRegistrableSize) => maxRegistrableSize - TotalInputVsize;
- public Money CalculateRemainingAmountCredentials(FeeRate feeRate, CoordinationFeeRate coordinationFeeRate) => Coin.EffectiveValue(feeRate, coordinationFeeRate);
+ public Money CalculateRemainingAmountCredentials(FeeRate feeRate, CoordinationFeeRate coordinationFeeRate) =>
+ Coin.EffectiveValue(feeRate, IsComingFromCoinJoin ? CoordinationFeeRate.Zero : coordinationFeeRate);
public void SetDeadlineRelativeTo(TimeSpan connectionConfirmationTimeout)
{

MODIFY
WalletWasabi/WabiSabi/Backend/Rounds/Arena.Partial.cs
WalletWasabi/WabiSabi/Backend/Rounds/Arena.Partial.cs
using NBitcoin;
using Nito.AsyncEx;
+using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@@ -57,7 +58,9 @@ namespace WalletWasabi.WabiSabi.Backend.Rounds
// not say anything about GUID version or randomness source,
// only that the probability of duplicates is very low).
var id = new Guid(Random.GetBytes(16));
- var alice = new Alice(coin, request.OwnershipProof, round, id);
+
+ var isComingFromCoinJoin = InMemoryCoinJoinIdStore.Contains(coin.Outpoint.Hash);
+ var alice = new Alice(coin, request.OwnershipProof, round, id, isComingFromCoinJoin);
if (alice.TotalInputAmount < round.MinAmountCredentialValue)
{
@@ -136,6 +139,7 @@ namespace WalletWasabi.WabiSabi.Backend.Rounds
{
throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.IncorrectRequestedVsizeCredentials, $"Round ({request.RoundId}): Incorrect requested vsize credentials.");
}
+
if (realAmountCredentialRequests.Delta != alice.CalculateRemainingAmountCredentials(round.FeeRate, round.CoordinationFeeRate))
{
throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.IncorrectRequestedAmountCredentials, $"Round ({request.RoundId}): Incorrect requested amount credentials.");

MODIFY
WalletWasabi/WabiSabi/Backend/Rounds/Arena.cs
WalletWasabi/WabiSabi/Backend/Rounds/Arena.cs
@@ -12,7 +12,7 @@ using WalletWasabi.Crypto.Randomness;
using WalletWasabi.WabiSabi.Backend.Banning;
using WalletWasabi.WabiSabi.Backend.Models;
using WalletWasabi.WabiSabi.Models.MultipartyTransaction;
-using WalletWasabi.WabiSabi.Backend.Rounds.Utils;
+using WalletWasabi.WabiSabi.Backend.Rounds.CoinJoinStorage;
namespace WalletWasabi.WabiSabi.Backend.Rounds
{
@@ -32,6 +32,7 @@ namespace WalletWasabi.WabiSabi.Backend.Rounds
Prison = prison;
TransactionArchiver = archiver;
Random = new SecureRandom();
+ InMemoryCoinJoinIdStore = new InMemoryCoinJoinIdStore(Enumerable.Empty<uint256>());
}
public HashSet<Round> Rounds { get; } = new();
@@ -42,6 +43,7 @@ namespace WalletWasabi.WabiSabi.Backend.Rounds
private Prison Prison { get; }
private SecureRandom Random { get; }
private CoinJoinTransactionArchiver? TransactionArchiver { get; }
+ private InMemoryCoinJoinIdStore InMemoryCoinJoinIdStore { get; }
protected override async Task ActionAsync(CancellationToken cancel)
{
@@ -240,6 +242,9 @@ namespace WalletWasabi.WabiSabi.Backend.Rounds
{
Config.MakeNextCoordinatorScriptDirty();
}
+
+ InMemoryCoinJoinIdStore.Add(coinjoin.GetHash());
+
round.SetPhase(Phase.Ended);
round.LogInfo($"Successfully broadcast the CoinJoin: {coinjoin.GetHash()}.");

RENAME
WalletWasabi/WabiSabi/Backend/Rounds/Utils/CoinJoinTransactionArchiver.cs
WalletWasabi/WabiSabi/Backend/Rounds/CoinJoinStorage/CoinJoinTransactionArchiver.cs
@@ -5,7 +5,7 @@ using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
-namespace WalletWasabi.WabiSabi.Backend.Rounds.Utils
+namespace WalletWasabi.WabiSabi.Backend.Rounds.CoinJoinStorage
{
public class CoinJoinTransactionArchiver
{

ADD
WalletWasabi/WabiSabi/Backend/Rounds/CoinJoinStorage/InMemoryCoinJoinIdStore.cs
+using NBitcoin;
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WalletWasabi.WabiSabi.Backend.Rounds.CoinJoinStorage
+{
+ public class InMemoryCoinJoinIdStore
+ {
+ public InMemoryCoinJoinIdStore(IEnumerable<uint256> coinJoinHashes)
+ {
+ CoinJoinIds = new ConcurrentDictionary<uint256, bool>(coinJoinHashes.ToDictionary(x => x, x => true));
+ }
+
+ public ConcurrentDictionary<uint256, bool> CoinJoinIds { get; }
+
+ public bool Contains(uint256 hash)
+ {
+ return CoinJoinIds.ContainsKey(hash);
+ }
+
+ public void Add(uint256 hash)
+ {
+ CoinJoinIds.TryAdd(hash, true);
+ }
+
+ public static InMemoryCoinJoinIdStore LoadFromFile(string filePath)
+ {
+ var lines = File.ReadAllLines(filePath).Select(x => uint256.Parse(x));
+ var store = new InMemoryCoinJoinIdStore(lines);
+ return store;
+ }
+ }
+}

MODIFY
WalletWasabi/WabiSabi/WabiSabiCoordinator.cs
WalletWasabi/WabiSabi/WabiSabiCoordinator.cs
@@ -8,7 +8,7 @@ using WalletWasabi.Services;
using WalletWasabi.WabiSabi.Backend;
using WalletWasabi.WabiSabi.Backend.Banning;
using WalletWasabi.WabiSabi.Backend.Rounds;
-using WalletWasabi.WabiSabi.Backend.Rounds.Utils;
+using WalletWasabi.WabiSabi.Backend.Rounds.CoinJoinStorage;
namespace WalletWasabi.WabiSabi
{

Add InMemoryCoinJoinIdStore