Esempio n. 1
0
        public async Task IncorrectRequestedWeightCredentialsAsync()
        {
            WabiSabiConfig cfg   = new();
            var            round = WabiSabiFactory.CreateRound(cfg);

            round.SetPhase(Phase.OutputRegistration);
            round.Alices.Add(WabiSabiFactory.CreateAlice());
            using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, round);

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

            var req = WabiSabiFactory.CreateOutputRegistrationRequest(round, weight: 30);

            var ex = await Assert.ThrowsAsync <WabiSabiProtocolException>(async() => await handler.RegisterOutputAsync(req));

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

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

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

            using Key key = new();

            await using PostRequestHandler handler = new(cfg, new(), arena, WabiSabiFactory.CreateMockRpc(key));

            var req = WabiSabiFactory.CreateInputsRegistrationRequest(key, round);
            var minAliceDeadline = DateTimeOffset.UtcNow + cfg.ConnectionConfirmationTimeout * 0.9;
            var resp             = await handler.RegisterInputAsync(req);

            AssertSingleAliceSuccessfullyRegistered(round, minAliceDeadline, resp);

            await arena.StopAsync(CancellationToken.None);
        }
Esempio n. 3
0
        public async Task AlicesSpentAsync()
        {
            WabiSabiConfig cfg = new()
            {
                MaxInputCountByRound           = 2,
                MinInputCountByRoundMultiplier = 0.5
            };

            using Key key1 = new();
            using Key key2 = new();
            var coin1 = WabiSabiFactory.CreateCoin(key1);
            var coin2 = WabiSabiFactory.CreateCoin(key2);

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

            mockRpc.Setup(rpc => rpc.SendRawTransactionAsync(It.IsAny <Transaction>()))
            .ThrowsAsync(new RPCException(RPCErrorCode.RPC_TRANSACTION_REJECTED, "", null));

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

            var(round, aliceClient1, aliceClient2) = await CreateRoundWithOutputsReadyToSignAsync(arena, key1, coin1, key2, coin2).ConfigureAwait(false);

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

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

            var signedCoinJoin = round.Assert <SigningState>().CreateTransaction();

            await aliceClient1.SignTransactionAsync(signedCoinJoin, CancellationToken.None);

            await aliceClient2.SignTransactionAsync(signedCoinJoin, CancellationToken.None);

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

            Assert.DoesNotContain(round, arena.Rounds);

            // 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);
        }
Esempio n. 4
0
    public async Task TimeoutInsufficientPeersAsync()
    {
        WabiSabiConfig cfg = new()
        {
            MaxInputCountByRound           = 2,
            MinInputCountByRoundMultiplier = 1,
            TransactionSigningTimeout      = 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);

        await aliceClient1.ReadyToSignAsync(CancellationToken.None);

        await aliceClient2.ReadyToSignAsync(CancellationToken.None);

        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 arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21));

        Assert.DoesNotContain(round, arena.Rounds.Where(x => x.Phase != Phase.Ended));
        Assert.Equal(Phase.Ended, round.Phase);
        Assert.False(round.WasTransactionBroadcast);
        Assert.Empty(arena.Rounds.Where(x => x is BlameRound));
        Assert.Contains(aliceClient2.SmartCoin.OutPoint, prison.GetInmates().Select(x => x.Utxo));

        await arena.StopAsync(CancellationToken.None);
    }
Esempio n. 5
0
        public async Task CreateNewAsync()
        {
            var config = new WabiSabiConfig {
                MaxInputCountByRound = 1
            };
            var       round = WabiSabiFactory.CreateRound(config);
            var       km    = ServiceFactory.CreateKeyManager("");
            var       key   = BitcoinFactory.CreateHdPubKey(km);
            SmartCoin coin1 = BitcoinFactory.CreateSmartCoin(key, Money.Coins(1m));

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

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

            await arena.TriggerAndWaitRoundAsync(TimeSpan.FromMinutes(1));

            await using var coordinator = new ArenaRequestHandler(config, new Prison(), arena, mockRpc.Object);
            var wabiSabiApi = new WabiSabiController(coordinator);

            ZeroCredentialPool amountCredentialPool = new();
            ZeroCredentialPool vsizeCredentialPool  = new();
            var insecureRandom = new InsecureRandom();
            var roundState     = RoundState.FromRound(round);
            var arenaClient    = new ArenaClient(
                roundState.CreateAmountCredentialClient(amountCredentialPool, insecureRandom),
                roundState.CreateVsizeCredentialClient(vsizeCredentialPool, insecureRandom),
                wabiSabiApi);

            Assert.Equal(Phase.InputRegistration, arena.Rounds.First().Phase);

            var bitcoinSecret = km.GetSecrets("", coin1.ScriptPubKey).Single().PrivateKey.GetBitcoinSecret(Network.Main);

            var aliceClient = new AliceClient(round.Id, arenaClient, coin1.Coin, round.FeeRate, bitcoinSecret);
            await aliceClient.RegisterInputAsync(CancellationToken.None);

            Task confirmationTask = aliceClient.ConfirmConnectionAsync(TimeSpan.FromSeconds(1), roundState.MaxVsizeAllocationPerAlice, CancellationToken.None);

            await arena.TriggerAndWaitRoundAsync(TimeSpan.FromMinutes(1));

            await confirmationTask;

            Assert.Equal(Phase.ConnectionConfirmation, arena.Rounds.First().Phase);
        }
Esempio n. 6
0
        public async Task ScriptNotAllowedAsync()
        {
            WabiSabiConfig cfg   = new();
            var            round = WabiSabiFactory.CreateRound(cfg);

            round.SetPhase(Phase.OutputRegistration);
            using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, round);

            using Key key = new();

            var req = WabiSabiFactory.CreateOutputRegistrationRequest(round, key.PubKey.GetAddress(ScriptPubKeyType.Legacy, Network.Main).ScriptPubKey);

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

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

            await arena.StopAsync(CancellationToken.None);
        }
Esempio n. 7
0
        public async Task TooMuchFundsAsync()
        {
            WabiSabiConfig cfg   = new() { MaxRegistrableAmount = Money.Coins(1.999m) };
            var            round = WabiSabiFactory.CreateRound(cfg);

            round.SetPhase(Phase.OutputRegistration);
            round.Alices.Add(WabiSabiFactory.CreateAlice(value: Money.Coins(2)));
            using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, round);

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

            var req = WabiSabiFactory.CreateOutputRegistrationRequest(round);

            var ex = await Assert.ThrowsAsync <WabiSabiProtocolException>(async() => await handler.RegisterOutputAsync(req));

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

            await arena.StopAsync(CancellationToken.None);
        }
Esempio n. 8
0
        public async Task CreateNewAsync()
        {
            var config = new WabiSabiConfig {
                MaxInputCountByRound = 1
            };
            var round = WabiSabiFactory.CreateRound(config);

            using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(config, round);

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

            var       km       = ServiceFactory.CreateKeyManager("");
            var       key      = BitcoinFactory.CreateHdPubKey(km);
            SmartCoin coin1    = BitcoinFactory.CreateSmartCoin(key, Money.Coins(1m));
            var       outpoint = coin1.OutPoint;

            var mockRpc = new Mock <IRPCClient>();

            mockRpc.Setup(rpc => rpc.GetTxOutAsync(outpoint.Hash, (int)outpoint.N, true))
            .ReturnsAsync(new NBitcoin.RPC.GetTxOutResponse
            {
                IsCoinBase    = false,
                Confirmations = coin1.Height,
                TxOut         = coin1.TxOut,
            });

            await using var coordinator = new ArenaRequestHandler(config, new Prison(), arena, mockRpc.Object);
            var apiClient = new ArenaClient(round.AmountCredentialIssuerParameters, round.WeightCredentialIssuerParameters, coordinator, new InsecureRandom());

            Assert.Equal(Phase.InputRegistration, arena.Rounds.First().Value.Phase);

            var bitcoinSecret = km.GetSecrets("", coin1.ScriptPubKey).Single().PrivateKey.GetBitcoinSecret(Network.Main);
            var aliceClient   = await AliceClient.CreateNewAsync(apiClient, new[] { coin1.Coin }, bitcoinSecret, round.Id, round.Hash, round.FeeRate);

            Task confirmationTask = aliceClient.ConfirmConnectionAsync(TimeSpan.FromSeconds(3), CancellationToken.None);

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

            await confirmationTask;

            Assert.Equal(Phase.ConnectionConfirmation, arena.Rounds.First().Value.Phase);
        }
        public async Task FailsBroadcastAsync()
        {
            WabiSabiConfig cfg = new()
            {
                MaxInputCountByRound           = 2,
                MinInputCountByRoundMultiplier = 0.5
            };

            var(key1, coin1, key2, coin2) = WabiSabiFactory.CreateCoinKeyPairs();

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

            mockRpc.Setup(rpc => rpc.SendRawTransactionAsync(It.IsAny <Transaction>()))
            .ThrowsAsync(new RPCException(RPCErrorCode.RPC_TRANSACTION_REJECTED, "", null));

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

            var(round, aliceClient1, aliceClient2) = await CreateRoundWithOutputsReadyToSignAsync(arena, key1, coin1, key2, coin2);

            await aliceClient1.ReadyToSignAsync(CancellationToken.None);

            await aliceClient2.ReadyToSignAsync(CancellationToken.None);

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

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

            var signedCoinJoin = round.Assert <SigningState>().CreateTransaction();

            await aliceClient1.SignTransactionAsync(signedCoinJoin, CancellationToken.None);

            await aliceClient2.SignTransactionAsync(signedCoinJoin, CancellationToken.None);

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

            Assert.DoesNotContain(round, arena.ActiveRounds);
            Assert.Equal(Phase.Ended, round.Phase);
            Assert.False(round.WasTransactionBroadcast);
            Assert.Empty(arena.Prison.GetInmates());

            await arena.StopAsync(CancellationToken.None);
        }
Esempio n. 10
0
    public async Task SomeBobsRegisteredTimeoutAsync()
    {
        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 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.TransactionSigning, round.Phase);
        var tx = round.Assert <SigningState>().CreateTransaction();

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

        await arena.StopAsync(CancellationToken.None);
    }
Esempio n. 11
0
        public async Task AliceTimesoutAsync()
        {
            // Alice times out when its deadline is reached.
            WabiSabiConfig cfg       = new();
            var            round     = WabiSabiFactory.CreateRound(cfg);
            var            km        = ServiceFactory.CreateKeyManager("");
            var            key       = BitcoinFactory.CreateHdPubKey(km);
            var            smartCoin = BitcoinFactory.CreateSmartCoin(key, 10m);
            var            rpc       = WabiSabiFactory.CreatePreconfiguredRpcClient(smartCoin.Coin);

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

            var arenaClient = WabiSabiFactory.CreateArenaClient(arena);

            using RoundStateUpdater roundStateUpdater = new(TimeSpan.FromSeconds(2), new ArenaRequestHandlerAdapter(arena));
            await roundStateUpdater.StartAsync(CancellationToken.None);

            // Register Alices.
            var esk = km.GetSecrets("", smartCoin.ScriptPubKey).Single();

            using CancellationTokenSource cancellationTokenSource = new();
            var task = AliceClient.CreateRegisterAndConfirmInputAsync(RoundState.FromRound(round), arenaClient, smartCoin, esk.PrivateKey.GetBitcoinSecret(round.Network), roundStateUpdater, cancellationTokenSource.Token);

            while (round.Alices.Count == 0)
            {
                await Task.Delay(10);
            }

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

            alice.Deadline = DateTimeOffset.UtcNow - TimeSpan.FromMilliseconds(1);
            await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21));

            Assert.Empty(round.Alices);

            cancellationTokenSource.Cancel();
            await Assert.ThrowsAsync <OperationCanceledException>(async() => await task);

            await roundStateUpdater.StopAsync(CancellationToken.None);

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

            using Key key = new();
            var coin = WabiSabiFactory.CreateCoin(key);

            using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, WabiSabiFactory.CreatePreconfiguredRpcClient(coin), 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);

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

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

        round.SetPhase(Phase.OutputRegistration);
        round.Alices.Add(WabiSabiFactory.CreateAlice(Money.Coins(1), round));

        var sha256Bounty = Script.FromHex("aa20000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f87");
        var req          = WabiSabiFactory.CreateOutputRegistrationRequest(round, sha256Bounty);
        var ex           = await Assert.ThrowsAsync <WabiSabiProtocolException>(async() => await arena.RegisterOutputAsync(req, CancellationToken.None));

        // The following assertion requires standardness to be checked before allowed script types
        Assert.Equal(WabiSabiProtocolErrorCode.NonStandardOutput, ex.ErrorCode);

        await arena.StopAsync(CancellationToken.None);
    }
    public async Task InputRegistrationTimedoutWithoutSufficientInputsAsync()
    {
        WabiSabiConfig cfg = new()
        {
            StandardInputRegistrationTimeout = TimeSpan.Zero,
            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));
        await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21));

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

        await arena.StopAsync(CancellationToken.None);
    }
Esempio n. 15
0
        public async Task InputNotWhitelistedAsync()
        {
            WabiSabiConfig cfg   = new();
            var            round = WabiSabiFactory.CreateRound(cfg);

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

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

            using Key key = new();

            await using PostRequestHandler handler = new(cfg, new(), arena, WabiSabiFactory.CreateMockRpc(key));
            var req = WabiSabiFactory.CreateInputsRegistrationRequest(blameRound);
            var ex  = await Assert.ThrowsAsync <WabiSabiProtocolException>(async() => await handler.RegisterInputAsync(req));

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

            await arena.StopAsync(CancellationToken.None);
        }
Esempio n. 16
0
        public async Task InputNotWhitelistedAsync()
        {
            WabiSabiConfig cfg   = new();
            var            round = WabiSabiFactory.CreateRound(cfg);

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

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

            using Key key = new();
            var mockRpc = WabiSabiFactory.CreatePreconfiguredRpcClient();

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

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

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

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

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

        using Key key = new();

        round.SetPhase(Phase.OutputRegistration);
        round.Alices.Add(WabiSabiFactory.CreateAlice(Money.Coins(1), round));

        var req = WabiSabiFactory.CreateOutputRegistrationRequest(round, key.PubKey.GetAddress(ScriptPubKeyType.Legacy, Network.Main).ScriptPubKey);
        var ex  = await Assert.ThrowsAsync <WabiSabiProtocolException>(async() => await arena.RegisterOutputAsync(req, CancellationToken.None));

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

        await arena.StopAsync(CancellationToken.None);
    }
Esempio n. 18
0
    public async Task RegisterSpentOrInNonExistentCoinAsync()
    {
        var httpClient = _apiApplicationFactory.CreateClient();

        var apiClient = await _apiApplicationFactory.CreateArenaClientAsync(httpClient);

        var rounds = (await apiClient.GetStatusAsync(RoundStateRequest.Empty, CancellationToken.None)).RoundStates;
        var round  = rounds.First(x => x.CoinjoinState is ConstructionState);

        // If an output is not in the utxo dataset then it is not unspent, this
        // means that the output is spent or simply doesn't even exist.
        var nonExistingOutPoint = new OutPoint();

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

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

        Assert.Equal(WabiSabiProtocolErrorCode.InputSpent, ex.ErrorCode);
    }
Esempio n. 19
0
        public async Task SuccessAsync()
        {
            WabiSabiConfig cfg   = new();
            var            round = WabiSabiFactory.CreateRound(cfg);

            round.SetPhase(Phase.OutputRegistration);
            round.Alices.Add(WabiSabiFactory.CreateAlice());
            using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, round);

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

            var req  = WabiSabiFactory.CreateOutputRegistrationRequest(round);
            var resp = await handler.RegisterOutputAsync(req, CancellationToken.None);

            Assert.NotEmpty(round.Bobs);
            Assert.NotNull(resp);
            Assert.NotNull(resp.AmountCredentials);
            Assert.NotNull(resp.VsizeCredentials);

            await arena.StopAsync(CancellationToken.None);
        }
Esempio n. 20
0
        public async Task InputRegistrationTimedoutAsync()
        {
            WabiSabiConfig cfg   = new() { StandardInputRegistrationTimeout = TimeSpan.Zero };
            var            round = WabiSabiFactory.CreateRound(cfg);

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

            using Key key = new();

            var req = WabiSabiFactory.CreateInputsRegistrationRequest(key, round);

            await using ArenaRequestHandler handler = new(cfg, new(), arena, WabiSabiFactory.CreateMockRpc(key));
            await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21));

            arena.Rounds.Add(round.Id, round);
            await RegisterAndAssertWrongPhaseAsync(req, handler);

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

            await arena.StopAsync(CancellationToken.None);
        }
Esempio n. 21
0
        public async Task WrongPhaseAsync()
        {
            MockArena      arena = new();
            WabiSabiConfig cfg   = new();
            var            round = WabiSabiFactory.CreateRound(cfg);

            arena.OnTryGetRound = _ => round;

            var req = new ConnectionConfirmationRequest(round.Id, Guid.NewGuid(), null !, null !, null !, null !);

            foreach (Phase phase in Enum.GetValues(typeof(Phase)))
            {
                if (phase != Phase.InputRegistration && phase != Phase.ConnectionConfirmation)
                {
                    round.Phase = phase;
                    await using PostRequestHandler handler = new(cfg, new Prison(), arena, new MockRpcClient());
                    var ex = Assert.Throws <WabiSabiProtocolException>(() => handler.ConfirmConnection(req));
                    Assert.Equal(WabiSabiProtocolErrorCode.WrongPhase, ex.ErrorCode);
                }
            }
        }
Esempio n. 22
0
        public async Task InputCantBeNotedAsync()
        {
            using Key key = new();
            var outpoint = BitcoinFactory.CreateOutPoint();

            WabiSabiConfig cfg   = new() { AllowNotedInputRegistration = false };
            var            round = WabiSabiFactory.CreateRound(cfg);

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

            arena.Prison.Punish(outpoint, Punishment.Noted, uint256.One);

            var arenaClient = WabiSabiFactory.CreateArenaClient(arena);

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

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

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

            round.Alices.Add(alice);
            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);

            await arena.StopAsync(CancellationToken.None);
        }
Esempio n. 24
0
        public async Task InputWhitelistedButBannedAsync()
        {
            MockArena arena  = new();
            Prison    prison = new();
            var       pair   = WabiSabiFactory.CreateInputRoundSignaturePair();

            prison.Punish(pair.Input, Punishment.Banned, Guid.NewGuid());
            WabiSabiConfig cfg   = new();
            var            round = WabiSabiFactory.CreateRound(cfg);

            round.Alices.Add(new Alice(pair.Input));
            Round blameRound = new(round);

            arena.OnTryGetRound = _ => blameRound;

            await using PostRequestHandler handler = new(cfg, prison, arena, new MockRpcClient());
            var req = WabiSabiFactory.CreateInputsRegistrationRequest(pair, blameRound);
            var ex  = await Assert.ThrowsAsync <WabiSabiProtocolException>(async() => await handler.RegisterInputAsync(req));

            Assert.Equal(WabiSabiProtocolErrorCode.InputBanned, ex.ErrorCode);
        }
Esempio n. 25
0
        public async Task InputCanBeNotedAsync()
        {
            MockArena arena  = new();
            Prison    prison = new();
            var       pair   = WabiSabiFactory.CreateInputRoundSignaturePair();

            prison.Punish(pair.Input, Punishment.Noted, Guid.NewGuid());
            WabiSabiConfig cfg   = new();
            var            round = WabiSabiFactory.CreateRound(cfg);

            arena.OnTryGetRound = _ => round;

            await using PostRequestHandler handler = new(cfg, prison, arena, new MockRpcClient());
            var req = WabiSabiFactory.CreateInputsRegistrationRequest(pair, round);
            var ex  = await Assert.ThrowsAnyAsync <Exception>(async() => await handler.RegisterInputAsync(req));

            if (ex is WabiSabiProtocolException wspex)
            {
                Assert.NotEqual(WabiSabiProtocolErrorCode.InputBanned, wspex.ErrorCode);
            }
        }
        public async Task NonStandardOutputAsync()
        {
            WabiSabiConfig cfg   = new();
            var            round = WabiSabiFactory.CreateRound(cfg);

            round.SetPhase(Phase.OutputRegistration);
            using Arena arena = await WabiSabiFactory.CreateAndStartArenaAsync(cfg, round);

            using Key key = new();

            var sha256Bounty = Script.FromHex("aa20000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f87");
            var req          = WabiSabiFactory.CreateOutputRegistrationRequest(round, sha256Bounty);

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

            // The following assertion requires standardness to be checked before allowed script types
            Assert.Equal(WabiSabiProtocolErrorCode.NonStandardOutput, ex.ErrorCode);

            await arena.StopAsync(CancellationToken.None);
        }
Esempio n. 27
0
        public async Task InputRegistrationTimedoutWithSufficientInputsAsync()
        {
            WabiSabiConfig cfg = new()
            {
                StandardInputRegistrationTimeout = TimeSpan.Zero,
                MaxInputCountByRound             = 4,
                MinInputCountByRoundMultiplier   = 0.5
            };
            var round = WabiSabiFactory.CreateRound(cfg);

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

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

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

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

            await arena.StopAsync(CancellationToken.None);
        }
    CreateRoundWithTwoConfirmedConnectionsAsync(Arena arena, IKeyChain keyChain, SmartCoin coin1, SmartCoin coin2)
    {
        // Get the round.
        await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21));

        var arenaClient = WabiSabiFactory.CreateArenaClient(arena);
        var round       = Assert.Single(arena.Rounds);

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

        using RoundStateUpdater roundStateUpdater = new(TimeSpan.FromSeconds(2), arena);
        await roundStateUpdater.StartAsync(CancellationToken.None);

        var task1 = AliceClient.CreateRegisterAndConfirmInputAsync(RoundState.FromRound(round), arenaClient, coin1, keyChain, roundStateUpdater, CancellationToken.None);
        var task2 = AliceClient.CreateRegisterAndConfirmInputAsync(RoundState.FromRound(round), arenaClient, coin2, keyChain, roundStateUpdater, CancellationToken.None);

        while (Phase.ConnectionConfirmation != round.Phase)
        {
            await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21));
        }
        await Task.WhenAll(task1, task2);

        var aliceClient1 = await task1;
        var aliceClient2 = await task2;

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

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

        await roundStateUpdater.StopAsync(CancellationToken.None);

        return(round,
               arenaClient,
               new[]
        {
            aliceClient1,
            aliceClient2
        });
    }
        CreateRoundWithTwoConfirmedConnectionsAsync(Arena arena, Key key1, SmartCoin coin1, Key key2, SmartCoin coin2)
        {
            // Create the round.
            await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21));

            var arenaClient = WabiSabiFactory.CreateArenaClient(arena);
            var round       = Assert.Single(arena.Rounds);

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

            using RoundStateUpdater roundStateUpdater = new(TimeSpan.FromSeconds(2), arena);
            await roundStateUpdater.StartAsync(CancellationToken.None);

            var identificationKey = new Key();
            var task1             = AliceClient.CreateRegisterAndConfirmInputAsync(RoundState.FromRound(round), arenaClient, coin1, key1.GetBitcoinSecret(round.Network), identificationKey, roundStateUpdater, CancellationToken.None);
            var task2             = AliceClient.CreateRegisterAndConfirmInputAsync(RoundState.FromRound(round), arenaClient, coin2, key2.GetBitcoinSecret(round.Network), identificationKey, roundStateUpdater, CancellationToken.None);

            while (Phase.ConnectionConfirmation != round.Phase)
            {
                await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21));
            }
            await Task.WhenAll(task1, task2);

            var aliceClient1 = task1.Result;
            var aliceClient2 = task2.Result;

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

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

            await roundStateUpdater.StopAsync(CancellationToken.None);

            return(round,
                   arenaClient,
                   new[]
            {
                aliceClient1,
                aliceClient2
            });
        }
        public async Task EveryoneSignedAsync()
        {
            WabiSabiConfig cfg = new()
            {
                MaxInputCountByRound           = 2,
                MinInputCountByRoundMultiplier = 0.5
            };

            using Key key1 = new();
            using Key key2 = new();
            var coin1 = WabiSabiFactory.CreateCoin(key1);
            var coin2 = WabiSabiFactory.CreateCoin(key2);

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

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

            var(round, aliceClient1, aliceClient2) = await CreateRoundWithOutputsReadyToSignAsync(arena, key1, coin1, key2, coin2);

            await aliceClient1.ReadyToSignAsync(CancellationToken.None);

            await aliceClient2.ReadyToSignAsync(CancellationToken.None);

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

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

            var signedCoinJoin = round.Assert <SigningState>().CreateTransaction();

            await aliceClient1.SignTransactionAsync(signedCoinJoin, CancellationToken.None);

            await aliceClient2.SignTransactionAsync(signedCoinJoin, CancellationToken.None);

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

            Assert.DoesNotContain(round, arena.ActiveRounds);
            Assert.Equal(Phase.Ended, round.Phase);

            await arena.StopAsync(CancellationToken.None);
        }