Example #1
0
 public async Task <RoundState[]> GetStatusAsync(CancellationToken cancellationToken)
 {
     using (await AsyncLock.LockAsync(cancellationToken).ConfigureAwait(false))
     {
         return(Rounds.Select(x => RoundState.FromRound(x)).ToArray());
     }
 }
Example #2
0
    public async Task <RoundStateResponse> GetStatusAsync(RoundStateRequest request, CancellationToken cancellationToken)
    {
        using (await AsyncLock.LockAsync(cancellationToken).ConfigureAwait(false))
        {
            var roundStates = Rounds.Select(x =>
            {
                var checkPoint = request.RoundCheckpoints.FirstOrDefault(y => y.RoundId == x.Id);
                return(RoundState.FromRound(x, checkPoint == default ? 0 : checkPoint.StateId));
            }).ToArray();

            return(new RoundStateResponse(roundStates, Array.Empty <CoinJoinFeeRateMedian>()));
        }
    }
    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 ArenaBuilder.From(cfg).With(rpc).CreateAndStartAsync(round);

        var arenaClient = WabiSabiFactory.CreateArenaClient(arena);

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

        // Register Alices.
        var keyChain = new KeyChain(km, new Kitchen(""));

        using CancellationTokenSource cancellationTokenSource = new();
        var task = AliceClient.CreateRegisterAndConfirmInputAsync(RoundState.FromRound(round), arenaClient, smartCoin, keyChain, 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();

        try
        {
            await task;
            throw new InvalidOperationException("The operation should throw!");
        }
        catch (Exception exc)
        {
            Assert.True(exc is OperationCanceledException or WabiSabiProtocolException);
        }

        await roundStateUpdater.StopAsync(CancellationToken.None);

        await arena.StopAsync(CancellationToken.None);
    }
Example #4
0
        CreateRoundWithOutputsReadyToSignAsync(Arena arena, Key key1, SmartCoin coin1, Key key2, SmartCoin coin2)
        {
            // Create the round.
            await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21));

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

            round.MaxVsizeAllocationPerAlice = 11 + 31 + MultipartyTransactionParameters.SharedOverhead;
            var arenaClient = WabiSabiFactory.CreateArenaClient(arena);

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

            using 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.OutputRegistration != round.Phase)
            {
                await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21));
            }

            await Task.WhenAll(task1, task2);

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

            // 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,
                aliceClient1.IssuedAmountCredentials.Take(ProtocolConstants.CredentialNumber),
                aliceClient1.IssuedVsizeCredentials.Take(ProtocolConstants.CredentialNumber),
                CancellationToken.None).ConfigureAwait(false);

            await bobClient.RegisterOutputAsync(
                destKey1.PubKey.WitHash.ScriptPubKey,
                aliceClient2.IssuedAmountCredentials.Take(ProtocolConstants.CredentialNumber),
                aliceClient2.IssuedVsizeCredentials.Take(ProtocolConstants.CredentialNumber),
                CancellationToken.None).ConfigureAwait(false);

            await roundStateUpdater.StopAsync(CancellationToken.None);

            return(round, aliceClient1, aliceClient2);
        }
Example #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);

            var insecureRandom = new InsecureRandom();
            var roundState     = RoundState.FromRound(round);
            var arenaClient    = new ArenaClient(
                roundState.CreateAmountCredentialClient(insecureRandom),
                roundState.CreateVsizeCredentialClient(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);

            using RoundStateUpdater roundStateUpdater = new(TimeSpan.FromSeconds(2), wabiSabiApi);
            Task confirmationTask = aliceClient.ConfirmConnectionAsync(
                TimeSpan.FromSeconds(1),
                new long[] { coin1.EffectiveValue(round.FeeRate) },
                new long[] { roundState.MaxVsizeAllocationPerAlice - coin1.ScriptPubKey.EstimateInputVsize() },
                roundStateUpdater,
                CancellationToken.None);

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

            await confirmationTask;

            Assert.Equal(Phase.ConnectionConfirmation, arena.Rounds.First().Phase);
        }
        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), arena);
            await roundStateUpdater.StartAsync(CancellationToken.None);

            // Register Alices.
            using var identificationKey = new Key();
            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), identificationKey, 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);
        }
        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
            });
        }
    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
        });
    }
Example #9
0
        public async Task FullCoinjoinAsyncTestAsync()
        {
            var config = new WabiSabiConfig {
                MaxInputCountByRound = 1
            };
            var round = WabiSabiFactory.CreateRound(config);

            round.MaxVsizeAllocationPerAlice = 255;
            using var key = new Key();
            var outpoint = BitcoinFactory.CreateOutPoint();
            var mockRpc  = new Mock <IRPCClient>();

            mockRpc.Setup(rpc => rpc.GetTxOutAsync(outpoint.Hash, (int)outpoint.N, true))
            .ReturnsAsync(new NBitcoin.RPC.GetTxOutResponse
            {
                IsCoinBase    = false,
                Confirmations = 200,
                TxOut         = new TxOut(Money.Coins(1m), key.PubKey.WitHash.GetAddress(Network.Main)),
            });
            mockRpc.Setup(rpc => rpc.EstimateSmartFeeAsync(It.IsAny <int>(), It.IsAny <EstimateSmartFeeMode>()))
            .ReturnsAsync(new EstimateSmartFeeResponse
            {
                Blocks  = 1000,
                FeeRate = new FeeRate(10m)
            });
            mockRpc.Setup(rpc => rpc.GetMempoolInfoAsync(It.IsAny <CancellationToken>()))
            .ReturnsAsync(new MemPoolInfo
            {
                MinRelayTxFee = 1
            });
            mockRpc.Setup(rpc => rpc.PrepareBatch()).Returns(mockRpc.Object);
            mockRpc.Setup(rpc => rpc.SendBatchAsync()).Returns(Task.CompletedTask);

            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);
            var insecureRandom   = new InsecureRandom();
            var roundState       = RoundState.FromRound(round);
            var aliceArenaClient = new ArenaClient(
                roundState.CreateAmountCredentialClient(insecureRandom),
                roundState.CreateVsizeCredentialClient(insecureRandom),
                wabiSabiApi);

            var inputRegistrationResponse = await aliceArenaClient.RegisterInputAsync(round.Id, outpoint, key, CancellationToken.None);

            var aliceId = inputRegistrationResponse.Value;

            var inputVsize       = Constants.P2wpkhInputVirtualSize;
            var amountsToRequest = new[]
            {
                Money.Coins(.75m) - round.FeeRate.GetFee(inputVsize),
                Money.Coins(.25m),
            }.Select(x => x.Satoshi).ToArray();

            using var destinationKey1 = new Key();
            using var destinationKey2 = new Key();
            var p2wpkhScriptSize = (long)destinationKey1.PubKey.WitHash.ScriptPubKey.EstimateOutputVsize();

            var vsizesToRequest = new[] { roundState.MaxVsizeAllocationPerAlice - (inputVsize + 2 * p2wpkhScriptSize), 2 * p2wpkhScriptSize };

            // Phase: Input Registration
            Assert.Equal(Phase.InputRegistration, round.Phase);

            var connectionConfirmationResponse1 = await aliceArenaClient.ConfirmConnectionAsync(
                round.Id,
                aliceId,
                amountsToRequest,
                vsizesToRequest,
                inputRegistrationResponse.IssuedAmountCredentials,
                inputRegistrationResponse.IssuedVsizeCredentials,
                CancellationToken.None);

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

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

            // Phase: Connection Confirmation
            var connectionConfirmationResponse2 = await aliceArenaClient.ConfirmConnectionAsync(
                round.Id,
                aliceId,
                amountsToRequest,
                vsizesToRequest,
                connectionConfirmationResponse1.IssuedAmountCredentials,
                connectionConfirmationResponse1.IssuedVsizeCredentials,
                CancellationToken.None);

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

            // Phase: Output Registration
            Assert.Equal(Phase.OutputRegistration, round.Phase);

            var bobArenaClient = new ArenaClient(
                roundState.CreateAmountCredentialClient(insecureRandom),
                roundState.CreateVsizeCredentialClient(insecureRandom),
                wabiSabiApi);

            var reissuanceResponse = await bobArenaClient.ReissueCredentialAsync(
                round.Id,
                amountsToRequest,
                Enumerable.Repeat(p2wpkhScriptSize, 2),
                connectionConfirmationResponse2.IssuedAmountCredentials.Take(ProtocolConstants.CredentialNumber),
                connectionConfirmationResponse2.IssuedVsizeCredentials.Skip(1).Take(ProtocolConstants.CredentialNumber),                 // first amount is the leftover value
                CancellationToken.None);

            Credential amountCred1     = reissuanceResponse.IssuedAmountCredentials.ElementAt(0);
            Credential amountCred2     = reissuanceResponse.IssuedAmountCredentials.ElementAt(1);
            Credential zeroAmountCred1 = reissuanceResponse.IssuedAmountCredentials.ElementAt(2);
            Credential zeroAmountCred2 = reissuanceResponse.IssuedAmountCredentials.ElementAt(3);

            Credential vsizeCred1     = reissuanceResponse.IssuedVsizeCredentials.ElementAt(0);
            Credential vsizeCred2     = reissuanceResponse.IssuedVsizeCredentials.ElementAt(1);
            Credential zeroVsizeCred1 = reissuanceResponse.IssuedVsizeCredentials.ElementAt(2);
            Credential zeroVsizeCred2 = reissuanceResponse.IssuedVsizeCredentials.ElementAt(3);

            await bobArenaClient.RegisterOutputAsync(
                round.Id,
                destinationKey1.PubKey.WitHash.ScriptPubKey,
                new[] { amountCred1, zeroAmountCred1 },
                new[] { vsizeCred1, zeroVsizeCred1 },
                CancellationToken.None);

            await bobArenaClient.RegisterOutputAsync(
                round.Id,
                destinationKey2.PubKey.WitHash.ScriptPubKey,
                new[] { amountCred2, zeroAmountCred2 },
                new[] { vsizeCred2, zeroVsizeCred2 },
                CancellationToken.None);

            await aliceArenaClient.ReadyToSignAsync(round.Id, aliceId, CancellationToken.None);

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

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

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

            Assert.Single(tx.Inputs);
            Assert.Equal(2, tx.Outputs.Count);
        }
Example #10
0
    public static void Log(this Round round, LogLevel logLevel, string logMessage, [CallerFilePath] string callerFilePath = "", [CallerMemberName] string callerMemberName = "", [CallerLineNumber] int callerLineNumber = -1)
    {
        var roundState = RoundState.FromRound(round);

        roundState.Log(logLevel, logMessage, callerFilePath: callerFilePath, callerMemberName: callerMemberName, callerLineNumber: callerLineNumber);
    }
Example #11
0
        public async Task RegisterOutputTestAsync()
        {
            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(2m));

            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 insecureRandom   = new InsecureRandom();
            var wabiSabiApi      = new WabiSabiController(coordinator);
            var roundState       = RoundState.FromRound(round);
            var aliceArenaClient = new ArenaClient(
                roundState.CreateAmountCredentialClient(insecureRandom),
                roundState.CreateVsizeCredentialClient(insecureRandom),
                wabiSabiApi);
            var bobArenaClient = new ArenaClient(
                roundState.CreateAmountCredentialClient(insecureRandom),
                roundState.CreateVsizeCredentialClient(insecureRandom),
                wabiSabiApi);

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

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

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

            var task = AliceClient.CreateRegisterAndConfirmInputAsync(RoundState.FromRound(round), aliceArenaClient, coin1, bitcoinSecret, roundStateUpdater, CancellationToken.None);

            do
            {
                await arena.TriggerAndWaitRoundAsync(TimeSpan.FromMinutes(1));
            }while (round.Phase != Phase.ConnectionConfirmation);

            var aliceClient = await task;

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

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

            using var destinationKey = new Key();
            var destination = destinationKey.PubKey.WitHash.ScriptPubKey;

            var bobClient = new BobClient(round.Id, bobArenaClient);

            await bobClient.RegisterOutputAsync(
                destination,
                aliceClient.IssuedAmountCredentials.Take(ProtocolConstants.CredentialNumber),
                aliceClient.IssuedVsizeCredentials.Take(ProtocolConstants.CredentialNumber),
                CancellationToken.None);

            var bob = Assert.Single(round.Bobs);

            Assert.Equal(destination, bob.Script);

            var credentialAmountSum = aliceClient.IssuedAmountCredentials.Take(ProtocolConstants.CredentialNumber).Sum(x => x.Value);

            Assert.Equal(credentialAmountSum, bob.CredentialAmount);
        }
Example #12
0
    public async Task RegisterOutputTestAsync()
    {
        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(2m));

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

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

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

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

        InsecureRandom insecureRandom   = InsecureRandom.Instance;
        var            roundState       = RoundState.FromRound(round);
        var            aliceArenaClient = new ArenaClient(
            roundState.CreateAmountCredentialClient(insecureRandom),
            roundState.CreateVsizeCredentialClient(insecureRandom),
            wabiSabiApi);
        var bobArenaClient = new ArenaClient(
            roundState.CreateAmountCredentialClient(insecureRandom),
            roundState.CreateVsizeCredentialClient(insecureRandom),
            wabiSabiApi);

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

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

        var keyChain = new KeyChain(km, new Kitchen(""));
        var task     = AliceClient.CreateRegisterAndConfirmInputAsync(RoundState.FromRound(round), aliceArenaClient, coin1, keyChain, roundStateUpdater, CancellationToken.None);

        do
        {
            await arena.TriggerAndWaitRoundAsync(TimeSpan.FromMinutes(1));
        }while (round.Phase != Phase.ConnectionConfirmation);

        var aliceClient = await task;

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

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

        using var destinationKey = new Key();
        var destination = destinationKey.PubKey.WitHash.ScriptPubKey;

        var bobClient = new BobClient(round.Id, bobArenaClient);

        await bobClient.RegisterOutputAsync(
            destination,
            aliceClient.IssuedAmountCredentials.Take(ProtocolConstants.CredentialNumber),
            aliceClient.IssuedVsizeCredentials.Take(ProtocolConstants.CredentialNumber),
            CancellationToken.None);

        var bob = Assert.Single(round.Bobs);

        Assert.Equal(destination, bob.Script);

        var credentialAmountSum = aliceClient.IssuedAmountCredentials.Take(ProtocolConstants.CredentialNumber).Sum(x => x.Value);

        Assert.Equal(credentialAmountSum, bob.CredentialAmount);
    }
Example #13
0
        public async Task RegisterOutputTestAsync()
        {
            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(2m));

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

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

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

            ZeroCredentialPool zeroAmountCredential = new();
            ZeroCredentialPool zeroVsizeCredential  = new();

            await using var coordinator = new ArenaRequestHandler(config, new Prison(), arena, mockRpc.Object);
            var insecureRandom   = new InsecureRandom();
            var wabiSabiApi      = new WabiSabiController(coordinator);
            var roundState       = RoundState.FromRound(round);
            var aliceArenaClient = new ArenaClient(
                roundState.CreateAmountCredentialClient(zeroAmountCredential, insecureRandom),
                roundState.CreateVsizeCredentialClient(zeroVsizeCredential, insecureRandom),
                wabiSabiApi);
            var bobArenaClient = new ArenaClient(
                roundState.CreateAmountCredentialClient(zeroAmountCredential, insecureRandom),
                roundState.CreateVsizeCredentialClient(zeroVsizeCredential, insecureRandom),
                wabiSabiApi);

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

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

            var aliceClient = new AliceClient(round.Id, aliceArenaClient, 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, round.Phase);

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

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

            using var destinationKey = new Key();
            var destination = destinationKey.PubKey.WitHash.ScriptPubKey;

            var bobClient = new BobClient(round.Id, bobArenaClient);

            await bobClient.RegisterOutputAsync(Money.Coins(0.25m), destination, aliceClient.RealAmountCredentials, aliceClient.RealVsizeCredentials, CancellationToken.None);

            var bob = Assert.Single(round.Bobs);

            Assert.Equal(destination, bob.Script);
            Assert.Equal(25_000_000, bob.CredentialAmount);
        }
 public Task <RoundState[]> GetStatusAsync(CancellationToken cancellableToken)
 {
     return(Task.FromResult(RequestHandler.Arena.Rounds.Select(x => RoundState.FromRound(x)).ToArray()));
 }