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

feat: add checks on input values in ec operations #963

Merged
merged 2 commits into from
Sep 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 3 additions & 5 deletions crates/evm/src/precompiles.cairo
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
mod blake2f;

mod ec_add;

mod ec_mul;
mod ec_operations;

mod ec_recover;

Expand All @@ -15,8 +13,8 @@ mod p256verify;
mod sha256;

pub use blake2f::Blake2f;
pub use ec_add::EcAdd;
pub use ec_mul::EcMul;
pub use ec_operations::ec_add::EcAdd;
pub use ec_operations::ec_mul::EcMul;
pub use ec_recover::EcRecover;
pub use identity::Identity;
pub use modexp::ModExp;
Expand Down
107 changes: 107 additions & 0 deletions crates/evm/src/precompiles/ec_operations.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
pub(crate) mod ec_add;
pub(crate) mod ec_mul;
use core::circuit::CircuitElement as CE;
use core::circuit::CircuitInput as CI;
use core::circuit::{
u96, u384, CircuitElement, CircuitInput, circuit_add, circuit_sub, circuit_mul, circuit_inverse,
EvalCircuitTrait, CircuitOutputsTrait, CircuitModulus, CircuitInputs
};
use core::num::traits::Zero;
use garaga::core::circuit::AddInputResultTrait2;

const BN254_ORDER: u256 = 0x30644E72E131A029B85045B68181585D2833E84879B9709143E1F593F0000001;
const BN254_PRIME: u256 = 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47;
const BN254_PRIME_LIMBS: [
u96
; 4] = [
0x6871ca8d3c208c16d87cfd47, 0xb85045b68181585d97816a91, 0x30644e72e131a029, 0x0
];

// Check if a point is on the curve.
// Point at infinity (0,0) will return false.
pub fn is_on_curve(x: u384, y: u384) -> bool {
let (b, _x, _y) = (CE::<CI<0>> {}, CE::<CI<1>> {}, CE::<CI<2>> {});

// Compute (y^2 - (x^3 + b)) % p_bn254
let x2 = circuit_mul(_x, _x);
let x3 = circuit_mul(x2, _x);
let y2 = circuit_mul(_y, _y);
let rhs = circuit_add(x3, b);
let check = circuit_sub(y2, rhs);

let modulus = TryInto::<_, CircuitModulus>::try_into(BN254_PRIME_LIMBS)
.unwrap(); // BN254 prime field modulus

let mut circuit_inputs = (check,).new_inputs();
// Prefill constants:
circuit_inputs = circuit_inputs.next_2([3, 0, 0, 0]);
// Fill inputs:
circuit_inputs = circuit_inputs.next_2(x);
circuit_inputs = circuit_inputs.next_2(y);

let outputs = circuit_inputs.done_2().eval(modulus).unwrap();
let zero_check: u384 = outputs.get_output(check);
return zero_check.is_zero();
}


// Double BN254 EC point without checking if the point is on the curve
pub fn double_ec_point_unchecked(x: u384, y: u384) -> (u384, u384) {
// CONSTANT stack
let in0 = CE::<CI<0>> {}; // 0x3
// INPUT stack
let (_x, _y) = (CE::<CI<1>> {}, CE::<CI<2>> {});

let x2 = circuit_mul(_x, _x);
let num = circuit_mul(in0, x2);
let den = circuit_add(_y, _y);
let inv_den = circuit_inverse(den);
let slope = circuit_mul(num, inv_den);
let slope_sqr = circuit_mul(slope, slope);

let nx = circuit_sub(circuit_sub(slope_sqr, _x), _x);
let ny = circuit_sub(circuit_mul(slope, circuit_sub(_x, nx)), _y);

let modulus = TryInto::<_, CircuitModulus>::try_into(BN254_PRIME_LIMBS)
.unwrap(); // BN254 prime field modulus

let mut circuit_inputs = (nx, ny,).new_inputs();
// Prefill constants:
circuit_inputs = circuit_inputs.next_2([0x3, 0x0, 0x0, 0x0]); // in0
// Fill inputs:
circuit_inputs = circuit_inputs.next_2(x); // in1
circuit_inputs = circuit_inputs.next_2(y); // in2

let outputs = circuit_inputs.done_2().eval(modulus).unwrap();

(outputs.get_output(nx), outputs.get_output(ny))
}


// returns true if a == b mod p_bn254
pub fn eq_mod_p(a: u384, b: u384) -> bool {
let in1 = CircuitElement::<CircuitInput<0>> {};
let in2 = CircuitElement::<CircuitInput<1>> {};
let sub = circuit_sub(in1, in2);

let modulus = TryInto::<_, CircuitModulus>::try_into(BN254_PRIME_LIMBS)
.unwrap(); // BN254 prime field modulus

let outputs = (sub,).new_inputs().next_2(a).next_2(b).done_2().eval(modulus).unwrap();

return outputs.get_output(sub).is_zero();
}

// returns true if a == -b mod p_bn254
pub fn eq_neg_mod_p(a: u384, b: u384) -> bool {
let _a = CE::<CI<0>> {};
let _b = CE::<CI<1>> {};
let check = circuit_add(_a, _b);

let modulus = TryInto::<_, CircuitModulus>::try_into(BN254_PRIME_LIMBS)
.unwrap(); // BN254 prime field modulus

let outputs = (check,).new_inputs().next_2(a).next_2(b).done_2().eval(modulus).unwrap();

return outputs.get_output(check).is_zero();
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ use core::circuit::{
use core::num::traits::Zero;
use core::option::Option;
use core::starknet::{EthAddress};
use crate::precompiles::ec_operations::{
eq_mod_p, eq_neg_mod_p, is_on_curve, double_ec_point_unchecked, BN254_ORDER, BN254_PRIME_LIMBS,
BN254_PRIME
};
use evm::errors::EVMError;
use evm::precompiles::Precompile;
use garaga::core::circuit::AddInputResultTrait2;
Expand All @@ -18,12 +22,6 @@ use utils::helpers::{load_word, U8SpanExTrait};

const BASE_COST: u64 = 150;
const U256_BYTES_LEN: usize = 32;
const PRIME: [
u96
; 4] = [
0x6871ca8d3c208c16d87cfd47, 0xb85045b68181585d97816a91, 0x30644e72e131a029, 0x0
];

pub impl EcAdd of Precompile {
#[inline(always)]
fn address() -> EthAddress {
Expand Down Expand Up @@ -69,6 +67,9 @@ pub impl EcAdd of Precompile {


fn ec_add(x1: u256, y1: u256, x2: u256, y2: u256) -> Option<(u256, u256)> {
if x1 >= BN254_PRIME || y1 >= BN254_PRIME || x2 >= BN254_PRIME || y2 >= BN254_PRIME {
return Option::None;
}
if x1 == 0 && y1 == 0 {
if x2 == 0 && y2 == 0 {
// Both are points at infinity, return either of them.
Expand Down Expand Up @@ -145,35 +146,6 @@ pub fn ec_safe_add(x1: u384, y1: u384, x2: u384, y2: u384) -> Option<(u384, u384
}
}


// Check if a point is on the curve.
// Point at infinity (0,0) will return false.
pub fn is_on_curve(x: u384, y: u384) -> bool {
let (b, _x, _y) = (CE::<CI<0>> {}, CE::<CI<1>> {}, CE::<CI<2>> {});

// Compute (y^2 - (x^3 + b)) % p_bn254
let x2 = circuit_mul(_x, _x);
let x3 = circuit_mul(x2, _x);
let y2 = circuit_mul(_y, _y);
let rhs = circuit_add(x3, b);
let check = circuit_sub(y2, rhs);

let modulus = TryInto::<_, CircuitModulus>::try_into(PRIME)
.unwrap(); // BN254 prime field modulus

let mut circuit_inputs = (check,).new_inputs();
// Prefill constants:
circuit_inputs = circuit_inputs.next_2([3, 0, 0, 0]);
// Fill inputs:
circuit_inputs = circuit_inputs.next_2(x);
circuit_inputs = circuit_inputs.next_2(y);

let outputs = circuit_inputs.done_2().eval(modulus).unwrap();
let zero_check: u384 = outputs.get_output(check);
return zero_check.is_zero();
}


// Add two BN254 EC points without checking if:
// - the points are on the curve
// - the points are not the same
Expand All @@ -191,7 +163,7 @@ fn add_ec_point_unchecked(xP: u384, yP: u384, xQ: u384, yQ: u384) -> (u384, u384
let nx = circuit_sub(circuit_sub(slope_sqr, _xP), _xQ);
let ny = circuit_sub(circuit_mul(slope, circuit_sub(_xP, nx)), _yP);

let modulus = TryInto::<_, CircuitModulus>::try_into(PRIME)
let modulus = TryInto::<_, CircuitModulus>::try_into(BN254_PRIME_LIMBS)
.unwrap(); // BN254 prime field modulus

let mut circuit_inputs = (nx, ny,).new_inputs();
Expand All @@ -206,64 +178,6 @@ fn add_ec_point_unchecked(xP: u384, yP: u384, xQ: u384, yQ: u384) -> (u384, u384
(outputs.get_output(nx), outputs.get_output(ny))
}

// Double BN254 EC point without checking if the point is on the curve
pub fn double_ec_point_unchecked(x: u384, y: u384) -> (u384, u384) {
// CONSTANT stack
let in0 = CE::<CI<0>> {}; // 0x3
// INPUT stack
let (_x, _y) = (CE::<CI<1>> {}, CE::<CI<2>> {});

let x2 = circuit_mul(_x, _x);
let num = circuit_mul(in0, x2);
let den = circuit_add(_y, _y);
let inv_den = circuit_inverse(den);
let slope = circuit_mul(num, inv_den);
let slope_sqr = circuit_mul(slope, slope);

let nx = circuit_sub(circuit_sub(slope_sqr, _x), _x);
let ny = circuit_sub(circuit_mul(slope, circuit_sub(_x, nx)), _y);

let modulus = TryInto::<_, CircuitModulus>::try_into(PRIME)
.unwrap(); // BN254 prime field modulus

let mut circuit_inputs = (nx, ny,).new_inputs();
// Prefill constants:
circuit_inputs = circuit_inputs.next_2([0x3, 0x0, 0x0, 0x0]); // in0
// Fill inputs:
circuit_inputs = circuit_inputs.next_2(x); // in1
circuit_inputs = circuit_inputs.next_2(y); // in2

let outputs = circuit_inputs.done_2().eval(modulus).unwrap();

(outputs.get_output(nx), outputs.get_output(ny))
}
// returns true if a == b mod p_bn254
fn eq_mod_p(a: u384, b: u384) -> bool {
let in1 = CircuitElement::<CircuitInput<0>> {};
let in2 = CircuitElement::<CircuitInput<1>> {};
let sub = circuit_sub(in1, in2);

let modulus = TryInto::<_, CircuitModulus>::try_into(PRIME)
.unwrap(); // BN254 prime field modulus

let outputs = (sub,).new_inputs().next_2(a).next_2(b).done_2().eval(modulus).unwrap();

return outputs.get_output(sub).is_zero();
}

// returns true if a == -b mod p_bn254
fn eq_neg_mod_p(a: u384, b: u384) -> bool {
let _a = CE::<CI<0>> {};
let _b = CE::<CI<1>> {};
let check = circuit_add(_a, _b);

let modulus = TryInto::<_, CircuitModulus>::try_into(PRIME)
.unwrap(); // BN254 prime field modulus

let outputs = (check,).new_inputs().next_2(a).next_2(b).done_2().eval(modulus).unwrap();

return outputs.get_output(check).is_zero();
}

#[cfg(test)]
mod tests {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
use core::circuit::u384;
use core::option::Option;
use core::starknet::{EthAddress};
use crate::precompiles::ec_operations::ec_add::ec_safe_add;

use crate::precompiles::ec_operations::{
eq_mod_p, eq_neg_mod_p, is_on_curve, double_ec_point_unchecked, BN254_ORDER, BN254_PRIME_LIMBS,
BN254_PRIME
};
use evm::errors::EVMError;
use evm::precompiles::Precompile;

use evm::precompiles::ec_add::{is_on_curve, double_ec_point_unchecked, ec_safe_add};
use utils::helpers::{load_word, ToBytes, U8SpanExTrait};

// const BN254_ORDER: u256 = 0x30644E72E131A029B85045B68181585D2833E84879B9709143E1F593F0000001;

const BASE_COST: u64 = 6000;
const U256_BYTES_LEN: usize = 32;

Expand Down Expand Up @@ -52,6 +54,9 @@ pub impl EcMul of Precompile {

// Returns Option::None in case of error.
fn ec_mul(x1: u256, y1: u256, s: u256) -> Option<(u256, u256)> {
if x1 >= BN254_PRIME || y1 >= BN254_PRIME {
return Option::None;
}
if x1 == 0 && y1 == 0 {
// Input point is at infinity, return it
return Option::Some((x1, y1));
Expand Down
Loading