public void CredentialIssuance()
        {
            using var rnd = new SecureRandom();
            var sk = new CredentialIssuerSecretKey(rnd);

            var client = new WabiSabiClient(sk.ComputeCredentialIssuerParameters(), rnd, 4300000000000, new ZeroCredentialPool());

            {
                // Null request. This requests `numberOfCredentials` zero-value credentials.
                var(credentialRequest, validationData) = client.CreateRequestForZeroAmount();

                Assert.True(credentialRequest.IsNullRequest);
                Assert.Equal(ProtocolConstants.CredentialNumber, credentialRequest.Requested.Count());
                var requested = credentialRequest.Requested.ToArray();
                Assert.Empty(requested[0].BitCommitments);
                Assert.Empty(requested[1].BitCommitments);
                Assert.Equal(0, credentialRequest.Delta);

                // Issuer part.
                var issuer = new CredentialIssuer(sk, rnd, 4300000000000);

                var credentialResponse  = issuer.HandleRequest(credentialRequest);
                var valuableCredentials = client.HandleResponse(credentialResponse, validationData);
                Assert.Empty(valuableCredentials);
                var issuedCredential = client.Credentials.TakeZeroValue().First();
                Assert.True(issuedCredential.Amount.IsZero);
            }

            {
                var present = client.Credentials.TakeZeroValue();
                var(credentialRequest, validationData) = client.CreateRequest(new[] { 100_000_000L }, present);
Beispiel #2
0
 public ArenaClient(
     WabiSabiClient amountCredentialClient,
     WabiSabiClient vsizeCredentialClient,
     IWabiSabiApiRequestHandler requestHandler)
 {
     AmountCredentialClient = amountCredentialClient;
     VsizeCredentialClient  = vsizeCredentialClient;
     RequestHandler         = requestHandler;
 }
Beispiel #3
0
 public ArenaClient(
     CredentialIssuerParameters amountCredentialIssuerParameters,
     CredentialIssuerParameters vsizeCredentialIssuerParameters,
     CredentialPool amountCredentialPool,
     CredentialPool vsizeCredentialPool,
     IArenaRequestHandler requestHandler,
     WasabiRandom random)
 {
     AmountCredentialClient = new WabiSabiClient(amountCredentialIssuerParameters, random, ProtocolConstants.MaxAmountPerAlice, amountCredentialPool);
     VsizeCredentialClient  = new WabiSabiClient(vsizeCredentialIssuerParameters, random, ProtocolConstants.MaxVsizePerAlice, vsizeCredentialPool);
     RequestHandler         = requestHandler;
 }
Beispiel #4
0
 public ArenaClient(
     CredentialIssuerParameters amountCredentialIssuerParameters,
     CredentialIssuerParameters weightCredentialIssuerParameters,
     CredentialPool amountCredentialPool,
     CredentialPool weightCredentialPool,
     IArenaRequestHandler requestHandler,
     WasabiRandom random)
 {
     AmountCredentialClient = new WabiSabiClient(amountCredentialIssuerParameters, ProtocolCredentialNumber, random, ProtocolMaxAmountPerAlice, amountCredentialPool);
     WeightCredentialClient = new WabiSabiClient(weightCredentialIssuerParameters, ProtocolCredentialNumber, random, ProtocolMaxWeightPerAlice, weightCredentialPool);
     RequestHandler         = requestHandler;
 }
Beispiel #5
0
        public void Splitting()
        {
            // Split 10 sats into 1, 1, 1, 1, 6.
            var numberOfCredentials = 2;

            using var rnd = new SecureRandom();
            var sk     = new CredentialIssuerSecretKey(rnd);
            var client = new WabiSabiClient(sk.ComputeCredentialIssuerParameters(), numberOfCredentials, rnd, 4300000000000);
            var issuer = new CredentialIssuer(sk, numberOfCredentials, rnd, 4300000000000);

            // Input Reg
            var(zeroCredentialRequest, zeroValidationData) = client.CreateRequestForZeroAmount();
            var zeroCredentialResponse = issuer.HandleRequest(zeroCredentialRequest);

            client.HandleResponse(zeroCredentialResponse, zeroValidationData);

            // Connection Conf
            var(credentialRequest, validationData)      = client.CreateRequest(new[] { 1L, 9L }, Array.Empty <Credential>());
            (zeroCredentialRequest, zeroValidationData) = client.CreateRequestForZeroAmount();

            Assert.Equal(10, credentialRequest.Delta);
            zeroCredentialResponse = issuer.HandleRequest(zeroCredentialRequest);
            var credentialResponse = issuer.HandleRequest(credentialRequest);

            client.HandleResponse(zeroCredentialResponse, zeroValidationData);
            client.HandleResponse(credentialResponse, validationData);

            // Output Reg
            (credentialRequest, validationData) = client.CreateRequest(new[] { 1L, 8L }, client.Credentials.Valuable);
            credentialResponse = issuer.HandleRequest(credentialRequest);
            client.HandleResponse(credentialResponse, validationData);
            Assert.Equal(-1, credentialRequest.Delta);

            (credentialRequest, validationData) = client.CreateRequest(new[] { 1L, 7L }, client.Credentials.Valuable);
            credentialResponse = issuer.HandleRequest(credentialRequest);
            client.HandleResponse(credentialResponse, validationData);
            Assert.Equal(-1, credentialRequest.Delta);

            (credentialRequest, validationData) = client.CreateRequest(new[] { 1L, 6L }, client.Credentials.Valuable);
            credentialResponse = issuer.HandleRequest(credentialRequest);
            client.HandleResponse(credentialResponse, validationData);
            Assert.Equal(-1, credentialRequest.Delta);

            (credentialRequest, validationData) = client.CreateRequest(Array.Empty <long>(), client.Credentials.Valuable.Where(x => x.Amount == Scalar.One).Take(1));
            credentialResponse = issuer.HandleRequest(credentialRequest);
            client.HandleResponse(credentialResponse, validationData);
            Assert.Equal(-1, credentialRequest.Delta);

            (credentialRequest, validationData) = client.CreateRequest(Array.Empty <long>(), client.Credentials.Valuable.Take(1));
            credentialResponse = issuer.HandleRequest(credentialRequest);
            client.HandleResponse(credentialResponse, validationData);
            Assert.Equal(-6, credentialRequest.Delta);
        }
        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()));
        }
        public void CorrectRangeProof()
        {
            using var rnd = new SecureRandom();
            var sk = new CredentialIssuerSecretKey(rnd);

            var client = new WabiSabiClient(sk.ComputeCredentialIssuerParameters(), rnd, 4300000000000, new ZeroCredentialPool());
            var issuer = new CredentialIssuer(sk, rnd, 4300000000000);

            Assert.Equal(42, client.RangeProofWidth);
            Assert.Equal(42, issuer.RangeProofWidth);

            client = new WabiSabiClient(sk.ComputeCredentialIssuerParameters(), rnd, 4400000000001, new ZeroCredentialPool());
            issuer = new CredentialIssuer(sk, rnd, 4400000000001);
            Assert.Equal(43, client.RangeProofWidth);
            Assert.Equal(43, issuer.RangeProofWidth);
        }
Beispiel #8
0
    public void CorrectRangeProof()
    {
        SecureRandom rnd = SecureRandom.Instance;
        var          sk  = new CredentialIssuerSecretKey(rnd);

        var client = new WabiSabiClient(sk.ComputeCredentialIssuerParameters(), rnd, 4300000000000);
        var issuer = new CredentialIssuer(sk, rnd, 4300000000000);

        Assert.Equal(42, client.RangeProofWidth);
        Assert.Equal(42, issuer.RangeProofWidth);

        client = new WabiSabiClient(sk.ComputeCredentialIssuerParameters(), rnd, 4400000000001);
        issuer = new CredentialIssuer(sk, rnd, 4400000000001);
        Assert.Equal(43, client.RangeProofWidth);
        Assert.Equal(43, issuer.RangeProofWidth);
    }
        public static InputsRegistrationRequest CreateInputsRegistrationRequest(IEnumerable <InputRoundSignaturePair>?pairs, Round?round)
        {
            var roundId = round?.Id ?? Guid.NewGuid();
            var inputRoundSignaturePairs = pairs ?? CreateInputRoundSignaturePairs(1, round?.Hash);
            var rnd = new InsecureRandom();

            var client = new WabiSabiClient(new CredentialIssuerSecretKey(rnd).ComputeCredentialIssuerParameters(), 2, rnd, 4300000000000);                     // Input Reg

            var(zeroAmountCredentialRequest, _) = client.CreateRequestForZeroAmount();
            var(zeroWeightCredentialRequest, _) = client.CreateRequestForZeroAmount();

            return(new(
                       roundId,
                       inputRoundSignaturePairs,
                       zeroAmountCredentialRequest,
                       zeroWeightCredentialRequest));
        }
Beispiel #10
0
        public void RegistrationMessageSerialization()
        {
            var converters = new JsonConverter[]
            {
                new ScalarJsonConverter(),
                new GroupElementJsonConverter(),
                new MoneySatoshiJsonConverter()
            };

            var numberOfCredentials = 2;

            using var rnd = new SecureRandom();
            var sk = new CredentialIssuerSecretKey(rnd);

            var issuer = new CredentialIssuer(sk, numberOfCredentials, rnd, 4300000000000);
            var client = new WabiSabiClient(sk.ComputeCredentialIssuerParameters(), numberOfCredentials, rnd, 4300000000000);

            (CredentialsRequest credentialRequest, CredentialsResponseValidation validationData) = client.CreateRequestForZeroAmount();
            var credentialResponse = issuer.HandleRequest(credentialRequest);

            client.HandleResponse(credentialResponse, validationData);
            var present = client.Credentials.ZeroValue.Take(numberOfCredentials);

            (credentialRequest, _) = client.CreateRequest(new[] { 1L }, present);

            // Registration request message.
            var serializedRequestMessage = JsonConvert.SerializeObject(credentialRequest, converters);

            Assert.Throws <NotSupportedException>(() => JsonConvert.DeserializeObject <ZeroCredentialsRequest>(serializedRequestMessage, converters));
            var deserializedRequestMessage = JsonConvert.DeserializeObject <RealCredentialsRequest>(serializedRequestMessage, converters);
            var reserializedRequestMessage = JsonConvert.SerializeObject(deserializedRequestMessage, converters);

            Assert.Equal(serializedRequestMessage, reserializedRequestMessage);

            // Registration response message.
            var serializedResponseMessage   = JsonConvert.SerializeObject(credentialResponse, converters);
            var deserializedResponseMessage = JsonConvert.DeserializeObject <CredentialsResponse>(serializedResponseMessage, converters);
            var reserializedResponseMessage = JsonConvert.SerializeObject(deserializedResponseMessage, converters);

            Assert.Equal(serializedResponseMessage, reserializedResponseMessage);
        }
Beispiel #11
0
        public void Process()
        {
            var numberOfCredentials = k;
            var rnd = new SecureRandom();
            var sk  = new CredentialIssuerSecretKey(rnd);

            var client = new WabiSabiClient(sk.ComputeCredentialIssuerParameters(), numberOfCredentials, rnd);

            var(credentialRequest, validationData) = client.CreateRequestForZeroAmount();
            var issuer             = new CredentialIssuer(sk, numberOfCredentials, rnd);
            var credentialResponse = issuer.HandleRequest(credentialRequest);

            client.HandleResponse(credentialResponse, validationData);

            var present = client.Credentials.ZeroValue.Take(numberOfCredentials);

            (credentialRequest, validationData) = client.CreateRequest(new[] { Money.Coins(1) }, present);
            var credentialRequested = credentialRequest.Requested.ToArray();

            credentialResponse = issuer.HandleRequest(credentialRequest);
            client.HandleResponse(credentialResponse, validationData);
        }
        public void RegistrationMessageSerialization()
        {
            var converters = new JsonConverter[]
            {
                new ScalarJsonConverter(),
                new GroupElementJsonConverter(),
                new MoneySatoshiJsonConverter()
            };

            using var rnd = new SecureRandom();
            var sk = new CredentialIssuerSecretKey(rnd);

            var issuer = new CredentialIssuer(sk, rnd, 4300000000000);
            var client = new WabiSabiClient(sk.ComputeCredentialIssuerParameters(), rnd, 4300000000000);

            (ICredentialsRequest credentialRequest, CredentialsResponseValidation validationData) = client.CreateRequestForZeroAmount();
            var credentialResponse = issuer.HandleRequest(credentialRequest);
            var present            = client.HandleResponse(credentialResponse, validationData);

            (credentialRequest, _) = client.CreateRequest(new[] { 1L }, present, CancellationToken.None);

            // Registration request message.
            var serializedRequestMessage = JsonConvert.SerializeObject(credentialRequest, converters);
            ZeroCredentialsRequest deserializedCredentialsRequest = JsonConvert.DeserializeObject <ZeroCredentialsRequest>(serializedRequestMessage, converters) !;

            Assert.NotSame(credentialRequest, deserializedCredentialsRequest);

            var deserializedRequestMessage = JsonConvert.DeserializeObject <RealCredentialsRequest>(serializedRequestMessage, converters);
            var reserializedRequestMessage = JsonConvert.SerializeObject(deserializedRequestMessage, converters);

            Assert.Equal(serializedRequestMessage, reserializedRequestMessage);

            // Registration response message.
            var serializedResponseMessage   = JsonConvert.SerializeObject(credentialResponse, converters);
            var deserializedResponseMessage = JsonConvert.DeserializeObject <CredentialsResponse>(serializedResponseMessage, converters);
            var reserializedResponseMessage = JsonConvert.SerializeObject(deserializedResponseMessage, converters);

            Assert.Equal(serializedResponseMessage, reserializedResponseMessage);
        }
        public void RegistrationMessageSerialization()
        {
            var converters = new JsonConverter[]
            {
                new ScalarJsonConverter(),
                new GroupElementJsonConverter(),
                new MoneySatoshiJsonConverter()
            };

            var numberOfCredentials = 2;
            var rnd = new SecureRandom();
            var sk  = new CoordinatorSecretKey(rnd);

            var issuer = new CredentialIssuer(sk, numberOfCredentials, rnd);
            var client = new WabiSabiClient(sk.ComputeCoordinatorParameters(), numberOfCredentials, rnd);

            var(credentialRequest, validationData) = client.CreateRequestForZeroAmount();
            var credentialResponse = issuer.HandleRequest(credentialRequest);

            client.HandleResponse(credentialResponse, validationData);
            var present = client.Credentials.ZeroValue.Take(numberOfCredentials);

            (credentialRequest, _) = client.CreateRequest(new[] { Money.Coins(1) }, present);

            // Registration request message
            var serializedRequestMessage   = JsonConvert.SerializeObject(credentialRequest, converters);
            var deserializedRequestMessage = JsonConvert.DeserializeObject <RegistrationRequestMessage>(serializedRequestMessage, converters);
            var reserializedRequestMessage = JsonConvert.SerializeObject(deserializedRequestMessage, converters);

            Assert.Equal(serializedRequestMessage, reserializedRequestMessage);

            // Registration response message
            var serializedResponseMessage   = JsonConvert.SerializeObject(credentialResponse, converters);
            var deserializedResponseMessage = JsonConvert.DeserializeObject <RegistrationResponseMessage>(serializedResponseMessage, converters);
            var reserializedResponseMessage = JsonConvert.SerializeObject(deserializedResponseMessage, converters);

            Assert.Equal(serializedResponseMessage, reserializedResponseMessage);
        }
 public ArenaClient(WabiSabiClient amountCredentialClient, WabiSabiClient weightCredentialClient, IArenaRequestHandler requestHandler)
 {
     AmountCredentialClient = amountCredentialClient;
     WeightCredentialClient = weightCredentialClient;
     RequestHandler         = requestHandler;
 }
        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());
        }
Beispiel #16
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);
        }
Beispiel #17
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);
    }
        public void CredentialIssuance()
        {
            var numberOfCredentials = 3;

            using var rnd = new SecureRandom();
            var sk = new CredentialIssuerSecretKey(rnd);

            var client = new WabiSabiClient(sk.ComputeCredentialIssuerParameters(), numberOfCredentials, rnd);

            {
                // Null request. This requests `numberOfCredentials` zero-value credentials.
                var(credentialRequest, validationData) = client.CreateRequestForZeroAmount();

                Assert.True(credentialRequest.IsNullRequest);
                Assert.Equal(numberOfCredentials, credentialRequest.Requested.Count());
                var requested = credentialRequest.Requested.ToArray();
                Assert.Empty(requested[0].BitCommitments);
                Assert.Empty(requested[1].BitCommitments);
                Assert.Empty(requested[2].BitCommitments);
                Assert.Equal(Money.Zero, credentialRequest.DeltaAmount);

                // Issuer part.
                var issuer = new CredentialIssuer(sk, numberOfCredentials, rnd);

                var credentialResponse = issuer.HandleRequest(credentialRequest);
                client.HandleResponse(credentialResponse, validationData);
                Assert.Equal(numberOfCredentials, client.Credentials.ZeroValue.Count());
                Assert.Empty(client.Credentials.Valuable);
                var issuedCredential = client.Credentials.ZeroValue.First();
                Assert.True(issuedCredential.Amount.IsZero);
            }

            {
                var present = client.Credentials.ZeroValue.Take(numberOfCredentials);
                var(credentialRequest, validationData) = client.CreateRequest(new[] { Money.Coins(1) }, present);

                Assert.False(credentialRequest.IsNullRequest);
                var credentialRequested = credentialRequest.Requested.ToArray();
                Assert.Equal(numberOfCredentials, credentialRequested.Length);
                Assert.NotEmpty(credentialRequested[0].BitCommitments);
                Assert.NotEmpty(credentialRequested[1].BitCommitments);

                // Issuer part.
                var issuer = new CredentialIssuer(sk, numberOfCredentials, rnd);

                var credentialResponse = issuer.HandleRequest(credentialRequest);
                client.HandleResponse(credentialResponse, validationData);
                var issuedCredential = Assert.Single(client.Credentials.Valuable);
                Assert.Equal(new Scalar(100_000_000), issuedCredential.Amount);

                Assert.Equal(2, client.Credentials.ZeroValue.Count());
                Assert.Equal(3, client.Credentials.All.Count());
            }

            {
                var valuableCredential = client.Credentials.Valuable.Take(1);
                var amounts            = Enumerable.Repeat(Money.Coins(0.5m), 2);
                var(credentialRequest, validationData) = client.CreateRequest(amounts, valuableCredential);

                Assert.False(credentialRequest.IsNullRequest);
                var requested = credentialRequest.Requested.ToArray();
                Assert.Equal(numberOfCredentials, requested.Length);
                Assert.NotEmpty(requested[0].BitCommitments);
                Assert.NotEmpty(requested[1].BitCommitments);
                Assert.Equal(Money.Zero, credentialRequest.DeltaAmount);

                // Issuer part.
                var issuer = new CredentialIssuer(sk, numberOfCredentials, rnd);

                var credentialResponse = issuer.HandleRequest(credentialRequest);
                client.HandleResponse(credentialResponse, validationData);
                var credentials = client.Credentials.All.ToArray();
                Assert.NotEmpty(credentials);
                Assert.Equal(3, credentials.Length);

                var valuableCredentials = client.Credentials.Valuable.ToArray();
                Assert.Equal(new Scalar(50_000_000), valuableCredentials[0].Amount);
                Assert.Equal(new Scalar(50_000_000), valuableCredentials[1].Amount);
            }

            {
                var client0 = new WabiSabiClient(sk.ComputeCredentialIssuerParameters(), numberOfCredentials, rnd);
                var(credentialRequest, validationData) = client0.CreateRequestForZeroAmount();

                var issuer             = new CredentialIssuer(sk, numberOfCredentials, rnd);
                var credentialResponse = issuer.HandleRequest(credentialRequest);
                client0.HandleResponse(credentialResponse, validationData);

                (credentialRequest, validationData) = client0.CreateRequest(new[] { Money.Coins(1m) }, Enumerable.Empty <Credential>());

                credentialResponse = issuer.HandleRequest(credentialRequest);
                client0.HandleResponse(credentialResponse, validationData);

                (credentialRequest, validationData) = client0.CreateRequest(Array.Empty <Money>(), client0.Credentials.Valuable);

                credentialResponse = issuer.HandleRequest(credentialRequest);
                client0.HandleResponse(credentialResponse, validationData);
                Assert.NotEmpty(client0.Credentials.All);
                Assert.Equal(numberOfCredentials, client0.Credentials.All.Count());
            }
        }
        public void InvalidCredentialRequests()
        {
            var numberOfCredentials = 3;

            using var rnd = new SecureRandom();
            var sk = new CredentialIssuerSecretKey(rnd);

            var issuer = new CredentialIssuer(sk, numberOfCredentials, rnd);
            {
                var client = new WabiSabiClient(sk.ComputeCredentialIssuerParameters(), numberOfCredentials, rnd);

                // Null request. This requests `numberOfCredentials` zero-value credentials.
                var(credentialRequest, validationData) = client.CreateRequestForZeroAmount();

                var credentialResponse = issuer.HandleRequest(credentialRequest);
                client.HandleResponse(credentialResponse, validationData);

                var(validCredentialRequest, _) = client.CreateRequest(Array.Empty <Money>(), client.Credentials.ZeroValue.Take(1));

                // Test incorrect number of presentations (one instead of 3.)
                var presented = validCredentialRequest.Presented.ToArray();
                var invalidCredentialRequest = new RegistrationRequestMessage(
                    validCredentialRequest.DeltaAmount,
                    new[] { presented[0] },                     // Should present 3 credentials.
                    validCredentialRequest.Requested,
                    validCredentialRequest.Proofs);

                var ex = Assert.Throws <WabiSabiException>(() => issuer.HandleRequest(invalidCredentialRequest));
                Assert.Equal(WabiSabiErrorCode.InvalidNumberOfPresentedCredentials, ex.ErrorCode);
                Assert.Equal("3 credential presentations were expected but 1 were received.", ex.Message);

                // Test incorrect number of presentations (0 instead of 3.)
                presented = credentialRequest.Presented.ToArray();
                invalidCredentialRequest = new RegistrationRequestMessage(
                    Money.Coins(2),
                    Array.Empty <CredentialPresentation>(),                    // Should present 3 credentials.
                    validCredentialRequest.Requested,
                    validCredentialRequest.Proofs);

                ex = Assert.Throws <WabiSabiException>(() => issuer.HandleRequest(invalidCredentialRequest));
                Assert.Equal(WabiSabiErrorCode.InvalidNumberOfPresentedCredentials, ex.ErrorCode);
                Assert.Equal("3 credential presentations were expected but 0 were received.", ex.Message);

                (validCredentialRequest, _) = client.CreateRequest(Array.Empty <Money>(), client.Credentials.All);

                // Test incorrect number of credential requests.
                invalidCredentialRequest = new RegistrationRequestMessage(
                    validCredentialRequest.DeltaAmount,
                    validCredentialRequest.Presented,
                    validCredentialRequest.Requested.Take(1),
                    validCredentialRequest.Proofs);

                ex = Assert.Throws <WabiSabiException>(() => issuer.HandleRequest(invalidCredentialRequest));
                Assert.Equal(WabiSabiErrorCode.InvalidNumberOfRequestedCredentials, ex.ErrorCode);
                Assert.Equal("3 credential requests were expected but 1 were received.", ex.Message);

                // Test incorrect number of credential requests.
                invalidCredentialRequest = new RegistrationRequestMessage(
                    Money.Coins(2),
                    Array.Empty <CredentialPresentation>(),
                    validCredentialRequest.Requested.Take(1),
                    validCredentialRequest.Proofs);

                ex = Assert.Throws <WabiSabiException>(() => issuer.HandleRequest(invalidCredentialRequest));
                Assert.Equal(WabiSabiErrorCode.InvalidNumberOfRequestedCredentials, ex.ErrorCode);
                Assert.Equal("3 credential requests were expected but 1 were received.", ex.Message);

                // Test invalid range proof.
                var requested = validCredentialRequest.Requested.ToArray();

                invalidCredentialRequest = new RegistrationRequestMessage(
                    validCredentialRequest.DeltaAmount,
                    validCredentialRequest.Presented,
                    new[] { requested[0], requested[1], new IssuanceRequest(requested[2].Ma, new[] { GroupElement.Infinity }) },
                    validCredentialRequest.Proofs);

                ex = Assert.Throws <WabiSabiException>(() => issuer.HandleRequest(invalidCredentialRequest));
                Assert.Equal(WabiSabiErrorCode.InvalidBitCommitment, ex.ErrorCode);
            }

            {
                var client = new WabiSabiClient(sk.ComputeCredentialIssuerParameters(), numberOfCredentials, rnd);
                var(validCredentialRequest, validationData) = client.CreateRequestForZeroAmount();

                // Test invalid proofs.
                var proofs = validCredentialRequest.Proofs.ToArray();
                proofs[0] = proofs[1];
                var invalidCredentialRequest = new RegistrationRequestMessage(
                    validCredentialRequest.DeltaAmount,
                    validCredentialRequest.Presented,
                    validCredentialRequest.Requested,
                    proofs);

                var ex = Assert.Throws <WabiSabiException>(() => issuer.HandleRequest(invalidCredentialRequest));
                Assert.Equal(WabiSabiErrorCode.CoordinatorReceivedInvalidProofs, ex.ErrorCode);
            }

            {
                var client = new WabiSabiClient(sk.ComputeCredentialIssuerParameters(), numberOfCredentials, rnd);
                var(validCredentialRequest, validationData) = client.CreateRequestForZeroAmount();

                var credentialResponse = issuer.HandleRequest(validCredentialRequest);
                client.HandleResponse(credentialResponse, validationData);

                (validCredentialRequest, validationData) = client.CreateRequest(Enumerable.Empty <Money>(), client.Credentials.All);

                issuer.HandleRequest(validCredentialRequest);
                var ex = Assert.Throws <WabiSabiException>(() => issuer.HandleRequest(validCredentialRequest));
                Assert.Equal(WabiSabiErrorCode.SerialNumberAlreadyUsed, ex.ErrorCode);
            }
        }