Skip to content

Commit

Permalink
Arrabiata/Gadget: implement elliptic curve scaling
Browse files Browse the repository at this point in the history
  • Loading branch information
dannywillems committed Jul 26, 2024
1 parent 0949eec commit a8f628b
Show file tree
Hide file tree
Showing 5 changed files with 161 additions and 1 deletion.
45 changes: 45 additions & 0 deletions arrabiata/src/constraints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,51 @@ impl<Fp: PrimeField> InterpreterEnv for Env<Fp> {
self.constant(BigInt::from(1_usize))
}

/// Double the elliptic curve point given by the affine coordinates
/// `(x1, y1)` and save the result in the registers `pos_x` and `pos_y`.
fn double_ec_point(
&mut self,
pos_x: Self::Position,
pos_y: Self::Position,
x1: Self::Variable,
y1: Self::Variable,
) -> (Self::Variable, Self::Variable) {
let lambda = {
let pos = self.allocate();
Expr::Atom(ExprInner::Cell(Variable {
col: pos,
row: CurrOrNext::Curr,
}))
};
let x3 = Expr::Atom(ExprInner::Cell(Variable {
col: pos_x,
row: CurrOrNext::Curr,
}));
let y3 = Expr::Atom(ExprInner::Cell(Variable {
col: pos_y,
row: CurrOrNext::Curr,
}));

// λ 2y1 = 3x1^2 + a
let x1_square = x1.clone() * x1.clone();
let two_x1_square = x1_square.clone() + x1_square.clone();
let three_x1_square = two_x1_square.clone() + x1_square.clone();
let two_y1 = y1.clone() + y1.clone();
let res = lambda.clone() * two_y1 - (three_x1_square + self.constant(self.a.clone()));
self.assert_zero(res);
// x3 = λ^2 - 2 x1
self.assert_equal(
x3.clone(),
lambda.clone() * lambda.clone() - x1.clone() - x1.clone(),
);
// y3 = λ(x1 - x3) - y1
self.assert_equal(
y3.clone(),
lambda.clone() * (x1.clone() - x3.clone()) - y1.clone(),
);
(x3, y3.clone())
}

fn compute_lambda(
&mut self,
pos: Self::Position,
Expand Down
17 changes: 16 additions & 1 deletion arrabiata/src/interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,16 @@ pub trait InterpreterEnv {
y2: Self::Variable,
) -> Self::Variable;

/// Double the elliptic curve point given by the affine coordinates
/// `(x1, y1)` and save the result in the registers `pos_x` and `pos_y`.
fn double_ec_point(
&mut self,
pos_x: Self::Position,
pos_y: Self::Position,
x1: Self::Variable,
y1: Self::Variable,
) -> (Self::Variable, Self::Variable);

/// Load the affine coordinates of the elliptic curve point currently saved
/// in the temporary accumulators. Temporary accumulators could be seen as
/// a CPU cache, an intermediate storage between the RAM (random access
Expand Down Expand Up @@ -479,7 +489,7 @@ pub fn run_ivc<E: InterpreterEnv>(env: &mut E, instr: Instruction) {
let pos = env.allocate();
unsafe { env.read_bit_of_folding_combiner(pos, processing_bit) }
};
let (_tmp_x, _tmp_y) = {
let (tmp_x, tmp_y) = {
let pos_x = env.allocate();
let pos_y = env.allocate();
unsafe { env.load_temporary_accumulators(pos_x, pos_y, ECAdditionSide::Left) }
Expand All @@ -489,6 +499,11 @@ pub fn run_ivc<E: InterpreterEnv>(env: &mut E, instr: Instruction) {
let pos_y = env.allocate();
unsafe { env.load_temporary_accumulators(pos_x, pos_y, ECAdditionSide::Right) }
};
let (_tmp_prime_x, _tmp_prime_y) = {
let pos_x = env.allocate();
let pos_y = env.allocate();
env.double_ec_point(pos_x, pos_y, tmp_x, tmp_y)
};
}
Instruction::EllipticCurveAddition(i_comm) => {
let (x1, y1) = {
Expand Down
50 changes: 50 additions & 0 deletions arrabiata/src/witness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -606,6 +606,56 @@ where
let res = (num * denom).mod_floor(&modulus);
self.write_column(pos, res)
}

/// Double the elliptic curve point given by the affine coordinates
/// `(x1, y1)` and save the result in the registers `pos_x` and `pos_y`.
fn double_ec_point(
&mut self,
pos_x: Self::Position,
pos_y: Self::Position,
x1: Self::Variable,
y1: Self::Variable,
) -> (Self::Variable, Self::Variable) {
let modulus: BigInt = if self.current_iteration % 2 == 0 {
Fp::modulus_biguint().into()
} else {
Fq::modulus_biguint().into()
};
// - λ = (3X1^2 + a) / (2Y1)
// We compute λ and use an additional column as a temporary value
// otherwise, we get a constraint of degree higher than 5
let lambda_pos = self.allocate();
let denom = {
let double_y1 = y1.clone() + y1.clone();
// We temporarily store the inverse of the denominator into the
// given position.
unsafe { self.inverse(lambda_pos, double_y1) }
};
let num = {
let a: BigInt = if self.current_iteration % 2 == 0 {
(E2::Params::COEFF_A).to_biguint().into()
} else {
(E1::Params::COEFF_A).to_biguint().into()
};
let x1_square = x1.clone() * x1.clone();
let two_x1_square = x1_square.clone() + x1_square.clone();
two_x1_square + x1_square + a
};
let lambda = (num * denom).mod_floor(&modulus);
self.write_column(lambda_pos, lambda.clone());
// - X3 = λ^2 - X1 - X2
let x3 = {
let double_x1 = x1.clone() + x1.clone();
let lambda_square = lambda.clone() * lambda.clone() - double_x1.clone();
self.write_column(pos_x, lambda_square.clone())
};
let y3 = {
let x1_minus_x3 = x1.clone() - x3.clone();
let res = lambda.clone() * x1_minus_x3 - y1.clone();
self.write_column(pos_y, res.clone())
};
(x3, y3)
}
}

impl<
Expand Down
10 changes: 10 additions & 0 deletions arrabiata/tests/constraints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,3 +144,13 @@ fn test_degree_of_constraints_ivc() {
assert_eq!(degree_per_constraints.get(&4), None);
assert_eq!(degree_per_constraints.get(&5), Some(&12));
}

#[test]
fn test_gadget_elliptic_curve_scaling() {
let instr = Instruction::EllipticCurveScaling(0, 0);
helper_compute_constraints_gadget(instr, 3);

let mut exp_degrees = HashMap::new();
exp_degrees.insert(2, 3);
helper_check_expected_degree_constraints(instr, exp_degrees);
}
40 changes: 40 additions & 0 deletions arrabiata/tests/witness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,3 +153,43 @@ fn test_unit_witness_elliptic_curve_addition() {
assert_eq!(exp_x3, env.state[6], "The x coordinate is incorrect");
assert_eq!(exp_y3, env.state[7], "The y coordinate is incorrect");
}

#[test]
fn test_witness_double_elliptic_curve_point() {
let mut rng = o1_utils::tests::make_test_rng(None);
let srs_log2_size = 6;
let sponge_e1: [BigInt; POSEIDON_STATE_SIZE] = std::array::from_fn(|_i| BigInt::from(42u64));
let mut env = Env::<Fp, Fq, Vesta, Pallas>::new(
srs_log2_size,
BigInt::from(1u64),
sponge_e1.clone(),
sponge_e1.clone(),
);

// Generate a random point
let (p1_x, p1_y, p1): (BigInt, BigInt, Pallas) = {
let p1_x = Fp::rand(&mut rng);
let mut p1 = Pallas::get_point_from_x(p1_x, false);
while p1.is_none() {
let p1_x = Fp::rand(&mut rng);
p1 = Pallas::get_point_from_x(p1_x, false);
}
let p1 = p1.unwrap();
let p1_y = p1.to_coordinates().unwrap().1;
(p1_x.to_biguint().into(), p1_y.to_biguint().into(), p1)
};

// Doubling in the environment
let pos_x = env.allocate();
let pos_y = env.allocate();
let p1_x = env.write_column(pos_x, p1_x);
let p1_y = env.write_column(pos_y, p1_y);
let (res_x, res_y) = env.double_ec_point(pos_x, pos_y, p1_x, p1_y);

let exp_res = p1 + p1;
let exp_x: BigInt = exp_res.to_coordinates().unwrap().0.to_biguint().into();
let exp_y: BigInt = exp_res.to_coordinates().unwrap().1.to_biguint().into();
println!("State: {:?}", env.state);
assert_eq!(res_x, exp_x, "The x coordinate is incorrect");
assert_eq!(res_y, exp_y, "The y coordinate is incorrect");
}

0 comments on commit a8f628b

Please sign in to comment.