コード例 #1
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);
    }
コード例 #2
0
        public async Task AllBobsRegisteredAsync()
        {
            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, arenaClient, alices) = await CreateRoundWithTwoConfirmedConnectionsAsync(arena, key1, coin1, key2, 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();
            await bobClient.RegisterOutputAsync(
                coin1.Amount - round.FeeRate.GetFee(coin1.ScriptPubKey.EstimateInputVsize()),
                destKey1.PubKey.WitHash.ScriptPubKey,
                amountCredentials1.Take(ProtocolConstants.CredentialNumber),
                vsizeCredentials1.Take(ProtocolConstants.CredentialNumber),
                CancellationToken.None);

            using var destKey2 = new Key();
            await bobClient.RegisterOutputAsync(
                coin2.Amount - round.FeeRate.GetFee(coin2.ScriptPubKey.EstimateInputVsize()),
                destKey2.PubKey.WitHash.ScriptPubKey,
                amountCredentials2.Take(ProtocolConstants.CredentialNumber),
                vsizeCredentials2.Take(ProtocolConstants.CredentialNumber),
                CancellationToken.None);

            foreach (var alice in alices)
            {
                await alice.ReadyToSignAsync(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);

            await arena.StopAsync(CancellationToken.None);
        }
コード例 #3
0
        CreateRoundWithOutputsReadyToSignAsync(Arena arena, Key key1, Coin coin1, Key key2, Coin coin2)
        {
            // Create the round.
            await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21));

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

            // Register Alices.
            var aliceClient1 = new AliceClient(round.Id, arenaClient, coin1, round.FeeRate, key1.GetBitcoinSecret(round.Network));
            var aliceClient2 = new AliceClient(round.Id, arenaClient, coin2, round.FeeRate, key2.GetBitcoinSecret(round.Network));

            await aliceClient1.RegisterInputAsync(CancellationToken.None).ConfigureAwait(false);

            await aliceClient2.RegisterInputAsync(CancellationToken.None).ConfigureAwait(false);

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

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

            // Confirm connections.
            await aliceClient1.ConfirmConnectionAsync(TimeSpan.FromMilliseconds(100), round.MaxVsizeAllocationPerAlice, CancellationToken.None).ConfigureAwait(false);

            await aliceClient2.ConfirmConnectionAsync(TimeSpan.FromMilliseconds(100), round.MaxVsizeAllocationPerAlice, CancellationToken.None).ConfigureAwait(false);

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

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

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

            using var destKey1 = new Key();
            using var destKey2 = new Key();
            await bobClient.RegisterOutputAsync(
                coin1.Amount - round.FeeRate.GetFee(coin1.ScriptPubKey.EstimateInputVsize()),
                destKey1.PubKey.WitHash.ScriptPubKey,
                aliceClient1.RealAmountCredentials,
                aliceClient1.RealVsizeCredentials,
                CancellationToken.None).ConfigureAwait(false);

            await bobClient.RegisterOutputAsync(
                coin2.Amount - round.FeeRate.GetFee(coin2.ScriptPubKey.EstimateInputVsize()),
                destKey1.PubKey.WitHash.ScriptPubKey,
                aliceClient2.RealAmountCredentials,
                aliceClient2.RealVsizeCredentials,
                CancellationToken.None).ConfigureAwait(false);

            return(round, aliceClient1, aliceClient2);
        }
コード例 #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);
        }
コード例 #5
0
        public async Task DoesntSwitchImmaturelyAsync()
        {
            WabiSabiConfig cfg = new()
            {
                MaxInputCountByRound           = 2,
                MinInputCountByRoundMultiplier = 0.5
            };

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

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

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

            var(round, arenaClient, alices) = await CreateRoundWithTwoConfirmedConnectionsAsync(arena, key1, coin1, key2, 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);
        }
コード例 #6
0
    public async Task StartOutputRegistrationAsync(
        BobClient bobClient,
        Script scriptPubKey,
        CancellationToken cancellationToken)
    {
        await Task.WhenAll(AmountCredentialToPresentTasks.Concat(VsizeCredentialToPresentTasks)).ConfigureAwait(false);

        IEnumerable <Credential> inputAmountCredentials = AmountCredentialToPresentTasks.Select(x => x.Result);
        IEnumerable <Credential> inputVsizeCredentials  = VsizeCredentialToPresentTasks.Select(x => x.Result);

        await bobClient.RegisterOutputAsync(
            scriptPubKey,
            inputAmountCredentials,
            inputVsizeCredentials,
            cancellationToken).ConfigureAwait(false);
    }
コード例 #7
0
        public async Task SomeBobsRegisteredTimeoutAsync()
        {
            WabiSabiConfig cfg = new()
            {
                MaxInputCountByRound           = 2,
                MinInputCountByRoundMultiplier = 0.5,
                OutputRegistrationTimeout      = TimeSpan.Zero
            };

            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).ConfigureAwait(false);

            var(round, arenaClient, alices) = await CreateRoundWithTwoConfirmedConnectionsAsync(arena, key1, coin1, key2, coin2).ConfigureAwait(false);

            var(realAmountCredentials1, realVsizeCredentials1) = alices[0];
            var(realAmountCredentials2, realVsizeCredentials2) = alices[1];

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

            using var destKey = new Key();
            await bobClient.RegisterOutputAsync(
                coin1.Amount - round.FeeRate.GetFee(coin1.ScriptPubKey.EstimateInputVsize()),
                destKey.PubKey.WitHash.ScriptPubKey,
                realAmountCredentials1,
                realVsizeCredentials1,
                CancellationToken.None).ConfigureAwait(false);

            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(cfg.BlameScript, tx.Outputs.Select(x => x.ScriptPubKey));

            await arena.StopAsync(CancellationToken.None);
        }
コード例 #8
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);
    }
コード例 #9
0
        CreateRoundWithOutputsReadyToSignAsync(Arena arena, Key key1, Coin coin1, Key key2, Coin coin2)
        {
            // Create the round.
            await arena.TriggerAndWaitRoundAsync(TimeSpan.FromSeconds(21));

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

            // Register Alices.
            var aliceClient1 = new AliceClient(round.Id, arenaClient, coin1, round.FeeRate, key1.GetBitcoinSecret(round.Network));
            var aliceClient2 = new AliceClient(round.Id, arenaClient, coin2, round.FeeRate, key2.GetBitcoinSecret(round.Network));

            await aliceClient1.RegisterInputAsync(CancellationToken.None).ConfigureAwait(false);

            await aliceClient2.RegisterInputAsync(CancellationToken.None).ConfigureAwait(false);

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

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

            // Confirm connections.
            using RoundStateUpdater roundStateUpdater = new(TimeSpan.FromSeconds(2), new ArenaRequestHandlerAdapter(arena));
            await aliceClient1.ConfirmConnectionAsync(
                TimeSpan.FromMilliseconds(100),
                new long[] { coin1.EffectiveValue(round.FeeRate) },
                new long[] { round.MaxVsizeAllocationPerAlice - coin1.ScriptPubKey.EstimateInputVsize() },
                roundStateUpdater,
                CancellationToken.None).ConfigureAwait(false);

            await aliceClient2.ConfirmConnectionAsync(
                TimeSpan.FromMilliseconds(100),
                new long[] { coin2.EffectiveValue(round.FeeRate) },
                new long[] { round.MaxVsizeAllocationPerAlice - coin2.ScriptPubKey.EstimateInputVsize() },
                roundStateUpdater,
                CancellationToken.None).ConfigureAwait(false);

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

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

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

            using var destKey1 = new Key();
            using var destKey2 = new Key();
            await bobClient.RegisterOutputAsync(
                coin1.Amount - round.FeeRate.GetFee(coin1.ScriptPubKey.EstimateInputVsize()),
                destKey1.PubKey.WitHash.ScriptPubKey,
                aliceClient1.IssuedAmountCredentials.Take(ProtocolConstants.CredentialNumber),
                aliceClient1.IssuedVsizeCredentials.Take(ProtocolConstants.CredentialNumber),
                CancellationToken.None).ConfigureAwait(false);

            await bobClient.RegisterOutputAsync(
                coin2.Amount - round.FeeRate.GetFee(coin2.ScriptPubKey.EstimateInputVsize()),
                destKey1.PubKey.WitHash.ScriptPubKey,
                aliceClient2.IssuedAmountCredentials.Take(ProtocolConstants.CredentialNumber),
                aliceClient2.IssuedVsizeCredentials.Take(ProtocolConstants.CredentialNumber),
                CancellationToken.None).ConfigureAwait(false);

            return(round, aliceClient1, aliceClient2);
        }
コード例 #10
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);
        }
コード例 #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 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);
    }
コード例 #12
0
        public async Task RegisterOutputTestAsync()
        {
            var config = new WabiSabiConfig {
                MaxInputCountByRound = 1
            };
            var round = WabiSabiFactory.CreateRound(config);

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

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

            var       km       = ServiceFactory.CreateKeyManager("");
            var       key      = BitcoinFactory.CreateHdPubKey(km);
            SmartCoin coin1    = BitcoinFactory.CreateSmartCoin(key, Money.Coins(2m));
            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,
            });

            CredentialPool amountCredential = new();
            CredentialPool vsizeCredential  = new();

            await using var coordinator = new ArenaRequestHandler(config, new Prison(), arena, mockRpc.Object);
            var aliceArenaClient = new ArenaClient(round.AmountCredentialIssuerParameters, round.VsizeCredentialIssuerParameters, amountCredential, vsizeCredential, coordinator, new InsecureRandom());
            var bobArenaClient   = new ArenaClient(round.AmountCredentialIssuerParameters, round.VsizeCredentialIssuerParameters, amountCredential, vsizeCredential, coordinator, new InsecureRandom());

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

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

            Task confirmationTask = aliceClient.ConfirmConnectionAsync(TimeSpan.FromSeconds(3), 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 destinationKey1 = new Key();
            using var destinationKey2 = new Key();
            using var destinationKey3 = new Key();
            using var destinationKey4 = new Key();

            var bobClient = new BobClient(round.Id, bobArenaClient);
            await bobClient.RegisterOutputAsync(Money.Coins(0.25m), destinationKey1.PubKey.WitHash.ScriptPubKey);

            await bobClient.RegisterOutputAsync(Money.Coins(0.25m), destinationKey2.PubKey.WitHash.ScriptPubKey);

            await bobClient.RegisterOutputAsync(Money.Coins(0.25m), destinationKey3.PubKey.WitHash.ScriptPubKey);

            await bobClient.RegisterOutputAsync(Money.Coins(0.25m), destinationKey4.PubKey.WitHash.ScriptPubKey);

            Assert.Equal(4, round.Bobs.Count);
        }
コード例 #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);
        }
コード例 #14
0
        public async Task DiffTooSmallToBlameAsync()
        {
            WabiSabiConfig cfg = new()
            {
                MaxInputCountByRound           = 2,
                MinInputCountByRoundMultiplier = 0.5,
                OutputRegistrationTimeout      = TimeSpan.Zero
            };

            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, arenaClient, alices) = await CreateRoundWithTwoConfirmedConnectionsAsync(arena, key1, coin1, key2, coin2).ConfigureAwait(false);

            var(realAmountCredentials1, realVsizeCredentials1) = alices[0];
            var(realAmountCredentials2, realVsizeCredentials2) = alices[1];

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

            using var destKey1 = new Key();
            using var destKey2 = new Key();
            await bobClient.RegisterOutputAsync(
                coin1.Amount - round.FeeRate.GetFee(coin1.ScriptPubKey.EstimateInputVsize()),
                destKey1.PubKey.WitHash.ScriptPubKey,
                realAmountCredentials1,
                realVsizeCredentials1,
                CancellationToken.None).ConfigureAwait(false);

            await bobClient.RegisterOutputAsync(
                coin2.Amount - round.FeeRate.GetFee(coin2.ScriptPubKey.EstimateInputVsize()),
                destKey2.PubKey.WitHash.ScriptPubKey,
                realAmountCredentials2,
                realVsizeCredentials2,
                CancellationToken.None).ConfigureAwait(false);

            // 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.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(cfg.BlameScript, tx.Outputs.Select(x => x.ScriptPubKey));

            await arena.StopAsync(CancellationToken.None);
        }