Exemplo n.º 1
0
    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);
    }
Exemplo n.º 2
0
    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);
    }
Exemplo n.º 3
0
        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);
        }
Exemplo n.º 4
0
    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);
    }
Exemplo n.º 5
0
        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);
        }
Exemplo n.º 6
0
        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);
        }
Exemplo n.º 7
0
        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);
        }
Exemplo n.º 8
0
        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);
        }
Exemplo n.º 11
0
        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);
        }
Exemplo n.º 13
0
    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);
        }
Exemplo n.º 17
0
        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);
        }