public InputsRegistrationRequest(Guid roundId, IEnumerable <InputRoundSignaturePair> inputRoundSignaturePairs, ZeroCredentialsRequest zeroAmountCredentialRequests, ZeroCredentialsRequest zeroWeightCredentialRequests)
 {
     RoundId = roundId;
     InputRoundSignaturePairs     = inputRoundSignaturePairs;
     ZeroAmountCredentialRequests = zeroAmountCredentialRequests;
     ZeroWeightCredentialRequests = zeroWeightCredentialRequests;
 }
 public ConnectionConfirmationRequest(Guid roundId, Guid aliceId, ZeroCredentialsRequest zeroAmountCredentialRequests, RealCredentialsRequest realAmountCredentialRequests, ZeroCredentialsRequest zeroWeightCredentialRequests, RealCredentialsRequest realWeightCredentialRequests)
 {
     RoundId = roundId;
     AliceId = aliceId;
     ZeroAmountCredentialRequests = zeroAmountCredentialRequests;
     RealAmountCredentialRequests = realAmountCredentialRequests;
     ZeroWeightCredentialRequests = zeroWeightCredentialRequests;
     RealWeightCredentialRequests = realWeightCredentialRequests;
 }
    public void EqualityTest()
    {
        // Request #1.
        ZeroCredentialsRequest request1 = GetZeroCredentialsRequest(1);

        // Request #2.
        ZeroCredentialsRequest request2 = GetZeroCredentialsRequest(1);

        Assert.Equal(request1, request2);

        // Request #3.
        ZeroCredentialsRequest request3 = GetZeroCredentialsRequest(2);

        Assert.NotEqual(request1, request3);
    }
        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 static InputsRegistrationResponse RegisterInput(
            WabiSabiConfig config,
            Guid roundId,
            IDictionary <Coin, byte[]> coinRoundSignaturePairs,
            ZeroCredentialsRequest zeroAmountCredentialRequests,
            ZeroCredentialsRequest zeroVsizeCredentialRequests,
            IDictionary <Guid, Round> rounds,
            Network network)
        {
            if (!rounds.TryGetValue(roundId, out var round))
            {
                throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.RoundNotFound);
            }

            var coins = coinRoundSignaturePairs.Select(x => x.Key);

            if (round.MaxInputCountByAlice < coins.Count())
            {
                throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.TooManyInputs);
            }
            if (round.IsBlameRound && coins.Select(x => x.Outpoint).Any(x => !round.BlameWhitelist.Contains(x)))
            {
                throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.InputNotWhitelisted);
            }

            foreach (var coinRoundSignaturePair in coinRoundSignaturePairs)
            {
                var coin      = coinRoundSignaturePair.Key;
                var signature = coinRoundSignaturePair.Value;

                var coinJoinInputCommitmentData = new CoinJoinInputCommitmentData("CoinJoinCoordinatorIdentifier", round.Hash);
                if (!OwnershipProof.VerifyCoinJoinInputProof(signature, coin.TxOut.ScriptPubKey, coinJoinInputCommitmentData))
                {
                    throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.WrongRoundSignature);
                }
            }

            var alice = new Alice(coinRoundSignaturePairs);

            if (alice.TotalInputAmount < round.MinRegistrableAmount)
            {
                throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.NotEnoughFunds);
            }
            if (alice.TotalInputAmount > round.MaxRegistrableAmount)
            {
                throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.TooMuchFunds);
            }

            if (alice.TotalInputVsize > round.PerAliceVsizeAllocation)
            {
                throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.TooMuchVsize);
            }

            if (round.IsInputRegistrationEnded(config.MaxInputCountByRound, config.GetInputRegistrationTimeout(round)))
            {
                throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.WrongPhase);
            }

            if (round.RemainingInputVsizeAllocation < round.PerAliceVsizeAllocation)
            {
                throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.TooMuchTotalWeight);
            }
            var commitAmountCredentialResponse = round.AmountCredentialIssuer.PrepareResponse(zeroAmountCredentialRequests);
            var commitVsizeCredentialResponse  = round.VsizeCredentialIssuer.PrepareResponse(zeroVsizeCredentialRequests);

            RemoveDuplicateAlices(rounds, alice);

            alice.SetDeadlineRelativeTo(round.ConnectionConfirmationTimeout);
            round.Alices.Add(alice);

            return(new(alice.Id,
                       commitAmountCredentialResponse.Commit(),
                       commitVsizeCredentialResponse.Commit()));
        }
Example #6
0
        public static InputsRegistrationResponse RegisterInput(
            WabiSabiConfig config,
            Guid roundId,
            IDictionary <Coin, byte[]> coinRoundSignaturePairs,
            ZeroCredentialsRequest zeroAmountCredentialRequests,
            ZeroCredentialsRequest zeroWeightCredentialRequests,
            IDictionary <Guid, Round> rounds,
            Network network)
        {
            if (!rounds.TryGetValue(roundId, out var round))
            {
                throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.RoundNotFound);
            }

            var alice = new Alice(coinRoundSignaturePairs);

            var coins = alice.Coins;

            if (round.MaxInputCountByAlice < coins.Count())
            {
                throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.TooManyInputs);
            }
            if (round.IsBlameRound && coins.Select(x => x.Outpoint).Any(x => !round.BlameWhitelist.Contains(x)))
            {
                throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.InputNotWhitelisted);
            }

            var inputValueSum  = Money.Zero;
            var inputWeightSum = 0;

            foreach (var coinRoundSignaturePair in alice.CoinRoundSignaturePairs)
            {
                var coin      = coinRoundSignaturePair.Key;
                var signature = coinRoundSignaturePair.Value;

                var coinJoinInputCommitmentData = new CoinJoinInputCommitmentData("CoinJoinCoordinatorIdentifier", round.Hash);
                if (!OwnershipProof.VerifyCoinJoinInputProof(signature, coin.TxOut.ScriptPubKey, coinJoinInputCommitmentData))
                {
                    throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.WrongRoundSignature);
                }
                inputValueSum += coin.TxOut.Value;

                // Convert conservative P2WPKH size in virtual bytes to weight units.
                inputWeightSum += coin.TxOut.ScriptPubKey.EstimateInputVsize() * 4;
            }

            if (inputValueSum < round.MinRegistrableAmount)
            {
                throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.NotEnoughFunds);
            }
            if (inputValueSum > round.MaxRegistrableAmount)
            {
                throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.TooMuchFunds);
            }

            if (inputWeightSum > round.RegistrableWeightCredentials)
            {
                throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.TooMuchWeight);
            }

            if (round.IsInputRegistrationEnded(config.MaxInputCountByRound, config.GetInputRegistrationTimeout(round)))
            {
                throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.WrongPhase);
            }

            var amountCredentialResponse = round.AmountCredentialIssuer.HandleRequest(zeroAmountCredentialRequests);
            var weightCredentialResponse = round.WeightCredentialIssuer.HandleRequest(zeroWeightCredentialRequests);

            RemoveDuplicateAlices(rounds, alice);

            alice.SetDeadlineRelativeTo(round.ConnectionConfirmationTimeout);
            round.Alices.Add(alice);

            return(new(alice.Id, amountCredentialResponse, weightCredentialResponse));
        }
        public static InputRegistrationResponse RegisterInput(
            WabiSabiConfig config,
            uint256 roundId,
            Coin coin,
            OwnershipProof ownershipProof,
            ZeroCredentialsRequest zeroAmountCredentialRequests,
            ZeroCredentialsRequest zeroVsizeCredentialRequests,
            HashSet <Round> rounds)
        {
            if (rounds.FirstOrDefault(x => x.Id == roundId) is not Round round)
            {
                throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.RoundNotFound);
            }

            if (round.IsInputRegistrationEnded(config.MaxInputCountByRound, config.GetInputRegistrationTimeout(round)))
            {
                throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.WrongPhase);
            }

            if (round.IsBlameRound && !round.BlameWhitelist.Contains(coin.Outpoint))
            {
                throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.InputNotWhitelisted);
            }

            // Compute but don't commit updated CoinJoin to round state, it will
            // be re-calculated on input confirmation. This is computed it here
            // for validation purposes.
            round.Assert <ConstructionState>().AddInput(coin);

            var coinJoinInputCommitmentData = new CoinJoinInputCommitmentData("CoinJoinCoordinatorIdentifier", round.Id);

            if (!OwnershipProof.VerifyCoinJoinInputProof(ownershipProof, coin.TxOut.ScriptPubKey, coinJoinInputCommitmentData))
            {
                throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.WrongOwnershipProof);
            }

            var alice = new Alice(coin, ownershipProof);

            if (alice.TotalInputAmount < round.MinRegistrableAmount)
            {
                throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.NotEnoughFunds);
            }
            if (alice.TotalInputAmount > round.MaxRegistrableAmount)
            {
                throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.TooMuchFunds);
            }

            if (alice.TotalInputVsize > round.MaxVsizeAllocationPerAlice)
            {
                throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.TooMuchVsize);
            }

            if (round.RemainingInputVsizeAllocation < round.MaxVsizeAllocationPerAlice)
            {
                throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.VsizeQuotaExceeded);
            }
            var commitAmountCredentialResponse = round.AmountCredentialIssuer.PrepareResponse(zeroAmountCredentialRequests);
            var commitVsizeCredentialResponse  = round.VsizeCredentialIssuer.PrepareResponse(zeroVsizeCredentialRequests);

            alice.SetDeadlineRelativeTo(round.ConnectionConfirmationTimeout);
            round.Alices.Add(alice);

            return(new(alice.Id,
                       commitAmountCredentialResponse.Commit(),
                       commitVsizeCredentialResponse.Commit()));
        }