Example #1
0
        public async Task SignTransactionAsync()
        {
            WabiSabiConfig config = new();
            Round          round  = WabiSabiFactory.CreateRound(config);

            using Key key1 = new();
            Alice alice1 = WabiSabiFactory.CreateAlice(key: key1, round: round);

            round.Alices.Add(alice1);

            using Key key2 = new();
            Alice alice2 = WabiSabiFactory.CreateAlice(key: key2, round: round);

            round.Alices.Add(alice2);

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

            var mockRpc = new Mock <IRPCClient>();

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

            var rnd          = new InsecureRandom();
            var amountClient = new WabiSabiClient(round.AmountCredentialIssuerParameters, rnd, 4300000000000L);
            var vsizeClient  = new WabiSabiClient(round.VsizeCredentialIssuerParameters, rnd, 2000L);
            var apiClient    = new ArenaClient(amountClient, vsizeClient, wabiSabiApi);

            round.SetPhase(Phase.TransactionSigning);

            var emptyState = round.Assert <ConstructionState>();

            // We can't use ``emptyState.Finalize()` because this is not a valid transaction so we fake it
            var finalizedEmptyState = new SigningState(emptyState.Parameters, emptyState.Inputs, emptyState.Outputs);

            // No inputs in the CoinJoin.
            await Assert.ThrowsAsync <ArgumentException>(async() =>
                                                         await apiClient.SignTransactionAsync(round.Id, alice1.Coin, new BitcoinSecret(key1, Network.Main), finalizedEmptyState.CreateUnsignedTransaction(), CancellationToken.None));

            var oneInput = emptyState.AddInput(alice1.Coin).Finalize();

            round.CoinjoinState = oneInput;

            // Trying to sign coins those are not in the CoinJoin.
            await Assert.ThrowsAsync <InvalidOperationException>(async() =>
                                                                 await apiClient.SignTransactionAsync(round.Id, alice2.Coin, new BitcoinSecret(key2, Network.Main), oneInput.CreateUnsignedTransaction(), CancellationToken.None));

            var twoInputs = emptyState.AddInput(alice1.Coin).AddInput(alice2.Coin).Finalize();

            round.CoinjoinState = twoInputs;

            // Trying to sign coins with the wrong secret.
            await Assert.ThrowsAsync <InvalidOperationException>(async() =>
                                                                 await apiClient.SignTransactionAsync(round.Id, alice1.Coin, new BitcoinSecret(key2, Network.Main), twoInputs.CreateUnsignedTransaction(), CancellationToken.None));

            Assert.False(round.Assert <SigningState>().IsFullySigned);

            var unsigned = round.Assert <SigningState>().CreateUnsignedTransaction();

            await apiClient.SignTransactionAsync(round.Id, alice1.Coin, new BitcoinSecret(key1, Network.Main), unsigned, CancellationToken.None);

            Assert.True(round.Assert <SigningState>().IsInputSigned(alice1.Coin.Outpoint));
            Assert.False(round.Assert <SigningState>().IsInputSigned(alice2.Coin.Outpoint));

            Assert.False(round.Assert <SigningState>().IsFullySigned);

            await apiClient.SignTransactionAsync(round.Id, alice2.Coin, new BitcoinSecret(key2, Network.Main), unsigned, CancellationToken.None);

            Assert.True(round.Assert <SigningState>().IsInputSigned(alice2.Coin.Outpoint));

            Assert.True(round.Assert <SigningState>().IsFullySigned);
        }
Example #2
0
    public async Task SignTransactionAsync()
    {
        WabiSabiConfig config   = new();
        Round          round    = WabiSabiFactory.CreateRound(config);
        var            password = "******";

        var km                  = ServiceFactory.CreateKeyManager(password);
        var keyChain            = new KeyChain(km, new Kitchen(password));
        var destinationProvider = new InternalDestinationProvider(km);

        var coins = destinationProvider.GetNextDestinations(2)
                    .Select(dest => (
                                Coin: new Coin(BitcoinFactory.CreateOutPoint(), new TxOut(Money.Coins(1.0m), dest)),
                                OwnershipProof: keyChain.GetOwnershipProof(dest, new CoinJoinInputCommitmentData("test", uint256.One))))
                    .ToArray();

        Alice alice1 = WabiSabiFactory.CreateAlice(coins[0].Coin, coins[0].OwnershipProof, round: round);

        round.Alices.Add(alice1);

        Alice alice2 = WabiSabiFactory.CreateAlice(coins[1].Coin, coins[1].OwnershipProof, round: round);

        round.Alices.Add(alice2);

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

        var mockRpc = new Mock <IRPCClient>();

        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 rnd          = InsecureRandom.Instance;
        var            amountClient = new WabiSabiClient(round.AmountCredentialIssuerParameters, rnd, 4300000000000L);
        var            vsizeClient  = new WabiSabiClient(round.VsizeCredentialIssuerParameters, rnd, 2000L);
        var            apiClient    = new ArenaClient(amountClient, vsizeClient, wabiSabiApi);

        round.SetPhase(Phase.TransactionSigning);

        var emptyState = round.Assert <ConstructionState>();

        // We can't use ``emptyState.Finalize()` because this is not a valid transaction so we fake it
        var finalizedEmptyState = new SigningState(round.Parameters, emptyState.Events);

        // No inputs in the coinjoin.
        await Assert.ThrowsAsync <ArgumentException>(async() =>
                                                     await apiClient.SignTransactionAsync(round.Id, alice1.Coin, coins[0].OwnershipProof, keyChain, finalizedEmptyState.CreateUnsignedTransaction(), CancellationToken.None));

        var oneInput = emptyState.AddInput(alice1.Coin).Finalize();

        round.CoinjoinState = oneInput;

        // Trying to sign coins those are not in the coinjoin.
        await Assert.ThrowsAsync <InvalidOperationException>(async() =>
                                                             await apiClient.SignTransactionAsync(round.Id, alice2.Coin, coins[1].OwnershipProof, keyChain, oneInput.CreateUnsignedTransaction(), CancellationToken.None));

        var twoInputs = emptyState.AddInput(alice1.Coin).AddInput(alice2.Coin).Finalize();

        round.CoinjoinState = twoInputs;

        Assert.False(round.Assert <SigningState>().IsFullySigned);
        var unsigned = round.Assert <SigningState>().CreateUnsignedTransaction();

        await apiClient.SignTransactionAsync(round.Id, alice1.Coin, coins[0].OwnershipProof, keyChain, unsigned, CancellationToken.None);

        Assert.True(round.Assert <SigningState>().IsInputSigned(alice1.Coin.Outpoint));
        Assert.False(round.Assert <SigningState>().IsInputSigned(alice2.Coin.Outpoint));

        Assert.False(round.Assert <SigningState>().IsFullySigned);

        await apiClient.SignTransactionAsync(round.Id, alice2.Coin, coins[1].OwnershipProof, keyChain, unsigned, CancellationToken.None);

        Assert.True(round.Assert <SigningState>().IsInputSigned(alice2.Coin.Outpoint));

        Assert.True(round.Assert <SigningState>().IsFullySigned);
    }