fn sign_with_balance_check<CSPRNG: RngCore + CryptoRng>(
        message: &[u8; 32],
        ring: &[(CompressedRistrettoPublic, CompressedCommitment)],
        real_index: usize,
        onetime_private_key: &RistrettoPrivate,
        value: u64,
        blinding: &Scalar,
        output_blinding: &Scalar,
        check_value_is_preserved: bool,
        rng: &mut CSPRNG,
    ) -> Result<Self, Error> {
        let ring_size = ring.len();

        if real_index >= ring_size {
            return Err(Error::IndexOutOfBounds);
        }

        let G = GENERATORS.B;
        let H = GENERATORS.B_blinding;

        let key_image = compute_key_image(onetime_private_key);

        // The uncompressed key_image.
        let I: RistrettoPoint = key_image.try_into().expect("key_image should decompress");

        // Uncompressed output commitment.
        // This ensures that each address and commitment encodes a valid Ristretto point.
        let output_commitment: Commitment = Commitment::new(value, *output_blinding);

        // Ring must decompress.
        let decompressed_ring = decompress_ring(ring)?;

        // Challenges `c_0, ... c_{ring_size - 1}`.
        let mut c: Vec<Scalar> = vec![Scalar::zero(); ring_size];

        // Responses `r_{0,0}, r_{0,1}, ... , r_{ring_size-1,0}, r_{ring_size-1,1}`.
        let mut r: Vec<Scalar> = vec![Scalar::zero(); 2 * ring_size];
        for i in 0..ring_size {
            if i == real_index {
                continue;
            }
            r[2 * i] = Scalar::random(rng);
            r[2 * i + 1] = Scalar::random(rng);
        }

        let alpha_0 = Scalar::random(rng);
        let alpha_1 = Scalar::random(rng);

        for n in 0..ring_size {
            // Iterate around the ring, starting at real_index.
            let i = (real_index + n) % ring_size;
            let (P_i, input_commitment) = &decompressed_ring[i];

            let (L0, R0, L1) = if i == real_index {
                // c_{i+1} = Hn( m | alpha_0 * G | alpha_0 * Hp(P_i) | alpha_1 * H )
                //         = Hn( m |      L0     |         R0        |      L1     )
                //
                // where P_i is the i^th onetime public key.
                // There is no R1 term because no key image is needed for the commitment to zero.

                let L0 = alpha_0 * G;
                let R0 = alpha_0 * hash_to_point(&P_i);
                let L1 = alpha_1 * H;
                (L0, R0, L1)
            } else {
                // c_{i+1} = Hn( m | r_{i,0} * G + c_i * P_i | r_{i,0} * Hp(P_i) + c_i * I | r_{i,1} * G + c_i * Z_i )
                //         = Hn( m |           L0            |               R0            |             L1          )
                //
                // where:
                // * P_i is the i^th onetime public key.
                // * I is the key image of the real input's private key,
                // * Z_i is the i^th "commitment to zero" = output_commitment - input_commitment.
                //
                // There is no R1 term because no key image is needed for the commitment to zero.

                let L0 = r[2 * i] * G + c[i] * P_i.as_ref();
                let R0 = r[2 * i] * hash_to_point(&P_i) + c[i] * I;
                let L1 =
                    r[2 * i + 1] * H + c[i] * (output_commitment.point - input_commitment.point);
                (L0, R0, L1)
            };

            c[(i + 1) % ring_size] = {
                let mut hasher = Blake2b::new();
                hasher.input(message);
                hasher.input(L0.compress().as_bytes());
                hasher.input(R0.compress().as_bytes());
                hasher.input(L1.compress().as_bytes());
                Scalar::from_hash::<Blake2b>(hasher)
            };
        }

        // "Close the loop" by computing responses for the real index.

        let s: Scalar = *onetime_private_key.as_ref();
        r[2 * real_index] = alpha_0 - c[real_index] * s;

        let z: Scalar = output_blinding - blinding;
        r[2 * real_index + 1] = alpha_1 - c[real_index] * z;

        if check_value_is_preserved {
            let (_, input_commitment) = decompressed_ring[real_index];
            let difference: RistrettoPoint = output_commitment.point - input_commitment.point;
            if difference != (z * H) {
                return Err(Error::ValueNotConserved);
            }
        }

        let responses: Vec<CurveScalar> = r.into_iter().map(CurveScalar::from).collect();

        Ok(RingMLSAG {
            c_zero: CurveScalar::from(c[0]),
            responses,
            key_image,
        })
    }
// Sign a ring of input addresses and amount commitments.
//
// Sign a ring of input addresses and amount commitments using a modified MLSAG
// that omits the "key image" term for the amount commitments (which do not need to be linkable).
//
// # Arguments
// * `message` - Message to be signed.
// * `ring` - A ring of input onetime addresses and amount commitments.
// * `real_index` - The index in the ring of the real input.
// * `onetime_private_key` - The real input's private key.
// * `value` - Value of the real input.
// * `blinding` - Blinding of the real input.
// * `output_blinding` - The output amount's blinding factor.
///* `check_value_is_preserved` - If true, check that the value of inputs equals value of outputs.
// * `rng` - Randomness.
Sign a ring of input addresses and amount commitments.
Sign a ring of input addresses and amount commitments using a modified MLSAG
that omits the "key image" term for the amount commitments (which do not need to be linkable).
Sign a ring of input addresses and amount commitments . Sign a ring of input addresses and amount commitments using a modified MLSAG that omits the " key image " term for the amount commitments ( which do not need to be linkable ) .
fn sign_with_balance_check<CSPRNG: RngCore + CryptoRng>(
        message: &[u8; 32],
        ring: &[(CompressedRistrettoPublic, CompressedCommitment)],
        real_index: usize,
        onetime_private_key: &RistrettoPrivate,
        value: u64,
        blinding: &Scalar,
        output_blinding: &Scalar,
        check_value_is_preserved: bool,
        rng: &mut CSPRNG,
    ) -> Result<Self, Error> {
        let ring_size = ring.len();
        if real_index >= ring_size {
            return Err(Error::IndexOutOfBounds);
        }
        let G = GENERATORS.B;
        let H = GENERATORS.B_blinding;
        let key_image = compute_key_image(onetime_private_key);
        // The uncompressed key_image.
        let I: RistrettoPoint = key_image.try_into().expect("key_image should decompress");
        // Uncompressed output commitment.
        // This ensures that each address and commitment encodes a valid Ristretto point.
        let output_commitment: Commitment = Commitment::new(value, *output_blinding);
        // Ring must decompress.
        let decompressed_ring = decompress_ring(ring)?;
        // Challenges `c_0, ... c_{ring_size - 1}`.
        let mut c: Vec<Scalar> = vec![Scalar::zero(); ring_size];
        // Responses `r_{0,0}, r_{0,1}, ... , r_{ring_size-1,0}, r_{ring_size-1,1}`.
        let mut r: Vec<Scalar> = vec![Scalar::zero(); 2 * ring_size];
        for i in 0..ring_size {
            if i == real_index {
                continue;
            }
            r[2 * i] = Scalar::random(rng);
            r[2 * i + 1] = Scalar::random(rng);
        }
        let alpha_0 = Scalar::random(rng);
        let alpha_1 = Scalar::random(rng);
        for n in 0..ring_size {
            // Iterate around the ring, starting at real_index.
            let i = (real_index + n) % ring_size;
            let (P_i, input_commitment) = &decompressed_ring[i];
            let (L0, R0, L1) = if i == real_index {
                // c_{i+1} = Hn( m | alpha_0 * G | alpha_0 * Hp(P_i) | alpha_1 * H )
                //         = Hn( m |      L0     |         R0        |      L1     )
                //
                // where P_i is the i^th onetime public key.
                // There is no R1 term because no key image is needed for the commitment to zero.
                let L0 = alpha_0 * G;
                let R0 = alpha_0 * hash_to_point(&P_i);
                let L1 = alpha_1 * H;
                (L0, R0, L1)
            } else {
                // c_{i+1} = Hn( m | r_{i,0} * G + c_i * P_i | r_{i,0} * Hp(P_i) + c_i * I | r_{i,1} * G + c_i * Z_i )
                //         = Hn( m |           L0            |               R0            |             L1          )
                //
                // where:
                // * P_i is the i^th onetime public key.
                // * I is the key image of the real input's private key,
                // * Z_i is the i^th "commitment to zero" = output_commitment - input_commitment.
                //
                // There is no R1 term because no key image is needed for the commitment to zero.
                let L0 = r[2 * i] * G + c[i] * P_i.as_ref();
                let R0 = r[2 * i] * hash_to_point(&P_i) + c[i] * I;
                let L1 =
                    r[2 * i + 1] * H + c[i] * (output_commitment.point - input_commitment.point);
                (L0, R0, L1)
            };
            c[(i + 1) % ring_size] = {
                let mut hasher = Blake2b::new();
                hasher.input(message);
                hasher.input(L0.compress().as_bytes());
                hasher.input(R0.compress().as_bytes());
                hasher.input(L1.compress().as_bytes());
                Scalar::from_hash::<Blake2b>(hasher)
            };
        }
        // "Close the loop" by computing responses for the real index.
        let s: Scalar = *onetime_private_key.as_ref();
        r[2 * real_index] = alpha_0 - c[real_index] * s;
        let z: Scalar = output_blinding - blinding;
        r[2 * real_index + 1] = alpha_1 - c[real_index] * z;
        if check_value_is_preserved {
            let (_, input_commitment) = decompressed_ring[real_index];
            let difference: RistrettoPoint = output_commitment.point - input_commitment.point;
            if difference != (z * H) {
                return Err(Error::ValueNotConserved);
            }
        }
        let responses: Vec<CurveScalar> = r.into_iter().map(CurveScalar::from).collect();
        Ok(RingMLSAG {
            c_zero: CurveScalar::from(c[0]),
            responses,
            key_image,
        })
    }
fn sign_with_balance_check < CSPRNG : RngCore + CryptoRng > ( message : & [ u8 ; 32 ] , ring : & [ ( CompressedRistrettoPublic , CompressedCommitment ) ] , real_index : usize , onetime_private_key : & RistrettoPrivate , value : u64 , blinding : & Scalar , output_blinding : & Scalar , check_value_is_preserved : bool , rng : & mut CSPRNG , ) -> Result < Self , Error > { let ring_size = ring . len ( ) ; if real_index >= ring_size { return Err ( Error :: IndexOutOfBounds ) ; } let G = GENERATORS . B ; let H = GENERATORS . B_blinding ; let key_image = compute_key_image ( onetime_private_key ) ; // The uncompressed key_image. let I : RistrettoPoint = key_image . try_into ( ) . expect ( " " ) ; // Uncompressed output commitment. // This ensures that each address and commitment encodes a valid Ristretto point. let output_commitment : Commitment = Commitment :: new ( value , * output_blinding ) ; // Ring must decompress. let decompressed_ring = decompress_ring ( ring ) ? ; // Challenges `c_0, ... c_{ring_size - 1}`. let mut c : Vec < Scalar > = vec ! [ Scalar zero ( ) ring_size ] ; // Responses `r_{0,0}, r_{0,1}, ... , r_{ring_size-1,0}, r_{ring_size-1,1}`. let mut r : Vec < Scalar > = vec ! [ Scalar zero ( ) 2 ring_size ] ; for i in 0 .. ring_size { if i == real_index { continue ; } r [ 2 * i ] = Scalar :: random ( rng ) ; r [ 2 * i + 1 ] = Scalar :: random ( rng ) ; } let alpha_0 = Scalar :: random ( rng ) ; let alpha_1 = Scalar :: random ( rng ) ; for n in 0 .. ring_size { // Iterate around the ring, starting at real_index. let i = ( real_index + n ) % ring_size ; let ( P_i , input_commitment ) = & decompressed_ring [ i ] ; let ( L0 , R0 , L1 ) = if i == real_index { // c_{i+1} = Hn( m | alpha_0 * G | alpha_0 * Hp(P_i) | alpha_1 * H ) //         = Hn( m |      L0     |         R0        |      L1     ) // // where P_i is the i^th onetime public key. // There is no R1 term because no key image is needed for the commitment to zero. let L0 = alpha_0 * G ; let R0 = alpha_0 * hash_to_point ( & P_i ) ; let L1 = alpha_1 * H ; ( L0 , R0 , L1 ) } else { // c_{i+1} = Hn( m | r_{i,0} * G + c_i * P_i | r_{i,0} * Hp(P_i) + c_i * I | r_{i,1} * G + c_i * Z_i ) //         = Hn( m |           L0            |               R0            |             L1          ) // // where: // * P_i is the i^th onetime public key. // * I is the key image of the real input's private key, // * Z_i is the i^th "commitment to zero" = output_commitment - input_commitment. // // There is no R1 term because no key image is needed for the commitment to zero. let L0 = r [ 2 * i ] * G + c [ i ] * P_i . as_ref ( ) ; let R0 = r [ 2 * i ] * hash_to_point ( & P_i ) + c [ i ] * I ; let L1 = r [ 2 * i + 1 ] * H + c [ i ] * ( output_commitment . point - input_commitment . point ) ; ( L0 , R0 , L1 ) } ; c [ ( i + 1 ) % ring_size ] = { let mut hasher = Blake2b :: new ( ) ; hasher . input ( message ) ; hasher . input ( L0 . compress ( ) . as_bytes ( ) ) ; hasher . input ( R0 . compress ( ) . as_bytes ( ) ) ; hasher . input ( L1 . compress ( ) . as_bytes ( ) ) ; Scalar :: from_hash :: < Blake2b > ( hasher ) } ; } // "Close the loop" by computing responses for the real index. let s : Scalar = * onetime_private_key . as_ref ( ) ; r [ 2 * real_index ] = alpha_0 - c [ real_index ] * s ; let z : Scalar = output_blinding - blinding ; r [ 2 * real_index + 1 ] = alpha_1 - c [ real_index ] * z ; if check_value_is_preserved { let ( _ , input_commitment ) = decompressed_ring [ real_index ] ; let difference : RistrettoPoint = output_commitment . point - input_commitment . point ; if difference != ( z * H ) { return Err ( Error :: ValueNotConserved ) ; } } let responses : Vec < CurveScalar > = r . into_iter ( ) . map ( CurveScalar :: from ) . collect ( ) ; Ok ( RingMLSAG { c_zero : CurveScalar :: from ( c [ 0 ] ) , responses , key_image , } ) }
Sign a ring of input addresses and amount commitments.
Sign a ring of input addresses and amount commitments .
message
&[u8; 32]
ring
&[(CompressedRistrettoPublic, CompressedCommitment)]
real_index
usize
onetime_private_key
&RistrettoPrivate
value
u64
blinding
&Scalar
output_blinding
&Scalar
check_value_is_preserved
bool
rng
&mut CSPRNG
message
&[u8; 32]
ring
&[(CompressedRistrettoPublic, CompressedCommitment)]
real_index
usize
onetime_private_key
&RistrettoPrivate
value
u64
blinding
&Scalar
output_blinding
&Scalar
check_value_is_preserved
bool
rng
&mut CSPRNG