Skip to content

Commit

Permalink
Add div by zero error (#1433)
Browse files Browse the repository at this point in the history
* Add div by zero error

* Add ec_recover_prod integration test

* Add unit tests

* Cargo fmt

* Update changelog

* Add integration tests

* Correct error message

* Fix bug add tests

* Remove space

* Run cargo fmt

* Add integration test program

---------

Co-authored-by: juan.mv <[email protected]>
  • Loading branch information
Juan-M-V and juan.mv authored Sep 7, 2023
1 parent 2b11e1d commit 1b8e5c0
Show file tree
Hide file tree
Showing 7 changed files with 361 additions and 2 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

#### Upcoming Changes

* fix: ec_recover hints no longer panic when divisor is 0 [#1433](https://github.com/lambdaclass/cairo-vm/pull/1433)

* feat: Implement the Serialize and Deserialize traits for the CairoPie struct [#1438](https://github.com/lambdaclass/cairo-vm/pull/1438)

* fix: Using UINT256_HINT no longer panics when b is greater than 2^256 [#1430](https://github.com/lambdaclass/cairo-vm/pull/1430)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
struct BigInt3 {
d0: felt,
d1: felt,
d2: felt,
}

func main() {
let x = BigInt3(d0=0, d1=0, d2=1);
let s = BigInt3(d0=0, d1=0, d2=1);
let n = BigInt3(d0=0, d1=0, d2=0);
ec_recover_product(x, s, n);
return();
}

func ec_recover_product(x:BigInt3, s:BigInt3, n:BigInt3) {
%{
from starkware.cairo.common.cairo_secp.secp_utils import pack
from starkware.python.math_utils import div_mod, safe_div
N = pack(ids.n, PRIME)
x = pack(ids.x, PRIME) % N
s = pack(ids.s, PRIME) % N
value = res = div_mod(x, s, N)
%}
return();
}
28 changes: 28 additions & 0 deletions cairo_programs/bad_programs/ec_recover_product_mod_m_zero.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
struct BigInt3 {
d0: felt,
d1: felt,
d2: felt,
}

func main() {
let a = BigInt3(d0=0, d1=0, d2=1);
let b = BigInt3(d0=0, d1=0, d2=1);
let m = BigInt3(d0=0, d1=0, d2=0);
ec_recover_product(a, b, m);
return();
}

func ec_recover_product(a:BigInt3, b:BigInt3, m:BigInt3) {
%{
from starkware.cairo.common.cairo_secp.secp_utils import pack
from starkware.python.math_utils import div_mod, safe_div
a = pack(ids.a, PRIME)
b = pack(ids.b, PRIME)
product = a * b
m = pack(ids.m, PRIME)
value = res = product % m
%}
return();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@

struct MyStruct1 {
d0: felt,
d1: felt,
d2: felt,
d3: felt,
}
struct MyStruct0 {
low: felt,
high: felt,
}
func main() {
let x =MyStruct1(d0=1, d1=1, d2=1, d3=1);
let div = MyStruct0(low=0, high=0);
hint_func(x, div);
return();
}

func hint_func(x: MyStruct1, div: MyStruct0) {
alloc_locals;
local quotient: MyStruct1;
local remainder: MyStruct0;

%{
def split(num: int, num_bits_shift: int, length: int):
a = []
for _ in range(length):
a.append( num & ((1 << num_bits_shift) - 1) )
num = num >> num_bits_shift
return tuple(a)

def pack(z, num_bits_shift: int) -> int:
limbs = (z.low, z.high)
return sum(limb << (num_bits_shift * i) for i, limb in enumerate(limbs))

def pack_extended(z, num_bits_shift: int) -> int:
limbs = (z.d0, z.d1, z.d2, z.d3)
return sum(limb << (num_bits_shift * i) for i, limb in enumerate(limbs))

x = pack_extended(ids.x, num_bits_shift = 128)
div = pack(ids.div, num_bits_shift = 128)

quotient, remainder = divmod(x, div)

quotient_split = split(quotient, num_bits_shift=128, length=4)

ids.quotient.d0 = quotient_split[0]
ids.quotient.d1 = quotient_split[1]
ids.quotient.d2 = quotient_split[2]
ids.quotient.d3 = quotient_split[3]

remainder_split = split(remainder, num_bits_shift=128, length=2)
ids.remainder.low = remainder_split[0]
ids.remainder.high = remainder_split[1]
%}
return();
}
104 changes: 103 additions & 1 deletion vm/src/hint_processor/builtin_hint_processor/ec_recover.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ use crate::{
hint_processor::hint_processor_definition::HintReference,
math_utils::div_mod,
serde::deserialize_program::ApTracking,
types::exec_scope::ExecutionScopes,
types::{errors::math_errors::MathError, exec_scope::ExecutionScopes},
vm::{errors::hint_errors::HintError, vm_core::VirtualMachine},
};
use num_bigint::BigInt;
use num_traits::Zero;

/* Implements Hint:
%{
Expand All @@ -29,6 +30,9 @@ pub fn ec_recover_divmod_n_packed(
ap_tracking: &ApTracking,
) -> Result<(), HintError> {
let n = BigInt3::from_var_name("n", vm, ids_data, ap_tracking)?.pack86();
if n.is_zero() {
return Err(MathError::DividedByZero.into());
}
let x = BigInt3::from_var_name("x", vm, ids_data, ap_tracking)?
.pack86()
.mod_floor(&n);
Expand Down Expand Up @@ -89,6 +93,9 @@ pub fn ec_recover_product_mod(
let a = BigInt3::from_var_name("a", vm, ids_data, ap_tracking)?.pack86();
let b = BigInt3::from_var_name("b", vm, ids_data, ap_tracking)?.pack86();
let m = BigInt3::from_var_name("m", vm, ids_data, ap_tracking)?.pack86();
if m.is_zero() {
return Err(MathError::DividedByZero.into());
}

let product = a * b;
let value = product.mod_floor(&m);
Expand All @@ -107,6 +114,9 @@ pub fn ec_recover_product_mod(
pub fn ec_recover_product_div_m(exec_scopes: &mut ExecutionScopes) -> Result<(), HintError> {
let product: &BigInt = exec_scopes.get_ref("product")?;
let m: &BigInt = exec_scopes.get_ref("m")?;
if m.is_zero() {
return Err(MathError::DividedByZero.into());
}
let value = product.div_floor(m);
exec_scopes.insert_value("k", value.clone());
exec_scopes.insert_value("value", value);
Expand All @@ -132,6 +142,7 @@ mod tests {
},
types::exec_scope::ExecutionScopes,
};
use assert_matches::assert_matches;

#[cfg(target_arch = "wasm32")]
use wasm_bindgen_test::*;
Expand Down Expand Up @@ -174,6 +185,41 @@ mod tests {
);
}

#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn run_ec_recover_divmod_n_is_zero() {
let mut vm = vm!();
let mut exec_scopes = ExecutionScopes::new();

vm.run_context.fp = 8;
let ids_data = non_continuous_ids_data![("n", -8), ("x", -5), ("s", -2)];

vm.segments = segments![
//n
((1, 0), 0),
((1, 1), 0),
((1, 2), 0),
//x
((1, 3), 25),
((1, 4), 0),
((1, 5), 0),
//s
((1, 6), 5),
((1, 7), 0),
((1, 8), 0)
];

assert_matches!(
run_hint!(
vm,
ids_data,
hint_code::EC_RECOVER_DIV_MOD_N_PACKED,
&mut exec_scopes
),
Err(HintError::Math(MathError::DividedByZero))
);
}

#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn run_ec_recover_sub_a_b_ok() {
Expand Down Expand Up @@ -251,6 +297,41 @@ mod tests {
);
}

#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn run_ec_recover_product_mod_m_zero() {
let mut vm = vm!();
let mut exec_scopes = ExecutionScopes::new();

vm.run_context.fp = 8;
let ids_data = non_continuous_ids_data![("a", -8), ("b", -5), ("m", -2)];

vm.segments = segments![
//a
((1, 0), 60),
((1, 1), 0),
((1, 2), 0),
//b
((1, 3), 2),
((1, 4), 0),
((1, 5), 0),
//m
((1, 6), 0),
((1, 7), 0),
((1, 8), 0)
];

assert_matches!(
run_hint!(
vm,
ids_data,
hint_code::EC_RECOVER_PRODUCT_MOD,
&mut exec_scopes
),
Err(HintError::Math(MathError::DividedByZero))
);
}

#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn run_ec_recover_product_div_m_ok() {
Expand All @@ -274,4 +355,25 @@ mod tests {
[("value", BigInt::from(2)), ("k", BigInt::from(2))]
);
}

#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn run_ec_recover_product_div_m_zero() {
let mut vm = vm!();
let mut exec_scopes = ExecutionScopes::new();
exec_scopes.insert_value("product", BigInt::from(250));
exec_scopes.insert_value("m", BigInt::from(0));

let ids_data = ids_data!["none"];

assert_matches!(
run_hint!(
vm,
ids_data,
hint_code::EC_RECOVER_PRODUCT_DIV_M,
&mut exec_scopes
),
Err(HintError::Math(MathError::DividedByZero))
);
}
}
29 changes: 28 additions & 1 deletion vm/src/hint_processor/builtin_hint_processor/vrf/fq.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@ use crate::{
math_utils::div_mod,
serde::deserialize_program::ApTracking,
stdlib::{collections::HashMap, prelude::*},
types::errors::math_errors::MathError,
vm::{errors::hint_errors::HintError, vm_core::VirtualMachine},
};
use num_bigint::{BigInt, ToBigInt};
use num_integer::div_rem;
use num_traits::One;
use num_traits::{One, Zero};

/// Implements hint:
/// ```python
Expand Down Expand Up @@ -58,6 +59,9 @@ pub fn uint512_unsigned_div_rem(

// Main logic:
// quotient, remainder = divmod(x, div)
if div.is_zero() {
return Err(MathError::DividedByZero.into());
}
let (quotient, remainder) = div_rem(x, div);

Uint512::from(&quotient).insert_from_var_name("quotient", vm, ids_data, ap_tracking)?;
Expand Down Expand Up @@ -161,6 +165,29 @@ mod tests {
];
}

#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn test_uint512_unsigned_div_rem_div_is_zero() {
let hint_code = hint_code::UINT512_UNSIGNED_DIV_REM;
let mut vm = vm_with_range_check!();

vm.segments = segments![
((1, 0), 2363463),
((1, 1), 566795),
((1, 2), 8760799),
((1, 3), 62362634),
((1, 4), 0),
((1, 5), 0)
];
// Create hint_data
let ids_data =
non_continuous_ids_data![("x", 0), ("div", 4), ("quotient", 6), ("remainder", 10)];
assert_matches!(
run_hint!(vm, ids_data, hint_code, exec_scopes_ref!()),
Err(HintError::Math(MathError::DividedByZero))
);
}

#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn test_inv_mod_p_uint256_ok() {
Expand Down
Loading

1 comment on commit 1b8e5c0

@github-actions
Copy link

Choose a reason for hiding this comment

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

⚠️ Performance Alert ⚠️

Possible performance regression was detected for benchmark.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold 1.30.

Benchmark suite Current: 1b8e5c0 Previous: 2b11e1d Ratio
add_u64_with_felt/1 4 ns/iter (± 0) 2 ns/iter (± 0) 2
add_u64_with_felt/2 4 ns/iter (± 0) 2 ns/iter (± 0) 2
add_u64_with_felt/3 2 ns/iter (± 0) 1 ns/iter (± 0) 2
add_u64_with_felt/4 2 ns/iter (± 0) 1 ns/iter (± 0) 2
add_u64_with_felt/5 2 ns/iter (± 0) 1 ns/iter (± 0) 2
add_u64_with_felt/6 4 ns/iter (± 0) 2 ns/iter (± 0) 2
add_u64_with_felt/7 4 ns/iter (± 0) 2 ns/iter (± 0) 2
add_u64_with_felt/8 3 ns/iter (± 0) 2 ns/iter (± 0) 1.50

This comment was automatically generated by workflow using github-action-benchmark.

CC: @unbalancedparentheses

Please sign in to comment.