public async Task AliceDoesntTimeoutInConnectionConfirmationAsync() { // Alice does not time out when it's not input registration anymore, // even though the deadline is reached. WabiSabiConfig cfg = new(); var round = WabiSabiFactory.CreateRound(cfg); round.SetPhase(Phase.ConnectionConfirmation); var alice = WabiSabiFactory.CreateAlice(round); round.Alices.Add(alice); using Arena arena = await ArenaBuilder.From(cfg).CreateAndStartAsync(round); var req = WabiSabiFactory.CreateConnectionConfirmationRequest(round); Assert.Single(round.Alices); DateTimeOffset preDeadline = DateTimeOffset.UtcNow - TimeSpan.FromMilliseconds(1); alice.Deadline = preDeadline; await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Single(round.Alices); Assert.Equal(preDeadline, alice.Deadline); await arena.StopAsync(CancellationToken.None); }
public async Task AliceDoesntTimeoutIfMaxInputCountReachedAsync() { // Alice does not time out if input reg is full with alices, // even though the deadline is reached and still in input reg. WabiSabiConfig cfg = new() { MaxInputCountByRound = 3 }; var round = WabiSabiFactory.CreateRound(cfg); var alice = WabiSabiFactory.CreateAlice(round); round.Alices.Add(alice); round.Alices.Add(WabiSabiFactory.CreateAlice(round)); round.Alices.Add(WabiSabiFactory.CreateAlice(round)); using Arena arena = await ArenaBuilder.From(cfg).CreateAndStartAsync(round); var req = WabiSabiFactory.CreateConnectionConfirmationRequest(round); Assert.Equal(3, round.Alices.Count); DateTimeOffset preDeadline = DateTimeOffset.UtcNow - TimeSpan.FromMilliseconds(1); alice.Deadline = preDeadline; await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(3, round.Alices.Count); Assert.Equal(preDeadline, alice.Deadline); await arena.StopAsync(CancellationToken.None); }
public async Task SuccessInConnectionConfirmationPhaseAsync() { WabiSabiConfig cfg = new(); var round = WabiSabiFactory.CreateRound(cfg); round.SetPhase(Phase.ConnectionConfirmation); var alice = WabiSabiFactory.CreateAlice(); var preDeadline = alice.Deadline; round.Alices.Add(alice); using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, round); var req = WabiSabiFactory.CreateConnectionConfirmationRequest(round); await using PostRequestHandler handler = new(cfg, new Prison(), arena, new MockRpcClient()); var resp = await handler.ConfirmConnectionAsync(req); Assert.NotNull(resp); Assert.NotNull(resp.ZeroAmountCredentials); Assert.NotNull(resp.ZeroWeightCredentials); Assert.NotNull(resp.RealAmountCredentials); Assert.NotNull(resp.RealWeightCredentials); Assert.Equal(preDeadline, alice.Deadline); Assert.True(alice.ConfirmedConnetion); await arena.StopAsync(CancellationToken.None); }
public async Task SuccessInInputRegistrationPhaseAsync() { WabiSabiConfig cfg = new(); var round = WabiSabiFactory.CreateRound(cfg); var alice = WabiSabiFactory.CreateAlice(round); var preDeadline = alice.Deadline; round.Alices.Add(alice); using Arena arena = await ArenaBuilder.From(cfg).CreateAndStartAsync(round); var req = WabiSabiFactory.CreateConnectionConfirmationRequest(round); var minAliceDeadline = DateTimeOffset.UtcNow + cfg.ConnectionConfirmationTimeout * 0.9; var resp = await arena.ConfirmConnectionAsync(req, CancellationToken.None); Assert.NotNull(resp); Assert.NotNull(resp.ZeroAmountCredentials); Assert.NotNull(resp.ZeroVsizeCredentials); Assert.Null(resp.RealAmountCredentials); Assert.Null(resp.RealVsizeCredentials); Assert.NotEqual(preDeadline, alice.Deadline); Assert.True(minAliceDeadline <= alice.Deadline); Assert.False(alice.ConfirmedConnection); await arena.StopAsync(CancellationToken.None); }
public async Task AliceDoesntTimeoutIfInputRegistrationTimedoutAsync() { // Alice does not time out if input registration timed out, // even though the deadline is reached and still in input reg. WabiSabiConfig cfg = new() { StandardInputRegistrationTimeout = TimeSpan.Zero }; var round = WabiSabiFactory.CreateRound(cfg); var alice = WabiSabiFactory.CreateAlice(); round.Alices.Add(alice); using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, round); var req = WabiSabiFactory.CreateConnectionConfirmationRequest(round); await using ArenaRequestHandler handler = new(cfg, new Prison(), arena, new MockRpcClient()); Assert.Single(round.Alices); DateTimeOffset preDeadline = DateTimeOffset.UtcNow - TimeSpan.FromMilliseconds(1); alice.Deadline = preDeadline; await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Single(round.Alices); Assert.Equal(preDeadline, alice.Deadline); await arena.StopAsync(CancellationToken.None); }
public async Task IncorrectRequestedAmountCredentialsAsync() { WabiSabiConfig cfg = new(); var round = WabiSabiFactory.CreateRound(cfg); round.SetPhase(Phase.ConnectionConfirmation); var alice = WabiSabiFactory.CreateAlice(); round.Alices.Add(alice); using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, round); var req = WabiSabiFactory.CreateConnectionConfirmationRequest(round); req = new( req.RoundId, req.AliceId, req.ZeroAmountCredentialRequests, WabiSabiFactory.CreateRealCredentialRequests(round, Money.Coins(3), null).amountReq, req.ZeroWeightCredentialRequests, req.RealWeightCredentialRequests); await using PostRequestHandler handler = new(cfg, new Prison(), arena, new MockRpcClient()); var ex = await Assert.ThrowsAsync <WabiSabiProtocolException>(async() => await handler.ConfirmConnectionAsync(req)); Assert.Equal(WabiSabiProtocolErrorCode.IncorrectRequestedAmountCredentials, ex.ErrorCode); Assert.False(alice.ConfirmedConnetion); await arena.StopAsync(CancellationToken.None); }
public async Task AliceDeadlineUpdatedAsync() { // Alice's deadline is updated by connection confirmation. WabiSabiConfig cfg = new(); var round = WabiSabiFactory.CreateRound(cfg); var alice = WabiSabiFactory.CreateAlice(); round.Alices.Add(alice); using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, round); var req = WabiSabiFactory.CreateConnectionConfirmationRequest(round); await using ArenaRequestHandler handler = new(cfg, new Prison(), arena, new MockRpcClient()); Assert.Single(round.Alices); DateTimeOffset preDeadline = DateTimeOffset.UtcNow - TimeSpan.FromMilliseconds(1); alice.Deadline = preDeadline; await handler.ConfirmConnectionAsync(req, CancellationToken.None); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Single(round.Alices); Assert.NotEqual(preDeadline, alice.Deadline); await arena.StopAsync(CancellationToken.None); }
public async Task WrongPhaseAsync() { WabiSabiConfig cfg = new(); var alice = WabiSabiFactory.CreateAlice(); var preDeadline = alice.Deadline; using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)).ConfigureAwait(false); var round = arena.Rounds.First().Value; round.Alices.Add(alice); var req = WabiSabiFactory.CreateConnectionConfirmationRequest(round); foreach (Phase phase in Enum.GetValues(typeof(Phase))) { if (phase != Phase.InputRegistration && phase != Phase.ConnectionConfirmation) { round.SetPhase(phase); await using PostRequestHandler handler = new(cfg, new Prison(), arena, new MockRpcClient()); var ex = await Assert.ThrowsAsync <WabiSabiProtocolException>(async() => await handler.ConfirmConnectionAsync(req)); Assert.Equal(WabiSabiProtocolErrorCode.WrongPhase, ex.ErrorCode); } } Assert.Equal(preDeadline, alice.Deadline); Assert.False(alice.ConfirmedConnetion); await arena.StopAsync(CancellationToken.None); }
public async Task SuccessInConnectionConfirmationPhaseAsync() { WabiSabiConfig cfg = new(); var round = WabiSabiFactory.CreateRound(cfg); round.SetPhase(Phase.ConnectionConfirmation); var alice = WabiSabiFactory.CreateAlice(round); var preDeadline = alice.Deadline; round.Alices.Add(alice); using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, round); var req = WabiSabiFactory.CreateConnectionConfirmationRequest(round); var resp = await arena.ConfirmConnectionAsync(req, CancellationToken.None); Assert.NotNull(resp); Assert.NotNull(resp.ZeroAmountCredentials); Assert.NotNull(resp.ZeroVsizeCredentials); Assert.NotNull(resp.RealAmountCredentials); Assert.NotNull(resp.RealVsizeCredentials); Assert.Equal(preDeadline, alice.Deadline); Assert.True(alice.ConfirmedConnection); await arena.StopAsync(CancellationToken.None); }
public async Task IncorrectRequestedVsizeCredentialsAsync() { WabiSabiConfig cfg = new(); var round = WabiSabiFactory.CreateRound(cfg); round.SetPhase(Phase.ConnectionConfirmation); var alice = WabiSabiFactory.CreateAlice(round); round.Alices.Add(alice); using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, round); Assert.Contains(alice, round.Alices); var incorrectVsizeCredentials = WabiSabiFactory.CreateRealCredentialRequests(round, null, 234).vsizeRequest; var req = WabiSabiFactory.CreateConnectionConfirmationRequest(round) with { RealVsizeCredentialRequests = incorrectVsizeCredentials }; var ex = await Assert.ThrowsAsync <WabiSabiProtocolException>(async() => await arena.ConfirmConnectionAsync(req, CancellationToken.None)); Assert.Equal(WabiSabiProtocolErrorCode.IncorrectRequestedVsizeCredentials, ex.ErrorCode); Assert.False(alice.ConfirmedConnection); await arena.StopAsync(CancellationToken.None); }
public async Task RoundNotFoundAsync() { using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(); await using PostRequestHandler handler = new(new WabiSabiConfig(), new Prison(), arena, new MockRpcClient()); var req = WabiSabiFactory.CreateConnectionConfirmationRequest(); var ex = await Assert.ThrowsAsync <WabiSabiProtocolException>(async() => await handler.ConfirmConnectionAsync(req)); Assert.Equal(WabiSabiProtocolErrorCode.RoundNotFound, ex.ErrorCode); await arena.StopAsync(CancellationToken.None); }
public async Task AliceNotFoundAsync() { WabiSabiConfig cfg = new(); var round = WabiSabiFactory.CreateRound(cfg); using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, round); var req = WabiSabiFactory.CreateConnectionConfirmationRequest(round); var ex = await Assert.ThrowsAsync <WabiSabiProtocolException>(async() => await arena.ConfirmConnectionAsync(req, CancellationToken.None)); Assert.Equal(WabiSabiProtocolErrorCode.AliceNotFound, ex.ErrorCode); await arena.StopAsync(CancellationToken.None); }
public async Task RoundNotFoundAsync() { var cfg = new WabiSabiConfig(); var nonExistingRound = WabiSabiFactory.CreateRound(cfg); using Arena arena = await ArenaBuilder.Default.CreateAndStartAsync(); var req = WabiSabiFactory.CreateConnectionConfirmationRequest(nonExistingRound); var ex = await Assert.ThrowsAsync <WabiSabiProtocolException>( async() => await arena.ConfirmConnectionAsync(req, CancellationToken.None)); Assert.Equal(WabiSabiProtocolErrorCode.RoundNotFound, ex.ErrorCode); await arena.StopAsync(CancellationToken.None); }
public async Task TimeoutSufficientPeersAsync() { WabiSabiConfig cfg = new() { MaxInputCountByRound = 2, MinInputCountByRoundMultiplier = 1, TransactionSigningTimeout = TimeSpan.Zero, OutputRegistrationTimeout = TimeSpan.Zero }; var mockRpc = new MockRpcClient(); mockRpc.OnSendRawTransactionAsync = _ => throw new RPCException(RPCErrorCode.RPC_TRANSACTION_REJECTED, "", null); using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, mockRpc); // Create the round. await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); var round = Assert.Single(arena.Rounds).Value; // Register Alices. using Key key1 = new(); using Key key2 = new(); var irreq1 = WabiSabiFactory.CreateInputsRegistrationRequest(key1, round); var irres1 = await arena.RegisterInputAsync( irreq1.RoundId, irreq1.InputRoundSignaturePairs.ToDictionary(x => new Coin(x.Input, new TxOut(Money.Coins(1), key1.PubKey.GetSegwitAddress(Network.Main))), x => x.RoundSignature), irreq1.ZeroAmountCredentialRequests, irreq1.ZeroWeightCredentialRequests); var irreq2 = WabiSabiFactory.CreateInputsRegistrationRequest(key2, round); var irres2 = await arena.RegisterInputAsync( irreq2.RoundId, irreq2.InputRoundSignaturePairs.ToDictionary(x => new Coin(x.Input, new TxOut(Money.Coins(1), key2.PubKey.GetSegwitAddress(Network.Main))), x => x.RoundSignature), irreq2.ZeroAmountCredentialRequests, irreq2.ZeroWeightCredentialRequests); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(Phase.ConnectionConfirmation, round.Phase); var alice1 = round.Alices.Single(x => x.Id == irres1.AliceId); var alice2 = round.Alices.Single(x => x.Id == irres2.AliceId); // Confirm connections. var ccresps = new List <(ConnectionConfirmationResponse resp, WabiSabiClient amountClient, WabiSabiClient weightClient, Guid aliceId)>(); var ccreq1 = WabiSabiFactory.CreateConnectionConfirmationRequest(round, irres1); var ccresp1 = await arena.ConfirmConnectionAsync(ccreq1.request); ccresps.Add((ccresp1, ccreq1.amountClient, ccreq1.weightClient, irres2.AliceId)); ccreq1.amountClient.HandleResponse(ccresp1.RealAmountCredentials !, ccreq1.amountValidation); ccreq1.weightClient.HandleResponse(ccresp1.RealWeightCredentials !, ccreq1.weightValidation); var ccreq2 = WabiSabiFactory.CreateConnectionConfirmationRequest(round, irres2); var ccresp2 = await arena.ConfirmConnectionAsync(ccreq2.request); ccresps.Add((ccresp2, ccreq2.amountClient, ccreq2.weightClient, irres1.AliceId)); ccreq2.amountClient.HandleResponse(ccresp2.RealAmountCredentials !, ccreq2.amountValidation); ccreq2.weightClient.HandleResponse(ccresp2.RealWeightCredentials !, ccreq2.weightValidation); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(Phase.OutputRegistration, round.Phase); // Register outputs. foreach (var orreq in WabiSabiFactory.CreateOutputRegistrationRequests(round, ccresps)) { var orresp = await arena.RegisterOutputAsync(orreq); } // Make sure not all alices signed. var alice3 = WabiSabiFactory.CreateAlice(); alice3.ConfirmedConnection = true; round.Alices.Add(alice3); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(Phase.TransactionSigning, round.Phase); var signedCoinJoin = round.Coinjoin.Clone(); var coin1 = alice1.Coins.First(); var coin2 = alice2.Coins.First(); var idx1 = signedCoinJoin.Inputs.IndexOf(signedCoinJoin.Inputs.Single(x => x.PrevOut == coin1.Outpoint)); var idx2 = signedCoinJoin.Inputs.IndexOf(signedCoinJoin.Inputs.Single(x => x.PrevOut == coin2.Outpoint)); signedCoinJoin.Sign(key1.GetBitcoinSecret(Network.Main), coin1); var txsigreq1 = new TransactionSignaturesRequest(round.Id, new[] { new InputWitnessPair((uint)idx1, signedCoinJoin.Inputs[idx1].WitScript) }); signedCoinJoin.Sign(key2.GetBitcoinSecret(Network.Main), coin2); var txsigreq2 = new TransactionSignaturesRequest(round.Id, new[] { new InputWitnessPair((uint)idx2, signedCoinJoin.Inputs[idx2].WitScript) }); await arena.SignTransactionAsync(txsigreq1); await arena.SignTransactionAsync(txsigreq2); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.DoesNotContain(round.Id, arena.Rounds.Keys); Assert.Single(arena.Rounds.Where(x => x.Value.IsBlameRound)); var badOutpoint = alice3.Coins.Select(x => x.Outpoint).First(); Assert.Contains(badOutpoint, arena.Prison.GetInmates().Select(x => x.Utxo)); var blameRound = arena.Rounds.Single(x => x.Value.IsBlameRound).Value; Assert.True(blameRound.IsBlameRound); Assert.NotNull(blameRound.BlameOf); Assert.Equal(round.Id, blameRound.BlameOf?.Id); var whitelist = blameRound.BlameWhitelist; Assert.Contains(alice1.Coins.Select(x => x.Outpoint).First(), whitelist); Assert.Contains(alice2.Coins.Select(x => x.Outpoint).First(), whitelist); Assert.DoesNotContain(badOutpoint, whitelist); await arena.StopAsync(CancellationToken.None); } }
public async Task EveryoneSignedAsync() { WabiSabiConfig cfg = new() { MaxInputCountByRound = 2, MinInputCountByRoundMultiplier = 0.5 }; using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg); // Create the round. await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); var round = Assert.Single(arena.Rounds).Value; // Register Alices. using Key key1 = new(); using Key key2 = new(); var irreq1 = WabiSabiFactory.CreateInputsRegistrationRequest(key1, round); var irres1 = await arena.RegisterInputAsync( irreq1.RoundId, irreq1.InputRoundSignaturePairs.ToDictionary(x => new Coin(x.Input, new TxOut(Money.Coins(1), key1.PubKey.GetSegwitAddress(Network.Main))), x => x.RoundSignature), irreq1.ZeroAmountCredentialRequests, irreq1.ZeroWeightCredentialRequests); var irreq2 = WabiSabiFactory.CreateInputsRegistrationRequest(key2, round); var irres2 = await arena.RegisterInputAsync( irreq2.RoundId, irreq2.InputRoundSignaturePairs.ToDictionary(x => new Coin(x.Input, new TxOut(Money.Coins(1), key2.PubKey.GetSegwitAddress(Network.Main))), x => x.RoundSignature), irreq2.ZeroAmountCredentialRequests, irreq2.ZeroWeightCredentialRequests); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(Phase.ConnectionConfirmation, round.Phase); var alice1 = round.Alices.Single(x => x.Id == irres1.AliceId); var alice2 = round.Alices.Single(x => x.Id == irres2.AliceId); // Confirm connections. var ccresps = new List <(ConnectionConfirmationResponse resp, WabiSabiClient amountClient, WabiSabiClient weightClient, Guid aliceId)>(); var ccreq1 = WabiSabiFactory.CreateConnectionConfirmationRequest(round, irres1); var ccresp1 = await arena.ConfirmConnectionAsync(ccreq1.request); ccresps.Add((ccresp1, ccreq1.amountClient, ccreq1.weightClient, irres2.AliceId)); ccreq1.amountClient.HandleResponse(ccresp1.RealAmountCredentials !, ccreq1.amountValidation); ccreq1.weightClient.HandleResponse(ccresp1.RealWeightCredentials !, ccreq1.weightValidation); var ccreq2 = WabiSabiFactory.CreateConnectionConfirmationRequest(round, irres2); var ccresp2 = await arena.ConfirmConnectionAsync(ccreq2.request); ccresps.Add((ccresp2, ccreq2.amountClient, ccreq2.weightClient, irres1.AliceId)); ccreq2.amountClient.HandleResponse(ccresp2.RealAmountCredentials !, ccreq2.amountValidation); ccreq2.weightClient.HandleResponse(ccresp2.RealWeightCredentials !, ccreq2.weightValidation); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(Phase.OutputRegistration, round.Phase); // Register outputs. foreach (var orreq in WabiSabiFactory.CreateOutputRegistrationRequests(round, ccresps)) { var orresp = await arena.RegisterOutputAsync(orreq); } await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(Phase.TransactionSigning, round.Phase); var signedCoinJoin = round.Coinjoin.Clone(); var coin1 = alice1.Coins.First(); var coin2 = alice2.Coins.First(); var idx1 = signedCoinJoin.Inputs.IndexOf(signedCoinJoin.Inputs.Single(x => x.PrevOut == coin1.Outpoint)); var idx2 = signedCoinJoin.Inputs.IndexOf(signedCoinJoin.Inputs.Single(x => x.PrevOut == coin2.Outpoint)); signedCoinJoin.Sign(key1.GetBitcoinSecret(Network.Main), coin1); var txsigreq1 = new TransactionSignaturesRequest(round.Id, new[] { new InputWitnessPair((uint)idx1, signedCoinJoin.Inputs[idx1].WitScript) }); signedCoinJoin.Sign(key2.GetBitcoinSecret(Network.Main), coin2); var txsigreq2 = new TransactionSignaturesRequest(round.Id, new[] { new InputWitnessPair((uint)idx2, signedCoinJoin.Inputs[idx2].WitScript) }); await arena.SignTransactionAsync(txsigreq1); await arena.SignTransactionAsync(txsigreq2); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(Phase.TransactionBroadcasting, round.Phase); await arena.StopAsync(CancellationToken.None); }
public async Task AlicesSpentAsync() { WabiSabiConfig cfg = new() { MaxInputCountByRound = 2, MinInputCountByRoundMultiplier = 0.5 }; var mockRpc = new MockRpcClient(); mockRpc.OnSendRawTransactionAsync = _ => throw new RPCException(RPCErrorCode.RPC_TRANSACTION_REJECTED, "", null); mockRpc.OnGetTxOutAsync ??= (_, _, _) => null; using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, mockRpc); // Create the round. await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); var round = Assert.Single(arena.Rounds).Value; // Register Alices. using Key key1 = new(); using Key key2 = new(); var irreq1 = WabiSabiFactory.CreateInputsRegistrationRequest(key1, round); var irres1 = await arena.RegisterInputAsync( irreq1.RoundId, irreq1.InputRoundSignaturePairs.ToDictionary(x => new Coin(x.Input, new TxOut(Money.Coins(1), key1.PubKey.GetSegwitAddress(Network.Main))), x => x.RoundSignature), irreq1.ZeroAmountCredentialRequests, irreq1.ZeroWeightCredentialRequests); var irreq2 = WabiSabiFactory.CreateInputsRegistrationRequest(key2, round); var irres2 = await arena.RegisterInputAsync( irreq2.RoundId, irreq2.InputRoundSignaturePairs.ToDictionary(x => new Coin(x.Input, new TxOut(Money.Coins(1), key2.PubKey.GetSegwitAddress(Network.Main))), x => x.RoundSignature), irreq2.ZeroAmountCredentialRequests, irreq2.ZeroWeightCredentialRequests); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(Phase.ConnectionConfirmation, round.Phase); var alice1 = round.Alices.Single(x => x.Id == irres1.AliceId); var alice2 = round.Alices.Single(x => x.Id == irres2.AliceId); // Confirm connections. var ccresps = new List <(ConnectionConfirmationResponse resp, WabiSabiClient amountClient, WabiSabiClient weightClient, Guid aliceId)>(); var ccreq1 = WabiSabiFactory.CreateConnectionConfirmationRequest(round, irres1); var ccresp1 = await arena.ConfirmConnectionAsync(ccreq1.request); ccresps.Add((ccresp1, ccreq1.amountClient, ccreq1.weightClient, irres2.AliceId)); ccreq1.amountClient.HandleResponse(ccresp1.RealAmountCredentials !, ccreq1.amountValidation); ccreq1.weightClient.HandleResponse(ccresp1.RealWeightCredentials !, ccreq1.weightValidation); var ccreq2 = WabiSabiFactory.CreateConnectionConfirmationRequest(round, irres2); var ccresp2 = await arena.ConfirmConnectionAsync(ccreq2.request); ccresps.Add((ccresp2, ccreq2.amountClient, ccreq2.weightClient, irres1.AliceId)); ccreq2.amountClient.HandleResponse(ccresp2.RealAmountCredentials !, ccreq2.amountValidation); ccreq2.weightClient.HandleResponse(ccresp2.RealWeightCredentials !, ccreq2.weightValidation); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(Phase.OutputRegistration, round.Phase); // Register outputs. foreach (var orreq in WabiSabiFactory.CreateOutputRegistrationRequests(round, ccresps)) { var orresp = await arena.RegisterOutputAsync(orreq); } await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(Phase.TransactionSigning, round.Phase); var signedCoinJoin = round.Coinjoin.Clone(); var coin1 = alice1.Coins.First(); var coin2 = alice2.Coins.First(); var idx1 = signedCoinJoin.Inputs.IndexOf(signedCoinJoin.Inputs.Single(x => x.PrevOut == coin1.Outpoint)); var idx2 = signedCoinJoin.Inputs.IndexOf(signedCoinJoin.Inputs.Single(x => x.PrevOut == coin2.Outpoint)); signedCoinJoin.Sign(key1.GetBitcoinSecret(Network.Main), coin1); var txsigreq1 = new TransactionSignaturesRequest(round.Id, new[] { new InputWitnessPair((uint)idx1, signedCoinJoin.Inputs[idx1].WitScript) }); signedCoinJoin.Sign(key2.GetBitcoinSecret(Network.Main), coin2); var txsigreq2 = new TransactionSignaturesRequest(round.Id, new[] { new InputWitnessPair((uint)idx2, signedCoinJoin.Inputs[idx2].WitScript) }); await arena.SignTransactionAsync(txsigreq1); await arena.SignTransactionAsync(txsigreq2); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.DoesNotContain(round.Id, arena.Rounds.Keys); // 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 SomeBobsRegisteredTimeoutAsync() { WabiSabiConfig cfg = new() { MaxInputCountByRound = 2, MinInputCountByRoundMultiplier = 0.5, OutputRegistrationTimeout = TimeSpan.Zero }; using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg); // Create the round. await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); var round = Assert.Single(arena.Rounds).Value; // Register Alices. using Key key1 = new(); var irreq1 = WabiSabiFactory.CreateInputsRegistrationRequest(key1, round); var irres1 = await arena.RegisterInputAsync( irreq1.RoundId, irreq1.InputRoundSignaturePairs.ToDictionary(x => new Coin(x.Input, new TxOut(Money.Coins(1), key1.PubKey.GetSegwitAddress(Network.Main))), x => x.RoundSignature), irreq1.ZeroAmountCredentialRequests, irreq1.ZeroVsizeCredentialRequests); using Key key2 = new(); var irreq2 = WabiSabiFactory.CreateInputsRegistrationRequest(key2, round); var irres2 = await arena.RegisterInputAsync( irreq2.RoundId, irreq2.InputRoundSignaturePairs.ToDictionary(x => new Coin(x.Input, new TxOut(Money.Coins(1), key2.PubKey.GetSegwitAddress(Network.Main))), x => x.RoundSignature), irreq2.ZeroAmountCredentialRequests, irreq2.ZeroVsizeCredentialRequests); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(Phase.ConnectionConfirmation, round.Phase); // Confirm connections. var ccresps = new List <(ConnectionConfirmationResponse resp, WabiSabiClient amountClient, WabiSabiClient vsizeClient, Guid aliceId)>(); var ccreq1 = WabiSabiFactory.CreateConnectionConfirmationRequest(round, irres1); var ccresp1 = await arena.ConfirmConnectionAsync(ccreq1.request); ccresps.Add((ccresp1, ccreq1.amountClient, ccreq1.vsizeClient, irres2.AliceId)); ccreq1.amountClient.HandleResponse(ccresp1.RealAmountCredentials !, ccreq1.amountValidation); ccreq1.vsizeClient.HandleResponse(ccresp1.RealVsizeCredentials !, ccreq1.vsizeValidation); var ccreq2 = WabiSabiFactory.CreateConnectionConfirmationRequest(round, irres2); var ccresp2 = await arena.ConfirmConnectionAsync(ccreq2.request); ccresps.Add((ccresp2, ccreq2.amountClient, ccreq2.vsizeClient, irres1.AliceId)); ccreq2.amountClient.HandleResponse(ccresp2.RealAmountCredentials !, ccreq2.amountValidation); ccreq2.vsizeClient.HandleResponse(ccresp2.RealVsizeCredentials !, ccreq2.vsizeValidation); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(Phase.OutputRegistration, round.Phase); // Register outputs. var orresp = await arena.RegisterOutputAsync(WabiSabiFactory.CreateOutputRegistrationRequests(round, ccresps).First()); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Equal(Phase.TransactionSigning, round.Phase); Assert.Equal(2, round.Coinjoin.Inputs.Count); Assert.Equal(2, round.Coinjoin.Outputs.Count); Assert.Contains(cfg.BlameScript, round.Coinjoin.Outputs.Select(x => x.ScriptPubKey)); await arena.StopAsync(CancellationToken.None); }