Example #1
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);
    }
Example #2
0
        public async Task SuccessAsync()
        {
            WabiSabiConfig cfg              = new();
            var            round            = WabiSabiFactory.CreateRound(cfg);
            var            initialRemaining = round.RemainingInputVsizeAllocation;
            var            alice            = WabiSabiFactory.CreateAlice();

            round.Alices.Add(alice);
            Assert.True(round.RemainingInputVsizeAllocation < initialRemaining);

            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);

            // Ensure that removing an alice freed up the input weight
            // allocation from the round
            Assert.Equal(initialRemaining, round.RemainingInputVsizeAllocation);

            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);
        }
Example #4
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);
        }
Example #5
0
        public async Task AllConfirmedStepsAsync()
        {
            WabiSabiConfig cfg   = new() { MaxInputCountByRound = 4, MinInputCountByRoundMultiplier = 0.5 };
            var            round = WabiSabiFactory.CreateRound(cfg);
            var            a1    = WabiSabiFactory.CreateAlice(round);
            var            a2    = WabiSabiFactory.CreateAlice(round);
            var            a3    = WabiSabiFactory.CreateAlice(round);
            var            a4    = WabiSabiFactory.CreateAlice(round);

            a1.ConfirmedConnection = true;
            a2.ConfirmedConnection = true;
            a3.ConfirmedConnection = true;
            a4.ConfirmedConnection = true;
            round.Alices.Add(a1);
            round.Alices.Add(a2);
            round.Alices.Add(a3);
            round.Alices.Add(a4);
            round.SetPhase(Phase.ConnectionConfirmation);
            using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, round);

            await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21));

            Assert.Equal(Phase.OutputRegistration, round.Phase);

            await arena.StopAsync(CancellationToken.None);
        }
Example #6
0
        public async Task SuccessAsync()
        {
            WabiSabiConfig cfg   = new();
            var            round = WabiSabiFactory.CreateRound(cfg);

            using Key key = new();
            Alice alice = WabiSabiFactory.CreateAlice(key: key);

            round.Alices.Add(alice);
            var coinjoin = round.Coinjoin;

            coinjoin.Inputs.Add(alice.Coins.First().Outpoint);
            round.SetPhase(Phase.TransactionSigning);
            using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, round);

            var signedCoinJoin = coinjoin.Clone();

            signedCoinJoin.Sign(key.GetBitcoinSecret(Network.Main), alice.Coins.First());

            var req = new TransactionSignaturesRequest(round.Id, new[] { new InputWitnessPair(0, signedCoinJoin.Inputs[0].WitScript) });

            await using ArenaRequestHandler handler = new(cfg, new Prison(), arena, new MockRpcClient());
            await handler.SignTransactionAsync(req);

            Assert.True(round.Coinjoin.Inputs.First().HasWitScript());
            await arena.StopAsync(CancellationToken.None);
        }
Example #7
0
        public async Task NotEnoughConfirmedTimedoutDestroysAsync()
        {
            WabiSabiConfig cfg = new()
            {
                MaxInputCountByRound           = 4,
                MinInputCountByRoundMultiplier = 0.5,
                ConnectionConfirmationTimeout  = TimeSpan.Zero
            };
            var round = WabiSabiFactory.CreateRound(cfg);
            var a1    = WabiSabiFactory.CreateAlice();
            var a2    = WabiSabiFactory.CreateAlice();
            var a3    = WabiSabiFactory.CreateAlice();
            var a4    = WabiSabiFactory.CreateAlice();

            a1.ConfirmedConnetion = true;
            a2.ConfirmedConnetion = false;
            a3.ConfirmedConnetion = false;
            a4.ConfirmedConnetion = false;
            round.Alices.Add(a1);
            round.Alices.Add(a2);
            round.Alices.Add(a3);
            round.Alices.Add(a4);
            round.SetPhase(Phase.ConnectionConfirmation);
            using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, round);

            await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21));

            Assert.DoesNotContain(round.Id, arena.Rounds.Keys);
            Assert.Equal(3, arena.Prison.CountInmates().noted);
            Assert.Equal(0, arena.Prison.CountInmates().banned);

            await arena.StopAsync(CancellationToken.None);
        }
    }
    public async Task InputRegistrationTimeoutCanBeModifiedRuntimeAsync()
    {
        WabiSabiConfig cfg = new()
        {
            StandardInputRegistrationTimeout = TimeSpan.FromHours(1),
            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));
        round.Alices.Add(WabiSabiFactory.CreateAlice(round));
        await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21));

        Assert.Equal(Phase.InputRegistration, round.Phase);

        round.InputRegistrationTimeFrame = round.InputRegistrationTimeFrame with {
            Duration = TimeSpan.Zero
        };

        await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21));

        Assert.Equal(Phase.ConnectionConfirmation, round.Phase);

        await arena.StopAsync(CancellationToken.None);
    }
}
    public async Task DetectSpentTxoBeforeSteppingIntoConnectionConfirmationAsync()
    {
        WabiSabiConfig cfg            = new() { MaxInputCountByRound = 3 };
        var            round          = WabiSabiFactory.CreateRound(cfg);
        var            offendingAlice = WabiSabiFactory.CreateAlice(round); // this Alice spent the coin after registration

        var mockRpc = WabiSabiFactory.CreatePreconfiguredRpcClient();

        mockRpc.Setup(rpc => rpc.GetTxOutAsync(offendingAlice.Coin.Outpoint.Hash, (int)offendingAlice.Coin.Outpoint.N, true, It.IsAny <CancellationToken>()))
        .ReturnsAsync((GetTxOutResponse?)null);

        using Arena arena = await ArenaBuilder.From(cfg).With(mockRpc).CreateAndStartAsync(round);

        round.Alices.Add(WabiSabiFactory.CreateAlice(round));
        round.Alices.Add(offendingAlice);
        round.Alices.Add(WabiSabiFactory.CreateAlice(round));
        await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21));

        Assert.Equal(Phase.InputRegistration, round.Phase);
        Assert.Equal(2, round.Alices.Count);         // the offending alice was removed

        round.Alices.Add(WabiSabiFactory.CreateAlice(round));
        await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21));

        Assert.Equal(Phase.ConnectionConfirmation, round.Phase);
        Assert.Equal(3, round.Alices.Count);

        await arena.StopAsync(CancellationToken.None);
    }
    public async Task BlameRoundTimedoutWithSufficientInputsAsync()
    {
        WabiSabiConfig cfg = new()
        {
            BlameInputRegistrationTimeout    = TimeSpan.Zero,
            StandardInputRegistrationTimeout = TimeSpan.FromHours(1),             // Test that this is disregarded.
            MaxInputCountByRound             = 4,
            MinInputCountByRoundMultiplier   = 0.5
        };
        var round  = WabiSabiFactory.CreateRound(cfg);
        var alice1 = WabiSabiFactory.CreateAlice(round);
        var alice2 = WabiSabiFactory.CreateAlice(round);
        var alice3 = WabiSabiFactory.CreateAlice(round);

        round.Alices.Add(alice1);
        round.Alices.Add(alice2);
        round.Alices.Add(alice3);
        var blameRound = WabiSabiFactory.CreateBlameRound(round, cfg);

        blameRound.Alices.Add(alice1);
        blameRound.Alices.Add(alice2);

        using Arena arena = await ArenaBuilder.From(cfg).CreateAndStartAsync(blameRound);

        await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21));

        Assert.Equal(Phase.ConnectionConfirmation, blameRound.Phase);

        await arena.StopAsync(CancellationToken.None);
    }
    public async Task BlameRoundTimedoutWithoutSufficientInputsAsync()
    {
        // This test also tests that the min input count multiplier is applied
        // against the max input count by round number and not against the
        // number of inputs awaited by the blame round itself.
        WabiSabiConfig cfg = new()
        {
            BlameInputRegistrationTimeout    = TimeSpan.Zero,
            StandardInputRegistrationTimeout = TimeSpan.FromHours(1),             // Test that this is disregarded.
            MaxInputCountByRound             = 4,
            MinInputCountByRoundMultiplier   = 0.5
        };
        var round  = WabiSabiFactory.CreateRound(cfg);
        var alice1 = WabiSabiFactory.CreateAlice(round);
        var alice2 = WabiSabiFactory.CreateAlice(round);

        round.Alices.Add(alice1);
        round.Alices.Add(alice2);
        var blameRound = WabiSabiFactory.CreateBlameRound(round, cfg);

        blameRound.Alices.Add(alice1);

        using Arena arena = await ArenaBuilder.From(cfg).CreateAndStartAsync(blameRound);

        await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21));

        Assert.Equal(Phase.Ended, blameRound.Phase);
        Assert.DoesNotContain(blameRound, arena.GetActiveRounds());

        await arena.StopAsync(CancellationToken.None);
    }
    public async Task SuccessWithAliceUpdateIntraRoundAsync()
    {
        WabiSabiConfig cfg   = new();
        var            round = WabiSabiFactory.CreateRound(cfg);

        using Key key = new();
        var ownershipProof = WabiSabiFactory.CreateOwnershipProof(key, round.Id);
        var coin           = WabiSabiFactory.CreateCoin(key);

        // Make sure an Alice have already been registered with the same input.
        var preAlice = WabiSabiFactory.CreateAlice(coin, WabiSabiFactory.CreateOwnershipProof(key), round);

        round.Alices.Add(preAlice);

        var rpc = WabiSabiFactory.CreatePreconfiguredRpcClient(coin);

        using Arena arena = await ArenaBuilder.From(cfg).With(rpc).CreateAndStartAsync(round);

        var arenaClient = WabiSabiFactory.CreateArenaClient(arena);
        var ex          = await Assert.ThrowsAsync <WabiSabiProtocolException>(async() => await arenaClient.RegisterInputAsync(round.Id, coin.Outpoint, ownershipProof, CancellationToken.None).ConfigureAwait(false));

        Assert.Equal(WabiSabiProtocolErrorCode.AliceAlreadyRegistered, ex.ErrorCode);

        await arena.StopAsync(CancellationToken.None);
    }
    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);
    }
Example #15
0
    public async Task InputRegistrationFullAsync()
    {
        WabiSabiConfig cfg   = new() { MaxInputCountByRound = 3 };
        var            round = WabiSabiFactory.CreateRound(cfg);

        using Key key = new();
        var ownershipProof = WabiSabiFactory.CreateOwnershipProof(key, round.Id);

        using Arena arena = await ArenaBuilder.From(cfg).CreateAndStartAsync(round);

        await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21));

        round.Alices.Add(WabiSabiFactory.CreateAlice(round));
        round.Alices.Add(WabiSabiFactory.CreateAlice(round));
        round.Alices.Add(WabiSabiFactory.CreateAlice(round));

        var arenaClient = WabiSabiFactory.CreateArenaClient(arena);
        var ex          = await Assert.ThrowsAsync <WrongPhaseException>(
            async() => await arenaClient.RegisterInputAsync(round.Id, BitcoinFactory.CreateOutPoint(), ownershipProof, CancellationToken.None));

        Assert.Equal(WabiSabiProtocolErrorCode.WrongPhase, ex.ErrorCode);
        Assert.Equal(Phase.InputRegistration, round.Phase);

        await arena.StopAsync(CancellationToken.None);
    }
Example #16
0
        public async Task InputWhitelistedButBannedAsync()
        {
            using Key key = new();
            var alice      = WabiSabiFactory.CreateAlice(key);
            var bannedCoin = alice.Coin.Outpoint;

            WabiSabiConfig cfg   = new();
            var            round = WabiSabiFactory.CreateRound(cfg);

            round.Alices.Add(alice);
            Round blameRound = WabiSabiFactory.CreateBlameRound(round, cfg);
            var   mockRpc    = WabiSabiFactory.CreatePreconfiguredRpcClient(alice.Coin);

            using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, mockRpc, round, blameRound);

            arena.Prison.Punish(bannedCoin, Punishment.Banned, uint256.Zero);
            await using ArenaRequestHandler handler = new(cfg, arena.Prison, arena, mockRpc.Object);

            var req = WabiSabiFactory.CreateInputRegistrationRequest(key: key, round: blameRound, prevout: bannedCoin);
            var ex  = await Assert.ThrowsAsync <WabiSabiProtocolException>(async() => await handler.RegisterInputAsync(req, CancellationToken.None));

            Assert.Equal(WabiSabiProtocolErrorCode.InputBanned, ex.ErrorCode);

            await arena.StopAsync(CancellationToken.None);
        }
    public async Task WrongPhaseAsync()
    {
        WabiSabiConfig cfg = new();

        using Arena arena = await ArenaBuilder.From(cfg).CreateAndStartAsync();

        await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21));

        var round = arena.Rounds.First();

        round.MaxVsizeAllocationPerAlice = 11 + 31 + MultipartyTransactionParameters.SharedOverhead;

        round.Alices.Add(WabiSabiFactory.CreateAlice(round));

        foreach (Phase phase in Enum.GetValues(typeof(Phase)))
        {
            if (phase != Phase.OutputRegistration)
            {
                var req = WabiSabiFactory.CreateOutputRegistrationRequest(round);
                round.SetPhase(phase);
                var ex = await Assert.ThrowsAsync <WrongPhaseException>(async() => await arena.RegisterOutputAsync(req, CancellationToken.None));

                Assert.Equal(WabiSabiProtocolErrorCode.WrongPhase, ex.ErrorCode);
            }
        }

        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);
        }
Example #19
0
        public async Task WrongCoinjoinSignatureAsync()
        {
            WabiSabiConfig cfg   = new();
            var            round = WabiSabiFactory.CreateRound(cfg);

            using Key key1 = new();
            Alice alice1 = WabiSabiFactory.CreateAlice(key: key1);

            using Key key2 = new();
            Alice alice2 = WabiSabiFactory.CreateAlice(key: key2);

            round.Alices.Add(alice1);
            round.Alices.Add(alice2);
            var coinjoin = round.Coinjoin;

            coinjoin.Inputs.Add(alice1.Coins.First().Outpoint);
            coinjoin.Inputs.Add(alice2.Coins.First().Outpoint);
            round.SetPhase(Phase.TransactionSigning);
            using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, round);

            // Submit the signature for the second alice to the first alice's input.
            var signedCoinJoin = coinjoin.Clone();

            signedCoinJoin.Sign(key2.GetBitcoinSecret(Network.Main), alice2.Coins.First());

            var req = new TransactionSignaturesRequest(round.Id, new[] { new InputWitnessPair(0, signedCoinJoin.Inputs[0].WitScript) });

            await using ArenaRequestHandler handler = new(cfg, new Prison(), arena, new MockRpcClient());
            var ex = await Assert.ThrowsAsync <WabiSabiProtocolException>(async() => await handler.SignTransactionAsync(req));

            Assert.Equal(WabiSabiProtocolErrorCode.WrongCoinjoinSignature, ex.ErrorCode);
            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);
        }
Example #21
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);
        }
Example #22
0
        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);
        }
Example #23
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);
        }
Example #24
0
    public async Task WrongPhaseAsync()
    {
        WabiSabiConfig cfg   = new();
        Round          round = WabiSabiFactory.CreateRound(cfg);

        using Arena arena = await ArenaBuilder.From(cfg).CreateAndStartAsync(round);

        await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21));

        // Refresh the Arena States because of vsize manipulation.
        await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21));

        round.Alices.Add(WabiSabiFactory.CreateAlice(round));

        foreach (Phase phase in Enum.GetValues(typeof(Phase)))
        {
            if (phase != Phase.OutputRegistration)
            {
                var req = WabiSabiFactory.CreateOutputRegistrationRequest(round);
                round.SetPhase(phase);
                var ex = await Assert.ThrowsAsync <WrongPhaseException>(async() => await arena.RegisterOutputAsync(req, CancellationToken.None));

                Assert.Equal(WabiSabiProtocolErrorCode.WrongPhase, ex.ErrorCode);
            }
        }

        await arena.StopAsync(CancellationToken.None);
    }
Example #25
0
        public async Task NotAllConfirmedStaysAsync()
        {
            WabiSabiConfig cfg   = new() { MaxInputCountByRound = 4, MinInputCountByRoundMultiplier = 0.5 };
            var            round = WabiSabiFactory.CreateRound(cfg);
            var            a1    = WabiSabiFactory.CreateAlice(round);
            var            a2    = WabiSabiFactory.CreateAlice(round);
            var            a3    = WabiSabiFactory.CreateAlice(round);
            var            a4    = WabiSabiFactory.CreateAlice(round);

            a1.ConfirmedConnection = true;
            a2.ConfirmedConnection = true;
            a3.ConfirmedConnection = true;
            a4.ConfirmedConnection = false;
            round.Alices.Add(a1);
            round.Alices.Add(a2);
            round.Alices.Add(a3);
            round.Alices.Add(a4);
            round.SetPhase(Phase.ConnectionConfirmation);

            Prison prison = new();

            using Arena arena = await ArenaBuilder.From(cfg, prison).CreateAndStartAsync(round);

            await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21));

            Assert.Equal(Phase.ConnectionConfirmation, round.Phase);
            Assert.Equal(0, prison.CountInmates().noted);
            Assert.Equal(0, prison.CountInmates().banned);

            await arena.StopAsync(CancellationToken.None);
        }
Example #26
0
        public async Task WrongPhaseAsync()
        {
            WabiSabiConfig cfg = new();

            using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg);

            await using ArenaRequestHandler handler = new(cfg, new Prison(), arena, new MockRpcClient());
            await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)).ConfigureAwait(false);

            var round = arena.Rounds.First();

            round.Alices.Add(WabiSabiFactory.CreateAlice());

            foreach (Phase phase in Enum.GetValues(typeof(Phase)))
            {
                if (phase != Phase.OutputRegistration)
                {
                    var req = WabiSabiFactory.CreateOutputRegistrationRequest(round);
                    round.SetPhase(phase);
                    var ex = await Assert.ThrowsAsync <WabiSabiProtocolException>(async() => await handler.RegisterOutputAsync(req, CancellationToken.None));

                    Assert.Equal(WabiSabiProtocolErrorCode.WrongPhase, ex.ErrorCode);
                }
            }

            await arena.StopAsync(CancellationToken.None);
        }
        public async Task CreatesRoundIfInBlameInputRegistrationAsync()
        {
            WabiSabiConfig cfg     = new();
            var            mockRpc = new MockRpcClient();

            mockRpc.OnEstimateSmartFeeAsync = async(target, _) =>
                                              await Task.FromResult(new EstimateSmartFeeResponse
            {
                Blocks  = target,
                FeeRate = new FeeRate(10m)
            });

            using Arena arena = new Arena(TimeSpan.FromSeconds(1), Network.Main, cfg, mockRpc, new Prison());
            Assert.Empty(arena.Rounds);
            await arena.StartAsync(CancellationToken.None).ConfigureAwait(false);

            await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)).ConfigureAwait(false);

            var round = Assert.Single(arena.Rounds).Value;

            round.SetPhase(Phase.ConnectionConfirmation);
            round.Alices.Add(WabiSabiFactory.CreateAlice());
            Round blameRound = WabiSabiFactory.CreateBlameRound(round, cfg);

            Assert.Equal(Phase.InputRegistration, blameRound.Phase);
            arena.Rounds.Add(blameRound.Id, blameRound);
            await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)).ConfigureAwait(false);

            Assert.Equal(3, arena.Rounds.Count);
            Assert.Equal(2, arena.Rounds.Where(x => x.Value.Phase == Phase.InputRegistration).Count());

            await arena.StopAsync(CancellationToken.None);
        }
    public async Task TimeoutSufficientPeersAsync()
    {
        WabiSabiConfig cfg = new()
        {
            MaxInputCountByRound           = 2,
            MinInputCountByRoundMultiplier = 1,
            TransactionSigningTimeout      = TimeSpan.Zero,
            OutputRegistrationTimeout      = 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);

        // Make sure not all alices signed.
        var alice3 = WabiSabiFactory.CreateAlice(round);

        alice3.ConfirmedConnection = true;
        round.Alices.Add(alice3);
        round.CoinjoinState = round.Assert <ConstructionState>().AddInput(alice3.Coin);
        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 aliceClient2.SignTransactionAsync(signedCoinJoin, keyChain, CancellationToken.None);

        await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21));

        Assert.DoesNotContain(round, arena.Rounds.Where(x => x.Phase != Phase.Ended));
        Assert.Single(arena.Rounds.Where(x => x is BlameRound));
        var badOutpoint = alice3.Coin.Outpoint;

        Assert.Contains(badOutpoint, prison.GetInmates().Select(x => x.Utxo));

        var onlyRound  = arena.Rounds.Single(x => x is BlameRound);
        var blameRound = Assert.IsType <BlameRound>(onlyRound);

        Assert.NotNull(blameRound.BlameOf);
        Assert.Equal(round.Id, blameRound.BlameOf.Id);

        var whitelist = blameRound.BlameWhitelist;

        Assert.Contains(aliceClient1.SmartCoin.OutPoint, whitelist);
        Assert.Contains(aliceClient2.SmartCoin.OutPoint, whitelist);
        Assert.DoesNotContain(badOutpoint, whitelist);

        await arena.StopAsync(CancellationToken.None);
    }
Example #29
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);
        }
Example #30
0
        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);
        }