public async Task TooMuchWeightAsync() { MockArena arena = new(); WabiSabiConfig cfg = new() { MaxRegistrableWeight = 1 }; var round = WabiSabiFactory.CreateRound(cfg); arena.OnTryGetRound = _ => round; MockRpcClient rpc = new(); using Key key = new(); rpc.OnGetTxOutAsync = (_, _, _) => new() { Confirmations = 1, ScriptPubKeyType = "witness_v0_keyhash", TxOut = new TxOut(Money.Coins(1), key.PubKey.GetSegwitAddress(Network.Main)) }; await using PostRequestHandler handler = new(cfg, new Prison(), arena, rpc); var req = WabiSabiFactory.CreateInputsRegistrationRequest(key, round); var ex = await Assert.ThrowsAsync <WabiSabiProtocolException>(async() => await handler.RegisterInputAsync(req)); Assert.Equal(WabiSabiProtocolErrorCode.TooMuchWeight, ex.ErrorCode); } } }
public async Task ScriptNotAllowedAsync() { WabiSabiConfig cfg = new(); var round = WabiSabiFactory.CreateRound(cfg); using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, round); MockRpcClient rpc = new(); using Key key = new(); rpc.OnGetTxOutAsync = (_, _, _) => new() { Confirmations = 1, TxOut = new(Money.Coins(1), key.PubKey.GetScriptAddress(Network.Main)) }; await using ArenaRequestHandler handler = new(cfg, new(), arena, rpc); var req = WabiSabiFactory.CreateInputsRegistrationRequest(round); var ex = await Assert.ThrowsAsync <WabiSabiProtocolException>(async() => await handler.RegisterInputAsync(req)); Assert.Equal(WabiSabiProtocolErrorCode.ScriptNotAllowed, ex.ErrorCode); await arena.StopAsync(CancellationToken.None); }
public async Task WrongPhaseAsync() { WabiSabiConfig cfg = new(); using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)).ConfigureAwait(false); var round = arena.Rounds.First().Value; using Key key = new(); var req = WabiSabiFactory.CreateInputsRegistrationRequest(key, round); await using ArenaRequestHandler handler = new(cfg, new(), arena, WabiSabiFactory.CreateMockRpc(key)); foreach (Phase phase in Enum.GetValues(typeof(Phase))) { if (phase != Phase.InputRegistration) { round.SetPhase(phase); await RegisterAndAssertWrongPhaseAsync(req, handler); } } 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); using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, round); MockRpcClient rpc = new(); using Key key = new(); rpc.OnGetTxOutAsync = (_, _, _) => new() { Confirmations = 1, ScriptPubKeyType = "witness_v0_keyhash", TxOut = new TxOut(Money.Coins(1), key.PubKey.GetSegwitAddress(Network.Main)) }; var req = WabiSabiFactory.CreateInputsRegistrationRequest(key, round); await using PostRequestHandler handler = new(cfg, new Prison(), arena, rpc); var minAliceDeadline = DateTimeOffset.UtcNow + cfg.ConnectionConfirmationTimeout * 0.9; await handler.RegisterInputAsync(req); var alice = Assert.Single(round.Alices); alice.Deadline = DateTimeOffset.UtcNow - TimeSpan.FromMilliseconds(1); await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); Assert.Empty(round.Alices); await arena.StopAsync(CancellationToken.None); }
public async Task InputRegistrationFullWeightAsync() { WabiSabiConfig cfg = new(); var round = WabiSabiFactory.CreateRound(cfg); using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, round); using Key key = new(); await using ArenaRequestHandler handler = new(cfg, new(), arena, WabiSabiFactory.CreateMockRpc(key)); // TODO add configuration parameter round.InitialInputVsizeAllocation = (int)round.PerAliceVsizeAllocation; // Add an alice await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)); round.Alices.Add(WabiSabiFactory.CreateAlice()); Assert.Equal(0, round.RemainingInputVsizeAllocation); // try to add an additional input var req = WabiSabiFactory.CreateInputsRegistrationRequest(key, round); var ex = await Assert.ThrowsAsync <WabiSabiProtocolException>(async() => await handler.RegisterInputAsync(req)); Assert.Equal(WabiSabiProtocolErrorCode.TooMuchTotalWeight, ex.ErrorCode); await arena.StopAsync(CancellationToken.None); }
public async Task SuccessWithAliceUpdateCrossRoundAsync() { WabiSabiConfig cfg = new(); var round = WabiSabiFactory.CreateRound(cfg); var anotherRound = WabiSabiFactory.CreateRound(cfg); using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, round, anotherRound); using Key key = new(); var req = WabiSabiFactory.CreateInputsRegistrationRequest(key, round); // Make sure an Alice have already been registered with the same input. var preAlice = WabiSabiFactory.CreateAlice(req.InputRoundSignaturePairs); anotherRound.Alices.Add(preAlice); await using ArenaRequestHandler handler = new(cfg, new(), arena, WabiSabiFactory.CreateMockRpc(key)); var minAliceDeadline = DateTimeOffset.UtcNow + cfg.ConnectionConfirmationTimeout * 0.9; var resp = await handler.RegisterInputAsync(req); AssertSingleAliceSuccessfullyRegistered(round, minAliceDeadline, resp); Assert.Empty(anotherRound.Alices); await arena.StopAsync(CancellationToken.None); }
public async Task AliceAlreadyRegisteredCrossRoundAsync() { WabiSabiConfig cfg = new(); var round = WabiSabiFactory.CreateRound(cfg); var anotherRound = WabiSabiFactory.CreateRound(cfg); using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, round, anotherRound); using Key key = new(); var req = WabiSabiFactory.CreateInputsRegistrationRequest(key, round); // Make sure an Alice have already been registered with the same input. var preAlice = WabiSabiFactory.CreateAlice(req.InputRoundSignaturePairs); anotherRound.Alices.Add(preAlice); anotherRound.SetPhase(Phase.ConnectionConfirmation); await using ArenaRequestHandler handler = new(cfg, new(), arena, WabiSabiFactory.CreateMockRpc(key)); var ex = await Assert.ThrowsAsync <WabiSabiProtocolException>(async() => await handler.RegisterInputAsync(req)); Assert.Equal(WabiSabiProtocolErrorCode.AliceAlreadyRegistered, ex.ErrorCode); await arena.StopAsync(CancellationToken.None); }
public async Task RoundNotFoundAsync() { MockArena arena = new(); arena.OnTryGetRound = _ => null; await using PostRequestHandler handler = new(new WabiSabiConfig(), new Prison(), arena, new MockRpcClient()); var req = WabiSabiFactory.CreateInputsRegistrationRequest(); var ex = await Assert.ThrowsAsync <WabiSabiProtocolException>(async() => await handler.RegisterInputAsync(req)); Assert.Equal(WabiSabiProtocolErrorCode.RoundNotFound, ex.ErrorCode); }
public async Task RoundNotFoundAsync() { using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(); using Key key = new(); await using ArenaRequestHandler handler = new(new(), new(), arena, WabiSabiFactory.CreateMockRpc(key)); var req = WabiSabiFactory.CreateInputsRegistrationRequest(); var ex = await Assert.ThrowsAsync <WabiSabiProtocolException>(async() => await handler.RegisterInputAsync(req)); Assert.Equal(WabiSabiProtocolErrorCode.RoundNotFound, ex.ErrorCode); await arena.StopAsync(CancellationToken.None); }
public async Task TooManyInputsAsync() { MockArena arena = new(); WabiSabiConfig cfg = new() { MaxInputCountByAlice = 3 }; var round = WabiSabiFactory.CreateRound(cfg); arena.OnTryGetRound = _ => round; await using PostRequestHandler handler = new(cfg, new Prison(), arena, new MockRpcClient()); var req = WabiSabiFactory.CreateInputsRegistrationRequest(WabiSabiFactory.CreateInputRoundSignaturePairs(4), round); var ex = await Assert.ThrowsAsync <WabiSabiProtocolException>(async() => await handler.RegisterInputAsync(req)); Assert.Equal(WabiSabiProtocolErrorCode.TooManyInputs, ex.ErrorCode); }
public async Task TooManyInputsAsync() { WabiSabiConfig cfg = new() { MaxInputCountByAlice = 3 }; var round = WabiSabiFactory.CreateRound(cfg); using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, round); using Key key = new(); await using ArenaRequestHandler handler = new(cfg, new(), arena, WabiSabiFactory.CreateMockRpc(key)); var req = WabiSabiFactory.CreateInputsRegistrationRequest(WabiSabiFactory.CreateInputRoundSignaturePairs(4), round); var ex = await Assert.ThrowsAsync <WabiSabiProtocolException>(async() => await handler.RegisterInputAsync(req)); Assert.Equal(WabiSabiProtocolErrorCode.TooManyInputs, ex.ErrorCode); await arena.StopAsync(CancellationToken.None); }
public async Task NonUniqueInputsAsync() { WabiSabiConfig cfg = new(); var round = WabiSabiFactory.CreateRound(cfg); using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, round); var inputSigPair = WabiSabiFactory.CreateInputRoundSignaturePair(); await using ArenaRequestHandler handler = new(cfg, new(), arena, WabiSabiFactory.CreateMockRpc()); var req = WabiSabiFactory.CreateInputsRegistrationRequest(new[] { inputSigPair, inputSigPair }, round); var ex = await Assert.ThrowsAsync <WabiSabiProtocolException>(async() => await handler.RegisterInputAsync(req)); Assert.Equal(WabiSabiProtocolErrorCode.NonUniqueInputs, ex.ErrorCode); await arena.StopAsync(CancellationToken.None); }
public async Task TooMuchWeightAsync() { WabiSabiConfig cfg = new() { RegistrableWeightCredentials = 1 }; var round = WabiSabiFactory.CreateRound(cfg); using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, round); using Key key = new(); await using ArenaRequestHandler handler = new(cfg, new(), arena, WabiSabiFactory.CreateMockRpc(key)); var req = WabiSabiFactory.CreateInputsRegistrationRequest(key, round); var ex = await Assert.ThrowsAsync <WabiSabiProtocolException>(async() => await handler.RegisterInputAsync(req)); Assert.Equal(WabiSabiProtocolErrorCode.TooMuchWeight, ex.ErrorCode); await arena.StopAsync(CancellationToken.None); }
public async Task NotEnoughFundsAsync() { WabiSabiConfig cfg = new() { MinRegistrableAmount = Money.Coins(2) }; 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 ex = await Assert.ThrowsAsync <WabiSabiProtocolException>(async() => await handler.RegisterInputAsync(req)); Assert.Equal(WabiSabiProtocolErrorCode.NotEnoughFunds, ex.ErrorCode); await arena.StopAsync(CancellationToken.None); }
public async Task InputNotWhitelistedAsync() { MockArena arena = new(); WabiSabiConfig cfg = new(); var round = WabiSabiFactory.CreateRound(cfg); round.Alices.Add(new Alice(BitcoinFactory.CreateOutPoint())); Round blameRound = new(round); arena.OnTryGetRound = _ => blameRound; await using PostRequestHandler handler = new(cfg, new Prison(), arena, new MockRpcClient()); var req = WabiSabiFactory.CreateInputsRegistrationRequest(blameRound); var ex = await Assert.ThrowsAsync <WabiSabiProtocolException>(async() => await handler.RegisterInputAsync(req)); Assert.Equal(WabiSabiProtocolErrorCode.InputNotWhitelisted, ex.ErrorCode); }
public async Task InputSpentAsync() { MockArena arena = new(); WabiSabiConfig cfg = new(); var round = WabiSabiFactory.CreateRound(cfg); arena.OnTryGetRound = _ => round; MockRpcClient rpc = new(); rpc.OnGetTxOutAsync = (_, _, _) => null; await using PostRequestHandler handler = new(cfg, new Prison(), arena, rpc); var req = WabiSabiFactory.CreateInputsRegistrationRequest(round); var ex = await Assert.ThrowsAsync <WabiSabiProtocolException>(async() => await handler.RegisterInputAsync(req)); Assert.Equal(WabiSabiProtocolErrorCode.InputSpent, ex.ErrorCode); }
public async Task InputBannedAsync() { 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); arena.OnTryGetRound = _ => round; await using PostRequestHandler handler = new(cfg, prison, arena, new MockRpcClient()); var req = WabiSabiFactory.CreateInputsRegistrationRequest(pair, round); var ex = await Assert.ThrowsAsync <WabiSabiProtocolException>(async() => await handler.RegisterInputAsync(req)); Assert.Equal(WabiSabiProtocolErrorCode.InputBanned, ex.ErrorCode); }
public async Task InputCantBeNotedAsync() { Prison prison = new(); var pair = WabiSabiFactory.CreateInputRoundSignaturePair(); prison.Punish(pair.Input, Punishment.Noted, Guid.NewGuid()); WabiSabiConfig cfg = new() { AllowNotedInputRegistration = false }; var round = WabiSabiFactory.CreateRound(cfg); using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, round); await using ArenaRequestHandler handler = new(cfg, prison, arena, WabiSabiFactory.CreateMockRpc()); var req = WabiSabiFactory.CreateInputsRegistrationRequest(pair, round); var ex = await Assert.ThrowsAsync <WabiSabiProtocolException>(async() => await handler.RegisterInputAsync(req)); Assert.Equal(WabiSabiProtocolErrorCode.InputBanned, ex.ErrorCode); await arena.StopAsync(CancellationToken.None); }
public async Task InputUnconfirmedAsync() { WabiSabiConfig cfg = new(); var round = WabiSabiFactory.CreateRound(cfg); using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, round); MockRpcClient rpc = new(); rpc.OnGetTxOutAsync = (_, _, _) => new() { Confirmations = 0 }; await using ArenaRequestHandler handler = new(cfg, new(), arena, rpc); var req = WabiSabiFactory.CreateInputsRegistrationRequest(round); var ex = await Assert.ThrowsAsync <WabiSabiProtocolException>(async() => await handler.RegisterInputAsync(req)); Assert.Equal(WabiSabiProtocolErrorCode.InputUnconfirmed, 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 ArenaRequestHandler 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 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 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 = WabiSabiFactory.CreateInputsRegistrationRequest(round); foreach (Phase phase in Enum.GetValues(typeof(Phase))) { if (phase != Phase.InputRegistration) { round.Phase = phase; await using PostRequestHandler handler = new(cfg, new Prison(), arena, new MockRpcClient()); var ex = await Assert.ThrowsAsync <WabiSabiProtocolException>(async() => await handler.RegisterInputAsync(req)); Assert.Equal(WabiSabiProtocolErrorCode.WrongPhase, ex.ErrorCode); } } }
public async Task InputWhitelistedAsync() { MockArena arena = new(); var pair = WabiSabiFactory.CreateInputRoundSignaturePair(); 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, new Prison(), arena, new MockRpcClient()); var req = WabiSabiFactory.CreateInputsRegistrationRequest(pair, blameRound); var ex = await Assert.ThrowsAnyAsync <Exception>(async() => await handler.RegisterInputAsync(req)); if (ex is WabiSabiProtocolException wspex) { Assert.NotEqual(WabiSabiProtocolErrorCode.InputNotWhitelisted, wspex.ErrorCode); } }
public async Task AliceAlreadyRegisteredIntraRoundAsync() { WabiSabiConfig cfg = new(); var round = WabiSabiFactory.CreateRound(cfg); using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, round); using Key key = new(); var req = WabiSabiFactory.CreateInputsRegistrationRequest(key, round); // Make sure an Alice have already been registered with the same input. var anotherAlice = WabiSabiFactory.CreateAlice(req.InputRoundSignaturePairs); round.Alices.Add(anotherAlice); round.SetPhase(Phase.ConnectionConfirmation); await using ArenaRequestHandler handler = new(cfg, new(), arena, WabiSabiFactory.CreateMockRpc(key)); await RegisterAndAssertWrongPhaseAsync(req, handler); await arena.StopAsync(CancellationToken.None); }
public async Task InputWhitelistedAsync() { var pair = WabiSabiFactory.CreateInputRoundSignaturePair(); WabiSabiConfig cfg = new(); var round = WabiSabiFactory.CreateRound(cfg); round.Alices.Add(WabiSabiFactory.CreateAlice(pair)); Round blameRound = WabiSabiFactory.CreateBlameRound(round, cfg); using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, round, blameRound); await using PostRequestHandler handler = new(cfg, new(), arena, WabiSabiFactory.CreateMockRpc()); var req = WabiSabiFactory.CreateInputsRegistrationRequest(pair, blameRound); var ex = await Assert.ThrowsAnyAsync <Exception>(async() => await handler.RegisterInputAsync(req)); if (ex is WabiSabiProtocolException wspex) { Assert.NotEqual(WabiSabiProtocolErrorCode.InputNotWhitelisted, wspex.ErrorCode); } await arena.StopAsync(CancellationToken.None); }
public async Task ScriptNotAllowedAsync() { MockArena arena = new(); WabiSabiConfig cfg = new(); var round = WabiSabiFactory.CreateRound(cfg); arena.OnTryGetRound = _ => round; MockRpcClient rpc = new(); using Key key = new(); rpc.OnGetTxOutAsync = (_, _, _) => new GetTxOutResponse { Confirmations = 1, TxOut = new TxOut(Money.Coins(1), key.PubKey.GetScriptAddress(Network.Main)) }; await using PostRequestHandler handler = new(cfg, new Prison(), arena, rpc); var req = WabiSabiFactory.CreateInputsRegistrationRequest(round); var ex = await Assert.ThrowsAsync <WabiSabiProtocolException>(async() => await handler.RegisterInputAsync(req)); Assert.Equal(WabiSabiProtocolErrorCode.ScriptNotAllowed, ex.ErrorCode); }
public async Task InputImmatureAsync() { MockArena arena = new(); WabiSabiConfig cfg = new(); var round = WabiSabiFactory.CreateRound(cfg); arena.OnTryGetRound = _ => round; MockRpcClient rpc = new(); var req = WabiSabiFactory.CreateInputsRegistrationRequest(round); for (int i = 1; i <= 100; i++) { rpc.OnGetTxOutAsync = (_, _, _) => new GetTxOutResponse { Confirmations = i, IsCoinBase = true }; await using PostRequestHandler handler = new(cfg, new Prison(), arena, rpc); var ex = await Assert.ThrowsAsync <WabiSabiProtocolException>(async() => await handler.RegisterInputAsync(req)); Assert.Equal(WabiSabiProtocolErrorCode.InputImmature, ex.ErrorCode); } }
public async Task InputRegistrationFullAsync() { WabiSabiConfig cfg = new() { MaxInputCountByRound = 3 }; var round = WabiSabiFactory.CreateRound(cfg); using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, round); 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)); round.Alices.Add(WabiSabiFactory.CreateAlice()); round.Alices.Add(WabiSabiFactory.CreateAlice(WabiSabiFactory.CreateInputRoundSignaturePairs(2))); await RegisterAndAssertWrongPhaseAsync(req, handler); Assert.Equal(Phase.InputRegistration, round.Phase); 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); } }