Unit test for the whole coinjoin client
MODIFY
WalletWasabi.Tests/UnitTests/WabiSabi/Integration/WabiSabiHttpApiIntegrationTests.cs
WalletWasabi.Tests/UnitTests/WabiSabi/Integration/WabiSabiHttpApiIntegrationTests.cs
@@ -5,9 +5,13 @@ using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using NBitcoin;
using WalletWasabi.BitcoinCore.Rpc;
+using WalletWasabi.Blockchain.Keys;
using WalletWasabi.Tests.Helpers;
+using WalletWasabi.WabiSabi.Backend;
using WalletWasabi.WabiSabi.Backend.Models;
+using WalletWasabi.WabiSabi.Client;
using WalletWasabi.WabiSabi.Models.MultipartyTransaction;
+using WalletWasabi.Wallets;
using Xunit;
namespace WalletWasabi.Tests.UnitTests.WabiSabi.Integration
@@ -42,6 +46,75 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Integration
Assert.Equal(WabiSabiProtocolErrorCode.InputSpent, wex.ErrorCode);
}
+ [Fact]
+ public async Task SoloCoinJoinTest()
+ {
+ const int inputCount = 2;
+
+ // At the end of the test a coinjoin transaction has to be created and broadcasted.
+ // we wait for 20 seconds before timing out.
+ const int coinjoinProcessTimeout = 20_000;
+ var transactionCompleted = new TaskCompletionSource<Transaction>();
+ using var cts = new CancellationTokenSource(coinjoinProcessTimeout);
+ cts.Token.Register(() => transactionCompleted.TrySetCanceled(), useSynchronizationContext: false);
+
+ // Create a key manager and use it to create two fake coins.
+ var keyManager = KeyManager.CreateNew(out var _, password: "");
+ keyManager.AssertCleanKeysIndexed();
+ var coins = keyManager.GetKeys()
+ .Take(inputCount)
+ .Select(x => new Coin(
+ BitcoinFactory.CreateOutPoint(),
+ new TxOut(Money.Coins(1), x.P2wpkhScript)))
+ .ToArray();
+
+ var httpClient = _apiApplicationFactory.WithWebHostBuilder(builder =>
+ {
+ builder.ConfigureServices(services =>
+ {
+ var rpc = BitcoinFactory.GetMockMinimalRpc();
+ // Make the coordinator to believe that those two coins are real and
+ // that they exist in the blockchain with many confirmations.
+ rpc.OnGetTxOutAsync = (txId, idx, _) => new()
+ {
+ Confirmations = 101,
+ IsCoinBase = false,
+ ScriptPubKeyType = "witness_v0_keyhash",
+ TxOut = coins.Single(x => x.Outpoint.Hash == txId && x.Outpoint.N == idx).TxOut
+ };
+ // Make the coordinator believe that the transaction is being
+ // broadcasted using the RPC interface. Once we receive this tx
+ // (the `SendRawTransationAsync` was invoked) we stop waiting
+ // and finish the waiting tasks to finish the test successfully.
+ rpc.OnSendRawTransactionAsync = (tx) =>
+ {
+ transactionCompleted.SetResult(tx);
+ return tx.GetHash();
+ };
+ // Instruct the coodinator DI container to use these two scoped
+ // services to build everything (wabisabi controller, arena, etc)
+ services.AddScoped<IRPCClient>(s => rpc);
+ services.AddScoped<WabiSabiConfig>(s => new WabiSabiConfig { MaxInputCountByRound = inputCount });
+ });
+ }).CreateClient();
+
+ // Create the coinjoin client
+ var apiClient = _apiApplicationFactory.CreateWabiSabiHttpApiClient(httpClient);
+ var rounds = await apiClient.GetStatusAsync(CancellationToken.None);
+ var roundState = rounds.First(x => x.CoinjoinState is ConstructionState);
+ var kitchen = new Kitchen();
+ kitchen.Cook("");
+ using var coinJoinClient = new CoinJoinClient(roundState.Id, apiClient, coins, kitchen, keyManager);
+
+ // Run the coinjoin client task.
+ await coinJoinClient.StartAsync(CancellationToken.None);
+
+ var boadcastedTx = await transactionCompleted.Task.ConfigureAwait(false); // wait for the transaction to be broadcasted.
+ Assert.NotNull(boadcastedTx);
+
+ await coinJoinClient.StopAsync(CancellationToken.None);
+ }
+
[Fact]
public async Task RegisterCoinAsync()
{

Unit test for the whole coinjoin client