Example #1
0
 public CredentialIssuer(CoordinatorSecretKey sk, int numberOfCredentials, WasabiRandom randomNumberGenerator)
 {
     CoordinatorSecretKey  = Guard.NotNull(nameof(sk), sk);
     NumberOfCredentials   = Guard.InRangeAndNotNull(nameof(numberOfCredentials), numberOfCredentials, 1, 100);
     CoordinatorParameters = CoordinatorSecretKey.ComputeCoordinatorParameters();
     RandomNumberGenerator = Guard.NotNull(nameof(randomNumberGenerator), randomNumberGenerator);
 }
        public RoundParameters(
            WabiSabiConfig wabiSabiConfig,
            Network network,
            WasabiRandom random,
            FeeRate feeRate,
            Round?blameOf = null,
            Prison?prison = null)
        {
            Network = network;
            Random  = random;
            FeeRate = feeRate;

            MaxInputCountByRound = wabiSabiConfig.MaxInputCountByRound;
            MinInputCountByRound = wabiSabiConfig.MinInputCountByRound;
            MinRegistrableAmount = wabiSabiConfig.MinRegistrableAmount;
            MaxRegistrableAmount = wabiSabiConfig.MaxRegistrableAmount;

            // Note that input registration timeouts can be modified runtime.
            StandardInputRegistrationTimeout = wabiSabiConfig.StandardInputRegistrationTimeout;
            ConnectionConfirmationTimeout    = wabiSabiConfig.ConnectionConfirmationTimeout;
            OutputRegistrationTimeout        = wabiSabiConfig.OutputRegistrationTimeout;
            TransactionSigningTimeout        = wabiSabiConfig.TransactionSigningTimeout;
            BlameInputRegistrationTimeout    = wabiSabiConfig.BlameInputRegistrationTimeout;

            BlameOf        = blameOf;
            IsBlameRound   = BlameOf is not null;
            BlameWhitelist = BlameOf
                             ?.Alices
                             .Select(x => x.Coin.Outpoint)
                             .Where(x => prison is null || !prison.IsBanned(x))
                             .ToHashSet()
                             ?? new HashSet <OutPoint>();
        }
Example #3
0
        private RespondToChallenge CommitToNonces(Transcript transcript, WasabiRandom random)
        {
            // With all the statements committed, generate a vector of random secret
            // nonces for every equation in underlying proof system. In order to
            // ensure that nonces are never reused (e.g. due to an insecure RNG) with
            // different challenges which would leak the witness, these are generated
            // as synthetic nonces that also depend on the witness data.
            var secretNonceProvider = transcript.CreateSyntheticSecretNonceProvider(Knowledge.Witness, random);

            // Actually generate all of the required nonces and save them in an array
            // because if the enumerable is evaluated several times the results will
            // be different.
            // Note, ToArray() is needed to make sure that secretNonces is captured
            // once and shared between the phases.
            var equations    = Knowledge.Statement.Equations;
            var secretNonces = secretNonceProvider.Sequence.Take(equations.Count()).ToArray();

            // The prover then commits to these, adding the corresponding public
            // points to the transcript.
            var publicNonces = new GroupElementVector(equations.Zip(secretNonces, (equation, pointSecretNonces) => pointSecretNonces * equation.Generators));

            transcript.CommitPublicNonces(publicNonces);

            return(() => Respond(transcript, publicNonces, secretNonces));
        }
        public RoundParameters(
            WabiSabiConfig wabiSabiConfig,
            Network network,
            WasabiRandom random,
            FeeRate feeRate,
            Round?blameOf = null)
        {
            Network = network;
            Random  = random;
            FeeRate = feeRate;

            MaxInputCountByAlice         = wabiSabiConfig.MaxInputCountByAlice;
            MinRegistrableAmount         = wabiSabiConfig.MinRegistrableAmount;
            MaxRegistrableAmount         = wabiSabiConfig.MaxRegistrableAmount;
            RegistrableWeightCredentials = wabiSabiConfig.RegistrableWeightCredentials;

            // Note that input registration timeouts can be modified runtime.
            ConnectionConfirmationTimeout = wabiSabiConfig.ConnectionConfirmationTimeout;
            OutputRegistrationTimeout     = wabiSabiConfig.OutputRegistrationTimeout;
            TransactionSigningTimeout     = wabiSabiConfig.TransactionSigningTimeout;

            BlameOf        = blameOf;
            IsBlameRound   = BlameOf is not null;
            BlameWhitelist = BlameOf
                             ?.Alices
                             .SelectMany(x => x.Coins)
                             .Select(x => x.Outpoint)
                             .ToHashSet()
                             ?? new HashSet <OutPoint>();
        }
Example #5
0
    public static string FromCharacters(int length, string characters, bool secureRandom = false)
    {
        WasabiRandom random = secureRandom ? SecureRandom.Instance : InsecureRandom.Instance;

        var res = random.GetString(length, characters);

        return(res);
    }
    public static string FromCharacters(int length, string characters, bool secureRandom = false)
    {
        using WasabiRandom random = secureRandom ? new SecureRandom() : new InsecureRandom();

        var res = random.GetString(length, characters);

        return(res);
    }
 public WabiSabiClient(
     CredentialIssuerParameters credentialIssuerParameters,
     WasabiRandom randomNumberGenerator,
     long rangeProofUpperBound)
 {
     RangeProofWidth            = (int)Math.Ceiling(Math.Log2(rangeProofUpperBound));
     RandomNumberGenerator      = Guard.NotNull(nameof(randomNumberGenerator), randomNumberGenerator);
     CredentialIssuerParameters = Guard.NotNull(nameof(credentialIssuerParameters), credentialIssuerParameters);
 }
Example #8
0
 public WabiSabiClient(
     CoordinatorParameters coordinatorParameters,
     int numberOfCredentials,
     WasabiRandom randomNumberGenerator)
 {
     RandomNumberGenerator = Guard.NotNull(nameof(randomNumberGenerator), randomNumberGenerator);
     NumberOfCredentials   = Guard.InRangeAndNotNull(nameof(numberOfCredentials), numberOfCredentials, 1, 100);
     CoordinatorParameters = Guard.NotNull(nameof(coordinatorParameters), coordinatorParameters);
     Credentials           = new CredentialPool();
 }
Example #9
0
 /// <summary>
 /// Initializes a new instance of the CredentialIssuer class.
 /// </summary>
 /// <param name="credentialIssuerSecretKey">The <see cref="CredentialIssuerSecretKey">coordinator's secret key</see> used to issue the credentials.</param>
 /// <param name="randomNumberGenerator">The random number generator.</param>
 public CredentialIssuer(
     CredentialIssuerSecretKey credentialIssuerSecretKey,
     WasabiRandom randomNumberGenerator,
     long maxAmount)
 {
     MaxAmount                  = maxAmount;
     RangeProofWidth            = (int)Math.Ceiling(Math.Log2(MaxAmount));
     CredentialIssuerSecretKey  = Guard.NotNull(nameof(credentialIssuerSecretKey), credentialIssuerSecretKey);
     CredentialIssuerParameters = CredentialIssuerSecretKey.ComputeCredentialIssuerParameters();
     RandomNumberGenerator      = Guard.NotNull(nameof(randomNumberGenerator), randomNumberGenerator);
 }
Example #10
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;
 }
Example #11
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;
 }
Example #12
0
 public WabiSabiClient(
     CredentialIssuerParameters credentialIssuerParameters,
     WasabiRandom randomNumberGenerator,
     ulong maxAmount,
     CredentialPool?credentialPool = null)
 {
     MaxAmount                  = maxAmount;
     RangeProofWidth            = (int)Math.Ceiling(Math.Log2(MaxAmount));
     RandomNumberGenerator      = Guard.NotNull(nameof(randomNumberGenerator), randomNumberGenerator);
     CredentialIssuerParameters = Guard.NotNull(nameof(credentialIssuerParameters), credentialIssuerParameters);
     Credentials                = credentialPool ?? new CredentialPool();
 }
Example #13
0
 public WabiSabiClient(
     CredentialIssuerParameters credentialIssuerParameters,
     int numberOfCredentials,
     WasabiRandom randomNumberGenerator,
     ulong maxAmount)
 {
     MaxAmount                  = maxAmount;
     RangeProofWidth            = (int)Math.Ceiling(Math.Log2(MaxAmount));
     RandomNumberGenerator      = Guard.NotNull(nameof(randomNumberGenerator), randomNumberGenerator);
     NumberOfCredentials        = Guard.InRangeAndNotNull(nameof(numberOfCredentials), numberOfCredentials, 1, 100);
     CredentialIssuerParameters = Guard.NotNull(nameof(credentialIssuerParameters), credentialIssuerParameters);
     Credentials                = new CredentialPool();
 }
Example #14
0
    /// <summary>
    /// Calculates how many inputs are desirable to be registered
    /// based on roughly the total number of coins in a wallet.
    /// Note: random biasing is applied.
    /// </summary>
    /// <returns>Desired input count.</returns>
    private static int GetInputTarget(int nonPrivateCount, int privateCount, WasabiRandom rnd)
    {
        var utxoCount                 = nonPrivateCount + privateCount;
        var utxoCountTarget           = 21;
        var minPrivateUtxoCountTarget = 10;
        var maxUtxoCountTarget        = 100;

        int targetInputCount;

        if (utxoCount < utxoCountTarget)
        {
            targetInputCount = 1;
        }
        else if (utxoCount > maxUtxoCountTarget ||
                 privateCount > utxoCountTarget ||
                 (privateCount > nonPrivateCount && privateCount >= minPrivateUtxoCountTarget))
        {
            targetInputCount = MaxInputsRegistrableByWallet;
        }
        else
        {
            var min = 2;
            var max = MaxInputsRegistrableByWallet - 1;

            var percent = (double)(utxoCount - utxoCountTarget) / (maxUtxoCountTarget - utxoCountTarget);
            targetInputCount = (int)Math.Round((max - min) * percent + min);
        }

        var distance = new Dictionary <int, int>();

        for (int i = 1; i <= MaxInputsRegistrableByWallet; i++)
        {
            distance.TryAdd(i, Math.Abs(i - targetInputCount));
        }

        foreach (var best in distance.OrderBy(x => x.Value))
        {
            if (rnd.GetInt(0, 10) < 5)
            {
                return(best.Key);
            }
        }

        return(targetInputCount);
    }
Example #15
0
    public Round(RoundParameters parameters, WasabiRandom random)
    {
        Parameters = parameters;

        CoinjoinState = new ConstructionState(Parameters);

        AmountCredentialIssuer           = new(new(random), random, Parameters.MaxAmountCredentialValue);
        VsizeCredentialIssuer            = new(new(random), random, Parameters.MaxVsizeCredentialValue);
        AmountCredentialIssuerParameters = AmountCredentialIssuer.CredentialIssuerSecretKey.ComputeCredentialIssuerParameters();
        VsizeCredentialIssuerParameters  = VsizeCredentialIssuer.CredentialIssuerSecretKey.ComputeCredentialIssuerParameters();

        InputRegistrationTimeFrame      = TimeFrame.Create(Parameters.StandardInputRegistrationTimeout).StartNow();
        ConnectionConfirmationTimeFrame = TimeFrame.Create(Parameters.ConnectionConfirmationTimeout);
        OutputRegistrationTimeFrame     = TimeFrame.Create(Parameters.OutputRegistrationTimeout);
        TransactionSigningTimeFrame     = TimeFrame.Create(Parameters.TransactionSigningTimeout);

        Id = CalculateHash();
    }
Example #16
0
        public RoundParameters(
            WabiSabiConfig wabiSabiConfig,
            Network network,
            WasabiRandom random,
            FeeRate feeRate)
        {
            Network = network;
            Random  = random;
            FeeRate = feeRate;

            MaxInputCountByRound = wabiSabiConfig.MaxInputCountByRound;
            MinInputCountByRound = wabiSabiConfig.MinInputCountByRound;
            MinRegistrableAmount = wabiSabiConfig.MinRegistrableAmount;
            MaxRegistrableAmount = wabiSabiConfig.MaxRegistrableAmount;

            // Note that input registration timeouts can be modified runtime.
            StandardInputRegistrationTimeout = wabiSabiConfig.StandardInputRegistrationTimeout;
            ConnectionConfirmationTimeout    = wabiSabiConfig.ConnectionConfirmationTimeout;
            OutputRegistrationTimeout        = wabiSabiConfig.OutputRegistrationTimeout;
            TransactionSigningTimeout        = wabiSabiConfig.TransactionSigningTimeout;
            BlameInputRegistrationTimeout    = wabiSabiConfig.BlameInputRegistrationTimeout;
        }
Example #17
0
 public static Proof Prove(Knowledge knowledge, WasabiRandom random)
 {
     return(ProofSystem.Prove(new Transcript(Array.Empty <byte>()), new[] { knowledge }, random).First());
 }
Example #18
0
    internal static ImmutableList <SmartCoin> SelectCoinsForRound(IEnumerable <SmartCoin> coins, RoundParameters parameters, bool consolidationMode, int anonScoreTarget, WasabiRandom rnd)
    {
        var filteredCoins = coins
                            .Where(x => parameters.AllowedInputAmounts.Contains(x.Amount))
                            .Where(x => parameters.AllowedInputTypes.Any(t => x.ScriptPubKey.IsScriptType(t)))
                            .Where(x => x.EffectiveValue(parameters.MiningFeeRate) > Money.Zero)
                            .ToShuffled()
                            .ToArray();

        var privateCoins = filteredCoins
                           .Where(x => x.HdPubKey.AnonymitySet >= anonScoreTarget)
                           .ToArray();
        var nonPrivateCoins = filteredCoins
                              .Where(x => x.HdPubKey.AnonymitySet < anonScoreTarget)
                              .ToArray();

        // Make sure it's ordered by 1 private and 1 non-private coins.
        // Otherwise we'd keep mixing private coins too much during the end of our mixing sessions.
        var organizedCoins = new List <SmartCoin>();

        for (int i = 0; i < Math.Max(privateCoins.Length, nonPrivateCoins.Length); i++)
        {
            if (i < nonPrivateCoins.Length)
            {
                var npc = nonPrivateCoins[i];
                organizedCoins.Add(npc);
            }
            if (i < privateCoins.Length)
            {
                var pc = privateCoins[i];
                organizedCoins.Add(pc);
            }
        }

        // How many inputs do we want to provide to the mix?
        int inputCount = Math.Min(
            organizedCoins.Count,
            consolidationMode ? MaxInputsRegistrableByWallet : GetInputTarget(nonPrivateCoins.Length, privateCoins.Length, rnd));

        // Always use the largest amounts, so we do not participate with insignificant amounts and fragment wallet needlessly.
        var largestAmounts = nonPrivateCoins
                             .OrderByDescending(x => x.Amount)
                             .Take(3)
                             .ToArray();

        // Select a group of coins those are close to each other by anonymity score.
        Dictionary <int, IEnumerable <SmartCoin> > groups = new();

        // Create a bunch of combinations.
        var sw1 = Stopwatch.StartNew();

        foreach (var coin in largestAmounts)
        {
            var baseGroup = organizedCoins.Except(new[] { coin }).Take(inputCount - 1).Concat(new[] { coin });
            TryAddGroup(parameters, groups, baseGroup);

            var sw2 = Stopwatch.StartNew();
            foreach (var group in organizedCoins
                     .Except(new[] { coin })
                     .CombinationsWithoutRepetition(inputCount - 1)
                     .Select(x => x.Concat(new[] { coin })))
            {
                TryAddGroup(parameters, groups, group);

                if (sw2.Elapsed > TimeSpan.FromSeconds(1))
                {
                    break;
                }
            }

            sw2.Reset();

            if (sw1.Elapsed > TimeSpan.FromSeconds(10))
            {
                break;
            }
        }

        if (!groups.Any())
        {
            return(ImmutableList <SmartCoin> .Empty);
        }

        // Select the group where the less coins coming from the same tx.
        var bestRep       = groups.Values.Select(x => GetReps(x)).Min(x => x);
        var bestRepGroups = groups.Values.Where(x => GetReps(x) == bestRep);

        var remainingLargestAmounts = bestRepGroups
                                      .Select(x => x.OrderByDescending(x => x.Amount).First())
                                      .ToHashSet();
        var largestAmount  = remainingLargestAmounts.RandomElement();
        var finalCandidate = bestRepGroups
                             .Where(x => x.OrderByDescending(x => x.Amount).First() == largestAmount)
                             .RandomElement();

        return(finalCandidate?.ToShuffled()?.ToImmutableList() ?? ImmutableList <SmartCoin> .Empty);
    }
Example #19
0
 public CredentialIssuerSecretKey(WasabiRandom rng)
     : this(rng.GetScalar(), rng.GetScalar(), rng.GetScalar(), rng.GetScalar(), rng.GetScalar())
 {
 }
        // Generate synthetic nonce using current state combined with additional randomness.
        public PublicNoncesSequence CreateSyntheticPublicNoncesProvider(IEnumerable <Scalar> secrets, WasabiRandom random)
        {
            // To integrate prior inputs for deterministic component of nonce
            // generation, first clone the state at the current point in the
            // transcript, which should already have the statement tag and public
            // inputs committed.
            var forked = _strobe.MakeCopy();

            // add secret inputs as key material
            foreach (var secret in secrets)
            {
                forked.Key(secret.ToBytes(), false);
            }

            // Add additional randomness
            forked.Key(random.GetBytes(KeySizeInBytes), false);

            IEnumerable <Scalar> NoncesGenerator()
            {
                while (true)
                {
                    yield return(new Scalar(forked.Prf(KeySizeInBytes, false)));
                }
            }

            // Generate a new scalar for each secret using this updated state as a seed.
            return(NoncesGenerator);
        }
Example #21
0
 public BlameRound(RoundParameters parameters, Round blameOf, ISet <OutPoint> blameWhitelist, WasabiRandom random)
     : base(parameters, random)
 {
     BlameOf                    = blameOf;
     BlameWhitelist             = blameWhitelist;
     InputRegistrationTimeFrame = TimeFrame.Create(Parameters.BlameInputRegistrationTimeout).StartNow();
 }
Example #22
0
 public static Proof Prove(Knowledge knowledge, WasabiRandom random)
 {
     return(Prover.Prove(new Transcript(new byte[0]), new[] { knowledge }, random).First());
 }
Example #23
0
        // TODO swap return value order, remove GroupElement argument
        // expect nonce provider instead of WasabiRandom?
        public static (Knowledge knowledge, IEnumerable <GroupElement> bitCommitments) RangeProof(Scalar a, Scalar r, int width, WasabiRandom rnd)
        {
            var ma   = a * Generators.Gg + r * Generators.Gh;
            var bits = Enumerable.Range(0, width).Select(i => a.GetBits(i, 1) == 0 ? Scalar.Zero : Scalar.One);

            // Generate bit commitments.
            // FIXME
            // - derive r_i from a, r, and additional randomness (like synthetic nonces)?
            // - maybe derive without randomness for idempotent requests?
            //   (deterministic blinding terms)? probably simpler to just save
            //   randomly generated credentials in memory or persistent storage, and
            //   re-request by loading and re-sending.
            // - long term fee credentials will definitely need deterministic
            //   randomness because the server can only give idempotent responses with
            //   its own records.
            var randomness     = Enumerable.Repeat(0, width).Select(_ => rnd.GetScalar()).ToArray();
            var bitCommitments = bits.Zip(randomness, (b, r) => b * Generators.Gg + r * Generators.Gh);

            var columns = width * 3 + 1;             // three witness terms per bit and one for the commitment
Example #24
0
 /// <summary>
 /// Initializes a new instance of the CredentialIssuer class.
 /// </summary>
 /// <param name="credentialIssuerSecretKey">The <see cref="CredentialIssuerSecretKey">coordinator's secret key</see> used to issue the credentials.</param>
 /// <param name="numberOfCredentials">The number of credentials that the protocol handles in each request/response.</param>
 /// <param name="randomNumberGenerator">The random number generator.</param>
 public CredentialIssuer(CredentialIssuerSecretKey credentialIssuerSecretKey, int numberOfCredentials, WasabiRandom randomNumberGenerator)
 {
     CredentialIssuerSecretKey  = Guard.NotNull(nameof(credentialIssuerSecretKey), credentialIssuerSecretKey);
     NumberOfCredentials        = Guard.InRangeAndNotNull(nameof(numberOfCredentials), numberOfCredentials, 1, 100);
     CredentialIssuerParameters = CredentialIssuerSecretKey.ComputeCredentialIssuerParameters();
     RandomNumberGenerator      = Guard.NotNull(nameof(randomNumberGenerator), randomNumberGenerator);
 }
Example #25
0
 // Generate synthetic nonce using current state combined with additional randomness.
 public SyntheticSecretNonceProvider CreateSyntheticSecretNonceProvider(IEnumerable <Scalar> secrets, WasabiRandom random)
 => new(_strobe.MakeCopy(), secrets, random);
Example #26
0
 public static Proof Prove(Statement statement, Scalar witness, WasabiRandom random)
 {
     return(Prove(statement, new ScalarVector(witness), random));
 }
    public SyntheticSecretNonceProvider(Strobe128 strobe, IEnumerable <Scalar> secrets, WasabiRandom random)
    {
        Guard.NotNullOrEmpty(nameof(secrets), secrets);
        _strobe      = strobe;
        _secretCount = secrets.Count();

        // add secret inputs as key material
        foreach (var secret in secrets)
        {
            _strobe.Key(secret.ToBytes(), false);
        }

        _strobe.Key(random.GetBytes(32), false);
    }
Example #28
0
 public static Proof Prove(Statement statement, ScalarVector witness, WasabiRandom random)
 {
     return(Prove(new Knowledge(statement, witness), random));
 }
Example #29
0
        public static IEnumerable <Proof> Prove(Transcript transcript, IEnumerable <Knowledge> knowledge, WasabiRandom random)
        {
            // Before anything else all components in a compound proof commit to the
            // individual sub-statement that will be proven, ensuring that the
            // challenges and therefore the responses depend on the statement as a
            // whole.
            foreach (var k in knowledge)
            {
                transcript.CommitStatement(k.Statement);
            }

            var deferredResponds = new List <DeferredProofCreator>();

            foreach (var k in knowledge)
            {
                // With all the statements committed, generate a vector of random secret
                // nonces for every equation in underlying proof system. In order to
                // ensure that nonces are never reused (e.g. due to an insecure RNG) with
                // different challenges which would leak the witness, these are generated
                // as synthetic nonces that also depend on the witness data.
                var          secretNonceProvider = transcript.CreateSyntheticSecretNonceProvider(k.Witness, random);
                ScalarVector secretNonces        = secretNonceProvider.GetScalarVector();

                // The prover then commits to these, adding the corresponding public
                // points to the transcript.
                var equations    = k.Statement.Equations;
                var publicNonces = new GroupElementVector(equations.Select(equation => secretNonces * equation.Generators));
                transcript.CommitPublicNonces(publicNonces);

                deferredResponds.Add((challenge) => new Proof(publicNonces, k.RespondToChallenge(challenge, secretNonces)));
            }

            // With the public nonces committed to the transcript the prover can then
            // derive a challenge that depend on the transcript state without needing
            // to interact with the verifier, but ensuring that they can't know the
            // challenge before the prover commitments are generated.
            Scalar challenge = transcript.GenerateChallenge();

            return(deferredResponds.Select(createProof => createProof(challenge)));
        }
 public CoordinatorSecretKey(WasabiRandom rng)
     : this(rng.GetScalar(), rng.GetScalar(), rng.GetScalar(), rng.GetScalar(), rng.GetScalar())
 {
 }