public async Task IncorrectRequestedWeightCredentialsAsync() { WabiSabiConfig cfg = new(); var round = WabiSabiFactory.CreateRound(cfg); round.SetPhase(Phase.OutputRegistration); round.Alices.Add(WabiSabiFactory.CreateAlice()); using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, round); await using ArenaRequestHandler handler = new(cfg, new Prison(), arena, new MockRpcClient()); var req = WabiSabiFactory.CreateOutputRegistrationRequest(round, weight: 30); var ex = await Assert.ThrowsAsync <WabiSabiProtocolException>(async() => await handler.RegisterOutputAsync(req)); Assert.Equal(WabiSabiProtocolErrorCode.IncorrectRequestedWeightCredentials, ex.ErrorCode); await arena.StopAsync(CancellationToken.None); }
public async Task SuccessAsync() { WabiSabiConfig cfg = new(); var round = WabiSabiFactory.CreateRound(cfg); using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, round); using Key key = new(); await using PostRequestHandler handler = new(cfg, new(), arena, WabiSabiFactory.CreateMockRpc(key)); var req = WabiSabiFactory.CreateInputsRegistrationRequest(key, round); var minAliceDeadline = DateTimeOffset.UtcNow + cfg.ConnectionConfirmationTimeout * 0.9; var resp = await handler.RegisterInputAsync(req); AssertSingleAliceSuccessfullyRegistered(round, minAliceDeadline, resp); await arena.StopAsync(CancellationToken.None); }
public async Task AlicesSpentAsync() { WabiSabiConfig cfg = new() { MaxInputCountByRound = 2, MinInputCountByRoundMultiplier = 0.5 }; using Key key1 = new(); using Key key2 = new(); var coin1 = WabiSabiFactory.CreateCoin(key1); var coin2 = WabiSabiFactory.CreateCoin(key2); var mockRpc = WabiSabiFactory.CreatePreconfiguredRpcClient(coin1, coin2); mockRpc.Setup(rpc => rpc.SendRawTransactionAsync(It.IsAny <Transaction>())) .ThrowsAsync(new RPCException(RPCErrorCode.RPC_TRANSACTION_REJECTED, "", null)); using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, mockRpc); var(round, aliceClient1, aliceClient2) = await CreateRoundWithOutputsReadyToSignAsync(arena, key1, coin1, key2, coin2).ConfigureAwait(false); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(Phase.TransactionSigning, round.Phase); var signedCoinJoin = round.Assert <SigningState>().CreateTransaction(); await aliceClient1.SignTransactionAsync(signedCoinJoin, CancellationToken.None); await aliceClient2.SignTransactionAsync(signedCoinJoin, CancellationToken.None); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.DoesNotContain(round, arena.Rounds); // There should be no inmate, because we aren't punishing spenders with banning // as there's no reason to ban already spent UTXOs, // the cost of spending the UTXO is the punishment instead. Assert.Empty(arena.Prison.GetInmates()); await arena.StopAsync(CancellationToken.None); }
public async Task TimeoutInsufficientPeersAsync() { WabiSabiConfig cfg = new() { MaxInputCountByRound = 2, MinInputCountByRoundMultiplier = 1, TransactionSigningTimeout = TimeSpan.Zero }; var(keyChain, coin1, coin2) = WabiSabiFactory.CreateCoinKeyPairs(); var mockRpc = WabiSabiFactory.CreatePreconfiguredRpcClient(coin1.Coin, coin2.Coin); mockRpc.Setup(rpc => rpc.SendRawTransactionAsync(It.IsAny <Transaction>(), It.IsAny <CancellationToken>())) .ThrowsAsync(new RPCException(RPCErrorCode.RPC_TRANSACTION_REJECTED, "", null)); Prison prison = new(); using Arena arena = await ArenaBuilder.From(cfg, mockRpc, prison).CreateAndStartAsync(); var(round, aliceClient1, aliceClient2) = await CreateRoundWithOutputsReadyToSignAsync(arena, keyChain, coin1, coin2); await aliceClient1.ReadyToSignAsync(CancellationToken.None); await aliceClient2.ReadyToSignAsync(CancellationToken.None); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(Phase.TransactionSigning, round.Phase); var signedCoinJoin = round.Assert <SigningState>().CreateTransaction(); await aliceClient1.SignTransactionAsync(signedCoinJoin, keyChain, CancellationToken.None); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.DoesNotContain(round, arena.Rounds.Where(x => x.Phase != Phase.Ended)); Assert.Equal(Phase.Ended, round.Phase); Assert.False(round.WasTransactionBroadcast); Assert.Empty(arena.Rounds.Where(x => x is BlameRound)); Assert.Contains(aliceClient2.SmartCoin.OutPoint, prison.GetInmates().Select(x => x.Utxo)); await arena.StopAsync(CancellationToken.None); }
public async Task CreateNewAsync() { var config = new WabiSabiConfig { MaxInputCountByRound = 1 }; var round = WabiSabiFactory.CreateRound(config); var km = ServiceFactory.CreateKeyManager(""); var key = BitcoinFactory.CreateHdPubKey(km); SmartCoin coin1 = BitcoinFactory.CreateSmartCoin(key, Money.Coins(1m)); var mockRpc = WabiSabiFactory.CreatePreconfiguredRpcClient(coin1.Coin); using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(config, mockRpc, round); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromMinutes(1)); await using var coordinator = new ArenaRequestHandler(config, new Prison(), arena, mockRpc.Object); var wabiSabiApi = new WabiSabiController(coordinator); ZeroCredentialPool amountCredentialPool = new(); ZeroCredentialPool vsizeCredentialPool = new(); var insecureRandom = new InsecureRandom(); var roundState = RoundState.FromRound(round); var arenaClient = new ArenaClient( roundState.CreateAmountCredentialClient(amountCredentialPool, insecureRandom), roundState.CreateVsizeCredentialClient(vsizeCredentialPool, insecureRandom), wabiSabiApi); Assert.Equal(Phase.InputRegistration, arena.Rounds.First().Phase); var bitcoinSecret = km.GetSecrets("", coin1.ScriptPubKey).Single().PrivateKey.GetBitcoinSecret(Network.Main); var aliceClient = new AliceClient(round.Id, arenaClient, coin1.Coin, round.FeeRate, bitcoinSecret); await aliceClient.RegisterInputAsync(CancellationToken.None); Task confirmationTask = aliceClient.ConfirmConnectionAsync(TimeSpan.FromSeconds(1), roundState.MaxVsizeAllocationPerAlice, CancellationToken.None); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromMinutes(1)); await confirmationTask; Assert.Equal(Phase.ConnectionConfirmation, arena.Rounds.First().Phase); }
public async Task ScriptNotAllowedAsync() { WabiSabiConfig cfg = new(); var round = WabiSabiFactory.CreateRound(cfg); round.SetPhase(Phase.OutputRegistration); using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, round); using Key key = new(); var req = WabiSabiFactory.CreateOutputRegistrationRequest(round, key.PubKey.GetAddress(ScriptPubKeyType.Legacy, Network.Main).ScriptPubKey); await using ArenaRequestHandler handler = new(cfg, new Prison(), arena, new MockRpcClient()); var ex = await Assert.ThrowsAsync <WabiSabiProtocolException>(async() => await handler.RegisterOutputAsync(req)); Assert.Equal(WabiSabiProtocolErrorCode.ScriptNotAllowed, ex.ErrorCode); await arena.StopAsync(CancellationToken.None); }
public async Task TooMuchFundsAsync() { WabiSabiConfig cfg = new() { MaxRegistrableAmount = Money.Coins(1.999m) }; var round = WabiSabiFactory.CreateRound(cfg); round.SetPhase(Phase.OutputRegistration); round.Alices.Add(WabiSabiFactory.CreateAlice(value: Money.Coins(2))); using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, round); await using ArenaRequestHandler handler = new(cfg, new Prison(), arena, new MockRpcClient()); var req = WabiSabiFactory.CreateOutputRegistrationRequest(round); var ex = await Assert.ThrowsAsync <WabiSabiProtocolException>(async() => await handler.RegisterOutputAsync(req)); Assert.Equal(WabiSabiProtocolErrorCode.TooMuchFunds, ex.ErrorCode); await arena.StopAsync(CancellationToken.None); }
public async Task CreateNewAsync() { var config = new WabiSabiConfig { MaxInputCountByRound = 1 }; var round = WabiSabiFactory.CreateRound(config); using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(config, round); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(1)); var km = ServiceFactory.CreateKeyManager(""); var key = BitcoinFactory.CreateHdPubKey(km); SmartCoin coin1 = BitcoinFactory.CreateSmartCoin(key, Money.Coins(1m)); var outpoint = coin1.OutPoint; var mockRpc = new Mock <IRPCClient>(); mockRpc.Setup(rpc => rpc.GetTxOutAsync(outpoint.Hash, (int)outpoint.N, true)) .ReturnsAsync(new NBitcoin.RPC.GetTxOutResponse { IsCoinBase = false, Confirmations = coin1.Height, TxOut = coin1.TxOut, }); await using var coordinator = new ArenaRequestHandler(config, new Prison(), arena, mockRpc.Object); var apiClient = new ArenaClient(round.AmountCredentialIssuerParameters, round.WeightCredentialIssuerParameters, coordinator, new InsecureRandom()); Assert.Equal(Phase.InputRegistration, arena.Rounds.First().Value.Phase); var bitcoinSecret = km.GetSecrets("", coin1.ScriptPubKey).Single().PrivateKey.GetBitcoinSecret(Network.Main); var aliceClient = await AliceClient.CreateNewAsync(apiClient, new[] { coin1.Coin }, bitcoinSecret, round.Id, round.Hash, round.FeeRate); Task confirmationTask = aliceClient.ConfirmConnectionAsync(TimeSpan.FromSeconds(3), CancellationToken.None); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(1)); await confirmationTask; Assert.Equal(Phase.ConnectionConfirmation, arena.Rounds.First().Value.Phase); }
public async Task FailsBroadcastAsync() { WabiSabiConfig cfg = new() { MaxInputCountByRound = 2, MinInputCountByRoundMultiplier = 0.5 }; var(key1, coin1, key2, coin2) = WabiSabiFactory.CreateCoinKeyPairs(); var mockRpc = WabiSabiFactory.CreatePreconfiguredRpcClient(coin1.Coin, coin2.Coin); mockRpc.Setup(rpc => rpc.SendRawTransactionAsync(It.IsAny <Transaction>())) .ThrowsAsync(new RPCException(RPCErrorCode.RPC_TRANSACTION_REJECTED, "", null)); using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, mockRpc); var(round, aliceClient1, aliceClient2) = await CreateRoundWithOutputsReadyToSignAsync(arena, key1, coin1, key2, coin2); await aliceClient1.ReadyToSignAsync(CancellationToken.None); await aliceClient2.ReadyToSignAsync(CancellationToken.None); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(Phase.TransactionSigning, round.Phase); var signedCoinJoin = round.Assert <SigningState>().CreateTransaction(); await aliceClient1.SignTransactionAsync(signedCoinJoin, CancellationToken.None); await aliceClient2.SignTransactionAsync(signedCoinJoin, CancellationToken.None); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.DoesNotContain(round, arena.ActiveRounds); Assert.Equal(Phase.Ended, round.Phase); Assert.False(round.WasTransactionBroadcast); Assert.Empty(arena.Prison.GetInmates()); await arena.StopAsync(CancellationToken.None); }
public async Task SomeBobsRegisteredTimeoutAsync() { WabiSabiConfig cfg = new() { MaxInputCountByRound = 2, MinInputCountByRoundMultiplier = 0.5, OutputRegistrationTimeout = TimeSpan.Zero, CoordinationFeeRate = CoordinationFeeRate.Zero }; var(keyChain, coin1, coin2) = WabiSabiFactory.CreateCoinKeyPairs(); var mockRpc = WabiSabiFactory.CreatePreconfiguredRpcClient(coin1.Coin, coin2.Coin); using Arena arena = await ArenaBuilder.From(cfg).With(mockRpc).CreateAndStartAsync(); var(round, arenaClient, alices) = await CreateRoundWithTwoConfirmedConnectionsAsync(arena, keyChain, coin1, coin2); var(amountCredentials1, vsizeCredentials1) = (alices[0].IssuedAmountCredentials, alices[0].IssuedVsizeCredentials); var(amountCredentials2, vsizeCredentials2) = (alices[1].IssuedAmountCredentials, alices[1].IssuedVsizeCredentials); // Register outputs. var bobClient = new BobClient(round.Id, arenaClient); using var destKey = new Key(); await bobClient.RegisterOutputAsync( destKey.PubKey.WitHash.ScriptPubKey, amountCredentials1.Take(ProtocolConstants.CredentialNumber), vsizeCredentials1.Take(ProtocolConstants.CredentialNumber), CancellationToken.None); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(Phase.TransactionSigning, round.Phase); var tx = round.Assert <SigningState>().CreateTransaction(); Assert.Equal(2, tx.Inputs.Count); Assert.Equal(2, tx.Outputs.Count); Assert.Contains(round.CoordinatorScript, tx.Outputs.Select(x => x.ScriptPubKey)); await arena.StopAsync(CancellationToken.None); }
public async Task AliceTimesoutAsync() { // Alice times out when its deadline is reached. WabiSabiConfig cfg = new(); var round = WabiSabiFactory.CreateRound(cfg); var km = ServiceFactory.CreateKeyManager(""); var key = BitcoinFactory.CreateHdPubKey(km); var smartCoin = BitcoinFactory.CreateSmartCoin(key, 10m); var rpc = WabiSabiFactory.CreatePreconfiguredRpcClient(smartCoin.Coin); using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, rpc, round); var arenaClient = WabiSabiFactory.CreateArenaClient(arena); using RoundStateUpdater roundStateUpdater = new(TimeSpan.FromSeconds(2), new ArenaRequestHandlerAdapter(arena)); await roundStateUpdater.StartAsync(CancellationToken.None); // Register Alices. var esk = km.GetSecrets("", smartCoin.ScriptPubKey).Single(); using CancellationTokenSource cancellationTokenSource = new(); var task = AliceClient.CreateRegisterAndConfirmInputAsync(RoundState.FromRound(round), arenaClient, smartCoin, esk.PrivateKey.GetBitcoinSecret(round.Network), roundStateUpdater, cancellationTokenSource.Token); while (round.Alices.Count == 0) { await Task.Delay(10); } var alice = Assert.Single(round.Alices); alice.Deadline = DateTimeOffset.UtcNow - TimeSpan.FromMilliseconds(1); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Empty(round.Alices); cancellationTokenSource.Cancel(); await Assert.ThrowsAsync <OperationCanceledException>(async() => await task); await roundStateUpdater.StopAsync(CancellationToken.None); await arena.StopAsync(CancellationToken.None); }
public async Task SuccessAsync() { WabiSabiConfig cfg = new(); var round = WabiSabiFactory.CreateRound(cfg); using Key key = new(); var coin = WabiSabiFactory.CreateCoin(key); using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, WabiSabiFactory.CreatePreconfiguredRpcClient(coin), round); var minAliceDeadline = DateTimeOffset.UtcNow + cfg.ConnectionConfirmationTimeout * 0.9; var arenaClient = WabiSabiFactory.CreateArenaClient(arena); var ownershipProof = WabiSabiFactory.CreateOwnershipProof(key, round.Id); var resp = await arenaClient.RegisterInputAsync(round.Id, coin.Outpoint, ownershipProof, CancellationToken.None); AssertSingleAliceSuccessfullyRegistered(round, minAliceDeadline, resp); await arena.StopAsync(CancellationToken.None); }
public async Task NonStandardOutputAsync() { WabiSabiConfig cfg = new(); var round = WabiSabiFactory.CreateRound(cfg); round.MaxVsizeAllocationPerAlice += 13; using Arena arena = await ArenaBuilder.From(cfg).CreateAndStartAsync(round); round.SetPhase(Phase.OutputRegistration); round.Alices.Add(WabiSabiFactory.CreateAlice(Money.Coins(1), round)); var sha256Bounty = Script.FromHex("aa20000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f87"); var req = WabiSabiFactory.CreateOutputRegistrationRequest(round, sha256Bounty); var ex = await Assert.ThrowsAsync <WabiSabiProtocolException>(async() => await arena.RegisterOutputAsync(req, CancellationToken.None)); // The following assertion requires standardness to be checked before allowed script types Assert.Equal(WabiSabiProtocolErrorCode.NonStandardOutput, ex.ErrorCode); await arena.StopAsync(CancellationToken.None); }
public async Task InputRegistrationTimedoutWithoutSufficientInputsAsync() { WabiSabiConfig cfg = new() { StandardInputRegistrationTimeout = TimeSpan.Zero, MaxInputCountByRound = 4, MinInputCountByRoundMultiplier = 0.5 }; var round = WabiSabiFactory.CreateRound(cfg); using Arena arena = await ArenaBuilder.From(cfg).CreateAndStartAsync(round); round.Alices.Add(WabiSabiFactory.CreateAlice(round)); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(Phase.Ended, round.Phase); Assert.DoesNotContain(round, arena.GetActiveRounds()); await arena.StopAsync(CancellationToken.None); }
public async Task InputNotWhitelistedAsync() { WabiSabiConfig cfg = new(); var round = WabiSabiFactory.CreateRound(cfg); round.Alices.Add(WabiSabiFactory.CreateAlice()); Round blameRound = WabiSabiFactory.CreateBlameRound(round, cfg); using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, round, blameRound); using Key key = new(); await using PostRequestHandler handler = new(cfg, new(), arena, WabiSabiFactory.CreateMockRpc(key)); var req = WabiSabiFactory.CreateInputsRegistrationRequest(blameRound); var ex = await Assert.ThrowsAsync <WabiSabiProtocolException>(async() => await handler.RegisterInputAsync(req)); Assert.Equal(WabiSabiProtocolErrorCode.InputNotWhitelisted, ex.ErrorCode); await arena.StopAsync(CancellationToken.None); }
public async Task InputNotWhitelistedAsync() { WabiSabiConfig cfg = new(); var round = WabiSabiFactory.CreateRound(cfg); round.Alices.Add(WabiSabiFactory.CreateAlice(round)); Round blameRound = WabiSabiFactory.CreateBlameRound(round, cfg); using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, round, blameRound); using Key key = new(); var mockRpc = WabiSabiFactory.CreatePreconfiguredRpcClient(); var req = WabiSabiFactory.CreateInputRegistrationRequest(round: blameRound); var ex = await Assert.ThrowsAsync <WabiSabiProtocolException>(async() => await arena.RegisterInputAsync(req, CancellationToken.None)); Assert.Equal(WabiSabiProtocolErrorCode.InputNotWhitelisted, ex.ErrorCode); await arena.StopAsync(CancellationToken.None); }
public async Task ScriptNotAllowedAsync() { WabiSabiConfig cfg = new(); var round = WabiSabiFactory.CreateRound(cfg); round.MaxVsizeAllocationPerAlice = 11 + 34 + MultipartyTransactionParameters.SharedOverhead; using Arena arena = await ArenaBuilder.From(cfg).CreateAndStartAsync(round); using Key key = new(); round.SetPhase(Phase.OutputRegistration); round.Alices.Add(WabiSabiFactory.CreateAlice(Money.Coins(1), round)); var req = WabiSabiFactory.CreateOutputRegistrationRequest(round, key.PubKey.GetAddress(ScriptPubKeyType.Legacy, Network.Main).ScriptPubKey); var ex = await Assert.ThrowsAsync <WabiSabiProtocolException>(async() => await arena.RegisterOutputAsync(req, CancellationToken.None)); Assert.Equal(WabiSabiProtocolErrorCode.ScriptNotAllowed, ex.ErrorCode); await arena.StopAsync(CancellationToken.None); }
public async Task RegisterSpentOrInNonExistentCoinAsync() { var httpClient = _apiApplicationFactory.CreateClient(); var apiClient = await _apiApplicationFactory.CreateArenaClientAsync(httpClient); var rounds = (await apiClient.GetStatusAsync(RoundStateRequest.Empty, CancellationToken.None)).RoundStates; var round = rounds.First(x => x.CoinjoinState is ConstructionState); // If an output is not in the utxo dataset then it is not unspent, this // means that the output is spent or simply doesn't even exist. var nonExistingOutPoint = new OutPoint(); using var signingKey = new Key(); var ownershipProof = WabiSabiFactory.CreateOwnershipProof(signingKey, round.Id); var ex = await Assert.ThrowsAsync <WabiSabiProtocolException>(async() => await apiClient.RegisterInputAsync(round.Id, nonExistingOutPoint, ownershipProof, CancellationToken.None)); Assert.Equal(WabiSabiProtocolErrorCode.InputSpent, ex.ErrorCode); }
public async Task SuccessAsync() { WabiSabiConfig cfg = new(); var round = WabiSabiFactory.CreateRound(cfg); round.SetPhase(Phase.OutputRegistration); round.Alices.Add(WabiSabiFactory.CreateAlice()); using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, round); await using ArenaRequestHandler handler = new(cfg, new Prison(), arena, new MockRpcClient()); var req = WabiSabiFactory.CreateOutputRegistrationRequest(round); var resp = await handler.RegisterOutputAsync(req, CancellationToken.None); Assert.NotEmpty(round.Bobs); Assert.NotNull(resp); Assert.NotNull(resp.AmountCredentials); Assert.NotNull(resp.VsizeCredentials); await arena.StopAsync(CancellationToken.None); }
public async Task InputRegistrationTimedoutAsync() { WabiSabiConfig cfg = new() { StandardInputRegistrationTimeout = TimeSpan.Zero }; var round = WabiSabiFactory.CreateRound(cfg); using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg); using Key key = new(); var req = WabiSabiFactory.CreateInputsRegistrationRequest(key, round); await using ArenaRequestHandler handler = new(cfg, new(), arena, WabiSabiFactory.CreateMockRpc(key)); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); arena.Rounds.Add(round.Id, round); await RegisterAndAssertWrongPhaseAsync(req, handler); Assert.Equal(Phase.InputRegistration, round.Phase); await arena.StopAsync(CancellationToken.None); }
public async Task WrongPhaseAsync() { MockArena arena = new(); WabiSabiConfig cfg = new(); var round = WabiSabiFactory.CreateRound(cfg); arena.OnTryGetRound = _ => round; var req = new ConnectionConfirmationRequest(round.Id, Guid.NewGuid(), null !, null !, null !, null !); foreach (Phase phase in Enum.GetValues(typeof(Phase))) { if (phase != Phase.InputRegistration && phase != Phase.ConnectionConfirmation) { round.Phase = phase; await using PostRequestHandler handler = new(cfg, new Prison(), arena, new MockRpcClient()); var ex = Assert.Throws <WabiSabiProtocolException>(() => handler.ConfirmConnection(req)); Assert.Equal(WabiSabiProtocolErrorCode.WrongPhase, ex.ErrorCode); } } }
public async Task InputCantBeNotedAsync() { using Key key = new(); var outpoint = BitcoinFactory.CreateOutPoint(); WabiSabiConfig cfg = new() { AllowNotedInputRegistration = false }; var round = WabiSabiFactory.CreateRound(cfg); using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, round); arena.Prison.Punish(outpoint, Punishment.Noted, uint256.One); var arenaClient = WabiSabiFactory.CreateArenaClient(arena); var ex = await Assert.ThrowsAsync <WabiSabiProtocolException>( async() => await arenaClient.RegisterInputAsync(round.Id, outpoint, key, CancellationToken.None)); Assert.Equal(WabiSabiProtocolErrorCode.InputBanned, ex.ErrorCode); await arena.StopAsync(CancellationToken.None); }
public async Task SuccessAsync() { WabiSabiConfig cfg = new(); var round = WabiSabiFactory.CreateRound(cfg); var alice = WabiSabiFactory.CreateAlice(); round.Alices.Add(alice); using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, round); await using ArenaRequestHandler handler = new(cfg, new Prison(), arena, new MockRpcClient()); // There's no such alice yet, so success. var req = new InputsRemovalRequest(round.Id, Guid.NewGuid()); await handler.RemoveInputAsync(req); // There was the alice we want to remove so success. req = new InputsRemovalRequest(round.Id, alice.Id); await handler.RemoveInputAsync(req); await arena.StopAsync(CancellationToken.None); }
public async Task InputWhitelistedButBannedAsync() { MockArena arena = new(); Prison prison = new(); var pair = WabiSabiFactory.CreateInputRoundSignaturePair(); prison.Punish(pair.Input, Punishment.Banned, Guid.NewGuid()); WabiSabiConfig cfg = new(); var round = WabiSabiFactory.CreateRound(cfg); round.Alices.Add(new Alice(pair.Input)); Round blameRound = new(round); arena.OnTryGetRound = _ => blameRound; await using PostRequestHandler handler = new(cfg, prison, arena, new MockRpcClient()); var req = WabiSabiFactory.CreateInputsRegistrationRequest(pair, blameRound); var ex = await Assert.ThrowsAsync <WabiSabiProtocolException>(async() => await handler.RegisterInputAsync(req)); Assert.Equal(WabiSabiProtocolErrorCode.InputBanned, ex.ErrorCode); }
public async Task InputCanBeNotedAsync() { MockArena arena = new(); Prison prison = new(); var pair = WabiSabiFactory.CreateInputRoundSignaturePair(); prison.Punish(pair.Input, Punishment.Noted, Guid.NewGuid()); WabiSabiConfig cfg = new(); var round = WabiSabiFactory.CreateRound(cfg); arena.OnTryGetRound = _ => round; await using PostRequestHandler handler = new(cfg, prison, arena, new MockRpcClient()); var req = WabiSabiFactory.CreateInputsRegistrationRequest(pair, round); var ex = await Assert.ThrowsAnyAsync <Exception>(async() => await handler.RegisterInputAsync(req)); if (ex is WabiSabiProtocolException wspex) { Assert.NotEqual(WabiSabiProtocolErrorCode.InputBanned, wspex.ErrorCode); } }
public async Task NonStandardOutputAsync() { WabiSabiConfig cfg = new(); var round = WabiSabiFactory.CreateRound(cfg); round.SetPhase(Phase.OutputRegistration); using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, round); using Key key = new(); var sha256Bounty = Script.FromHex("aa20000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f87"); var req = WabiSabiFactory.CreateOutputRegistrationRequest(round, sha256Bounty); await using ArenaRequestHandler handler = new(cfg, new Prison(), arena, new MockRpcClient()); var ex = await Assert.ThrowsAsync <WabiSabiProtocolException>(async() => await handler.RegisterOutputAsync(req)); // The following assertion requires standardness to be checked before allowed script types Assert.Equal(WabiSabiProtocolErrorCode.NonStandardOutput, ex.ErrorCode); await arena.StopAsync(CancellationToken.None); }
public async Task InputRegistrationTimedoutWithSufficientInputsAsync() { WabiSabiConfig cfg = new() { StandardInputRegistrationTimeout = TimeSpan.Zero, MaxInputCountByRound = 4, MinInputCountByRoundMultiplier = 0.5 }; var round = WabiSabiFactory.CreateRound(cfg); round.Alices.Add(WabiSabiFactory.CreateAlice()); round.Alices.Add(WabiSabiFactory.CreateAlice()); using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, round); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(Phase.ConnectionConfirmation, round.Phase); await arena.StopAsync(CancellationToken.None); }
CreateRoundWithTwoConfirmedConnectionsAsync(Arena arena, IKeyChain keyChain, SmartCoin coin1, SmartCoin coin2) { // Get the round. await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); var arenaClient = WabiSabiFactory.CreateArenaClient(arena); var round = Assert.Single(arena.Rounds); // Refresh the Arena States because of vsize manipulation. await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); using RoundStateUpdater roundStateUpdater = new(TimeSpan.FromSeconds(2), arena); await roundStateUpdater.StartAsync(CancellationToken.None); var task1 = AliceClient.CreateRegisterAndConfirmInputAsync(RoundState.FromRound(round), arenaClient, coin1, keyChain, roundStateUpdater, CancellationToken.None); var task2 = AliceClient.CreateRegisterAndConfirmInputAsync(RoundState.FromRound(round), arenaClient, coin2, keyChain, roundStateUpdater, CancellationToken.None); while (Phase.ConnectionConfirmation != round.Phase) { await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); } await Task.WhenAll(task1, task2); var aliceClient1 = await task1; var aliceClient2 = await task2; await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(Phase.OutputRegistration, round.Phase); await roundStateUpdater.StopAsync(CancellationToken.None); return(round, arenaClient, new[] { aliceClient1, aliceClient2 }); }
CreateRoundWithTwoConfirmedConnectionsAsync(Arena arena, Key key1, SmartCoin coin1, Key key2, SmartCoin coin2) { // Create the round. await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); var arenaClient = WabiSabiFactory.CreateArenaClient(arena); var round = Assert.Single(arena.Rounds); round.MaxVsizeAllocationPerAlice = 11 + 31 + MultipartyTransactionParameters.SharedOverhead; using RoundStateUpdater roundStateUpdater = new(TimeSpan.FromSeconds(2), arena); await roundStateUpdater.StartAsync(CancellationToken.None); var identificationKey = new Key(); var task1 = AliceClient.CreateRegisterAndConfirmInputAsync(RoundState.FromRound(round), arenaClient, coin1, key1.GetBitcoinSecret(round.Network), identificationKey, roundStateUpdater, CancellationToken.None); var task2 = AliceClient.CreateRegisterAndConfirmInputAsync(RoundState.FromRound(round), arenaClient, coin2, key2.GetBitcoinSecret(round.Network), identificationKey, roundStateUpdater, CancellationToken.None); while (Phase.ConnectionConfirmation != round.Phase) { await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); } await Task.WhenAll(task1, task2); var aliceClient1 = task1.Result; var aliceClient2 = task2.Result; await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(Phase.OutputRegistration, round.Phase); await roundStateUpdater.StopAsync(CancellationToken.None); return(round, arenaClient, new[] { aliceClient1, aliceClient2 }); }
public async Task EveryoneSignedAsync() { WabiSabiConfig cfg = new() { MaxInputCountByRound = 2, MinInputCountByRoundMultiplier = 0.5 }; using Key key1 = new(); using Key key2 = new(); var coin1 = WabiSabiFactory.CreateCoin(key1); var coin2 = WabiSabiFactory.CreateCoin(key2); var mockRpc = WabiSabiFactory.CreatePreconfiguredRpcClient(coin1, coin2); using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, mockRpc); var(round, aliceClient1, aliceClient2) = await CreateRoundWithOutputsReadyToSignAsync(arena, key1, coin1, key2, coin2); await aliceClient1.ReadyToSignAsync(CancellationToken.None); await aliceClient2.ReadyToSignAsync(CancellationToken.None); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(Phase.TransactionSigning, round.Phase); var signedCoinJoin = round.Assert <SigningState>().CreateTransaction(); await aliceClient1.SignTransactionAsync(signedCoinJoin, CancellationToken.None); await aliceClient2.SignTransactionAsync(signedCoinJoin, CancellationToken.None); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.DoesNotContain(round, arena.ActiveRounds); Assert.Equal(Phase.Ended, round.Phase); await arena.StopAsync(CancellationToken.None); }