Skip to content

Commit 8f4672e

Browse files
Added Twisted Edwards chip
1 parent 22c297e commit 8f4672e

File tree

21 files changed

+1670
-287
lines changed

21 files changed

+1670
-287
lines changed

Cargo.lock

Lines changed: 258 additions & 173 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ members = [
5959
"extensions/ecc/transpiler",
6060
"extensions/ecc/guest",
6161
"extensions/ecc/sw-setup",
62+
"extensions/ecc/te-setup",
6263
"extensions/ecc/tests",
6364
"extensions/pairing/circuit",
6465
"extensions/pairing/transpiler",
@@ -159,6 +160,7 @@ openvm-ecc-circuit = { path = "extensions/ecc/circuit", default-features = false
159160
openvm-ecc-transpiler = { path = "extensions/ecc/transpiler", default-features = false }
160161
openvm-ecc-guest = { path = "extensions/ecc/guest", default-features = false }
161162
openvm-ecc-sw-setup = { path = "extensions/ecc/sw-setup", default-features = false }
163+
openvm-ecc-te-setup = { path = "extensions/ecc/te-setup", default-features = false }
162164
openvm-pairing-circuit = { path = "extensions/pairing/circuit", default-features = false }
163165
openvm-pairing-transpiler = { path = "extensions/pairing/transpiler", default-features = false }
164166
openvm-pairing-guest = { path = "extensions/pairing/guest", default-features = false }
@@ -248,6 +250,7 @@ k256 = { version = "0.13.3", default-features = false }
248250
elliptic-curve = { version = "0.13.8", default-features = false }
249251
ecdsa = { version = "0.16.9", default-features = false }
250252
num-bigint = { version = "0.4.6", default-features = false }
253+
num-bigint-dig = { version = "0.8.4", default-features = false }
251254
num-integer = { version = "0.1.46", default-features = false }
252255
num-traits = { version = "0.2.19", default-features = false }
253256
ff = { version = "0.13.0", default-features = false }

extensions/ecc/circuit/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ openvm-rv32-adapters = { workspace = true }
2121
openvm-ecc-transpiler = { workspace = true }
2222

2323
num-bigint = { workspace = true }
24+
num-bigint-dig = { workspace = true }
2425
num-traits = { workspace = true }
2526
strum = { workspace = true }
2627
derive_more = { workspace = true }
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Twisted Edwards (te) curve operations
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
use std::{cell::RefCell, rc::Rc};
2+
3+
use num_bigint::BigUint;
4+
use num_traits::One;
5+
use openvm_circuit_primitives::var_range::VariableRangeCheckerBus;
6+
use openvm_mod_circuit_builder::{ExprBuilder, ExprBuilderConfig, FieldExpr};
7+
8+
pub fn ec_add_expr(
9+
config: ExprBuilderConfig, // The coordinate field.
10+
range_bus: VariableRangeCheckerBus,
11+
a_biguint: BigUint,
12+
d_biguint: BigUint,
13+
) -> FieldExpr {
14+
config.check_valid();
15+
let builder = ExprBuilder::new(config, range_bus.range_max_bits);
16+
let builder = Rc::new(RefCell::new(builder));
17+
18+
let x1 = ExprBuilder::new_input(builder.clone());
19+
let y1 = ExprBuilder::new_input(builder.clone());
20+
let x2 = ExprBuilder::new_input(builder.clone());
21+
let y2 = ExprBuilder::new_input(builder.clone());
22+
let a = ExprBuilder::new_const(builder.clone(), a_biguint.clone());
23+
let d = ExprBuilder::new_const(builder.clone(), d_biguint.clone());
24+
let one = ExprBuilder::new_const(builder.clone(), BigUint::one());
25+
26+
let x1y2 = x1.clone() * y2.clone();
27+
let x2y1 = x2.clone() * y1.clone();
28+
let y1y2 = y1 * y2;
29+
let x1x2 = x1 * x2;
30+
let dx1x2y1y2 = d * x1x2.clone() * y1y2.clone();
31+
32+
let mut x3 = (x1y2 + x2y1) / (one.clone() + dx1x2y1y2.clone());
33+
let mut y3 = (y1y2 - a * x1x2) / (one - dx1x2y1y2);
34+
35+
x3.save_output();
36+
y3.save_output();
37+
38+
let builder = builder.borrow().clone();
39+
40+
FieldExpr::new_with_setup_values(builder, range_bus, true, vec![a_biguint, d_biguint])
41+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
mod add;
2+
pub use add::*;
3+
4+
#[cfg(test)]
5+
mod tests;
6+
7+
use std::sync::{Arc, Mutex};
8+
9+
use openvm_circuit::{arch::VmChipWrapper, system::memory::OfflineMemory};
10+
use openvm_circuit_derive::InstructionExecutor;
11+
use openvm_circuit_primitives::var_range::SharedVariableRangeCheckerChip;
12+
use openvm_circuit_primitives_derive::{BytesStateful, Chip, ChipUsageGetter};
13+
use openvm_ecc_transpiler::Rv32EdwardsOpcode;
14+
use openvm_mod_circuit_builder::{ExprBuilderConfig, FieldExpressionCoreChip};
15+
use openvm_rv32_adapters::Rv32VecHeapAdapterChip;
16+
use openvm_stark_backend::p3_field::PrimeField32;
17+
18+
/// BLOCK_SIZE: how many cells do we read at a time, must be a power of 2.
19+
/// BLOCKS: how many blocks do we need to represent one input or output
20+
/// For example, for bls12_381, BLOCK_SIZE = 16, each element has 3 blocks and with two elements per input AffinePoint, BLOCKS = 6.
21+
/// For secp256k1, BLOCK_SIZE = 32, BLOCKS = 2.
22+
#[derive(Chip, ChipUsageGetter, InstructionExecutor, BytesStateful)]
23+
pub struct TeEcAddChip<F: PrimeField32, const BLOCKS: usize, const BLOCK_SIZE: usize>(
24+
VmChipWrapper<
25+
F,
26+
Rv32VecHeapAdapterChip<F, 2, BLOCKS, BLOCKS, BLOCK_SIZE, BLOCK_SIZE>,
27+
FieldExpressionCoreChip,
28+
>,
29+
);
30+
31+
// converts from num_bigint::BigUint to num_bigint_dig::BigInt in order to use num_bigint_dig::algorithms::jacobi
32+
fn num_bigint_to_num_bigint_dig(x: &num_bigint::BigUint) -> num_bigint_dig::BigInt {
33+
num_bigint_dig::BigInt::from_bytes_le(num_bigint_dig::Sign::Plus, &x.to_bytes_le())
34+
}
35+
36+
impl<F: PrimeField32, const BLOCKS: usize, const BLOCK_SIZE: usize>
37+
TeEcAddChip<F, BLOCKS, BLOCK_SIZE>
38+
{
39+
pub fn new(
40+
adapter: Rv32VecHeapAdapterChip<F, 2, BLOCKS, BLOCKS, BLOCK_SIZE, BLOCK_SIZE>,
41+
config: ExprBuilderConfig,
42+
offset: usize,
43+
a: num_bigint::BigUint,
44+
d: num_bigint::BigUint,
45+
range_checker: SharedVariableRangeCheckerChip,
46+
offline_memory: Arc<Mutex<OfflineMemory<F>>>,
47+
) -> Self {
48+
// Ensure that the addition operation is complete
49+
assert!(
50+
num_bigint_dig::algorithms::jacobi(
51+
&num_bigint_to_num_bigint_dig(&a),
52+
&num_bigint_to_num_bigint_dig(&config.modulus)
53+
) == 1
54+
);
55+
assert!(
56+
num_bigint_dig::algorithms::jacobi(
57+
&num_bigint_to_num_bigint_dig(&d),
58+
&num_bigint_to_num_bigint_dig(&config.modulus)
59+
) == -1
60+
);
61+
62+
let expr = ec_add_expr(config, range_checker.bus(), a, d);
63+
let core = FieldExpressionCoreChip::new(
64+
expr,
65+
offset,
66+
vec![
67+
Rv32EdwardsOpcode::EC_ADD as usize,
68+
Rv32EdwardsOpcode::SETUP_EC_ADD as usize,
69+
],
70+
vec![],
71+
range_checker,
72+
"TeEcAdd",
73+
true,
74+
);
75+
Self(VmChipWrapper::new(adapter, core, offline_memory))
76+
}
77+
}
Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
use std::str::FromStr;
2+
3+
use num_bigint::BigUint;
4+
use num_traits::FromPrimitive;
5+
use openvm_circuit::arch::{testing::VmChipTestBuilder, BITWISE_OP_LOOKUP_BUS};
6+
use openvm_circuit_primitives::{
7+
bigint::utils::big_uint_to_limbs,
8+
bitwise_op_lookup::{BitwiseOperationLookupBus, SharedBitwiseOperationLookupChip},
9+
};
10+
use openvm_ecc_transpiler::Rv32EdwardsOpcode;
11+
use openvm_instructions::{riscv::RV32_CELL_BITS, LocalOpcode};
12+
use openvm_mod_circuit_builder::{test_utils::biguint_to_limbs, ExprBuilderConfig, FieldExpr};
13+
use openvm_rv32_adapters::{rv32_write_heap_default, Rv32VecHeapAdapterChip};
14+
use openvm_stark_backend::p3_field::FieldAlgebra;
15+
use openvm_stark_sdk::p3_baby_bear::BabyBear;
16+
17+
use super::TeEcAddChip;
18+
19+
const NUM_LIMBS: usize = 32;
20+
const LIMB_BITS: usize = 8;
21+
const BLOCK_SIZE: usize = 32;
22+
type F = BabyBear;
23+
24+
lazy_static::lazy_static! {
25+
pub static ref SampleEcPoints: Vec<(BigUint, BigUint)> = {
26+
// Base point of edwards25519
27+
let x1 = BigUint::from_str(
28+
"15112221349535400772501151409588531511454012693041857206046113283949847762202",
29+
)
30+
.unwrap();
31+
let y1 = BigUint::from_str(
32+
"46316835694926478169428394003475163141307993866256225615783033603165251855960",
33+
)
34+
.unwrap();
35+
36+
// random point on edwards25519
37+
let x2 = BigUint::from_u32(2).unwrap();
38+
let y2 = BigUint::from_str(
39+
"11879831548380997166425477238087913000047176376829905612296558668626594440753",
40+
)
41+
.unwrap();
42+
43+
// This is the sum of (x1, y1) and (x2, y2).
44+
let x3 = BigUint::from_str(
45+
"44969869612046584870714054830543834361257841801051546235130567688769346152934",
46+
)
47+
.unwrap();
48+
let y3 = BigUint::from_str(
49+
"50796027728050908782231253190819121962159170739537197094456293084373503699602",
50+
)
51+
.unwrap();
52+
53+
// This is 2 * (x1, y1)
54+
let x4 = BigUint::from_str(
55+
"39226743113244985161159605482495583316761443760287217110659799046557361995496",
56+
)
57+
.unwrap();
58+
let y4 = BigUint::from_str(
59+
"12570354238812836652656274015246690354874018829607973815551555426027032771563",
60+
)
61+
.unwrap();
62+
63+
vec![(x1, y1), (x2, y2), (x3, y3), (x4, y4)]
64+
};
65+
66+
pub static ref Edwards25519_Prime: BigUint = BigUint::from_str(
67+
"57896044618658097711785492504343953926634992332820282019728792003956564819949",
68+
)
69+
.unwrap();
70+
71+
pub static ref Edwards25519_A: BigUint = BigUint::from_str(
72+
"57896044618658097711785492504343953926634992332820282019728792003956564819948",
73+
)
74+
.unwrap();
75+
76+
pub static ref Edwards25519_D: BigUint = BigUint::from_str(
77+
"37095705934669439343138083508754565189542113879843219016388785533085940283555",
78+
)
79+
.unwrap();
80+
81+
pub static ref Edwards25519_A_LIMBS: [BabyBear; NUM_LIMBS] =
82+
big_uint_to_limbs(&Edwards25519_A, LIMB_BITS)
83+
.into_iter()
84+
.map(BabyBear::from_canonical_usize)
85+
.collect::<Vec<_>>()
86+
.try_into()
87+
.unwrap();
88+
pub static ref Edwards25519_D_LIMBS: [BabyBear; NUM_LIMBS] =
89+
big_uint_to_limbs(&Edwards25519_D, LIMB_BITS)
90+
.into_iter()
91+
.map(BabyBear::from_canonical_usize)
92+
.collect::<Vec<_>>()
93+
.try_into()
94+
.unwrap();
95+
}
96+
97+
fn prime_limbs(expr: &FieldExpr) -> Vec<BabyBear> {
98+
expr.prime_limbs
99+
.iter()
100+
.map(|n| BabyBear::from_canonical_usize(*n))
101+
.collect::<Vec<_>>()
102+
}
103+
104+
#[test]
105+
fn test_add() {
106+
let mut tester: VmChipTestBuilder<F> = VmChipTestBuilder::default();
107+
let config = ExprBuilderConfig {
108+
modulus: Edwards25519_Prime.clone(),
109+
num_limbs: NUM_LIMBS,
110+
limb_bits: LIMB_BITS,
111+
};
112+
let bitwise_bus = BitwiseOperationLookupBus::new(BITWISE_OP_LOOKUP_BUS);
113+
let bitwise_chip = SharedBitwiseOperationLookupChip::<RV32_CELL_BITS>::new(bitwise_bus);
114+
let adapter = Rv32VecHeapAdapterChip::<F, 2, 2, 2, BLOCK_SIZE, BLOCK_SIZE>::new(
115+
tester.execution_bus(),
116+
tester.program_bus(),
117+
tester.memory_bridge(),
118+
tester.address_bits(),
119+
bitwise_chip.clone(),
120+
);
121+
let mut chip = TeEcAddChip::new(
122+
adapter,
123+
config,
124+
Rv32EdwardsOpcode::CLASS_OFFSET,
125+
Edwards25519_A.clone(),
126+
Edwards25519_D.clone(),
127+
tester.range_checker(),
128+
tester.offline_memory_mutex_arc(),
129+
);
130+
//assert_eq!(chip.0.core.expr().builder.num_variables, 12);
131+
assert_eq!(chip.0.core.air.expr.builder.num_variables, 12);
132+
133+
let (p1_x, p1_y) = SampleEcPoints[0].clone();
134+
let (p2_x, p2_y) = SampleEcPoints[1].clone();
135+
136+
let p1_x_limbs =
137+
biguint_to_limbs::<NUM_LIMBS>(p1_x.clone(), LIMB_BITS).map(BabyBear::from_canonical_u32);
138+
let p1_y_limbs =
139+
biguint_to_limbs::<NUM_LIMBS>(p1_y.clone(), LIMB_BITS).map(BabyBear::from_canonical_u32);
140+
let p2_x_limbs =
141+
biguint_to_limbs::<NUM_LIMBS>(p2_x.clone(), LIMB_BITS).map(BabyBear::from_canonical_u32);
142+
let p2_y_limbs =
143+
biguint_to_limbs::<NUM_LIMBS>(p2_y.clone(), LIMB_BITS).map(BabyBear::from_canonical_u32);
144+
145+
let r = chip
146+
.0
147+
.core
148+
//.expr()
149+
.air
150+
.expr
151+
.execute(vec![p1_x, p1_y, p2_x, p2_y], vec![true]);
152+
assert_eq!(r.len(), 12);
153+
154+
let outputs = chip
155+
.0
156+
.core
157+
.air
158+
.output_indices()
159+
.iter()
160+
.map(|i| &r[*i])
161+
.collect::<Vec<_>>();
162+
assert_eq!(outputs[0], &SampleEcPoints[2].0);
163+
assert_eq!(outputs[1], &SampleEcPoints[2].1);
164+
165+
//let prime_limbs: [BabyBear; NUM_LIMBS] = prime_limbs(chip.0.core.expr()).try_into().unwrap();
166+
let prime_limbs: [BabyBear; NUM_LIMBS] = prime_limbs(&chip.0.core.air.expr).try_into().unwrap();
167+
let mut one_limbs = [BabyBear::ONE; NUM_LIMBS];
168+
one_limbs[0] = BabyBear::ONE;
169+
let setup_instruction = rv32_write_heap_default(
170+
&mut tester,
171+
vec![prime_limbs, *Edwards25519_A_LIMBS],
172+
vec![*Edwards25519_D_LIMBS],
173+
chip.0.core.air.offset + Rv32EdwardsOpcode::SETUP_EC_ADD as usize,
174+
);
175+
tester.execute(&mut chip, &setup_instruction);
176+
177+
let instruction = rv32_write_heap_default(
178+
&mut tester,
179+
vec![p1_x_limbs, p1_y_limbs],
180+
vec![p2_x_limbs, p2_y_limbs],
181+
chip.0.core.air.offset + Rv32EdwardsOpcode::EC_ADD as usize,
182+
);
183+
184+
tester.execute(&mut chip, &instruction);
185+
186+
let tester = tester.build().load(chip).load(bitwise_chip).finalize();
187+
188+
tester.simple_test().expect("Verification failed");
189+
}

extensions/ecc/circuit/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,8 @@ pub use weierstrass_chip::*;
44
mod weierstrass_extension;
55
pub use weierstrass_extension::*;
66

7+
mod edwards_chip;
8+
pub use edwards_chip::*;
9+
710
mod config;
811
pub use config::*;

0 commit comments

Comments
 (0)