Ejemplo n.º 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.Alices.Add(alice1);

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

            round.Alices.Add(alice2);

            var coinjoin = round.Coinjoin;

            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 rnd          = new InsecureRandom();
            var amountClient = new WabiSabiClient(round.AmountCredentialIssuerParameters, 2, rnd, 4300000000000ul);
            var weightClient = new WabiSabiClient(round.WeightCredentialIssuerParameters, 2, rnd, 2000ul);
            var apiClient    = new ArenaClient(amountClient, weightClient, coordinator);

            round.SetPhase(Phase.TransactionSigning);

            // No inputs in the CoinJoin.
            await Assert.ThrowsAsync <ArgumentException>(async() => await apiClient.SignTransactionAsync(round.Id, alice1.Coins.ToArray(), new BitcoinSecret(key1, Network.Main), coinjoin));

            coinjoin.Inputs.Add(alice1.Coins.First().Outpoint);

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

            coinjoin.Inputs.Add(alice2.Coins.First().Outpoint);

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

            Assert.False(round.Coinjoin.HasWitness);

            await apiClient.SignTransactionAsync(round.Id, alice1.Coins.ToArray(), new BitcoinSecret(key1, Network.Main), coinjoin);

            Assert.True(round.Coinjoin.Inputs.Where(i => alice1.Coins.Select(c => c.Outpoint).Contains(i.PrevOut)).All(i => i.HasWitScript()));

            await apiClient.SignTransactionAsync(round.Id, alice2.Coins.ToArray(), new BitcoinSecret(key2, Network.Main), coinjoin);

            Assert.True(round.Coinjoin.Inputs.Where(i => alice2.Coins.Select(c => c.Outpoint).Contains(i.PrevOut)).All(i => i.HasWitScript()));
        }
Ejemplo n.º 2
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);
        }
Ejemplo n.º 3
0
        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);
        }
Ejemplo n.º 4
0
        public async Task RemoveInputAsyncTestAsync()
        {
            var config = new WabiSabiConfig();
            var round  = WabiSabiFactory.CreateRound(config);

            round.SetPhase(Phase.ConnectionConfirmation);
            var fundingTx = BitcoinFactory.CreateSmartTransaction(ownOutputCount: 1);
            var coin      = fundingTx.WalletOutputs.First().Coin;
            var alice     = new Alice(coin, new OwnershipProof(), round, Guid.NewGuid());

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

            await using var coordinator = new ArenaRequestHandler(config, new Prison(), arena, arena.Rpc);
            var wabiSabiApi = new WabiSabiController(coordinator);
            var apiClient   = new ArenaClient(null !, null !, wabiSabiApi);

            round.SetPhase(Phase.InputRegistration);

            await apiClient.RemoveInputAsync(round.Id, alice.Id, CancellationToken.None);

            Assert.Empty(round.Alices);
        }
Ejemplo n.º 5
0
        public async Task RemoveInputAsyncTest()
        {
            var config = new WabiSabiConfig();
            var round  = WabiSabiFactory.CreateRound(config);

            round.SetPhase(Phase.ConnectionConfirmation);
            var fundingTx = BitcoinFactory.CreateSmartTransaction(ownOutputCount: 1);
            var coin      = fundingTx.WalletOutputs.First().Coin;
            var alice     = new Alice(new Dictionary <Coin, byte[]> {
                { coin, Array.Empty <byte>() }
            });

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

            await using var coordinator = new ArenaRequestHandler(config, new Prison(), arena, arena.Rpc);
            var apiClient = new ArenaClient(null !, null !, coordinator);

            round.SetPhase(Phase.InputRegistration);

            await apiClient.RemoveInputAsync(round.Id, alice.Id);

            Assert.Empty(round.Alices);
        }
Ejemplo n.º 6
0
        public async Task RegisterInputAsyncTest()
        {
            var config = new WabiSabiConfig();
            var round  = WabiSabiFactory.CreateRound(config);

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

            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)),
            });
            await using var coordinator = new ArenaRequestHandler(config, new Prison(), arena, mockRpc.Object);

            var rnd = new InsecureRandom();
            var protocolCredentialNumber  = 2;
            var protocolMaxWeightPerAlice = 1_000L;
            var amountClient = new WabiSabiClient(round.AmountCredentialIssuerParameters, protocolCredentialNumber, rnd, 4_300_000_000_000ul);
            var weightClient = new WabiSabiClient(round.WeightCredentialIssuerParameters, protocolCredentialNumber, rnd, (ulong)protocolMaxWeightPerAlice);

            var apiClient = new ArenaClient(amountClient, weightClient, coordinator);

            var aliceId = await apiClient.RegisterInputAsync(Money.Coins(1m), outpoint, key, round.Id, round.Hash);

            Assert.NotEqual(Guid.Empty, aliceId);
            Assert.Empty(apiClient.AmountCredentialClient.Credentials.Valuable);

            var reissuanceAmounts = new[]
            {
                Money.Coins(.75m) - round.FeeRate.GetFee(Constants.P2wpkhInputVirtualSize),
                Money.Coins(.25m)
            };

            var inputWeight           = 4 * Constants.P2wpkhInputVirtualSize;
            var inputRemainingWeights = new[] { protocolMaxWeightPerAlice - inputWeight };

            // Phase: Input Registration
            await apiClient.ConfirmConnectionAsync(
                round.Id,
                aliceId,
                inputRemainingWeights,
                apiClient.AmountCredentialClient.Credentials.ZeroValue.Take(protocolCredentialNumber),
                reissuanceAmounts);

            Assert.Empty(apiClient.AmountCredentialClient.Credentials.Valuable);

            // Phase: Connection Confirmation
            round.SetPhase(Phase.ConnectionConfirmation);
            await apiClient.ConfirmConnectionAsync(
                round.Id,
                aliceId,
                inputRemainingWeights,
                apiClient.AmountCredentialClient.Credentials.ZeroValue.Take(protocolCredentialNumber),
                reissuanceAmounts);

            Assert.Single(apiClient.AmountCredentialClient.Credentials.Valuable, x => x.Amount.ToMoney() == reissuanceAmounts.First());
            Assert.Single(apiClient.AmountCredentialClient.Credentials.Valuable, x => x.Amount.ToMoney() == reissuanceAmounts.Last());
        }
Ejemplo n.º 7
0
 public WabiSabiController(ArenaRequestHandler handler)
 {
     RequestHandler = handler;
 }
Ejemplo n.º 8
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);
        }
Ejemplo n.º 9
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);
        }
Ejemplo n.º 10
0
        private static async Task RegisterAndAssertWrongPhaseAsync(InputsRegistrationRequest req, ArenaRequestHandler handler)
        {
            var ex = await Assert.ThrowsAsync <WabiSabiProtocolException>(async() => await handler.RegisterInputAsync(req));

            Assert.Equal(WabiSabiProtocolErrorCode.WrongPhase, ex.ErrorCode);
        }
Ejemplo n.º 11
0
        public async Task FullCoinjoinAsyncTest()
        {
            var config = new WabiSabiConfig {
                MaxInputCountByRound = 1
            };
            var round = WabiSabiFactory.CreateRound(config);

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

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

            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)),
            });
            await using var coordinator = new ArenaRequestHandler(config, new Prison(), arena, mockRpc.Object);

            var apiClient         = new ArenaClient(round.AmountCredentialIssuerParameters, round.WeightCredentialIssuerParameters, coordinator, new InsecureRandom());
            var amountCredentials = apiClient.AmountCredentialClient.Credentials;
            var weightCredentials = apiClient.WeightCredentialClient.Credentials;

            var aliceId = await apiClient.RegisterInputAsync(Money.Coins(1m), outpoint, key, round.Id, round.Hash);

            Assert.NotEqual(Guid.Empty, aliceId);
            Assert.Empty(amountCredentials.Valuable);

            var reissuanceAmounts = new[]
            {
                Money.Coins(.75m) - round.FeeRate.GetFee(Constants.P2wpkhInputVirtualSize),
                Money.Coins(.25m)
            };

            var inputWeight           = Constants.WitnessScaleFactor * Constants.P2wpkhInputVirtualSize;
            var inputRemainingWeights = new[] { (long)ArenaClient.ProtocolMaxWeightPerAlice - inputWeight };

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

            await apiClient.ConfirmConnectionAsync(
                round.Id,
                aliceId,
                inputRemainingWeights,
                amountCredentials.ZeroValue.Take(ArenaClient.ProtocolCredentialNumber),
                reissuanceAmounts);

            Assert.Empty(amountCredentials.Valuable);

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

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

            // Phase: Connection Confirmation
            await apiClient.ConfirmConnectionAsync(
                round.Id,
                aliceId,
                inputRemainingWeights,
                amountCredentials.ZeroValue.Take(ArenaClient.ProtocolCredentialNumber),
                reissuanceAmounts);

            Assert.Single(apiClient.AmountCredentialClient.Credentials.Valuable, x => x.Amount.ToMoney() == reissuanceAmounts.First());
            Assert.Single(apiClient.AmountCredentialClient.Credentials.Valuable, x => x.Amount.ToMoney() == reissuanceAmounts.Last());

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

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

            // Phase: Output Registration
            using var destinationKey1 = new Key();
            using var destinationKey2 = new Key();

            await apiClient.RegisterOutputAsync(
                round.Id,
                reissuanceAmounts[0],
                destinationKey1.PubKey.WitHash.ScriptPubKey,
                apiClient.AmountCredentialClient.Credentials.Valuable,
                apiClient.WeightCredentialClient.Credentials.Valuable);

            await apiClient.RegisterOutputAsync(
                round.Id,
                reissuanceAmounts[1],
                destinationKey2.PubKey.WitHash.ScriptPubKey,
                apiClient.AmountCredentialClient.Credentials.Valuable,
                apiClient.WeightCredentialClient.Credentials.Valuable);

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

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

            Assert.Equal(1, round.Coinjoin.Inputs.Count);
            Assert.Equal(2, round.Coinjoin.Outputs.Count);
        }
Ejemplo n.º 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 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);
        }
Ejemplo n.º 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);
        }