Beispiel #1
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);
        }