From 60f0d630864affa5cf1865f7d6644beabd0501af Mon Sep 17 00:00:00 2001 From: Will Song Date: Wed, 10 Dec 2025 16:08:06 -0500 Subject: [PATCH] add secp256r1 precompile --- sdk/patch-libs/src/lib.rs | 12 +- sdk/patch-libs/src/secp256r1.rs | 69 +++++++++ sdk/sdk/src/riscv_ecalls/mod.rs | 11 ++ sdk/sdk/src/riscv_ecalls/secp256r1.rs | 86 +++++++++++ vm/src/chips/gadgets/curves/mod.rs | 2 +- .../chips/gadgets/curves/weierstrass/mod.rs | 7 + .../gadgets/curves/weierstrass/secp256k1.rs | 6 +- .../gadgets/curves/weierstrass/secp256r1.rs | 142 ++++++++++++++++++ .../weierstrass/weierstrass_add.rs | 9 ++ .../weierstrass/weierstrass_decompress.rs | 17 +++ .../weierstrass/weierstrass_double.rs | 9 ++ vm/src/emulator/riscv/record.rs | 3 + vm/src/emulator/riscv/syscalls/code.rs | 19 ++- vm/src/emulator/riscv/syscalls/mod.rs | 14 +- .../riscv/syscalls/precompiles/ec/event.rs | 6 +- .../riscv/syscalls/precompiles/mod.rs | 9 ++ .../syscalls/precompiles/weierstrass/add.rs | 5 + .../precompiles/weierstrass/decompress.rs | 5 + .../precompiles/weierstrass/double.rs | 7 + vm/src/instances/chiptype/riscv_chiptype.rs | 14 +- .../instances/compiler/shapes/riscv_shape.rs | 21 +-- vm/src/machine/estimator.rs | 1 + 22 files changed, 453 insertions(+), 21 deletions(-) create mode 100644 sdk/patch-libs/src/secp256r1.rs create mode 100644 sdk/sdk/src/riscv_ecalls/secp256r1.rs create mode 100644 vm/src/chips/gadgets/curves/weierstrass/secp256r1.rs diff --git a/sdk/patch-libs/src/lib.rs b/sdk/patch-libs/src/lib.rs index 4fbdedaa..eff8c678 100644 --- a/sdk/patch-libs/src/lib.rs +++ b/sdk/patch-libs/src/lib.rs @@ -4,6 +4,7 @@ pub mod bn254; pub mod ed25519; pub mod io; pub mod secp256k1; +pub mod secp256r1; pub mod unconstrained; pub mod utils; pub mod verify; @@ -37,7 +38,7 @@ extern "C" { /// Executes an Ed25519 curve decompression on the given point. pub fn syscall_ed_decompress(point: &mut [u8; 64]); - /// Executes an Sepc256k1 curve addition on the given points. + /// Executes an Secp256k1 curve addition on the given points. pub fn syscall_secp256k1_add(p: *mut [u32; 16], q: *const [u32; 16]); /// Executes an Secp256k1 curve doubling on the given point. @@ -46,6 +47,15 @@ extern "C" { /// Executes an Secp256k1 curve decompression on the given point. pub fn syscall_secp256k1_decompress(point: &mut [u8; 64], is_odd: bool); + /// Executes an Secp256r1 curve addition on the given points. + pub fn syscall_secp256r1_add(p: *mut [u32; 16], q: *const [u32; 16]); + + /// Executes an Secp256r1 curve doubling on the given point. + pub fn syscall_secp256r1_double(p: *mut [u32; 16]); + + /// Executes an Secp256r1 curve decompression on the given point. + pub fn syscall_secp256r1_decompress(point: &mut [u8; 64], is_odd: bool); + /// Executes a Bn254 curve addition on the given points. pub fn syscall_bn254_add(p: *mut [u32; 16], q: *const [u32; 16]); diff --git a/sdk/patch-libs/src/secp256r1.rs b/sdk/patch-libs/src/secp256r1.rs new file mode 100644 index 00000000..aa2faf1b --- /dev/null +++ b/sdk/patch-libs/src/secp256r1.rs @@ -0,0 +1,69 @@ +use crate::{ + syscall_secp256r1_add, syscall_secp256r1_double, + utils::{AffinePoint, WeierstrassAffinePoint, WeierstrassPoint}, +}; + +/// The number of limbs in [Secp256r1Point]. +pub const N: usize = 16; + +/// An affine point on the Secp256r1 curve. +#[derive(Copy, Clone)] +#[repr(align(4))] +pub struct Secp256r1Point(pub WeierstrassPoint); + +impl WeierstrassAffinePoint for Secp256r1Point { + fn infinity() -> Self { + Self(WeierstrassPoint::Infinity) + } + + fn is_infinity(&self) -> bool { + matches!(self.0, WeierstrassPoint::Infinity) + } +} + +impl AffinePoint for Secp256r1Point { + const GENERATOR: [u32; N] = [ + 3633889942, 4104206661, 770388896, 1996717441, 1671708914, 4173129445, 3777774151, + 1796723186, 935285237, 3417718888, 1798397646, 734933847, 2081398294, 2397563722, + 4263149467, 1340293858, + ]; + + fn new(limbs: [u32; N]) -> Self { + Self(WeierstrassPoint::Affine(limbs)) + } + + fn limbs_ref(&self) -> &[u32; N] { + match &self.0 { + WeierstrassPoint::Infinity => panic!("Infinity point has no limbs"), + WeierstrassPoint::Affine(limbs) => limbs, + } + } + + fn limbs_mut(&mut self) -> &mut [u32; N] { + match &mut self.0 { + WeierstrassPoint::Infinity => panic!("Infinity point has no limbs"), + WeierstrassPoint::Affine(limbs) => limbs, + } + } + + fn complete_add_assign(&mut self, other: &Self) { + self.weierstrass_add_assign(other); + } + + fn add_assign(&mut self, other: &Self) { + let a = self.limbs_mut(); + let b = other.limbs_ref(); + unsafe { + syscall_secp256r1_add(a, b); + } + } + + fn double(&mut self) { + match &mut self.0 { + WeierstrassPoint::Infinity => (), + WeierstrassPoint::Affine(limbs) => unsafe { + syscall_secp256r1_double(limbs); + }, + } + } +} diff --git a/sdk/sdk/src/riscv_ecalls/mod.rs b/sdk/sdk/src/riscv_ecalls/mod.rs index bd8a5e36..b791741a 100644 --- a/sdk/sdk/src/riscv_ecalls/mod.rs +++ b/sdk/sdk/src/riscv_ecalls/mod.rs @@ -9,6 +9,7 @@ mod keccak_permute; mod memory; mod poseidon2; mod secp256k1; +mod secp256r1; mod sha_compress; mod sha_extend; mod sys; @@ -60,6 +61,16 @@ pub const SECP256K1_DOUBLE: u32 = 0x00_00_01_0B; /// Executes `K256_DECOMPRESS`. pub const SECP256K1_DECOMPRESS: u32 = 0x00_00_01_0C; +/// Executes `SECP256R1_ADD`. +pub const SECP256R1_ADD: u32 = 0x00_01_01_30; + +/// Executes `SECP256R1_DOUBLE`. +pub const SECP256R1_DOUBLE: u32 = 0x00_00_01_31; + +/// Executes `R256_DECOMPRESS`. +#[allow(clippy::mistyped_literal_suffixes)] +pub const SECP256R1_DECOMPRESS: u32 = 0x00_00_01_32; + /// Executes `BN254_ADD`. pub const BN254_ADD: u32 = 0x00_01_01_0E; diff --git a/sdk/sdk/src/riscv_ecalls/secp256r1.rs b/sdk/sdk/src/riscv_ecalls/secp256r1.rs new file mode 100644 index 00000000..24aa5120 --- /dev/null +++ b/sdk/sdk/src/riscv_ecalls/secp256r1.rs @@ -0,0 +1,86 @@ +#[cfg(target_os = "zkvm")] +use core::arch::asm; + +/// Adds two Secp256r1 points. +/// +/// The result is stored in the first point. +/// +/// ### Safety +/// +/// The caller must ensure that `p` and `q` are valid pointers to data that is aligned along a four +/// byte boundary. Additionally, the caller must ensure that `p` and `q` are valid points on the +/// secp256r1 curve, and that `p` and `q` are not equal to each other. +#[allow(unused_variables)] +#[no_mangle] +pub extern "C" fn syscall_secp256r1_add(p: *mut [u32; 16], q: *mut [u32; 16]) { + #[cfg(target_os = "zkvm")] + unsafe { + asm!( + "ecall", + in("t0") crate::riscv_ecalls::SECP256R1_ADD, + in("a0") p, + in("a1") q + ); + } + + #[cfg(not(target_os = "zkvm"))] + unreachable!() +} + +/// Double a Secp256r1 point. +/// +/// The result is stored in-place in the supplied buffer. +/// +/// ### Safety +/// +/// The caller must ensure that `p` is valid pointer to data that is aligned along a four byte +/// boundary. +#[allow(unused_variables)] +#[no_mangle] +pub extern "C" fn syscall_secp256r1_double(p: *mut [u32; 16]) { + #[cfg(target_os = "zkvm")] + unsafe { + asm!( + "ecall", + in("t0") crate::riscv_ecalls::SECP256R1_DOUBLE, + in("a0") p, + in("a1") 0 + ); + } + + #[cfg(not(target_os = "zkvm"))] + unreachable!() +} + +/// Decompresses a compressed Secp256r1 point. +/// +/// The input array should be 64 bytes long, with the first 32 bytes containing the X coordinate in +/// big-endian format. The second half of the input will be overwritten with the Y coordinate of the +/// decompressed point in big-endian format using the point's parity (is_odd). +/// +/// ### Safety +/// +/// The caller must ensure that `point` is valid pointer to data that is aligned along a four byte +/// boundary. +#[allow(unused_variables)] +#[no_mangle] +pub extern "C" fn syscall_secp256r1_decompress(point: &mut [u8; 64], is_odd: bool) { + #[cfg(target_os = "zkvm")] + { + // Memory system/FpOps are little endian so we'll just flip the whole array before/after + point.reverse(); + let p = point.as_mut_ptr(); + unsafe { + asm!( + "ecall", + in("t0") crate::riscv_ecalls::SECP256R1_DECOMPRESS, + in("a0") p, + in("a1") is_odd as u8 + ); + } + point.reverse(); + } + + #[cfg(not(target_os = "zkvm"))] + unreachable!() +} diff --git a/vm/src/chips/gadgets/curves/mod.rs b/vm/src/chips/gadgets/curves/mod.rs index 22774ac8..529e7bcd 100644 --- a/vm/src/chips/gadgets/curves/mod.rs +++ b/vm/src/chips/gadgets/curves/mod.rs @@ -66,7 +66,7 @@ impl Display for CurveType { pub struct AffinePoint { pub x: BigUint, pub y: BigUint, - _marker: std::marker::PhantomData, + _marker: std::marker::PhantomData E>, } impl AffinePoint { diff --git a/vm/src/chips/gadgets/curves/weierstrass/mod.rs b/vm/src/chips/gadgets/curves/weierstrass/mod.rs index d9ce6a88..cc65be5b 100644 --- a/vm/src/chips/gadgets/curves/weierstrass/mod.rs +++ b/vm/src/chips/gadgets/curves/weierstrass/mod.rs @@ -17,6 +17,13 @@ use crate::chips::gadgets::utils::conversions::{biguint_to_rug, rug_to_biguint}; pub mod bls381; pub mod bn254; pub mod secp256k1; +pub mod secp256r1; + +// re-export names +pub use bls381::Bls12381; +pub use bn254::Bn254; +pub use secp256k1::Secp256k1; +pub use secp256r1::Secp256r1; /// Parameters that specify a short Weierstrass curve : y^2 = x^3 + ax + b. pub trait WeierstrassParameters: EllipticCurveParameters { diff --git a/vm/src/chips/gadgets/curves/weierstrass/secp256k1.rs b/vm/src/chips/gadgets/curves/weierstrass/secp256k1.rs index b87f7f64..8438c25a 100644 --- a/vm/src/chips/gadgets/curves/weierstrass/secp256k1.rs +++ b/vm/src/chips/gadgets/curves/weierstrass/secp256k1.rs @@ -107,8 +107,8 @@ pub fn secp256k1_sqrt(n: &BigUint) -> BigUint { let mut bytes = [0_u8; 32]; bytes[32 - be_bytes.len()..].copy_from_slice(&be_bytes); let fe = FieldElement::from_bytes(&bytes.into()).unwrap(); - let result_bytes = fe.sqrt().unwrap().normalize().to_bytes(); - BigUint::from_be_bytes(&result_bytes as &[u8]) + let result_bytes = fe.sqrt().expect("bad sqrt").normalize().to_bytes(); + BigUint::from_be_bytes(&result_bytes) } #[cfg(test)] @@ -118,7 +118,7 @@ mod tests { use rand::thread_rng; #[test] - fn test_secp256k_sqrt() { + fn test_secp256k1_sqrt() { let mut rng = thread_rng(); for _ in 0..10 { // Check that sqrt(x^2)^2 == x^2 diff --git a/vm/src/chips/gadgets/curves/weierstrass/secp256r1.rs b/vm/src/chips/gadgets/curves/weierstrass/secp256r1.rs new file mode 100644 index 00000000..eb770ca0 --- /dev/null +++ b/vm/src/chips/gadgets/curves/weierstrass/secp256r1.rs @@ -0,0 +1,142 @@ +//! Modulo defining the Secp256r1 curve and its base field. The constants are all taken from +//! https://std.neuromancer.sk/secg/secp256r1 +//! +//! Also known as NIST P256 + +use std::str::FromStr; + +use elliptic_curve::{sec1::ToEncodedPoint, subtle::Choice}; +use hybrid_array::Array; +use num::{ + traits::{FromBytes, ToBytes}, + BigUint, +}; +use p256::{elliptic_curve::point::DecompressPoint, FieldElement}; +use serde::{Deserialize, Serialize}; +use typenum::{U32, U62}; + +use super::{SwCurve, WeierstrassParameters}; +use crate::chips::gadgets::{ + curves::{AffinePoint, CurveType, EllipticCurve, EllipticCurveParameters}, + utils::field_params::{FieldParameters, NumLimbs}, +}; + +#[derive(Debug, Default, Clone, Copy, PartialEq, Serialize, Deserialize)] +/// Secp256r1 curve parameter +pub struct Secp256r1Parameters; + +pub type Secp256r1 = SwCurve; + +#[derive(Debug, Default, Clone, Copy, PartialEq, Serialize, Deserialize)] +/// Secp256r1 base field parameter +pub struct Secp256r1BaseField; + +impl FieldParameters for Secp256r1BaseField { + const MODULUS: &'static [u8] = &[ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, + ]; + + /// A rough witness-offset estimate given the size of the limbs and the size of the field. + const WITNESS_OFFSET: usize = 1usize << 14; + + fn modulus() -> BigUint { + BigUint::from_bytes_le(Self::MODULUS) + } +} + +impl NumLimbs for Secp256r1BaseField { + type Limbs = U32; + type Witness = U62; +} + +impl EllipticCurveParameters for Secp256r1Parameters { + type BaseField = Secp256r1BaseField; + const CURVE_TYPE: CurveType = CurveType::Secp256r1; +} + +impl WeierstrassParameters for Secp256r1Parameters { + const A: Array = Array([ + 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, + ]); + + const B: Array = Array([ + 0x4b, 0x60, 0xd2, 0x27, 0x3e, 0x3c, 0xce, 0x3b, 0xf6, 0xb0, 0x53, 0xcc, 0xb0, 0x06, 0x1d, + 0x65, 0xbc, 0x86, 0x98, 0x76, 0x55, 0xbd, 0xeb, 0xb3, 0xe7, 0x93, 0x3a, 0xaa, 0xd8, 0x35, + 0xc6, 0x5a, + ]); + + fn generator() -> (BigUint, BigUint) { + let x = BigUint::from_str( + "48439561293906451759052585252797914202762949526041747995844080717082404635286", + ) + .unwrap(); + let y = BigUint::from_str( + "36134250956749795798585127919587881956611106672985015071877198253568414405109", + ) + .unwrap(); + (x, y) + } + + fn prime_group_order() -> BigUint { + BigUint::from_slice(&[ + 0xfc632551, 0xf3b9cac2, 0xa7179e84, 0xbce6faad, 0xffffffff, 0xffffffff, 0x00000000, + 0xffffffff, + ]) + } + + fn a_int() -> BigUint { + BigUint::from_bytes_le(&Self::A) + } + + fn b_int() -> BigUint { + BigUint::from_bytes_le(&Self::B) + } +} + +pub fn secp256r1_decompress(bytes_be: &[u8], sign: u32) -> AffinePoint { + let computed_point = p256::AffinePoint::decompress(bytes_be.into(), Choice::from(sign as u8)) + .expect("bad decompress"); + let point = computed_point.to_encoded_point(false); + + let x = BigUint::from_bytes_be(point.x().unwrap()); + let y = BigUint::from_bytes_be(point.y().unwrap()); + AffinePoint::::new(x, y) +} + +pub fn secp256r1_sqrt(n: &BigUint) -> BigUint { + let be_bytes = n.to_be_bytes(); + let mut bytes = [0_u8; 32]; + bytes[32 - be_bytes.len()..].copy_from_slice(&be_bytes); + let fe = FieldElement::from_bytes(&bytes.into()).unwrap(); + let result_bytes = fe.sqrt().expect("bad sqrt").to_bytes(); + BigUint::from_be_bytes(&result_bytes) +} + +#[cfg(test)] +mod tests { + use super::*; + use num::bigint::RandBigInt; + use rand::thread_rng; + + #[test] + fn test_secp256r1_sqrt() { + let mut rng = thread_rng(); + for _ in 0..10 { + // Check that sqrt(x^2)^2 == x^2 + // We use x^2 since not all field elements have a square root + let x = rng.gen_biguint(256) % Secp256r1BaseField::modulus(); + let x_2 = (&x * &x) % Secp256r1BaseField::modulus(); + let sqrt = secp256r1_sqrt(&x_2); + + println!("sqrt: {}", sqrt); + + let sqrt_2 = (&sqrt * &sqrt) % Secp256r1BaseField::modulus(); + + assert_eq!(sqrt_2, x_2); + } + } +} diff --git a/vm/src/chips/precompiles/weierstrass/weierstrass_add.rs b/vm/src/chips/precompiles/weierstrass/weierstrass_add.rs index ac611b1a..2b60971a 100644 --- a/vm/src/chips/precompiles/weierstrass/weierstrass_add.rs +++ b/vm/src/chips/precompiles/weierstrass/weierstrass_add.rs @@ -156,6 +156,7 @@ impl ChipBehavior for WeierstrassAddAssign fn name(&self) -> String { match E::CURVE_TYPE { CurveType::Secp256k1 => "Secp256k1AddAssign".to_string(), + CurveType::Secp256r1 => "Secp256r1AddAssign".to_string(), CurveType::Bn254 => "Bn254AddAssign".to_string(), CurveType::Bls12381 => "Bls12381AddAssign".to_string(), _ => panic!("Unsupported curve: {}", E::CURVE_TYPE), @@ -169,6 +170,7 @@ impl ChipBehavior for WeierstrassAddAssign ) -> RowMajorMatrix { let events = match E::CURVE_TYPE { CurveType::Secp256k1 => &input.get_precompile_events(SyscallCode::SECP256K1_ADD), + CurveType::Secp256r1 => &input.get_precompile_events(SyscallCode::SECP256R1_ADD), CurveType::Bn254 => &input.get_precompile_events(SyscallCode::BN254_ADD), CurveType::Bls12381 => &input.get_precompile_events(SyscallCode::BLS12381_ADD), _ => panic!("Unsupported curve"), @@ -185,6 +187,7 @@ impl ChipBehavior for WeierstrassAddAssign let event = match precompile_event { PrecompileEvent::Secp256k1Add(event) + | PrecompileEvent::Secp256r1Add(event) | PrecompileEvent::Bn254Add(event) | PrecompileEvent::Bls12381Add(event) => event, _ => unreachable!(), @@ -264,6 +267,9 @@ impl ChipBehavior for WeierstrassAddAssign CurveType::Secp256k1 => !chunk .get_precompile_events(SyscallCode::SECP256K1_ADD) .is_empty(), + CurveType::Secp256r1 => !chunk + .get_precompile_events(SyscallCode::SECP256R1_ADD) + .is_empty(), CurveType::Bn254 => !chunk .get_precompile_events(SyscallCode::BN254_ADD) .is_empty(), @@ -403,6 +409,9 @@ where CurveType::Secp256k1 => { CB::F::from_canonical_u32(SyscallCode::SECP256K1_ADD.syscall_id()) } + CurveType::Secp256r1 => { + CB::F::from_canonical_u32(SyscallCode::SECP256R1_ADD.syscall_id()) + } CurveType::Bn254 => CB::F::from_canonical_u32(SyscallCode::BN254_ADD.syscall_id()), CurveType::Bls12381 => { CB::F::from_canonical_u32(SyscallCode::BLS12381_ADD.syscall_id()) diff --git a/vm/src/chips/precompiles/weierstrass/weierstrass_decompress.rs b/vm/src/chips/precompiles/weierstrass/weierstrass_decompress.rs index 5deda3b7..0d1ae7ea 100644 --- a/vm/src/chips/precompiles/weierstrass/weierstrass_decompress.rs +++ b/vm/src/chips/precompiles/weierstrass/weierstrass_decompress.rs @@ -15,6 +15,7 @@ use crate::{ weierstrass::{ bls381::{bls12381_sqrt, Bls12381}, secp256k1::{secp256k1_sqrt, Secp256k1}, + secp256r1::{secp256r1_sqrt, Secp256r1}, WeierstrassParameters, }, CurveType, EllipticCurve, @@ -120,6 +121,12 @@ impl Default for WeierstrassDecompressChip { } } +impl Default for WeierstrassDecompressChip { + fn default() -> Self { + Self::with_lsb_rule() + } +} + impl WeierstrassDecompressChip { pub const fn new(sign_rule: SignChoiceRule) -> Self { Self { @@ -164,6 +171,7 @@ impl WeierstrassDecom let sqrt_fn = match E::CURVE_TYPE { CurveType::Bls12381 => bls12381_sqrt, CurveType::Secp256k1 => secp256k1_sqrt, + CurveType::Secp256r1 => secp256r1_sqrt, _ => panic!("Unsupported curve: {}", E::CURVE_TYPE), }; let y = cols.y.populate(blu_events, &x_3_plus_b, sqrt_fn); @@ -183,6 +191,7 @@ impl ChipBehavior fn name(&self) -> String { match E::CURVE_TYPE { CurveType::Secp256k1 => "Secp256k1Decompress".to_string(), + CurveType::Secp256r1 => "Secp256r1Decompress".to_string(), CurveType::Bls12381 => "Bls12381Decompress".to_string(), _ => panic!("Unsupported curve: {}", E::CURVE_TYPE), } @@ -199,6 +208,7 @@ impl ChipBehavior ) -> RowMajorMatrix { let events = match E::CURVE_TYPE { CurveType::Secp256k1 => input.get_precompile_events(SyscallCode::SECP256K1_DECOMPRESS), + CurveType::Secp256r1 => input.get_precompile_events(SyscallCode::SECP256R1_DECOMPRESS), CurveType::Bls12381 => input.get_precompile_events(SyscallCode::BLS12381_DECOMPRESS), _ => panic!("Unsupported curve"), }; @@ -216,6 +226,7 @@ impl ChipBehavior let event = match precompile_event { PrecompileEvent::Secp256k1Decompress(event) + | PrecompileEvent::Secp256r1Decompress(event) | PrecompileEvent::Bls12381Decompress(event) => event, _ => unreachable!(), }; @@ -324,6 +335,9 @@ impl ChipBehavior CurveType::Secp256k1 => !chunk .get_precompile_events(SyscallCode::SECP256K1_DECOMPRESS) .is_empty(), + CurveType::Secp256r1 => !chunk + .get_precompile_events(SyscallCode::SECP256R1_DECOMPRESS) + .is_empty(), CurveType::Bls12381 => !chunk .get_precompile_events(SyscallCode::BLS12381_DECOMPRESS) .is_empty(), @@ -556,6 +570,9 @@ where CurveType::Secp256k1 => { CB::F::from_canonical_u32(SyscallCode::SECP256K1_DECOMPRESS.syscall_id()) } + CurveType::Secp256r1 => { + CB::F::from_canonical_u32(SyscallCode::SECP256R1_DECOMPRESS.syscall_id()) + } _ => panic!("Unsupported curve: {}", E::CURVE_TYPE), }; diff --git a/vm/src/chips/precompiles/weierstrass/weierstrass_double.rs b/vm/src/chips/precompiles/weierstrass/weierstrass_double.rs index a2da58a2..256a5c5d 100644 --- a/vm/src/chips/precompiles/weierstrass/weierstrass_double.rs +++ b/vm/src/chips/precompiles/weierstrass/weierstrass_double.rs @@ -173,6 +173,7 @@ impl ChipBehavior fn name(&self) -> String { match E::CURVE_TYPE { CurveType::Secp256k1 => "Secp256k1DoubleAssign".to_string(), + CurveType::Secp256r1 => "Secp256r1DoubleAssign".to_string(), CurveType::Bn254 => "Bn254DoubleAssign".to_string(), CurveType::Bls12381 => "Bls12381DoubleAssign".to_string(), _ => panic!("Unsupported curve: {}", E::CURVE_TYPE), @@ -192,6 +193,7 @@ impl ChipBehavior let events = match E::CURVE_TYPE { CurveType::Secp256k1 => input.get_precompile_events(SyscallCode::SECP256K1_DOUBLE), + CurveType::Secp256r1 => input.get_precompile_events(SyscallCode::SECP256R1_DOUBLE), CurveType::Bn254 => input.get_precompile_events(SyscallCode::BN254_DOUBLE), CurveType::Bls12381 => input.get_precompile_events(SyscallCode::BLS12381_DOUBLE), _ => panic!("Unsupported curve"), @@ -211,6 +213,7 @@ impl ChipBehavior let (_syscall_event, precompile_event) = event_pair; let event = match precompile_event { PrecompileEvent::Secp256k1Double(event) + | PrecompileEvent::Secp256r1Double(event) | PrecompileEvent::Bn254Double(event) | PrecompileEvent::Bls12381Double(event) => event, _ => unreachable!(), @@ -281,6 +284,9 @@ impl ChipBehavior CurveType::Secp256k1 => !chunk .get_precompile_events(SyscallCode::SECP256K1_DOUBLE) .is_empty(), + CurveType::Secp256r1 => !chunk + .get_precompile_events(SyscallCode::SECP256R1_DOUBLE) + .is_empty(), CurveType::Bn254 => !chunk .get_precompile_events(SyscallCode::BN254_DOUBLE) .is_empty(), @@ -431,6 +437,9 @@ where CurveType::Secp256k1 => { CB::F::from_canonical_u32(SyscallCode::SECP256K1_DOUBLE.syscall_id()) } + CurveType::Secp256r1 => { + CB::F::from_canonical_u32(SyscallCode::SECP256R1_DOUBLE.syscall_id()) + } CurveType::Bn254 => CB::F::from_canonical_u32(SyscallCode::BN254_DOUBLE.syscall_id()), CurveType::Bls12381 => { CB::F::from_canonical_u32(SyscallCode::BLS12381_DOUBLE.syscall_id()) diff --git a/vm/src/emulator/riscv/record.rs b/vm/src/emulator/riscv/record.rs index b5d409c4..32ac3f20 100644 --- a/vm/src/emulator/riscv/record.rs +++ b/vm/src/emulator/riscv/record.rs @@ -214,14 +214,17 @@ impl EmulationRecord { SyscallCode::BN254_DOUBLE => THRESHOLD_2POW16.min(opts.deferred), SyscallCode::BLS12381_DECOMPRESS => THRESHOLD_2POW16.min(opts.deferred), SyscallCode::SECP256K1_DECOMPRESS => THRESHOLD_2POW16.min(opts.deferred), + SyscallCode::SECP256R1_DECOMPRESS => THRESHOLD_2POW16.min(opts.deferred), SyscallCode::ED_ADD => THRESHOLD_2POW15.min(opts.deferred), SyscallCode::BN254_ADD => THRESHOLD_2POW15.min(opts.deferred), SyscallCode::SECP256K1_FP_ADD => THRESHOLD_2POW16.min(opts.deferred), SyscallCode::BN254_FP_ADD => THRESHOLD_2POW16.min(opts.deferred), SyscallCode::SECP256K1_ADD => THRESHOLD_2POW15.min(opts.deferred), + SyscallCode::SECP256R1_ADD => THRESHOLD_2POW15.min(opts.deferred), SyscallCode::BLS12381_FP2_ADD => THRESHOLD_2POW15.min(opts.deferred), SyscallCode::BN254_FP2_ADD => THRESHOLD_2POW15.min(opts.deferred), SyscallCode::SECP256K1_DOUBLE => THRESHOLD_2POW16.min(opts.deferred), + SyscallCode::SECP256R1_DOUBLE => THRESHOLD_2POW16.min(opts.deferred), _ => opts.deferred, }; diff --git a/vm/src/emulator/riscv/syscalls/code.rs b/vm/src/emulator/riscv/syscalls/code.rs index 55aaec4a..768ecf8b 100644 --- a/vm/src/emulator/riscv/syscalls/code.rs +++ b/vm/src/emulator/riscv/syscalls/code.rs @@ -20,8 +20,11 @@ use strum_macros::EnumIter; #[derive( Debug, Copy, Clone, PartialEq, Eq, Hash, EnumIter, Ord, PartialOrd, Serialize, Deserialize, )] -#[allow(non_camel_case_types)] -#[allow(clippy::upper_case_acronyms)] +#[allow( + clippy::mistyped_literal_suffixes, + clippy::upper_case_acronyms, + non_camel_case_types +)] pub enum SyscallCode { /// Halts the program. HALT = 0x00_00_00_00, @@ -139,6 +142,15 @@ pub enum SyscallCode { /// Executes the `POSEIDON2_PERMUTE` precompile. POSEIDON2_PERMUTE = 0x00_01_01_2F, + + /// Executes the `SECP256R1_ADD` precompile. + SECP256R1_ADD = 0x00_01_01_30, + + /// Executes the `SECP256R1_DOUBLE` precompile. + SECP256R1_DOUBLE = 0x00_00_01_31, + + /// Executes the `SECP256R1_DECOMPRESS` precompile. + SECP256R1_DECOMPRESS = 0x00_00_01_32, } impl SyscallCode { @@ -185,6 +197,9 @@ impl SyscallCode { 0x00_01_01_2E => SyscallCode::SECP256K1_FP_MUL, 0x00_00_01_1C => SyscallCode::BLS12381_DECOMPRESS, 0x00_01_01_2F => SyscallCode::POSEIDON2_PERMUTE, + 0x00_01_01_30 => SyscallCode::SECP256R1_ADD, + 0x00_00_01_31 => SyscallCode::SECP256R1_DOUBLE, + 0x00_00_01_32_u32 => SyscallCode::SECP256R1_DECOMPRESS, _ => panic!("invalid syscall number: {}", value), } } diff --git a/vm/src/emulator/riscv/syscalls/mod.rs b/vm/src/emulator/riscv/syscalls/mod.rs index bb46094c..0a9386f7 100644 --- a/vm/src/emulator/riscv/syscalls/mod.rs +++ b/vm/src/emulator/riscv/syscalls/mod.rs @@ -15,7 +15,7 @@ use crate::{ chips::gadgets::{ curves::{ edwards::ed25519::{Ed25519, Ed25519Parameters}, - weierstrass::{bls381::Bls12381, bn254::Bn254, secp256k1::Secp256k1}, + weierstrass::{Bls12381, Bn254, Secp256k1, Secp256r1}, }, field::field_op::FieldOperation, }, @@ -207,6 +207,10 @@ where SyscallCode::SECP256K1_ADD, Arc::new(WeierstrassAddAssignSyscall::::new()), ); + syscall_map.insert( + SyscallCode::SECP256R1_ADD, + Arc::new(WeierstrassAddAssignSyscall::::new()), + ); syscall_map.insert( SyscallCode::BN254_ADD, Arc::new(WeierstrassAddAssignSyscall::::new()), @@ -220,6 +224,10 @@ where SyscallCode::SECP256K1_DOUBLE, Arc::new(WeierstrassDoubleAssignSyscall::::new()), ); + syscall_map.insert( + SyscallCode::SECP256R1_DOUBLE, + Arc::new(WeierstrassDoubleAssignSyscall::::new()), + ); syscall_map.insert( SyscallCode::BN254_DOUBLE, Arc::new(WeierstrassDoubleAssignSyscall::::new()), @@ -237,6 +245,10 @@ where SyscallCode::SECP256K1_DECOMPRESS, Arc::new(WeierstrassDecompressSyscall::::new()), ); + syscall_map.insert( + SyscallCode::SECP256R1_DECOMPRESS, + Arc::new(WeierstrassDecompressSyscall::::new()), + ); syscall_map.insert( SyscallCode::POSEIDON2_PERMUTE, diff --git a/vm/src/emulator/riscv/syscalls/precompiles/ec/event.rs b/vm/src/emulator/riscv/syscalls/precompiles/ec/event.rs index 9bfb1a5c..13cd1563 100644 --- a/vm/src/emulator/riscv/syscalls/precompiles/ec/event.rs +++ b/vm/src/emulator/riscv/syscalls/precompiles/ec/event.rs @@ -3,7 +3,10 @@ use crate::{ chips::riscv_memory::event::{MemoryLocalEvent, MemoryReadRecord, MemoryWriteRecord}, gadgets::{ curves::{ - weierstrass::{bls381::bls12381_decompress, secp256k1::secp256k1_decompress}, + weierstrass::{ + bls381::bls12381_decompress, secp256k1::secp256k1_decompress, + secp256r1::secp256r1_decompress, + }, AffinePoint, CurveType, EllipticCurve, }, utils::{ @@ -127,6 +130,7 @@ pub fn create_ec_decompress_event( let decompress_fn = match E::CURVE_TYPE { CurveType::Bls12381 => bls12381_decompress::, CurveType::Secp256k1 => secp256k1_decompress::, + CurveType::Secp256r1 => secp256r1_decompress::, _ => panic!("Unsupported curve: {}", E::CURVE_TYPE), }; diff --git a/vm/src/emulator/riscv/syscalls/precompiles/mod.rs b/vm/src/emulator/riscv/syscalls/precompiles/mod.rs index 702b238d..8b0da6e1 100644 --- a/vm/src/emulator/riscv/syscalls/precompiles/mod.rs +++ b/vm/src/emulator/riscv/syscalls/precompiles/mod.rs @@ -42,6 +42,12 @@ pub enum PrecompileEvent { Secp256k1Double(EllipticCurveDoubleEvent), /// Secp256k1 curve decompress precompile event. Secp256k1Decompress(EllipticCurveDecompressEvent), + /// Secp256r1 curve add precompile event. + Secp256r1Add(EllipticCurveAddEvent), + /// Secp256r1 curve double precompile event. + Secp256r1Double(EllipticCurveDoubleEvent), + /// Secp256r1 curve decompress precompile event. + Secp256r1Decompress(EllipticCurveDecompressEvent), /// K256 curve decompress precompile event. K256Decompress(EllipticCurveDecompressEvent), /// Bn254 curve add precompile event. @@ -102,17 +108,20 @@ impl PrecompileLocalMemory for Vec<(SyscallEvent, PrecompileEvent)> { // iterators.push(e.local_mem_access.iter()); // } PrecompileEvent::Secp256k1Add(e) + | PrecompileEvent::Secp256r1Add(e) | PrecompileEvent::EdAdd(e) | PrecompileEvent::Bn254Add(e) | PrecompileEvent::Bls12381Add(e) => { iterators.push(e.local_mem_access.iter()); } PrecompileEvent::Secp256k1Double(e) + | PrecompileEvent::Secp256r1Double(e) | PrecompileEvent::Bn254Double(e) | PrecompileEvent::Bls12381Double(e) => { iterators.push(e.local_mem_access.iter()); } PrecompileEvent::Secp256k1Decompress(e) + | PrecompileEvent::Secp256r1Decompress(e) | PrecompileEvent::K256Decompress(e) | PrecompileEvent::Bls12381Decompress(e) => { iterators.push(e.local_mem_access.iter()); diff --git a/vm/src/emulator/riscv/syscalls/precompiles/weierstrass/add.rs b/vm/src/emulator/riscv/syscalls/precompiles/weierstrass/add.rs index 20d46859..8747ef4e 100644 --- a/vm/src/emulator/riscv/syscalls/precompiles/weierstrass/add.rs +++ b/vm/src/emulator/riscv/syscalls/precompiles/weierstrass/add.rs @@ -40,6 +40,11 @@ impl Syscall for WeierstrassAddAssignSyscall { syscall_event, PrecompileEvent::Secp256k1Add(event), ), + CurveType::Secp256r1 => rt.record_mut().add_precompile_event( + syscall_code, + syscall_event, + PrecompileEvent::Secp256r1Add(event), + ), CurveType::Bn254 => { rt.record_mut().add_precompile_event( syscall_code, diff --git a/vm/src/emulator/riscv/syscalls/precompiles/weierstrass/decompress.rs b/vm/src/emulator/riscv/syscalls/precompiles/weierstrass/decompress.rs index f1b25a2f..8b57c32e 100644 --- a/vm/src/emulator/riscv/syscalls/precompiles/weierstrass/decompress.rs +++ b/vm/src/emulator/riscv/syscalls/precompiles/weierstrass/decompress.rs @@ -40,6 +40,11 @@ impl Syscall for WeierstrassDecompressSyscall { syscall_event, PrecompileEvent::Secp256k1Decompress(event), ), + CurveType::Secp256r1 => rt.record_mut().add_precompile_event( + syscall_code, + syscall_event, + PrecompileEvent::Secp256r1Decompress(event), + ), CurveType::Bls12381 => rt.record_mut().add_precompile_event( syscall_code, syscall_event, diff --git a/vm/src/emulator/riscv/syscalls/precompiles/weierstrass/double.rs b/vm/src/emulator/riscv/syscalls/precompiles/weierstrass/double.rs index d26842e2..05b6b600 100644 --- a/vm/src/emulator/riscv/syscalls/precompiles/weierstrass/double.rs +++ b/vm/src/emulator/riscv/syscalls/precompiles/weierstrass/double.rs @@ -42,6 +42,13 @@ impl Syscall for WeierstrassDoubleAssignSyscall { PrecompileEvent::Secp256k1Double(event), ); } + CurveType::Secp256r1 => { + rt.record_mut().add_precompile_event( + syscall_code, + syscall_event, + PrecompileEvent::Secp256r1Double(event), + ); + } CurveType::Bn254 => { rt.record_mut().add_precompile_event( syscall_code, diff --git a/vm/src/instances/chiptype/riscv_chiptype.rs b/vm/src/instances/chiptype/riscv_chiptype.rs index a744463c..9ebb3a58 100644 --- a/vm/src/instances/chiptype/riscv_chiptype.rs +++ b/vm/src/instances/chiptype/riscv_chiptype.rs @@ -30,9 +30,8 @@ use crate::{ curves::{ edwards::ed25519::{Ed25519, Ed25519Parameters}, weierstrass::{ - bls381::{Bls12381, Bls381BaseField}, - bn254::{Bn254, Bn254BaseField}, - secp256k1::Secp256k1, + bls381::Bls381BaseField, bn254::Bn254BaseField, Bls12381, Bn254, Secp256k1, + Secp256r1, }, }, field::secp256k1::Secp256k1BaseField, @@ -79,11 +78,14 @@ type FpOpSecp256k1 = FpOpChip; type WsBn254Add = WeierstrassAddAssignChip; type WsBls381Add = WeierstrassAddAssignChip; type WsSecp256k1Add = WeierstrassAddAssignChip; +type WsSecp256r1Add = WeierstrassAddAssignChip; type WsDecompressBls381 = WeierstrassDecompressChip; type WsDecompressSecp256k1 = WeierstrassDecompressChip; +type WsDecompressSecp256r1 = WeierstrassDecompressChip; type WsDoubleBn254 = WeierstrassDoubleAssignChip; type WsDoubleBls381 = WeierstrassDoubleAssignChip; type WsDoubleSecp256k1 = WeierstrassDoubleAssignChip; +type WsDoubleSecp256r1 = WeierstrassDoubleAssignChip; define_chip_type!( RiscvChipType, @@ -96,11 +98,14 @@ define_chip_type!( (WsBn254Add, WsBn254Add), (WsBls381Add, WsBls381Add), (WsSecp256k1Add, WsSecp256k1Add), + (WsSecp256r1Add, WsSecp256r1Add), (WsDecompressBls381, WsDecompressBls381), (WsDecompressSecp256k1, WsDecompressSecp256k1), + (WsDecompressSecp256r1, WsDecompressSecp256r1), (WsDoubleBn254, WsDoubleBn254), (WsDoubleBls381, WsDoubleBls381), (WsDoubleSecp256k1, WsDoubleSecp256k1), + (WsDoubleSecp256r1, WsDoubleSecp256r1), (ShaExtend, ShaExtendChip), (MemoryInitialize, MemoryInitializeFinalizeChip), (MemoryFinalize, MemoryInitializeFinalizeChip), @@ -142,11 +147,14 @@ impl RiscvChipType { Self::WsBn254Add(Default::default()), Self::WsBls381Add(Default::default()), Self::WsSecp256k1Add(Default::default()), + Self::WsSecp256r1Add(Default::default()), Self::WsDecompressBls381(Default::default()), Self::WsDecompressSecp256k1(Default::default()), + Self::WsDecompressSecp256r1(Default::default()), Self::WsDoubleBn254(Default::default()), Self::WsDoubleBls381(Default::default()), Self::WsDoubleSecp256k1(Default::default()), + Self::WsDoubleSecp256r1(Default::default()), Self::ShaExtend(Default::default()), Self::MemoryInitialize(MemoryInitializeFinalizeChip::new( MemoryChipType::Initialize, diff --git a/vm/src/instances/compiler/shapes/riscv_shape.rs b/vm/src/instances/compiler/shapes/riscv_shape.rs index c60163f0..815c2db4 100644 --- a/vm/src/instances/compiler/shapes/riscv_shape.rs +++ b/vm/src/instances/compiler/shapes/riscv_shape.rs @@ -205,27 +205,30 @@ pub(crate) fn precompile_rows_per_event(chip_name: &str) -> usize { pub(crate) fn precompile_syscall_code(chip_name: &str) -> SyscallCode { match chip_name { "Bls12381AddAssign" => SyscallCode::BLS12381_ADD, + "Bls12381Decompress" => SyscallCode::BLS12381_DECOMPRESS, + "Bls12381DoubleAssign" => SyscallCode::BLS12381_DOUBLE, + "Bls381Fp2AddSub" => SyscallCode::BLS12381_FP2_ADD, + "Bls381Fp2Mul" => SyscallCode::BLS12381_FP2_MUL, + "Bls381FpOp" => SyscallCode::BLS12381_FP_ADD, "Bn254AddAssign" => SyscallCode::BN254_ADD, "Bn254DoubleAssign" => SyscallCode::BN254_DOUBLE, - "Bn254FpOp" => SyscallCode::BN254_FP_ADD, "Bn254Fp2AddSub" => SyscallCode::BN254_FP2_ADD, "Bn254Fp2Mul" => SyscallCode::BN254_FP2_MUL, + "Bn254FpOp" => SyscallCode::BN254_FP_ADD, "EdAddAssign" => SyscallCode::ED_ADD, "EdDecompress" => SyscallCode::ED_DECOMPRESS, "KeccakPermute" => SyscallCode::KECCAK_PERMUTE, + "Poseidon2Permute" => SyscallCode::POSEIDON2_PERMUTE, "Secp256k1AddAssign" => SyscallCode::SECP256K1_ADD, + "Secp256k1Decompress" => SyscallCode::SECP256K1_DECOMPRESS, "Secp256k1DoubleAssign" => SyscallCode::SECP256K1_DOUBLE, + "Secp256k1FpOp" => SyscallCode::SECP256K1_FP_ADD, + "Secp256r1AddAssign" => SyscallCode::SECP256R1_ADD, + "Secp256r1Decompress" => SyscallCode::SECP256R1_DECOMPRESS, + "Secp256r1DoubleAssign" => SyscallCode::SECP256R1_DOUBLE, "ShaCompress" => SyscallCode::SHA_COMPRESS, "ShaExtend" => SyscallCode::SHA_EXTEND, "Uint256MulMod" => SyscallCode::UINT256_MUL, - "Bls12381Decompress" => SyscallCode::BLS12381_DECOMPRESS, - "Secp256k1Decompress" => SyscallCode::SECP256K1_DECOMPRESS, - "Bls12381DoubleAssign" => SyscallCode::BLS12381_DOUBLE, - "Bls381FpOp" => SyscallCode::BLS12381_FP_ADD, - "Bls381Fp2Mul" => SyscallCode::BLS12381_FP2_MUL, - "Bls381Fp2AddSub" => SyscallCode::BLS12381_FP2_ADD, - "Secp256k1FpOp" => SyscallCode::SECP256K1_FP_ADD, - "Poseidon2Permute" => SyscallCode::POSEIDON2_PERMUTE, _ => { unreachable!("precompile {} not supported yet", chip_name); } diff --git a/vm/src/machine/estimator.rs b/vm/src/machine/estimator.rs index 1cd1ba7b..26eb5012 100644 --- a/vm/src/machine/estimator.rs +++ b/vm/src/machine/estimator.rs @@ -115,6 +115,7 @@ pub struct EventSizeCapture<'a> { bls381_add: PrecompileEstimator, bls381_double: PrecompileEstimator, bls381_decompress: PrecompileEstimator, + // TODO: add secp256r1 for estimate-cost // Field operations fp_bn254: PrecompileEstimator,