snarkvm-circuit-program 0.16.12

Program circuit library for a decentralized virtual machine
Documentation
// Copyright (C) 2019-2023 Aleo Systems Inc.
// This file is part of the snarkVM library.

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
// http://www.apache.org/licenses/LICENSE-2.0

// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use super::*;

impl<E: Environment> CastLossy<Address<E>> for Field<E> {
    /// Casts a `Field` to an `Address`.
    ///
    /// This operation attempts to recover the group element from the given field,
    /// which is then used to construct the address. See the documentation of `Field::cast_lossy`
    /// on the `Group` type for more details.
    #[inline]
    fn cast_lossy(&self) -> Address<E> {
        // Perform a lossy cast to a group element.
        let group: Group<E> = self.cast_lossy();
        // Convert the group element to an address.
        Address::from_group(group)
    }
}

impl<E: Environment> CastLossy<Boolean<E>> for Field<E> {
    /// Casts a `Field` to a `Boolean`, with lossy truncation.
    /// This operation returns the least significant bit of the field.
    #[inline]
    fn cast_lossy(&self) -> Boolean<E> {
        let bits_le = self.to_bits_le();
        debug_assert!(!bits_le.is_empty(), "An integer must have at least one bit");
        bits_le[0].clone()
    }
}

impl<E: Environment> CastLossy<Field<E>> for Field<E> {
    /// Casts a `Field` to a `Field`.
    /// This is an identity cast, so it is **always** lossless.
    #[inline]
    fn cast_lossy(&self) -> Field<E> {
        self.clone()
    }
}

impl<E: Environment> CastLossy<Group<E>> for Field<E> {
    /// Casts a `Field` to a `Group`.
    ///
    /// This operation attempts to recover the group element from the given field.
    ///
    /// If the field is a valid x-coordinate, then the group element is returned.
    /// If the field is not a valid x-coordinate, then if the field is the one element,
    /// the generator of the prime-order subgroup is returned.
    /// Otherwise, Elligator-2 is applied to the field element to recover a group element.
    #[inline]
    fn cast_lossy(&self) -> Group<E> {
        // This method requires that an `x-coordinate` of 1 is an invalid group element.
        // This is used by the ternary below, which uses 'is_one' to determine whether to return the generator.
        debug_assert!(console::Group::from_x_coordinate(<console::Field<E::Network> as console::One>::one()).is_err());

        // Determine if the field element is zero.
        let is_x_zero = self.is_zero();
        // Determine if the field element is one.
        let is_x_one = self.is_one();

        // Initialize the group generator.
        let generator = Group::generator();

        // Determine the input to Elligator-2, based on the x-coordinate.
        let elligator_input = Field::ternary(&is_x_zero, &Field::one(), self);
        // Perform Elligator-2 on the field element, to recover a group element.
        let elligator = Elligator2::encode(&elligator_input);

        // Determine the initial x-coordinate, if the given field element is one.
        let initial_x = Field::ternary(&is_x_one, &generator.to_x_coordinate(), &elligator.to_x_coordinate());
        // Determine the initial y-coordinate, if the given field element is one.
        let initial_y = Field::ternary(&is_x_one, &generator.to_y_coordinate(), &elligator.to_y_coordinate());

        // Determine the y-coordinate, if the x-coordinate is valid.
        let possible_y: Field<E> = {
            // Set the x-coordinate.
            let x = self.clone();
            // Derive the y-coordinate.
            witness!(|x| match console::Group::from_x_coordinate(x) {
                Ok(point) => point.to_y_coordinate(),
                Err(_) => console::Zero::zero(),
            })
        };
        // Determine if the recovered y-coordinate is zero.
        let is_y_zero = possible_y.is_zero();

        // Determine the final x-coordinate, based on whether the possible y-coordinate is zero.
        let final_x = Field::ternary(&is_y_zero, &initial_x, self);
        // Determine the final y-coordinate, based on whether the possible y-coordinate is zero.
        let final_y = Field::ternary(&is_y_zero, &initial_y, &possible_y);

        // Return the result.
        Group::from_xy_coordinates(final_x, final_y)
    }
}

impl<E: Environment, I: IntegerType> CastLossy<Integer<E, I>> for Field<E> {
    /// Casts a `Field` to an `Integer`, with lossy truncation.
    /// This operation truncates the field to an integer.
    #[inline]
    fn cast_lossy(&self) -> Integer<E, I> {
        Integer::from_field_lossy(self)
    }
}

impl<E: Environment> CastLossy<Scalar<E>> for Field<E> {
    /// Casts a `Field` to a `Scalar`, with lossy truncation.
    /// This operation truncates the field to a scalar.
    #[inline]
    fn cast_lossy(&self) -> Scalar<E> {
        Scalar::from_field_lossy(self)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use console::CastLossy as _;
    use console_root::{
        network::Testnet3,
        prelude::{One, TestRng, Uniform, Zero},
    };
    use snarkvm_circuit_types::environment::{count_is, count_less_than, Circuit, Eject, Inject, Mode, UpdatableCount};

    use std::fmt::Debug;

    const ITERATIONS: usize = 100;

    fn sample_values(
        i: usize,
        mode: Mode,
        rng: &mut TestRng,
    ) -> (console_root::types::Field<Testnet3>, Field<Circuit>) {
        let console_value = match i {
            0 => console_root::types::Field::<Testnet3>::zero(),
            1 => console_root::types::Field::<Testnet3>::one(),
            _ => Uniform::rand(rng),
        };
        let circuit_value = Field::<Circuit>::new(mode, console_value);
        (console_value, circuit_value)
    }

    check_cast_lossy!(cast_lossy, Field<Circuit>, console_root::types::Field::<Testnet3>);

    #[test]
    fn test_field_to_address() {
        check_cast_lossy::<Address<Circuit>, console_root::types::Address<Testnet3>>(
            Mode::Constant,
            count_less_than!(551, 0, 0, 0),
        );
        check_cast_lossy::<Address<Circuit>, console_root::types::Address<Testnet3>>(
            Mode::Public,
            count_is!(277, 0, 899, 904),
        );
        check_cast_lossy::<Address<Circuit>, console_root::types::Address<Testnet3>>(
            Mode::Private,
            count_is!(277, 0, 899, 904),
        );
    }

    #[test]
    fn test_field_to_boolean() {
        check_cast_lossy::<Boolean<Circuit>, console_root::types::Boolean<Testnet3>>(
            Mode::Constant,
            count_is!(253, 0, 0, 0),
        );
        check_cast_lossy::<Boolean<Circuit>, console_root::types::Boolean<Testnet3>>(
            Mode::Public,
            count_is!(0, 0, 505, 507),
        );
        check_cast_lossy::<Boolean<Circuit>, console_root::types::Boolean<Testnet3>>(
            Mode::Private,
            count_is!(0, 0, 505, 507),
        );
    }

    #[test]
    fn test_field_to_field() {
        check_cast_lossy::<Field<Circuit>, console_root::types::Field<Testnet3>>(Mode::Constant, count_is!(0, 0, 0, 0));
        check_cast_lossy::<Field<Circuit>, console_root::types::Field<Testnet3>>(Mode::Public, count_is!(0, 0, 0, 0));
        check_cast_lossy::<Field<Circuit>, console_root::types::Field<Testnet3>>(Mode::Private, count_is!(0, 0, 0, 0));
    }

    #[test]
    fn test_field_to_group() {
        check_cast_lossy::<Group<Circuit>, console_root::types::Group<Testnet3>>(
            Mode::Constant,
            count_less_than!(551, 0, 0, 0),
        );
        check_cast_lossy::<Group<Circuit>, console_root::types::Group<Testnet3>>(
            Mode::Public,
            count_is!(277, 0, 899, 904),
        );
        check_cast_lossy::<Group<Circuit>, console_root::types::Group<Testnet3>>(
            Mode::Private,
            count_is!(277, 0, 899, 904),
        );
    }

    #[test]
    fn test_field_to_i8() {
        check_cast_lossy::<I8<Circuit>, console_root::types::I8<Testnet3>>(Mode::Constant, count_is!(253, 0, 0, 0));
        check_cast_lossy::<I8<Circuit>, console_root::types::I8<Testnet3>>(Mode::Public, count_is!(0, 0, 505, 507));
        check_cast_lossy::<I8<Circuit>, console_root::types::I8<Testnet3>>(Mode::Private, count_is!(0, 0, 505, 507));
    }

    #[test]
    fn test_field_to_i16() {
        check_cast_lossy::<I16<Circuit>, console_root::types::I16<Testnet3>>(Mode::Constant, count_is!(253, 0, 0, 0));
        check_cast_lossy::<I16<Circuit>, console_root::types::I16<Testnet3>>(Mode::Public, count_is!(0, 0, 505, 507));
        check_cast_lossy::<I16<Circuit>, console_root::types::I16<Testnet3>>(Mode::Private, count_is!(0, 0, 505, 507));
    }

    #[test]
    fn test_field_to_i32() {
        check_cast_lossy::<I32<Circuit>, console_root::types::I32<Testnet3>>(Mode::Constant, count_is!(253, 0, 0, 0));
        check_cast_lossy::<I32<Circuit>, console_root::types::I32<Testnet3>>(Mode::Public, count_is!(0, 0, 505, 507));
        check_cast_lossy::<I32<Circuit>, console_root::types::I32<Testnet3>>(Mode::Private, count_is!(0, 0, 505, 507));
    }

    #[test]
    fn test_field_to_i64() {
        check_cast_lossy::<I64<Circuit>, console_root::types::I64<Testnet3>>(Mode::Constant, count_is!(253, 0, 0, 0));
        check_cast_lossy::<I64<Circuit>, console_root::types::I64<Testnet3>>(Mode::Public, count_is!(0, 0, 505, 507));
        check_cast_lossy::<I64<Circuit>, console_root::types::I64<Testnet3>>(Mode::Private, count_is!(0, 0, 505, 507));
    }

    #[test]
    fn test_field_to_i128() {
        check_cast_lossy::<I128<Circuit>, console_root::types::I128<Testnet3>>(Mode::Constant, count_is!(253, 0, 0, 0));
        check_cast_lossy::<I128<Circuit>, console_root::types::I128<Testnet3>>(Mode::Public, count_is!(0, 0, 505, 507));
        check_cast_lossy::<I128<Circuit>, console_root::types::I128<Testnet3>>(
            Mode::Private,
            count_is!(0, 0, 505, 507),
        );
    }

    #[test]
    fn test_field_to_scalar() {
        check_cast_lossy::<Scalar<Circuit>, console_root::types::Scalar<Testnet3>>(
            Mode::Constant,
            count_is!(253, 0, 0, 0),
        );
        check_cast_lossy::<Scalar<Circuit>, console_root::types::Scalar<Testnet3>>(
            Mode::Public,
            count_is!(0, 0, 505, 507),
        );
        check_cast_lossy::<Scalar<Circuit>, console_root::types::Scalar<Testnet3>>(
            Mode::Private,
            count_is!(0, 0, 505, 507),
        );
    }

    #[test]
    fn test_field_to_u8() {
        check_cast_lossy::<U8<Circuit>, console_root::types::U8<Testnet3>>(Mode::Constant, count_is!(253, 0, 0, 0));
        check_cast_lossy::<U8<Circuit>, console_root::types::U8<Testnet3>>(Mode::Public, count_is!(0, 0, 505, 507));
        check_cast_lossy::<U8<Circuit>, console_root::types::U8<Testnet3>>(Mode::Private, count_is!(0, 0, 505, 507));
    }

    #[test]
    fn test_field_to_u16() {
        check_cast_lossy::<U16<Circuit>, console_root::types::U16<Testnet3>>(Mode::Constant, count_is!(253, 0, 0, 0));
        check_cast_lossy::<U16<Circuit>, console_root::types::U16<Testnet3>>(Mode::Public, count_is!(0, 0, 505, 507));
        check_cast_lossy::<U16<Circuit>, console_root::types::U16<Testnet3>>(Mode::Private, count_is!(0, 0, 505, 507));
    }

    #[test]
    fn test_field_to_u32() {
        check_cast_lossy::<U32<Circuit>, console_root::types::U32<Testnet3>>(Mode::Constant, count_is!(253, 0, 0, 0));
        check_cast_lossy::<U32<Circuit>, console_root::types::U32<Testnet3>>(Mode::Public, count_is!(0, 0, 505, 507));
        check_cast_lossy::<U32<Circuit>, console_root::types::U32<Testnet3>>(Mode::Private, count_is!(0, 0, 505, 507));
    }

    #[test]
    fn test_field_to_u64() {
        check_cast_lossy::<U64<Circuit>, console_root::types::U64<Testnet3>>(Mode::Constant, count_is!(253, 0, 0, 0));
        check_cast_lossy::<U64<Circuit>, console_root::types::U64<Testnet3>>(Mode::Public, count_is!(0, 0, 505, 507));
        check_cast_lossy::<U64<Circuit>, console_root::types::U64<Testnet3>>(Mode::Private, count_is!(0, 0, 505, 507));
    }

    #[test]
    fn test_field_to_u128() {
        check_cast_lossy::<U128<Circuit>, console_root::types::U128<Testnet3>>(Mode::Constant, count_is!(253, 0, 0, 0));
        check_cast_lossy::<U128<Circuit>, console_root::types::U128<Testnet3>>(Mode::Public, count_is!(0, 0, 505, 507));
        check_cast_lossy::<U128<Circuit>, console_root::types::U128<Testnet3>>(
            Mode::Private,
            count_is!(0, 0, 505, 507),
        );
    }
}