Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Fix] Avoid ejecting the scalar value when loading and storing a circuit. #2534

Draft
wants to merge 5 commits into
base: staging
Choose a base branch
from

Conversation

d0cd
Copy link
Contributor

@d0cd d0cd commented Aug 23, 2024

This PR avoids ejecting the scalar value when loading and storing a circuit.

Failing Case

This issue was found in a deployed program.

Suppose that we have a program cast operation from a field to a scalar.
Suppose that r0 is a field element that is larger than the scalar modulus.

function foo:
    input r0 as field.private;
    cast r0 into r1 as scalar;

When the cast instruction is executed, constraints are added to check that the casted scalar value is correct.
During deployment, these constraints are not checked for satisfaction (by design), and an invalid scalar element is stored into the registers.
store_circuit ejects the stored value and checks for type equality.
However, the implementation of Eject for Scalar requires that the element is a valid a scalar and panics otherwise.

Solution

The eject is done to check that the type of the stored value is correct.
To avoid this failing case, we directly check for type equality against the circuit value.

Testing

This PR includes an integration test that enumerates all LiteralType combinations of cast and cast.lossy and tests them in a program context.

CI is running in this branch.

Considerations

We considered alternate solutions including:

  • restricting the cast operation on scalars
  • ensuring that Scalar::eject does not fail
    however, these solutions would either require a migration or impact a large portion of the codebase.

@@ -169,6 +169,25 @@ pub struct CastOperation<N: Network, const VARIANT: u8> {
}

impl<N: Network, const VARIANT: u8> CastOperation<N, VARIANT> {
/// Initializes a new `cast` instruction.
#[inline]
pub fn new(operands: Vec<Operand<N>>, destination: Register<N>, cast_type: CastType<N>) -> Result<Self> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this new method only use for testing purposes?

If so, I would advise as an independent reviewer that this method be denoted as CastOperation::new_testing_only with a #[cfg(test)] guard on it.

// We do a special check for scalar values, as there is a possibility of an overflow via
// field to scalar conversion in deployment verification.
match register_type_is_scalar && circuit_value_is_scalar {
true => {}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment above states that We do a special check for scalar values however there is no check here on L174 for the scalar case. Can you clarify what exactly should be checked here?

// We do a special check for scalar values, as there is a possibility of an overflow via
// field to scalar conversion in deployment verification.
match register_type_is_scalar && circuit_value_is_scalar {
true => {}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment above states that We do a special check for scalar values however there is no check here on L107 for the scalar case. Can you clarify what exactly should be checked here?

match register_type_is_scalar && circuit_value_is_scalar {
true => {}
false => {
stack.matches_register_type(&circuit::Eject::eject_value(&circuit_value), &register_type)?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about other types that might have scalars internally?

e.g. Structs that have scalar elements or arrays of scalars?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants