Example #1
0
 public void AddressReusePunishment()
 {
     // If there's reuse in input and output side, then output side didn't gain, nor lose anonymity.
     var analyser = ServiceFactory.CreateBlockchainAnalyzer();
     var km       = ServiceFactory.CreateKeyManager();
     var reuse    = BitcoinFactory.CreateHdPubKey(km);
     var tx       = BitcoinFactory.CreateSmartTransaction(
         9,
         Enumerable.Repeat(Money.Coins(1m), 9),
         new[] { (Money.Coins(1.1m), 100, BitcoinFactory.CreateHdPubKey(km)) },
    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 #3
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 CreateNewAsync()
        {
            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(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);

            CredentialPool amountCredentialPool = new();
            CredentialPool weightCredentialPool = new();
            var            arenaClient          = new ArenaClient(round.AmountCredentialIssuerParameters, round.WeightCredentialIssuerParameters, amountCredentialPool, weightCredentialPool, 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(arenaClient, 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, arena.Rounds.First().Value.Phase);
        }
    public void SelectNonPrivateCoinFromOneCoinSetOfCoins()
    {
        // This test is to make sure that we select the only non-private coin when it is the only coin in the wallet.
        const int AnonymitySet      = 10;
        var       km                = KeyManager.CreateNew(out _, "", Network.Main);
        var       coinsToSelectFrom = Enumerable
                                      .Empty <SmartCoin>()
                                      .Prepend(BitcoinFactory.CreateSmartCoin(BitcoinFactory.CreateHdPubKey(km), Money.Coins(1m), 0, anonymitySet: AnonymitySet - 1))
                                      .ToList();

        var coins = CoinJoinClient.SelectCoinsForRound(
            coins: coinsToSelectFrom,
            CreateMultipartyTransactionParameters(),
            consolidationMode: false,
            anonScoreTarget: AnonymitySet,
            ConfigureRng(1));

        Assert.Single(coins);
    }
    public void SelectNothingFromFullyPrivateSetOfCoins()
    {
        // This test is to make sure no coins are selected when all coins are private.
        const int AnonymitySet      = 10;
        var       km                = KeyManager.CreateNew(out _, "", Network.Main);
        var       coinsToSelectFrom = Enumerable
                                      .Range(0, 10)
                                      .Select(i => BitcoinFactory.CreateSmartCoin(BitcoinFactory.CreateHdPubKey(km), Money.Coins(1m), 0, anonymitySet: AnonymitySet + 1))
                                      .ToList();

        var coins = CoinJoinClient.SelectCoinsForRound(
            coins: coinsToSelectFrom,
            CreateMultipartyTransactionParameters(),
            consolidationMode: false,
            anonScoreTarget: AnonymitySet,
            ConfigureRng(5));

        Assert.Empty(coins);
    }
        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);
        }
    public void TwoNonPrivateCoinInSetOfCoins()
    {
        const int MinAnonimitySet = 10;
        var       km = KeyManager.CreateNew(out _, "", Network.Main);
        var       coinsToSelectFrom = Enumerable
                                      .Empty <SmartCoin>()
                                      .Prepend(BitcoinFactory.CreateSmartCoin(BitcoinFactory.CreateHdPubKey(km), Money.Coins(1m), 0, anonymitySet: MinAnonimitySet - 1))
                                      .Prepend(BitcoinFactory.CreateSmartCoin(BitcoinFactory.CreateHdPubKey(km), Money.Coins(1m), 0, anonymitySet: MinAnonimitySet - 1))
                                      .ToList();

        var coins = CoinJoinClient.SelectCoinsForRound(
            coins: coinsToSelectFrom,
            CreateMultipartyTransactionParameters(),
            consolidationMode: false,
            minAnonScoreTarget: MinAnonimitySet,
            ConfigureRng(1));

        Assert.Single(coins);
    }
    public void SelectTwoNonPrivateCoinsFromTwoCoinsSetOfCoinsConsolidationMode()
    {
        // This test is to make sure that we select more than one non-private coin.
        const int AnonymitySet      = 10;
        var       km                = KeyManager.CreateNew(out _, "", Network.Main);
        var       coinsToSelectFrom = Enumerable
                                      .Empty <SmartCoin>()
                                      .Prepend(BitcoinFactory.CreateSmartCoin(BitcoinFactory.CreateHdPubKey(km), Money.Coins(1m), 0, anonymitySet: AnonymitySet - 1))
                                      .Prepend(BitcoinFactory.CreateSmartCoin(BitcoinFactory.CreateHdPubKey(km), Money.Coins(1m), 0, anonymitySet: AnonymitySet - 1))
                                      .ToList();

        var coins = CoinJoinClient.SelectCoinsForRound(
            coins: coinsToSelectFrom,
            CreateMultipartyTransactionParameters(),
            consolidationMode: true,
            anonScoreTarget: AnonymitySet,
            ConfigureRng(1));

        Assert.Equal(2, coins.Count);
    }
    public void OnlyOneNonPrivateCoinInBigSetOfCoinsConsolidationMode()
    {
        const int MinAnonimitySet = 10;
        var       km = KeyManager.CreateNew(out _, "", Network.Main);
        SmartCoin smallerAnonCoin   = BitcoinFactory.CreateSmartCoin(BitcoinFactory.CreateHdPubKey(km), Money.Coins(1m), 0, anonymitySet: MinAnonimitySet - 1);
        var       coinsToSelectFrom = Enumerable
                                      .Range(0, 10)
                                      .Select(i => BitcoinFactory.CreateSmartCoin(BitcoinFactory.CreateHdPubKey(km), Money.Coins(1m), 0, anonymitySet: MinAnonimitySet + 1))
                                      .Prepend(smallerAnonCoin)
                                      .ToList();

        var coins = CoinJoinClient.SelectCoinsForRound(
            coins: coinsToSelectFrom,
            CreateMultipartyTransactionParameters(),
            consolidationMode: true,
            minAnonScoreTarget: MinAnonimitySet,
            ConfigureRng(5));

        Assert.Contains(smallerAnonCoin, coins);
        Assert.Equal(10, coins.Count);
    }
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);
        }