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

            using Key key = new();
            var alice      = WabiSabiFactory.CreateAlice(key, round);
            var bannedCoin = alice.Coin.Outpoint;

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

            Prison prison = new();

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

            prison.Punish(bannedCoin, Punishment.Banned, uint256.Zero);

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

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

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

        using Key key = new();
        var coin             = WabiSabiFactory.CreateCoin(key);
        var rpc              = WabiSabiFactory.CreatePreconfiguredRpcClient(coin);
        var coinJoinIdsStore = new InMemoryCoinJoinIdStore();

        coinJoinIdsStore.Add(coin.Outpoint.Hash);
        using Arena arena = await ArenaBuilder.From(cfg).With(rpc).With(coinJoinIdsStore).CreateAndStartAsync(round);

        var minAliceDeadline = DateTimeOffset.UtcNow + cfg.ConnectionConfirmationTimeout * 0.9;
        var arenaClient      = WabiSabiFactory.CreateArenaClient(arena);
        var ownershipProof   = WabiSabiFactory.CreateOwnershipProof(key, round.Id);

        var(resp, _) = await arenaClient.RegisterInputAsync(round.Id, coin.Outpoint, ownershipProof, CancellationToken.None);

        AssertSingleAliceSuccessfullyRegistered(round, minAliceDeadline, resp);

        var myAlice = Assert.Single(round.Alices);

        Assert.True(myAlice.IsPayingZeroCoordinationFee);

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

        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.ConfirmConnectionAsync(req, CancellationToken.None);

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

        Assert.Single(round.Alices);
        Assert.NotEqual(preDeadline, alice.Deadline);

        await arena.StopAsync(CancellationToken.None);
    }
    public async Task 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);
    }
Example #7
0
    public async Task WrongPhaseAsync()
    {
        WabiSabiConfig cfg = new();

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

        var round       = arena.Rounds.First();
        var alice       = WabiSabiFactory.CreateAlice(round);
        var preDeadline = alice.Deadline;

        round.Alices.Add(alice);
        await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21)).ConfigureAwait(false);

        var req = WabiSabiFactory.CreateConnectionConfirmationRequest(round);

        foreach (Phase phase in Enum.GetValues(typeof(Phase)))
        {
            if (phase != Phase.InputRegistration && phase != Phase.ConnectionConfirmation)
            {
                round.SetPhase(phase);
                var ex = await Assert.ThrowsAsync <WabiSabiProtocolException>(
                    async() => await arena.ConfirmConnectionAsync(req, CancellationToken.None));

                Assert.Equal(WabiSabiProtocolErrorCode.WrongPhase, ex.ErrorCode);
            }
        }
        Assert.Equal(preDeadline, alice.Deadline);
        Assert.False(alice.ConfirmedConnection);

        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 #9
0
    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 ArenaBuilder.From(cfg).CreateAndStartAsync(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 #10
0
    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 ArenaBuilder.From(cfg).CreateAndStartAsync(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 #11
0
    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();

        var req = new TransactionSignaturesRequest(round.Id, 0, WitScript.Empty);

        foreach (Phase phase in Enum.GetValues(typeof(Phase)))
        {
            if (phase != Phase.TransactionSigning)
            {
                round.SetPhase(phase);

                var ex = await Assert.ThrowsAsync <WrongPhaseException>(async() =>
                                                                        await arena.SignTransactionAsync(req, CancellationToken.None));

                Assert.Equal(WabiSabiProtocolErrorCode.WrongPhase, 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();

        var req = new InputsRemovalRequest(round.Id, Guid.NewGuid());

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

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

        await arena.StopAsync(CancellationToken.None);
    }
    public async Task SuccessAsync()
    {
        WabiSabiConfig cfg              = new();
        var            round            = WabiSabiFactory.CreateRound(cfg);
        var            initialRemaining = round.RemainingInputVsizeAllocation;
        var            alice            = WabiSabiFactory.CreateAlice(round);

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

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

        // There's no such alice yet, so success.
        var req = new InputsRemovalRequest(round.Id, Guid.NewGuid());
        await arena.RemoveInputAsync(req, CancellationToken.None);

        // There was the alice we want to remove so success.
        req = new InputsRemovalRequest(round.Id, alice.Id);
        await arena.RemoveInputAsync(req, CancellationToken.None);

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

        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 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);
    }
}
Example #16
0
    public async Task DoesntSwitchImmaturelyAsync()
    {
        WabiSabiConfig cfg = new()
        {
            MaxInputCountByRound           = 2,
            MinInputCountByRoundMultiplier = 0.5
        };

        var(keyChain, coin1, coin2) = WabiSabiFactory.CreateCoinKeyPairs();

        var mockRpc = WabiSabiFactory.CreatePreconfiguredRpcClient(coin1.Coin, coin2.Coin);

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

        var(round, arenaClient, alices) = await CreateRoundWithTwoConfirmedConnectionsAsync(arena, keyChain, coin1, coin2);

        var(amountCredentials1, vsizeCredentials1) = (alices[0].IssuedAmountCredentials, alices[0].IssuedVsizeCredentials);
        var(amountCredentials2, vsizeCredentials2) = (alices[1].IssuedAmountCredentials, alices[1].IssuedVsizeCredentials);

        // Register outputs.
        var bobClient = new BobClient(round.Id, arenaClient);

        using var destKey = new Key();
        await bobClient.RegisterOutputAsync(
            destKey.PubKey.WitHash.ScriptPubKey,
            amountCredentials1.Take(ProtocolConstants.CredentialNumber),
            vsizeCredentials1.Take(ProtocolConstants.CredentialNumber),
            CancellationToken.None);

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

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

        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);
    }
Example #18
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 #19
0
    private static Arena CreateArena(WabiSabiConfig cfg, IRPCClient rpc)
    {
        var arenaBuilder = ArenaBuilder.From(cfg).With(rpc);

        arenaBuilder.Period = TimeSpan.FromSeconds(1);
        return(arenaBuilder.Create());
    }
Example #20
0
    public async Task InputRegistrationTimedoutAsync()
    {
        WabiSabiConfig cfg   = new() { StandardInputRegistrationTimeout = TimeSpan.Zero };
        var            round = WabiSabiFactory.CreateRound(cfg);

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

        var coin = WabiSabiFactory.CreateCoin(key);
        var rpc  = WabiSabiFactory.CreatePreconfiguredRpcClient(coin);

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

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

        arena.Rounds.Add(round);

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

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

        await arena.StopAsync(CancellationToken.None);
    }
Example #21
0
    public async Task InputImmatureAsync()
    {
        using Key key = new();
        WabiSabiConfig cfg            = new();
        var            round          = WabiSabiFactory.CreateRound(cfg);
        var            ownershipProof = WabiSabiFactory.CreateOwnershipProof(key, round.Id);

        var rpc    = WabiSabiFactory.CreatePreconfiguredRpcClient();
        var rpcCfg = rpc.SetupSequence(rpc => rpc.GetTxOutAsync(It.IsAny <uint256>(), It.IsAny <int>(), It.IsAny <bool>(), It.IsAny <CancellationToken>()));

        foreach (var i in Enumerable.Range(1, 100))
        {
            rpcCfg = rpcCfg.ReturnsAsync(new NBitcoin.RPC.GetTxOutResponse {
                Confirmations = i, IsCoinBase = true
            });
        }
        using Arena arena = await ArenaBuilder.From(cfg).With(rpc).CreateAndStartAsync(round);

        var arenaClient = WabiSabiFactory.CreateArenaClient(arena);

        var req = WabiSabiFactory.CreateInputRegistrationRequest(round: round);

        foreach (var i in Enumerable.Range(1, 100))
        {
            var ex = await Assert.ThrowsAsync <WabiSabiProtocolException>(
                async() => await arenaClient.RegisterInputAsync(round.Id, BitcoinFactory.CreateOutPoint(), ownershipProof, CancellationToken.None));

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

        await arena.StopAsync(CancellationToken.None);
    }
Example #22
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 #23
0
    public async Task InputCanBeNotedAsync()
    {
        using Key key = new();
        var outpoint = BitcoinFactory.CreateOutPoint();

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

        Prison prison = new();

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

        prison.Punish(outpoint, Punishment.Noted, uint256.One);

        var ownershipProof = WabiSabiFactory.CreateOwnershipProof(key);

        var arenaClient = WabiSabiFactory.CreateArenaClient(arena);

        var ex = await Assert.ThrowsAsync <WabiSabiProtocolException>(
            async() => await arenaClient.RegisterInputAsync(round.Id, outpoint, ownershipProof, CancellationToken.None));

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

        await arena.StopAsync(CancellationToken.None);
    }
Example #24
0
    public async Task InputUnconfirmedAsync()
    {
        using Key key = new();
        WabiSabiConfig cfg            = new();
        var            round          = WabiSabiFactory.CreateRound(cfg);
        var            ownershipProof = WabiSabiFactory.CreateOwnershipProof(key, round.Id);

        var mockRpc = new Mock <IRPCClient>();

        mockRpc.Setup(rpc => rpc.GetTxOutAsync(It.IsAny <uint256>(), It.IsAny <int>(), It.IsAny <bool>(), It.IsAny <CancellationToken>()))
        .ReturnsAsync(new NBitcoin.RPC.GetTxOutResponse {
            Confirmations = 0
        });

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

        var arenaClient = WabiSabiFactory.CreateArenaClient(arena);

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

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

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

        round.SetPhase(Phase.ConnectionConfirmation);
        var fundingTx = BitcoinFactory.CreateSmartTransaction(ownOutputCount: 1);
        var coin      = fundingTx.WalletOutputs.First().Coin;
        var alice     = new Alice(coin, new OwnershipProof(), round, Guid.NewGuid(), false);

        round.Alices.Add(alice);

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

        using var memoryCache = new MemoryCache(new MemoryCacheOptions());
        var idempotencyRequestCache = new IdempotencyRequestCache(memoryCache);

        using CoinJoinFeeRateStatStore coinJoinFeeRateStatStore = new(config, arena.Rpc);
        var wabiSabiApi = new WabiSabiController(idempotencyRequestCache, arena, coinJoinFeeRateStatStore);

        var apiClient = new ArenaClient(null !, null !, wabiSabiApi);

        round.SetPhase(Phase.InputRegistration);

        await apiClient.RemoveInputAsync(round.Id, alice.Id, CancellationToken.None);

        Assert.Empty(round.Alices);
    }
    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 #27
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);
    }
    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);
    }
Example #29
0
    public async Task DiffTooSmallToBlameAsync()
    {
        WabiSabiConfig cfg = new()
        {
            MaxInputCountByRound           = 2,
            MinInputCountByRoundMultiplier = 0.5,
            OutputRegistrationTimeout      = TimeSpan.Zero,
            CoordinationFeeRate            = CoordinationFeeRate.Zero
        };

        var(keyChain, coin1, coin2) = WabiSabiFactory.CreateCoinKeyPairs();

        var mockRpc = WabiSabiFactory.CreatePreconfiguredRpcClient(coin1.Coin, coin2.Coin);

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

        var(round, arenaClient, alices) = await CreateRoundWithTwoConfirmedConnectionsAsync(arena, keyChain, coin1, coin2);

        var(amountCredentials1, vsizeCredentials1) = (alices[0].IssuedAmountCredentials, alices[0].IssuedVsizeCredentials);
        var(amountCredentials2, vsizeCredentials2) = (alices[1].IssuedAmountCredentials, alices[1].IssuedVsizeCredentials);

        // Register outputs.
        var bobClient = new BobClient(round.Id, arenaClient);

        using var destKey1 = new Key();
        using var destKey2 = new Key();
        await bobClient.RegisterOutputAsync(
            destKey1.PubKey.WitHash.ScriptPubKey,
            amountCredentials1.Take(ProtocolConstants.CredentialNumber),
            vsizeCredentials1.Take(ProtocolConstants.CredentialNumber),
            CancellationToken.None);

        await bobClient.RegisterOutputAsync(
            destKey2.PubKey.WitHash.ScriptPubKey,
            amountCredentials2.Take(ProtocolConstants.CredentialNumber),
            vsizeCredentials2.Take(ProtocolConstants.CredentialNumber),
            CancellationToken.None);

        // Add another input. The input must be able to pay for itself, but
        // the remaining amount after deducting the fees needs to be less
        // than the minimum.
        var txParams   = round.Assert <ConstructionState>().Parameters;
        var extraAlice = WabiSabiFactory.CreateAlice(txParams.FeeRate.GetFee(Constants.P2wpkhInputVirtualSize) + txParams.AllowedOutputAmounts.Min - new Money(1L), round);

        round.Alices.Add(extraAlice);
        round.CoinjoinState = round.Assert <ConstructionState>().AddInput(extraAlice.Coin);

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

        Assert.Equal(Phase.TransactionSigning, round.Phase);
        var tx = round.Assert <SigningState>().CreateTransaction();

        Assert.Equal(3, tx.Inputs.Count);
        Assert.Equal(2, tx.Outputs.Count);
        Assert.DoesNotContain(round.CoordinatorScript, tx.Outputs.Select(x => x.ScriptPubKey));

        await arena.StopAsync(CancellationToken.None);
    }
Example #30
0
    private int _agentDecisionInterval; // To replace with a call to DecisionRequester.DecisionPeriod if possible
    // (not possible at the moment as it's internal and we cannot call GetComponent on internals)

    internal void Awake()
    {
        _builder = new ArenaBuilder(gameObject,
                                    spawnedObjectsHolder,
                                    maxSpawnAttemptsForPrefabs,
                                    maxSpawnAttemptsForAgent);
        _environmentManager = GameObject.FindObjectOfType <EnvironmentManager>();
        agent = transform.FindChildWithTag("agent").GetComponent <Agent>();
        _agentDecisionInterval = transform.FindChildWithTag("agent").GetComponent <DecisionPeriod>().decisionPeriod;
        _fades = blackScreens.GetFades();
    }
Example #31
0
        /// <summary>
        /// This function reloads the arena with a new difficulty.
        /// </summary>
        /// <param name="difficulty">The new arena's difficulty</param>
        public void loadNewArena(ArenaDifficulty difficulty)
        {
            collidables = new List<Entity>();

            if (content == null)
                content = new ContentManager(SceneManager.Game.Services, "Content");

            //populate the GameSave object
            GameSave.seed = Environment.TickCount;
            GameSave.level = controller.getLevel();
            GameSave.partyHealth = PartyUtils.getPartyHealth();
            GameSave.score = controller.getScore();
            //SAVE
            SaveUtils.getInstance().saveGame(GameSave);

            controller.setGenerator(new Random(GameSave.seed));

            // Set the new ambience colour
            ambientColour = controller.getAmbience();

            // Generate the arena
            ArenaBuilder builder = new ArenaBuilder(controller.getLevelSize(), controller.getLevelSize(),
                content, SceneManager.GraphicsDevice.Viewport.AspectRatio, difficulty);
            baseArena = builder.buildArenaBase();
            StartTile = builder.getStartTile();

            //
            if (effect == null)
                effect = new BasicEffect(SceneManager.Game.GraphicsDevice);//null

            Hero = new Character();
            camera = new Camera(effect, SceneManager.Game.Window.ClientBounds.Width, SceneManager.Game.Window.ClientBounds.Height, SceneManager.GraphicsDevice.Viewport.AspectRatio, Hero.getPOSITION());
            //load model
            Hero.LoadModel(content, SceneManager.GraphicsDevice.Viewport.AspectRatio);
            potionsUsed = 0;
            MysteryBoxUsed = false;

            // Load the level start conversation
            currentConversation = DialogueUtils.makeConversation((ConversationType)controller.getLevel() - 1);
            currentConversation.load(content);

            //load score dispaly
            ScoreDisplay = new Scoring();
            ScoreDisplay.load(content);

            // Debug arena
            printDebugArena();
        }
Example #32
0
        /**
         * This function is called when a scene is made active.
         */
        public override void loadScene(ContentManager content)
        {
            collidables = new List<Entity>();

            if (content == null)
                content = new ContentManager(SceneManager.Game.Services, "Content");

            // Load the shader
            HLSLeffect = content.Load<Effect>("Effects/Shader");

            if (!loaded)
            {
                GameSave.seed = Environment.TickCount;

                GameSave.level = controller.getLevel();
                GameSave.partyHealth = PartyUtils.getPartyHealth();
                GameSave.score = controller.getScore();
                //SAVE
                SaveUtils.getInstance().saveGame(GameSave);

                controller.setGenerator(new Random(GameSave.seed));
            }

            // Generate the arena
            ArenaBuilder builder = new ArenaBuilder(controller.getLevelSize(), controller.getLevelSize(),
                content, SceneManager.GraphicsDevice.Viewport.AspectRatio, controller.getLevelDifficulty());
            baseArena = builder.buildArenaBase();
            StartTile = builder.getStartTile();

            //
            if (effect == null)
                effect = new BasicEffect(SceneManager.Game.GraphicsDevice);//null

            FlashLightAngle = MIN_FLASH;
            Hero = new Character();
            camera = new Camera(effect, SceneManager.Game.Window.ClientBounds.Width, SceneManager.Game.Window.ClientBounds.Height, SceneManager.GraphicsDevice.Viewport.AspectRatio, Hero.getPOSITION());
            //load model
            Hero.LoadModel(content, SceneManager.GraphicsDevice.Viewport.AspectRatio);

            table = new ArenaTable(getStartTile(), content);
            skybox = new ArenaSkybox(getStartTile(), content);

            potionsUsed = 0;
            MysteryBoxUsed = false;

            //load CombatInfo at top left
            Vector2 start = new Vector2(10, 45);
            foreach (PlayerSprite ps in PartyUtils.getParty())
            {
                ps.getCombatInfo().init(content, start);
                start.Y += 60;
            }

            pauseMenu.load(content, SceneManager.GraphicsDevice.Viewport);
            pauseMenu.center();

            // Load the level start conversation
            currentConversation = DialogueUtils.makeConversation((ConversationType)controller.getLevel() - 1);
            currentConversation.load(content);

            //load score dispaly
            ScoreDisplay = new Scoring();
            ScoreDisplay.load(content);
        }