From 21913488f6af53326985aa6c6235f75327edab39 Mon Sep 17 00:00:00 2001 From: Avaneesh Kulkarni Date: Thu, 24 Apr 2025 13:31:18 -0400 Subject: [PATCH 01/41] Remove explicit calls to setup functions throughout the codebase --- .../pairing/tests/programs/openvm_init_fp12_mul_bls12_381.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/pairing/tests/programs/openvm_init_fp12_mul_bls12_381.rs b/extensions/pairing/tests/programs/openvm_init_fp12_mul_bls12_381.rs index 00181b03ef..c130859ad8 100644 --- a/extensions/pairing/tests/programs/openvm_init_fp12_mul_bls12_381.rs +++ b/extensions/pairing/tests/programs/openvm_init_fp12_mul_bls12_381.rs @@ -1,4 +1,4 @@ // This file is automatically generated by cargo openvm. Do not rename or edit. -openvm_algebra_guest::moduli_macros::moduli_init! { "4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787" } -openvm_algebra_guest::complex_macros::complex_init! { Bls12_381Fp2 { mod_idx = 0 } } +openvm_algebra_guest::moduli_macros::moduli_init! { "21888242871839275222246405745257275088696311157297823662689037894645226208583" } +openvm_algebra_guest::complex_macros::complex_init! { Bn254Fp2 { mod_idx = 0 } } openvm_ecc_guest::sw_macros::sw_init! { } From 85cd3d4b52c6cfa0ef698cd3f60f0b41cf1fea57 Mon Sep 17 00:00:00 2001 From: Avaneesh Kulkarni Date: Thu, 17 Apr 2025 18:17:21 -0400 Subject: [PATCH 02/41] Remove guest code and tests from sha256 extension --- Cargo.lock | 15 --------- Cargo.toml | 1 - extensions/sha256/guest/src/lib.rs | 23 ------------- extensions/sha256/tests/Cargo.toml | 23 ------------- extensions/sha256/tests/programs/Cargo.toml | 25 --------------- .../sha256/tests/programs/examples/sha.rs | 30 ----------------- extensions/sha256/tests/src/lib.rs | 32 ------------------- 7 files changed, 149 deletions(-) delete mode 100644 extensions/sha256/tests/Cargo.toml delete mode 100644 extensions/sha256/tests/programs/Cargo.toml delete mode 100644 extensions/sha256/tests/programs/examples/sha.rs delete mode 100644 extensions/sha256/tests/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 7031868f8f..bc415991ce 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4888,21 +4888,6 @@ dependencies = [ "sha2", ] -[[package]] -name = "openvm-sha256-integration-tests" -version = "1.1.2" -dependencies = [ - "eyre", - "openvm-circuit", - "openvm-instructions", - "openvm-rv32im-transpiler", - "openvm-sha256-circuit", - "openvm-sha256-transpiler", - "openvm-stark-sdk", - "openvm-toolchain-tests", - "openvm-transpiler", -] - [[package]] name = "openvm-sha256-transpiler" version = "1.1.2" diff --git a/Cargo.toml b/Cargo.toml index bded0ba140..1b1e242111 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -56,7 +56,6 @@ members = [ "extensions/sha256/circuit", "extensions/sha256/transpiler", "extensions/sha256/guest", - "extensions/sha256/tests", "extensions/ecc/circuit", "extensions/ecc/transpiler", "extensions/ecc/guest", diff --git a/extensions/sha256/guest/src/lib.rs b/extensions/sha256/guest/src/lib.rs index cb34bcd5aa..c1f8ee0649 100644 --- a/extensions/sha256/guest/src/lib.rs +++ b/extensions/sha256/guest/src/lib.rs @@ -5,14 +5,6 @@ pub const OPCODE: u8 = 0x0b; pub const SHA256_FUNCT3: u8 = 0b100; pub const SHA256_FUNCT7: u8 = 0x1; -/// The sha256 cryptographic hash function. -#[inline(always)] -pub fn sha256(input: &[u8]) -> [u8; 32] { - let mut output = [0u8; 32]; - set_sha256(input, &mut output); - output -} - /// zkvm native implementation of sha256 /// # Safety /// @@ -28,18 +20,3 @@ pub fn sha256(input: &[u8]) -> [u8; 32] { extern "C" fn zkvm_sha256_impl(bytes: *const u8, len: usize, output: *mut u8) { openvm_platform::custom_insn_r!(opcode = OPCODE, funct3 = SHA256_FUNCT3, funct7 = SHA256_FUNCT7, rd = In output, rs1 = In bytes, rs2 = In len); } - -/// Sets `output` to the sha256 hash of `input`. -pub fn set_sha256(input: &[u8], output: &mut [u8; 32]) { - #[cfg(not(target_os = "zkvm"))] - { - use sha2::{Digest, Sha256}; - let mut hasher = Sha256::new(); - hasher.update(input); - output.copy_from_slice(hasher.finalize().as_ref()); - } - #[cfg(target_os = "zkvm")] - { - zkvm_sha256_impl(input.as_ptr(), input.len(), output.as_mut_ptr() as *mut u8); - } -} diff --git a/extensions/sha256/tests/Cargo.toml b/extensions/sha256/tests/Cargo.toml deleted file mode 100644 index 52fb11f9ea..0000000000 --- a/extensions/sha256/tests/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -[package] -name = "openvm-sha256-integration-tests" -description = "Integration tests for the OpenVM sha256 extension" -version.workspace = true -authors.workspace = true -edition.workspace = true -homepage.workspace = true -repository.workspace = true - -[dependencies] -openvm-instructions = { workspace = true } -openvm-stark-sdk.workspace = true -openvm-circuit = { workspace = true, features = ["test-utils"] } -openvm-transpiler.workspace = true -openvm-sha256-transpiler.workspace = true -openvm-sha256-circuit.workspace = true -openvm-rv32im-transpiler.workspace = true -openvm-toolchain-tests = { path = "../../../crates/toolchain/tests" } -eyre.workspace = true - -[features] -default = ["parallel"] -parallel = ["openvm-circuit/parallel"] diff --git a/extensions/sha256/tests/programs/Cargo.toml b/extensions/sha256/tests/programs/Cargo.toml deleted file mode 100644 index dad3842cea..0000000000 --- a/extensions/sha256/tests/programs/Cargo.toml +++ /dev/null @@ -1,25 +0,0 @@ -[workspace] -[package] -name = "openvm-keccak256-test-programs" -version = "0.0.0" -edition = "2021" - -[dependencies] -openvm = { path = "../../../../crates/toolchain/openvm" } -openvm-platform = { path = "../../../../crates/toolchain/platform" } -openvm-sha256-guest = { path = "../../guest" } -hex = { version = "0.4.3", default-features = false, features = ["alloc"] } -serde = { version = "1.0", default-features = false, features = [ - "alloc", - "derive", -] } - - -[features] -default = [] -std = ["serde/std", "openvm/std"] - -[profile.release] -panic = "abort" -lto = "thin" # turn on lto = fat to decrease binary size, but this optimizes out some missing extern links so we shouldn't use it for testing -# strip = "symbols" diff --git a/extensions/sha256/tests/programs/examples/sha.rs b/extensions/sha256/tests/programs/examples/sha.rs deleted file mode 100644 index fffbc677a7..0000000000 --- a/extensions/sha256/tests/programs/examples/sha.rs +++ /dev/null @@ -1,30 +0,0 @@ -#![cfg_attr(not(feature = "std"), no_main)] -#![cfg_attr(not(feature = "std"), no_std)] - -extern crate alloc; - -use alloc::vec::Vec; -use core::hint::black_box; - -use hex::FromHex; -use openvm_sha256_guest::sha256; - -openvm::entry!(main); - -pub fn main() { - let test_vectors = [ - ("", "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"), - ("98c1c0bdb7d5fea9a88859f06c6c439f", "b6b2c9c9b6f30e5c66c977f1bd7ad97071bee739524aecf793384890619f2b05"), - ("5b58f4163e248467cc1cd3eecafe749e8e2baaf82c0f63af06df0526347d7a11327463c115210a46b6740244eddf370be89c", "ac0e25049870b91d78ef6807bb87fce4603c81abd3c097fba2403fd18b6ce0b7"), - ("9ad198539e3160194f38ac076a782bd5210a007560d1fce9ef78f8a4a5e4d78c6b96c250cff3520009036e9c6087d5dab587394edda862862013de49a12072485a6c01165ec0f28ffddf1873fbd53e47fcd02fb6a5ccc9622d5588a92429c663ce298cb71b50022fc2ec4ba9f5bbd250974e1a607b165fee16e8f3f2be20d7348b91a2f518ce928491900d56d9f86970611580350cee08daea7717fe28a73b8dcfdea22a65ed9f5a09198de38e4e4f2cc05b0ba3dd787a5363ab6c9f39dcb66c1a29209b1d6b1152769395df8150b4316658ea6ab19af94903d643fcb0ae4d598035ebe73c8b1b687df1ab16504f633c929569c6d0e5fae6eea43838fbc8ce2c2b43161d0addc8ccf945a9c4e06294e56a67df0000f561f61b630b1983ba403e775aaeefa8d339f669d1e09ead7eae979383eda983321e1743e5404b4b328da656de79ff52d179833a6bd5129f49432d74d001996c37c68d9ab49fcff8061d193576f396c20e1f0d9ee83a51290ba60efa9c3cb2e15b756321a7ca668cdbf63f95ec33b1c450aa100101be059dc00077245b25a6a66698dee81953ed4a606944076e2858b1420de0095a7f60b08194d6d9a997009d345c71f63a7034b976e409af8a9a040ac7113664609a7adedb76b2fadf04b0348392a1650526eb2a4d6ed5e4bbcda8aabc8488b38f4f5d9a398103536bb8250ed82a9b9825f7703c263f9e", "080ad71239852124fc26758982090611b9b19abf22d22db3a57f67a06e984a23") - - ]; - for (input, expected_output) in test_vectors.iter() { - let input = Vec::from_hex(input).unwrap(); - let expected_output = Vec::from_hex(expected_output).unwrap(); - let output = sha256(&black_box(input)); - if output != *expected_output { - panic!(); - } - } -} diff --git a/extensions/sha256/tests/src/lib.rs b/extensions/sha256/tests/src/lib.rs deleted file mode 100644 index 7afaa08e56..0000000000 --- a/extensions/sha256/tests/src/lib.rs +++ /dev/null @@ -1,32 +0,0 @@ -#[cfg(test)] -mod tests { - use eyre::Result; - use openvm_circuit::utils::air_test; - use openvm_instructions::exe::VmExe; - use openvm_rv32im_transpiler::{ - Rv32ITranspilerExtension, Rv32IoTranspilerExtension, Rv32MTranspilerExtension, - }; - use openvm_sha256_circuit::Sha256Rv32Config; - use openvm_sha256_transpiler::Sha256TranspilerExtension; - use openvm_stark_sdk::p3_baby_bear::BabyBear; - use openvm_toolchain_tests::{build_example_program_at_path, get_programs_dir}; - use openvm_transpiler::{transpiler::Transpiler, FromElf}; - - type F = BabyBear; - - #[test] - fn test_sha256() -> Result<()> { - let config = Sha256Rv32Config::default(); - let elf = build_example_program_at_path(get_programs_dir!(), "sha", &config)?; - let openvm_exe = VmExe::from_elf( - elf, - Transpiler::::default() - .with_extension(Rv32ITranspilerExtension) - .with_extension(Rv32MTranspilerExtension) - .with_extension(Rv32IoTranspilerExtension) - .with_extension(Sha256TranspilerExtension), - )?; - air_test(config, openvm_exe); - Ok(()) - } -} From 9d464b3ca4ed7c912ac2874a24b56f8c6111cbf4 Mon Sep 17 00:00:00 2001 From: Avaneesh Kulkarni Date: Thu, 17 Apr 2025 18:18:00 -0400 Subject: [PATCH 03/41] Remove guest code and tests from keccak256 extension --- Cargo.lock | 15 --------- Cargo.toml | 1 - extensions/keccak256/guest/src/lib.rs | 33 ------------------- extensions/keccak256/tests/Cargo.toml | 23 ------------- .../keccak256/tests/programs/Cargo.toml | 25 -------------- .../tests/programs/examples/keccak.rs | 32 ------------------ extensions/keccak256/tests/src/lib.rs | 32 ------------------ 7 files changed, 161 deletions(-) delete mode 100644 extensions/keccak256/tests/Cargo.toml delete mode 100644 extensions/keccak256/tests/programs/Cargo.toml delete mode 100644 extensions/keccak256/tests/programs/examples/keccak.rs delete mode 100644 extensions/keccak256/tests/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index bc415991ce..5da8e868dd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4419,21 +4419,6 @@ dependencies = [ "tiny-keccak", ] -[[package]] -name = "openvm-keccak256-integration-tests" -version = "1.1.2" -dependencies = [ - "eyre", - "openvm-circuit", - "openvm-instructions", - "openvm-keccak256-circuit", - "openvm-keccak256-transpiler", - "openvm-rv32im-transpiler", - "openvm-stark-sdk", - "openvm-toolchain-tests", - "openvm-transpiler", -] - [[package]] name = "openvm-keccak256-transpiler" version = "1.1.2" diff --git a/Cargo.toml b/Cargo.toml index 1b1e242111..4ac36c31d9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -52,7 +52,6 @@ members = [ "extensions/keccak256/circuit", "extensions/keccak256/transpiler", "extensions/keccak256/guest", - "extensions/keccak256/tests", "extensions/sha256/circuit", "extensions/sha256/transpiler", "extensions/sha256/guest", diff --git a/extensions/keccak256/guest/src/lib.rs b/extensions/keccak256/guest/src/lib.rs index 459c4c910d..22d91e0154 100644 --- a/extensions/keccak256/guest/src/lib.rs +++ b/extensions/keccak256/guest/src/lib.rs @@ -1,30 +1,10 @@ #![no_std] -#[cfg(target_os = "zkvm")] -use core::mem::MaybeUninit; - /// This is custom-0 defined in RISC-V spec document pub const OPCODE: u8 = 0x0b; pub const KECCAK256_FUNCT3: u8 = 0b100; pub const KECCAK256_FUNCT7: u8 = 0; -/// The keccak256 cryptographic hash function. -#[inline(always)] -pub fn keccak256(input: &[u8]) -> [u8; 32] { - #[cfg(not(target_os = "zkvm"))] - { - let mut output = [0u8; 32]; - set_keccak256(input, &mut output); - output - } - #[cfg(target_os = "zkvm")] - { - let mut output = MaybeUninit::<[u8; 32]>::uninit(); - native_keccak256(input.as_ptr(), input.len(), output.as_mut_ptr() as *mut u8); - unsafe { output.assume_init() } - } -} - /// Native hook for keccak256 for use with `alloy-primitives` "native-keccak" feature. /// /// # Safety @@ -50,16 +30,3 @@ extern "C" fn native_keccak256(bytes: *const u8, len: usize, output: *mut u8) { rs2 = In len ); } - -/// Sets `output` to the keccak256 hash of `input`. -pub fn set_keccak256(input: &[u8], output: &mut [u8; 32]) { - #[cfg(not(target_os = "zkvm"))] - { - use tiny_keccak::Hasher; - let mut hasher = tiny_keccak::Keccak::v256(); - hasher.update(input); - hasher.finalize(output); - } - #[cfg(target_os = "zkvm")] - native_keccak256(input.as_ptr(), input.len(), output.as_mut_ptr() as *mut u8); -} diff --git a/extensions/keccak256/tests/Cargo.toml b/extensions/keccak256/tests/Cargo.toml deleted file mode 100644 index d79e18dd5f..0000000000 --- a/extensions/keccak256/tests/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -[package] -name = "openvm-keccak256-integration-tests" -description = "Integration tests for the OpenVM keccak256 extension" -version.workspace = true -authors.workspace = true -edition.workspace = true -homepage.workspace = true -repository.workspace = true - -[dependencies] -openvm-instructions = { workspace = true } -openvm-stark-sdk.workspace = true -openvm-circuit = { workspace = true, features = ["test-utils"] } -openvm-transpiler.workspace = true -openvm-keccak256-transpiler.workspace = true -openvm-keccak256-circuit.workspace = true -openvm-rv32im-transpiler.workspace = true -openvm-toolchain-tests = { path = "../../../crates/toolchain/tests" } -eyre.workspace = true - -[features] -default = ["parallel"] -parallel = ["openvm-circuit/parallel"] diff --git a/extensions/keccak256/tests/programs/Cargo.toml b/extensions/keccak256/tests/programs/Cargo.toml deleted file mode 100644 index 8eb24c3af1..0000000000 --- a/extensions/keccak256/tests/programs/Cargo.toml +++ /dev/null @@ -1,25 +0,0 @@ -[workspace] -[package] -name = "openvm-keccak256-test-programs" -version = "0.0.0" -edition = "2021" - -[dependencies] -openvm = { path = "../../../../crates/toolchain/openvm" } -openvm-platform = { path = "../../../../crates/toolchain/platform" } -openvm-keccak256-guest = { path = "../../guest" } -hex = { version = "0.4.3", default-features = false, features = ["alloc"] } -serde = { version = "1.0", default-features = false, features = [ - "alloc", - "derive", -] } - - -[features] -default = [] -std = ["serde/std", "openvm/std"] - -[profile.release] -panic = "abort" -lto = "thin" # turn on lto = fat to decrease binary size, but this optimizes out some missing extern links so we shouldn't use it for testing -# strip = "symbols" diff --git a/extensions/keccak256/tests/programs/examples/keccak.rs b/extensions/keccak256/tests/programs/examples/keccak.rs deleted file mode 100644 index d850bfdab2..0000000000 --- a/extensions/keccak256/tests/programs/examples/keccak.rs +++ /dev/null @@ -1,32 +0,0 @@ -#![cfg_attr(not(feature = "std"), no_main)] -#![cfg_attr(not(feature = "std"), no_std)] - -extern crate alloc; - -use alloc::vec::Vec; -use core::hint::black_box; - -use hex::FromHex; -use openvm_keccak256_guest::keccak256; - -openvm::entry!(main); - -pub fn main() { - let test_vectors = [ - ("", "C5D2460186F7233C927E7DB2DCC703C0E500B653CA82273B7BFAD8045D85A470"), // ShortMsgKAT_256 Len = 0 - ("CC", "EEAD6DBFC7340A56CAEDC044696A168870549A6A7F6F56961E84A54BD9970B8A"), // ShortMsgKAT_256 Len = 8 - ("B55C10EAE0EC684C16D13463F29291BF26C82E2FA0422A99C71DB4AF14DD9C7F33EDA52FD73D017CC0F2DBE734D831F0D820D06D5F89DACC485739144F8CFD4799223B1AFF9031A105CB6A029BA71E6E5867D85A554991C38DF3C9EF8C1E1E9A7630BE61CAABCA69280C399C1FB7A12D12AEFC", "0347901965D3635005E75A1095695CCA050BC9ED2D440C0372A31B348514A889"), // ShortMsgKAT_256 Len = 920 - ("2EDC282FFB90B97118DD03AAA03B145F363905E3CBD2D50ECD692B37BF000185C651D3E9726C690D3773EC1E48510E42B17742B0B0377E7DE6B8F55E00A8A4DB4740CEE6DB0830529DD19617501DC1E9359AA3BCF147E0A76B3AB70C4984C13E339E6806BB35E683AF8527093670859F3D8A0FC7D493BCBA6BB12B5F65E71E705CA5D6C948D66ED3D730B26DB395B3447737C26FAD089AA0AD0E306CB28BF0ACF106F89AF3745F0EC72D534968CCA543CD2CA50C94B1456743254E358C1317C07A07BF2B0ECA438A709367FAFC89A57239028FC5FECFD53B8EF958EF10EE0608B7F5CB9923AD97058EC067700CC746C127A61EE3", "DD1D2A92B3F3F3902F064365838E1F5F3468730C343E2974E7A9ECFCD84AA6DB"), // ShortMsgKAT_256 Len = 1952, - ("724627916C50338643E6996F07877EAFD96BDF01DA7E991D4155B9BE1295EA7D21C9391F4C4A41C75F77E5D27389253393725F1427F57914B273AB862B9E31DABCE506E558720520D33352D119F699E784F9E548FF91BC35CA147042128709820D69A8287EA3257857615EB0321270E94B84F446942765CE882B191FAEE7E1C87E0F0BD4E0CD8A927703524B559B769CA4ECE1F6DBF313FDCF67C572EC4185C1A88E86EC11B6454B371980020F19633B6B95BD280E4FBCB0161E1A82470320CEC6ECFA25AC73D09F1536F286D3F9DACAFB2CD1D0CE72D64D197F5C7520B3CCB2FD74EB72664BA93853EF41EABF52F015DD591500D018DD162815CC993595B195", "EA0E416C0F7B4F11E3F00479FDDF954F2539E5E557753BD546F69EE375A5DE29"), // LongMsgKAT_256 Len = 2048 - ("6E1CADFB2A14C5FFB1DD69919C0124ED1B9A414B2BEA1E5E422D53B022BDD13A9C88E162972EBB9852330006B13C5B2F2AFBE754AB7BACF12479D4558D19DDBB1A6289387B3AC084981DF335330D1570850B97203DBA5F20CF7FF21775367A8401B6EBE5B822ED16C39383232003ABC412B0CE0DD7C7DA064E4BB73E8C58F222A1512D5FE6D947316E02F8AA87E7AA7A3AA1C299D92E6414AE3B927DB8FF708AC86A09B24E1884743BC34067BB0412453B4A6A6509504B550F53D518E4BCC3D9C1EFDB33DA2EACCB84C9F1CAEC81057A8508F423B25DB5500E5FC86AB3B5EB10D6D0BF033A716DDE55B09FD53451BBEA644217AE1EF91FAD2B5DCC6515249C96EE7EABFD12F1EF65256BD1CFF2087DABF2F69AD1FFB9CF3BC8CA437C7F18B6095BC08D65DF99CC7F657C418D8EB109FDC91A13DC20A438941726EF24F9738B6552751A320C4EA9C8D7E8E8592A3B69D30A419C55FB6CB0850989C029AAAE66305E2C14530B39EAA86EA3BA2A7DECF4B2848B01FAA8AA91F2440B7CC4334F63061CE78AA1589BEFA38B194711697AE3AADCB15C9FBF06743315E2F97F1A8B52236ACB444069550C2345F4ED12E5B8E881CDD472E803E5DCE63AE485C2713F81BC307F25AC74D39BAF7E3BC5E7617465C2B9C309CB0AC0A570A7E46C6116B2242E1C54F456F6589E20B1C0925BF1CD5F9344E01F63B5BA9D4671ABBF920C7ED32937A074C33836F0E019DFB6B35D865312C6058DFDAFF844C8D58B75071523E79DFBAB2EA37479DF12C474584F4FF40F00F92C6BADA025CE4DF8FAF0AFB2CE75C07773907CA288167D6B011599C3DE0FFF16C1161D31DF1C1DDE217CB574ED5A33751759F8ED2B1E6979C5088B940926B9155C9D250B479948C20ACB5578DC02C97593F646CC5C558A6A0F3D8D273258887CCFF259197CB1A7380622E371FD2EB5376225EC04F9ED1D1F2F08FA2376DB5B790E73086F581064ED1C5F47E989E955D77716B50FB64B853388FBA01DAC2CEAE99642341F2DA64C56BEFC4789C051E5EB79B063F2F084DB4491C3C5AA7B4BCF7DD7A1D7CED1554FA67DCA1F9515746A237547A4A1D22ACF649FA1ED3B9BB52BDE0C6996620F8CFDB293F8BACAD02BCE428363D0BB3D391469461D212769048219220A7ED39D1F9157DFEA3B4394CA8F5F612D9AC162BF0B961BFBC157E5F863CE659EB235CF98E8444BC8C7880BDDCD0B3B389AAA89D5E05F84D0649EEBACAB4F1C75352E89F0E9D91E4ACA264493A50D2F4AED66BD13650D1F18E7199E931C78AEB763E903807499F1CD99AF81276B615BE8EC709B039584B2B57445B014F6162577F3548329FD288B0800F936FC5EA1A412E3142E609FC8E39988CA53DF4D8FB5B5FB5F42C0A01648946AC6864CFB0E92856345B08E5DF0D235261E44CFE776456B40AEF0AC1A0DFA2FE639486666C05EA196B0C1A9D346435E03965E6139B1CE10129F8A53745F80100A94AE04D996C13AC14CF2713E39DFBB19A936CF3861318BD749B1FB82F40D73D714E406CBEB3D920EA037B7DE566455CCA51980F0F53A762D5BF8A4DBB55AAC0EDDB4B1F2AED2AA3D01449D34A57FDE4329E7FF3F6BECE4456207A4225218EE9F174C2DE0FF51CEAF2A07CF84F03D1DF316331E3E725C5421356C40ED25D5ABF9D24C4570FED618CA41000455DBD759E32E2BF0B6C5E61297C20F752C3042394CE840C70943C451DD5598EB0E4953CE26E833E5AF64FC1007C04456D19F87E45636F456B7DC9D31E757622E2739573342DE75497AE181AAE7A5425756C8E2A7EEF918E5C6A968AEFE92E8B261BBFE936B19F9E69A3C90094096DAE896450E1505ED5828EE2A7F0EA3A28E6EC47C0AF711823E7689166EA07ECA00FFC493131D65F93A4E1D03E0354AFC2115CFB8D23DAE8C6F96891031B23226B8BC82F1A73DAA5BB740FC8CC36C0975BEFA0C7895A9BBC261EDB7FD384103968F7A18353D5FE56274E4515768E4353046C785267DE01E816A2873F97AAD3AB4D7234EBFD9832716F43BE8245CF0B4408BA0F0F764CE9D24947AB6ABDD9879F24FCFF10078F5894B0D64F6A8D3EA3DD92A0C38609D3C14FDC0A44064D501926BE84BF8034F1D7A8C5F382E6989BFFA2109D4FBC56D1F091E8B6FABFF04D21BB19656929D19DECB8E8291E6AE5537A169874E0FE9890DFF11FFD159AD23D749FB9E8B676E2C31313C16D1EFA06F4D7BC191280A4EE63049FCEF23042B20303AECDD412A526D7A53F760A089FBDF13F361586F0DCA76BB928EDB41931D11F679619F948A6A9E8DBA919327769006303C6EF841438A7255C806242E2E7FF4621BB0F8AFA0B4A248EAD1A1E946F3E826FBFBBF8013CE5CC814E20FEF21FA5DB19EC7FF0B06C592247B27E500EB4705E6C37D41D09E83CB0A618008CA1AAAE8A215171D817659063C2FA385CFA3C1078D5C2B28CE7312876A276773821BE145785DFF24BBB24D590678158A61EA49F2BE56FDAC8CE7F94B05D62F15ADD351E5930FD4F31B3E7401D5C0FF7FC845B165FB6ABAFD4788A8B0615FEC91092B34B710A68DA518631622BA2AAE5D19010D307E565A161E64A4319A6B261FB2F6A90533997B1AEC32EF89CF1F232696E213DAFE4DBEB1CF1D5BBD12E5FF2EBB2809184E37CD9A0E58A4E0AF099493E6D8CC98B05A2F040A7E39515038F6EE21FC25F8D459A327B83EC1A28A234237ACD52465506942646AC248EC96EBBA6E1B092475F7ADAE4D35E009FD338613C7D4C12E381847310A10E6F02C02392FC32084FBE939689BC6518BE27AF7842DEEA8043828E3DFFE3BBAC4794CA0CC78699722709F2E4B0EAE7287DEB06A27B462423EC3F0DF227ACF589043292685F2C0E73203E8588B62554FF19D6260C7FE48DF301509D33BE0D8B31D3F658C921EF7F55449FF3887D91BFB894116DF57206098E8C5835B", "3C79A3BD824542C20AF71F21D6C28DF2213A041F77DD79A328A0078123954E7B"), // LongMsgKAT_256 Len = 16664 - ("7ADC0B6693E61C269F278E6944A5A2D8300981E40022F839AC644387BFAC9086650085C2CDC585FEA47B9D2E52D65A2B29A7DC370401EF5D60DD0D21F9E2B90FAE919319B14B8C5565B0423CEFB827D5F1203302A9D01523498A4DB10374", "4CC2AFF141987F4C2E683FA2DE30042BACDCD06087D7A7B014996E9CFEAA58CE"), // ShortMsgKAT_256 Len = 752 - ]; - for (input, expected_output) in test_vectors.iter() { - let input = Vec::from_hex(input).unwrap(); - let expected_output = Vec::from_hex(expected_output).unwrap(); - let output = keccak256(&black_box(input)); - if output != *expected_output { - panic!(); - } - } -} diff --git a/extensions/keccak256/tests/src/lib.rs b/extensions/keccak256/tests/src/lib.rs deleted file mode 100644 index 69b1ef65d3..0000000000 --- a/extensions/keccak256/tests/src/lib.rs +++ /dev/null @@ -1,32 +0,0 @@ -#[cfg(test)] -mod tests { - use eyre::Result; - use openvm_circuit::utils::air_test; - use openvm_instructions::exe::VmExe; - use openvm_keccak256_circuit::Keccak256Rv32Config; - use openvm_keccak256_transpiler::Keccak256TranspilerExtension; - use openvm_rv32im_transpiler::{ - Rv32ITranspilerExtension, Rv32IoTranspilerExtension, Rv32MTranspilerExtension, - }; - use openvm_stark_sdk::p3_baby_bear::BabyBear; - use openvm_toolchain_tests::{build_example_program_at_path, get_programs_dir}; - use openvm_transpiler::{transpiler::Transpiler, FromElf}; - - type F = BabyBear; - - #[test] - fn test_keccak256() -> Result<()> { - let config = Keccak256Rv32Config::default(); - let elf = build_example_program_at_path(get_programs_dir!(), "keccak", &config)?; - let openvm_exe = VmExe::from_elf( - elf, - Transpiler::::default() - .with_extension(Keccak256TranspilerExtension) - .with_extension(Rv32ITranspilerExtension) - .with_extension(Rv32MTranspilerExtension) - .with_extension(Rv32IoTranspilerExtension), - )?; - air_test(config, openvm_exe); - Ok(()) - } -} From a3f85f6333ccca6bb057e14f72a4d16e2d8d982a Mon Sep 17 00:00:00 2001 From: Avaneesh Kulkarni Date: Tue, 22 Apr 2025 10:59:34 -0400 Subject: [PATCH 04/41] Minor macro changes for algebra re-org --- extensions/algebra/moduli-macros/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/algebra/moduli-macros/src/lib.rs b/extensions/algebra/moduli-macros/src/lib.rs index e0dd416fad..62a3f6c267 100644 --- a/extensions/algebra/moduli-macros/src/lib.rs +++ b/extensions/algebra/moduli-macros/src/lib.rs @@ -719,10 +719,10 @@ pub fn moduli_declare(input: TokenStream) -> TokenStream { fn reduce_le_bytes(bytes: &[u8]) -> Self { let mut res = ::ZERO; // base should be 2 ^ #limbs which exceeds what Self can represent - let mut base = Self::from_le_bytes(&[255u8; #limbs]); + let mut base = ::from_le_bytes(&[255u8; #limbs]); base += ::ONE; for chunk in bytes.chunks(#limbs).rev() { - res = res * &base + Self::from_le_bytes(chunk); + res = res * &base + ::from_le_bytes(chunk); } res } From 6b4bc53b3b1b75e479296d71e5e1db781a084eb1 Mon Sep 17 00:00:00 2001 From: Avaneesh Kulkarni Date: Tue, 22 Apr 2025 15:53:56 -0400 Subject: [PATCH 05/41] Add hint-based square root implementation --- Cargo.lock | 3 + docs/specs/ISA.md | 14 +- docs/specs/RISCV.md | 2 + docs/specs/transpiler.md | 2 + extensions/algebra/circuit/Cargo.toml | 1 + .../algebra/circuit/src/modular_extension.rs | 261 +++++++++++++++++- extensions/algebra/guest/Cargo.toml | 2 + extensions/algebra/guest/src/lib.rs | 15 + extensions/algebra/moduli-macros/src/lib.rs | 178 +++++++++++- extensions/algebra/tests/programs/Cargo.toml | 2 + .../algebra/tests/programs/examples/sqrt.rs | 38 +++ extensions/algebra/tests/src/lib.rs | 16 ++ extensions/algebra/transpiler/src/lib.rs | 35 ++- .../ecc/circuit/src/weierstrass_extension.rs | 90 +----- 14 files changed, 557 insertions(+), 102 deletions(-) create mode 100644 extensions/algebra/tests/programs/examples/sqrt.rs diff --git a/Cargo.lock b/Cargo.lock index 5da8e868dd..4364c9ee91 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3932,6 +3932,7 @@ version = "1.1.2" dependencies = [ "derive-new 0.6.0", "derive_more 1.0.0", + "eyre", "halo2curves-axiom", "itertools 0.14.0", "num-bigint 0.4.6", @@ -3973,6 +3974,8 @@ dependencies = [ "once_cell", "openvm-algebra-complex-macros", "openvm-algebra-moduli-macros", + "openvm-custom-insn", + "openvm-rv32im-guest", "serde-big-array", "strum_macros", ] diff --git a/docs/specs/ISA.md b/docs/specs/ISA.md index 06c8d1bb5c..c33597ca59 100644 --- a/docs/specs/ISA.md +++ b/docs/specs/ISA.md @@ -614,9 +614,7 @@ the same format that is congruent modulo `N` to the respective operation applied For each instruction, the operand `d` is fixed to be `1` and `e` is fixed to be `2`. Each instruction performs block accesses with block size `4` in address space `1` and block size `N::BLOCK_SIZE` in -address space `2`, where `N::NUM_LIMBS` is divisible by `N::BLOCK_SIZE`. Recall that `N::BLOCK_SIZE` must be a power of - -2. +address space `2`, where `N::NUM_LIMBS` is divisible by `N::BLOCK_SIZE`. Recall that `N::BLOCK_SIZE` must be a power of 2. | Name | Operands | Description | | ------------------------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | @@ -638,6 +636,16 @@ format with each limb having `LIMB_BITS` bits. | ISEQMOD_RV32\ | `a,b,c,1,2` | `[a:4]_1 = [r32{0}(b): N::NUM_LIMBS]_2 == [r32{0}(c): N::NUM_LIMBS]_2 (mod N) ? 1 : 0`. Enforces that `[r32{0}(b): N::NUM_LIMBS]_2, [r32{0}(c): N::NUM_LIMBS]_2` are less than `N` and then sets the register value of `[a:4]_1` to `1` or `0` depending on whether the two big integers are equal. | | SETUP_ISEQMOD_RV32\ | `a,b,c,1,2` | `assert([r32{0}(b): N::NUM_LIMBS]_2 == N)` in the chip that handles modular equality. For the sake of implementation convenience it also writes something (can be anything) into register value of `[a:4]_1` | +#### Phantom Sub-Instructions + + +| Name | Discriminant | Operands | Description | +| -------------- | ------------ | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| HintNonQr\ | 0x50 | `_,_,c_upper` | Use `c_upper` to determine the index of the modulus from the list of supported moduli. Reset the hint stream to equal a quadratic nonresidue modulo `N`. | +| HintSqrt\ | 0x51 | `a,_,c_upper` | Use `c_upper` to determine the index of the modulus from the list of supported moduli. Read from memory `x = [r32{0}(a): N::NUM_LIMBS]_2`. If `x` is a quadratic residue modulo `N`, reset the hint stream to `[1u8, 0u8, 0u8, 0u8]` followed by a square root of `x`. If `x` is not a quadratic residue, reset the hint stream to `[0u8; 4]` followed by a square root of `x * non_qr`, where `non_qr` is the quadratic nonresidue returned by `HintNonQr`. | + +# + #### Complex Extension Field A complex extension field `Fp2` is the quadratic extension of a prime field `Fp` with irreducible polynomial `X^2 + 1`. diff --git a/docs/specs/RISCV.md b/docs/specs/RISCV.md index d921872497..ef82c392fe 100644 --- a/docs/specs/RISCV.md +++ b/docs/specs/RISCV.md @@ -157,6 +157,8 @@ We use `config.mod_idx(N)` to denote the index of `N` in this list. In the list | divmod\ | R | 0101011 | 000 | `idx*8+3` | `[rd: N::NUM_LIMBS]_2 = [rs1: N::NUM_LIMBS]_2 / [rs2: N::NUM_LIMBS]_2 (mod N)` (undefined when `gcd([rs2: N::NUM_LIMBS]_2, N) != 1`) | | iseqmod\ | R | 0101011 | 000 | `idx*8+4` | `rd = [rs1: N::NUM_LIMBS]_2 == [rs2: N::NUM_LIMBS]_2 (mod N) ? 1 : 0`. If `rd != x0`, enforces that `[rs1: N::NUM_LIMBS]_2` and `[rs2: N::NUM_LIMBS]_2` are both less than `N` and then sets `rd` equal to boolean comparison value. If `rd = x0`, this is a no-op. | | setup\ | R | 0101011 | 000 | `idx*8+5` | `assert([rs1: N::NUM_LIMBS]_2 == N)` in the chip defined by the register index of `rs2`. For the sake of implementation convenience it also writes an unconstrained value into `[rd: N::NUM_LIMBS]_2` if `ind(rs2) = 0,1` (for add_sub, mul_div) or it overwrites the register value of `rd` with an unconstrained value if `ind(rs2) = 2` (for iseq). If `ind(rs2) = 2`, then the instruction is **invalid** if `rd = x0`. | +| hint_non_qr\ | R | 0101011 | 001 | `idx*8+6` | Reset the hint stream to equal `non_qr` where `non_qr` is a quadratic nonresidue modulo `N`. The same `non_qr` is returned in each execution of this instruction. `rd`, `rs1`, and `rs2` should be `x0`. | +| hint_sqrt\ | R | 0101011 | 001 | `idx*8+7` | Read `x = [rs1: N::NUM_LIMBS]_2`. If `x` is a quadratic residue modulo `N` then reset the hint stream to `[1u0, 0u8, 0u8, 0u8]` concatenated with a square root of `x`. If `x` is not a quadratic residue, then reset the hint stream to `[0u8; 4]` concatenated with a square root of `x * non_qr` where `non_qr` is the quadratic nonresidue returned by `hint_non_qr`. `rd` and `rs2` should be `x0`. | Since `funct7` is 7-bits, up to 16 moduli can be supported simultaneously. We use `idx*8` to leave some room for future expansion. diff --git a/docs/specs/transpiler.md b/docs/specs/transpiler.md index a8b94ef2b0..e334d446a4 100644 --- a/docs/specs/transpiler.md +++ b/docs/specs/transpiler.md @@ -186,6 +186,8 @@ Each VM extension's behavior is specified below. | divmod\ | DIVMOD_RV32\ `ind(rd), ind(rs1), ind(rs2), 1, 2` | | iseqmod\ | ISEQMOD_RV32\ `ind(rd), ind(rs1), ind(rs2), 1, 2` if `rd != x0`, otherwise PHANTOM `_, _, disc(Nop)` | | setup\ | SETUP_ADDSUBMOD_RV32\ `ind(rd), ind(rs1), x0, 1, 2` if `ind(rs2) = 0`, SETUP_MULDIVMOD_RV32\ `ind(rd), ind(rs1), x0, 1, 2` if `ind(rs2) = 1`, SETUP_ISEQMOD_RV32\ `ind(rd), ind(rs1), x0, 1, 2` if `ind(rs2) = 2` | +| hint_non_qr | PHANTOM `0, 0, phantom_c(curve_idx, HintNonQr)` | +| hint_sqrt | PHANTOM `ind(rs1), 0, phantom_c(curve_idx, HintSqrt)` | #### Complex Extension Field Arithmetic diff --git a/extensions/algebra/circuit/Cargo.toml b/extensions/algebra/circuit/Cargo.toml index 7949fb0946..258bff450b 100644 --- a/extensions/algebra/circuit/Cargo.toml +++ b/extensions/algebra/circuit/Cargo.toml @@ -30,6 +30,7 @@ derive-new = { workspace = true } serde.workspace = true serde_with = { workspace = true } serde-big-array = { workspace = true } +eyre = { workspace = true } [dev-dependencies] halo2curves-axiom = { workspace = true } diff --git a/extensions/algebra/circuit/src/modular_extension.rs b/extensions/algebra/circuit/src/modular_extension.rs index 6050045aed..1ff331566d 100644 --- a/extensions/algebra/circuit/src/modular_extension.rs +++ b/extensions/algebra/circuit/src/modular_extension.rs @@ -1,6 +1,7 @@ use derive_more::derive::From; -use num_bigint::BigUint; -use openvm_algebra_transpiler::Rv32ModularArithmeticOpcode; +use num_bigint::{BigUint, RandBigInt}; +use num_traits::{FromPrimitive, One}; +use openvm_algebra_transpiler::{ModularPhantom, Rv32ModularArithmeticOpcode}; use openvm_circuit::{ self, arch::{SystemPort, VmExtension, VmInventory, VmInventoryBuilder, VmInventoryError}, @@ -11,10 +12,11 @@ use openvm_circuit_primitives::bitwise_op_lookup::{ BitwiseOperationLookupBus, SharedBitwiseOperationLookupChip, }; use openvm_circuit_primitives_derive::{Chip, ChipUsageGetter}; -use openvm_instructions::{LocalOpcode, VmOpcode}; +use openvm_instructions::{LocalOpcode, PhantomDiscriminant, VmOpcode}; use openvm_mod_circuit_builder::ExprBuilderConfig; use openvm_rv32_adapters::{Rv32IsEqualModAdapterChip, Rv32VecHeapAdapterChip}; use openvm_stark_backend::p3_field::PrimeField32; +use rand::{rngs::StdRng, SeedableRng}; use serde::{Deserialize, Serialize}; use serde_with::{serde_as, DisplayFromStr}; use strum::EnumCount; @@ -27,7 +29,7 @@ use crate::modular_chip::{ #[derive(Clone, Debug, derive_new::new, Serialize, Deserialize)] pub struct ModularExtension { #[serde_as(as = "Vec")] - pub supported_modulus: Vec, + pub supported_moduli: Vec, } impl ModularExtension { @@ -99,7 +101,7 @@ impl VmExtension for ModularExtension { let iseq_opcodes = (Rv32ModularArithmeticOpcode::IS_EQ as usize) ..=(Rv32ModularArithmeticOpcode::SETUP_ISEQ as usize); - for (i, modulus) in self.supported_modulus.iter().enumerate() { + for (i, modulus) in self.supported_moduli.iter().enumerate() { // determine the number of bytes needed to represent a prime field element let bytes = modulus.bits().div_ceil(8); let start_offset = @@ -229,8 +231,257 @@ impl VmExtension for ModularExtension { } else { panic!("Modulus too large"); } + + let non_qr_hint_sub_ex = phantom::NonQrHintSubEx::new(self.supported_moduli.clone()); + builder.add_phantom_sub_executor( + non_qr_hint_sub_ex.clone(), + PhantomDiscriminant(ModularPhantom::HintNonQr as u16), + )?; + + let sqrt_hint_sub_ex = phantom::SqrtHintSubEx::new(non_qr_hint_sub_ex); + builder.add_phantom_sub_executor( + sqrt_hint_sub_ex, + PhantomDiscriminant(ModularPhantom::HintSqrt as u16), + )?; } Ok(inventory) } } + +pub(crate) mod phantom { + use std::{ + iter::{once, repeat}, + ops::Deref, + }; + + use eyre::bail; + use num_bigint::BigUint; + use openvm_circuit::{ + arch::{PhantomSubExecutor, Streams}, + system::memory::MemoryController, + }; + use openvm_instructions::{riscv::RV32_MEMORY_AS, PhantomDiscriminant}; + use openvm_rv32im_circuit::adapters::unsafe_read_rv32_register; + use openvm_stark_backend::p3_field::PrimeField32; + + use super::{find_non_qr, mod_sqrt}; + + #[derive(derive_new::new)] + pub struct SqrtHintSubEx(NonQrHintSubEx); + + impl Deref for SqrtHintSubEx { + type Target = NonQrHintSubEx; + + fn deref(&self) -> &NonQrHintSubEx { + &self.0 + } + } + + // Given x returns either a sqrt of x or a sqrt of x * non_qr, whichever exists. + // Note that non_qr is fixed for each modulus. + impl PhantomSubExecutor for SqrtHintSubEx { + fn phantom_execute( + &mut self, + memory: &MemoryController, + streams: &mut Streams, + _: PhantomDiscriminant, + a: F, + _: F, + c_upper: u16, + ) -> eyre::Result<()> { + let mod_idx = c_upper as usize; + if mod_idx >= self.supported_moduli.len() { + bail!( + "Modulus index {mod_idx} out of range: {} supported moduli", + self.supported_moduli.len() + ); + } + let modulus = &self.supported_moduli[mod_idx]; + let num_limbs: usize = if modulus.bits().div_ceil(8) <= 32 { + 32 + } else if modulus.bits().div_ceil(8) <= 48 { + 48 + } else { + bail!("Modulus too large") + }; + + let rs1 = unsafe_read_rv32_register(memory, a); + let mut x_limbs: Vec = Vec::with_capacity(num_limbs); + for i in 0..num_limbs { + let limb = memory.unsafe_read_cell( + F::from_canonical_u32(RV32_MEMORY_AS), + F::from_canonical_u32(rs1 + i as u32), + ); + x_limbs.push(limb.as_canonical_u32() as u8); + } + let x = BigUint::from_bytes_le(&x_limbs); + + let (success, sqrt) = match mod_sqrt(&x, modulus, &self.non_qrs[mod_idx]) { + Some(sqrt) => (true, sqrt), + None => { + let sqrt = mod_sqrt( + &(&x * &self.non_qrs[mod_idx]), + modulus, + &self.non_qrs[mod_idx], + ) + .expect("Either x or x * non_qr should be a square"); + (false, sqrt) + } + }; + + let hint_bytes = once(F::from_bool(success)) + .chain(repeat(F::ZERO)) + .take(4) + .chain( + sqrt.to_bytes_le() + .into_iter() + .map(F::from_canonical_u8) + .chain(repeat(F::ZERO)) + .take(num_limbs), + ) + .collect(); + streams.hint_stream = hint_bytes; + Ok(()) + } + } + + #[derive(Clone)] + pub struct NonQrHintSubEx { + pub supported_moduli: Vec, + pub non_qrs: Vec, + } + + impl NonQrHintSubEx { + pub fn new(supported_moduli: Vec) -> Self { + let non_qrs = supported_moduli.iter().map(find_non_qr).collect(); + Self { + supported_moduli, + non_qrs, + } + } + } + + impl PhantomSubExecutor for NonQrHintSubEx { + fn phantom_execute( + &mut self, + _: &MemoryController, + streams: &mut Streams, + _: PhantomDiscriminant, + _: F, + _: F, + c_upper: u16, + ) -> eyre::Result<()> { + let mod_idx = c_upper as usize; + if mod_idx >= self.supported_moduli.len() { + bail!( + "Modulus index {mod_idx} out of range: {} supported moduli", + self.supported_moduli.len() + ); + } + let modulus = &self.supported_moduli[mod_idx]; + + let num_limbs: usize = if modulus.bits().div_ceil(8) <= 32 { + 32 + } else if modulus.bits().div_ceil(8) <= 48 { + 48 + } else { + bail!("Modulus too large") + }; + + let hint_bytes = self.non_qrs[mod_idx] + .to_bytes_le() + .into_iter() + .map(F::from_canonical_u8) + .chain(repeat(F::ZERO)) + .take(num_limbs) + .collect(); + streams.hint_stream = hint_bytes; + Ok(()) + } + } +} + +/// Find the square root of `x` modulo `modulus` with `non_qr` a +/// quadratic nonresidue of the field. +pub fn mod_sqrt(x: &BigUint, modulus: &BigUint, non_qr: &BigUint) -> Option { + if modulus % 4u32 == BigUint::from_u8(3).unwrap() { + // x^(1/2) = x^((p+1)/4) when p = 3 mod 4 + let exponent = (modulus + BigUint::one()) >> 2; + let ret = x.modpow(&exponent, modulus); + if &ret * &ret % modulus == x % modulus { + Some(ret) + } else { + None + } + } else { + // Tonelli-Shanks algorithm + // https://en.wikipedia.org/wiki/Tonelli%E2%80%93Shanks_algorithm#The_algorithm + let mut q = modulus - BigUint::one(); + let mut s = 0; + while &q % 2u32 == BigUint::ZERO { + s += 1; + q /= 2u32; + } + let z = non_qr; + let mut m = s; + let mut c = z.modpow(&q, modulus); + let mut t = x.modpow(&q, modulus); + let mut r = x.modpow(&((q + BigUint::one()) >> 1), modulus); + loop { + if t == BigUint::ZERO { + return Some(BigUint::ZERO); + } + if t == BigUint::one() { + return Some(r); + } + let mut i = 0; + let mut tmp = t.clone(); + while tmp != BigUint::one() && i < m { + tmp = &tmp * &tmp % modulus; + i += 1; + } + if i == m { + // self is not a quadratic residue + return None; + } + for _ in 0..m - i - 1 { + c = &c * &c % modulus; + } + let b = c; + m = i; + c = &b * &b % modulus; + t = ((t * &b % modulus) * &b) % modulus; + r = (r * b) % modulus; + } + } +} + +// Returns a non-quadratic residue in the field +pub fn find_non_qr(modulus: &BigUint) -> BigUint { + if modulus % 4u32 == BigUint::from(3u8) { + // p = 3 mod 4 then -1 is a quadratic residue + modulus - BigUint::one() + } else if modulus % 8u32 == BigUint::from(5u8) { + // p = 5 mod 8 then 2 is a non-quadratic residue + // since 2^((p-1)/2) = (-1)^((p^2-1)/8) + BigUint::from_u8(2u8).unwrap() + } else { + let mut rng = StdRng::from_entropy(); + let mut non_qr = rng.gen_biguint_range( + &BigUint::from_u8(2).unwrap(), + &(modulus - BigUint::from_u8(1).unwrap()), + ); + // To check if non_qr is a quadratic nonresidue, we compute non_qr^((p-1)/2) + // If the result is p-1, then non_qr is a quadratic nonresidue + // Otherwise, non_qr is a quadratic residue + let exponent = (modulus - BigUint::one()) >> 1; + while non_qr.modpow(&exponent, modulus) != modulus - BigUint::one() { + non_qr = rng.gen_biguint_range( + &BigUint::from_u8(2).unwrap(), + &(modulus - BigUint::from_u8(1).unwrap()), + ); + } + non_qr + } +} diff --git a/extensions/algebra/guest/Cargo.toml b/extensions/algebra/guest/Cargo.toml index 64f4e61d51..89548bbb60 100644 --- a/extensions/algebra/guest/Cargo.toml +++ b/extensions/algebra/guest/Cargo.toml @@ -10,6 +10,8 @@ repository.workspace = true [dependencies] openvm-algebra-moduli-macros = { workspace = true } openvm-algebra-complex-macros = { workspace = true } +openvm-rv32im-guest = { workspace = true } +openvm-custom-insn = { workspace = true } serde-big-array.workspace = true strum_macros.workspace = true once_cell = { workspace = true, features = ["race", "alloc"] } diff --git a/extensions/algebra/guest/src/lib.rs b/extensions/algebra/guest/src/lib.rs index c93bb8920b..448035987c 100644 --- a/extensions/algebra/guest/src/lib.rs +++ b/extensions/algebra/guest/src/lib.rs @@ -17,6 +17,8 @@ pub enum ModArithBaseFunct7 { DivMod, IsEqMod, SetupMod, + HintNonQr, + HintSqrt, } impl ModArithBaseFunct7 { @@ -52,8 +54,13 @@ use core::{ pub use field::Field; #[cfg(not(target_os = "zkvm"))] use num_bigint::BigUint; +pub use once_cell; pub use openvm_algebra_complex_macros as complex_macros; pub use openvm_algebra_moduli_macros as moduli_macros; +#[cfg(target_os = "zkvm")] +pub use openvm_custom_insn; +#[cfg(target_os = "zkvm")] +pub use openvm_rv32im_guest; pub use serde_big_array::BigArray; use strum_macros::FromRepr; @@ -233,3 +240,11 @@ pub trait Reduce: Sized { Self::reduce_le_bytes(&bytes.iter().rev().copied().collect::>()) } } + +// Note that we use a hint-based approach to prove whether the square root exists. +// This approach works for prime moduli, but not necessarily for composite moduli, +// which is why the Sqrt trait requires the Field trait, not just the IntMod trait. +pub trait Sqrt: Field { + /// Returns a square root of `self` if it exists. + fn sqrt(&self) -> Option; +} diff --git a/extensions/algebra/moduli-macros/src/lib.rs b/extensions/algebra/moduli-macros/src/lib.rs index 62a3f6c267..1b64b125e7 100644 --- a/extensions/algebra/moduli-macros/src/lib.rs +++ b/extensions/algebra/moduli-macros/src/lib.rs @@ -34,6 +34,7 @@ pub fn moduli_declare(input: TokenStream) -> TokenStream { let struct_name = item.name.to_string(); let struct_name = syn::Ident::new(&struct_name, span.into()); let mut modulus: Option = None; + let mut prime: Option = None; for param in item.params { match param.name.to_string().as_str() { "modulus" => { @@ -44,9 +45,28 @@ pub fn moduli_declare(input: TokenStream) -> TokenStream { { modulus = Some(value.value()); } else { - return syn::Error::new_spanned(param.value, "Expected a string literal") - .to_compile_error() - .into(); + return syn::Error::new_spanned( + param.value, + "Expected a string literal for macro argument `modulus`", + ) + .to_compile_error() + .into(); + } + } + "prime" => { + if let syn::Expr::Lit(syn::ExprLit { + lit: syn::Lit::Bool(value), + .. + }) = param.value + { + prime = Some(value.value()); + } else { + return syn::Error::new_spanned( + param.value, + "Expected a boolean literal for macro argument `prime`", + ) + .to_compile_error() + .into(); } } _ => { @@ -64,6 +84,8 @@ pub fn moduli_declare(input: TokenStream) -> TokenStream { let mut limbs = modulus_bytes.len(); let mut block_size = 32; + let prime = prime.unwrap_or(false); + if limbs <= 32 { limbs = 32; } else if limbs <= 48 { @@ -98,6 +120,8 @@ pub fn moduli_declare(input: TokenStream) -> TokenStream { create_extern_func!(mul_extern_func); create_extern_func!(div_extern_func); create_extern_func!(is_eq_extern_func); + create_extern_func!(hint_sqrt_extern_func); + create_extern_func!(hint_non_qr_extern_func); create_extern_func!(moduli_setup_extern_func); let block_size = proc_macro::Literal::usize_unsuffixed(block_size); @@ -730,6 +754,154 @@ pub fn moduli_declare(input: TokenStream) -> TokenStream { }); output.push(result); + + if prime { + // implement Field and Sqrt traits for prime moduli + let field_and_sqrt_impl = TokenStream::from(quote::quote_spanned! { span.into() => + impl ::openvm_algebra_guest::Field for #struct_name { + const ZERO: Self = ::ZERO; + const ONE: Self = ::ONE; + + type SelfRef<'a> = &'a Self; + + fn double_assign(&mut self) { + ::openvm_algebra_guest::IntMod::double_assign(self); + } + + fn square_assign(&mut self) { + ::openvm_algebra_guest::IntMod::square_assign(self); + } + + } + + impl openvm_algebra_guest::Sqrt for #struct_name { + // Returns a sqrt of self if it exists, otherwise None. + // Note that we use a hint-based approach to prove whether the square root exists. + // This approach works for prime moduli, but not necessarily for composite moduli, + // which is why we have the sqrt method in the Field trait, not the IntMod trait. + fn sqrt(&self) -> Option { + match self.honest_host_sqrt() { + // self is a square + Some(Some(sqrt)) => Some(sqrt), + // self is not a square + Some(None) => None, + // host is dishonest + None => { + // host is dishonest, enter infinite loop + loop { + openvm::io::println("ERROR: Square root hint is invalid. Entering infinite loop."); + } + } + } + } + } + + impl #struct_name { + // Returns None if the hint is incorrect (i.e. the host is dishonest) + // Returns Some(None) if the hint proves that self is not a quadratic residue + // Otherwise, returns Some(Some(sqrt)) where sqrt is a square root of self + fn honest_host_sqrt(&self) -> Option> { + let (is_square, sqrt) = self.hint_sqrt_impl()?; + + if is_square { + // ensure sqrt < modulus + ::assert_reduced(&sqrt); + + if &(&sqrt * &sqrt) == self { + Some(Some(sqrt)) + } else { + None + } + } else { + // ensure sqrt < modulus + ::assert_reduced(&sqrt); + + if &sqrt * &sqrt == self * Self::get_non_qr() { + Some(None) + } else { + None + } + } + } + + + // Returns None if the hint is malformed. + // Otherwise, returns Some((is_square, sqrt)) where sqrt is a square root of self if is_square is true, + // and a square root of self * non_qr if is_square is false. + fn hint_sqrt_impl(&self) -> Option<(bool, Self)> { + #[cfg(not(target_os = "zkvm"))] + { + unimplemented!(); + } + #[cfg(target_os = "zkvm")] + { + use ::openvm_algebra_guest::{openvm_custom_insn, openvm_rv32im_guest}; // needed for hint_store_u32! and hint_buffer_u32! + + let is_square = core::mem::MaybeUninit::::uninit(); + let sqrt = core::mem::MaybeUninit::<#struct_name>::uninit(); + unsafe { + #hint_sqrt_extern_func(self as *const #struct_name as usize); + let is_square_ptr = is_square.as_ptr() as *const u32; + openvm_rv32im_guest::hint_store_u32!(is_square_ptr); + openvm_rv32im_guest::hint_buffer_u32!(sqrt.as_ptr() as *const u8, <#struct_name as ::openvm_algebra_guest::IntMod>::NUM_LIMBS / 4); + let is_square = is_square.assume_init(); + if is_square == 0 || is_square == 1 { + Some((is_square == 1, sqrt.assume_init())) + } else { + None + } + } + } + } + + // Generate a non quadratic residue by using a hint + fn init_non_qr() -> alloc::boxed::Box<#struct_name> { + #[cfg(not(target_os = "zkvm"))] + { + unimplemented!(); + } + #[cfg(target_os = "zkvm")] + { + use ::openvm_algebra_guest::{openvm_custom_insn, openvm_rv32im_guest}; // needed for hint_buffer_u32! + + let mut non_qr_uninit = core::mem::MaybeUninit::::uninit(); + let mut non_qr; + unsafe { + #hint_non_qr_extern_func(); + let ptr = non_qr_uninit.as_ptr() as *const u8; + openvm_rv32im_guest::hint_buffer_u32!(ptr, ::NUM_LIMBS / 4); + non_qr = non_qr_uninit.assume_init(); + } + // ensure non_qr < modulus + ::assert_reduced(&non_qr); + + use ::openvm_algebra_guest::{DivUnsafe, ExpBytes}; + // construct exp = (p-1)/2 as an integer by first constraining exp = (p-1)/2 (mod p) and then exp < p + let exp = -::ONE.div_unsafe(Self::from_const_u8(2)); + ::assert_reduced(&exp); + + if non_qr.exp_bytes(true, &::to_be_bytes(&exp)) != -::ONE + { + // non_qr is not a non quadratic residue, so host is dishonest + loop { + openvm::io::println("ERROR: Non quadratic residue hint is invalid. Entering infinite loop."); + } + } + + alloc::boxed::Box::new(non_qr) + } + } + + // This function is public for use in tests + pub fn get_non_qr() -> &'static #struct_name { + static non_qr: ::openvm_algebra_guest::once_cell::race::OnceBox<#struct_name> = ::openvm_algebra_guest::once_cell::race::OnceBox::new(); + &non_qr.get_or_init(Self::init_non_qr) + } + } + }); + + output.push(field_and_sqrt_impl); + } } TokenStream::from_iter(output) diff --git a/extensions/algebra/tests/programs/Cargo.toml b/extensions/algebra/tests/programs/Cargo.toml index ef4a8c4c1b..9d067de693 100644 --- a/extensions/algebra/tests/programs/Cargo.toml +++ b/extensions/algebra/tests/programs/Cargo.toml @@ -11,6 +11,8 @@ openvm-platform = { path = "../../../../crates/toolchain/platform" } openvm-algebra-guest = { path = "../../guest" } openvm-algebra-moduli-macros = { path = "../../../algebra/moduli-macros", default-features = false } openvm-algebra-complex-macros = { path = "../../../algebra/complex-macros", default-features = false } +#openvm-custom-insn = { path = "../../../../crates/toolchain/custom_insn" } + num-bigint = { version = "0.4", default-features = false } serde = { version = "1.0", default-features = false, features = [ "alloc", diff --git a/extensions/algebra/tests/programs/examples/sqrt.rs b/extensions/algebra/tests/programs/examples/sqrt.rs new file mode 100644 index 0000000000..ee995ae81a --- /dev/null +++ b/extensions/algebra/tests/programs/examples/sqrt.rs @@ -0,0 +1,38 @@ +#![cfg_attr(not(feature = "std"), no_main)] +#![cfg_attr(not(feature = "std"), no_std)] + +use openvm_algebra_guest::{Field, IntMod, Sqrt}; + +extern crate alloc; + +openvm::entry!(main); + +openvm_algebra_moduli_macros::moduli_declare! { + Secp256k1Coord { + modulus = "0xFFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE FFFFFC2F", + prime = true + } +} + +openvm_algebra_moduli_macros::moduli_init!( + "0xFFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE FFFFFC2F" +); + +pub fn main() { + setup_all_moduli(); + + let a = Secp256k1Coord::from_u32(4); + let sqrt = a.sqrt(); + assert_eq!(sqrt, Some(Secp256k1Coord::from_u32(2))); + + let b = ::ZERO - ::ONE; + let sqrt = b.sqrt(); + // -1 is not a quadratic residue modulo p when p = 3 mod 4 + // See https://math.stackexchange.com/questions/735400/if-p-equiv-3-mod-4-with-p-prime-prove-1-is-a-non-quadratic-residue-modulo + assert_eq!(sqrt, None); + + let expected = b * Secp256k1Coord::from_u32(2).invert(); + let c = expected.square(); + let result = c.sqrt(); + assert!(result == Some(expected.clone()) || result == Some(-expected)); +} diff --git a/extensions/algebra/tests/src/lib.rs b/extensions/algebra/tests/src/lib.rs index 3232de8ec1..c7668ed45e 100644 --- a/extensions/algebra/tests/src/lib.rs +++ b/extensions/algebra/tests/src/lib.rs @@ -165,4 +165,20 @@ mod tests { .unwrap(); air_test(config, openvm_exe); } + + #[test] + fn test_sqrt() -> Result<()> { + let elf = build_example_program_at_path(get_programs_dir!(), "sqrt")?; + let openvm_exe = VmExe::from_elf( + elf, + Transpiler::::default() + .with_extension(Rv32ITranspilerExtension) + .with_extension(Rv32MTranspilerExtension) + .with_extension(Rv32IoTranspilerExtension) + .with_extension(ModularTranspilerExtension), + )?; + let config = Rv32ModularConfig::new(vec![SECP256K1_CONFIG.modulus.clone()]); + air_test(config, openvm_exe); + Ok(()) + } } diff --git a/extensions/algebra/transpiler/src/lib.rs b/extensions/algebra/transpiler/src/lib.rs index 8785a93480..74d3f9182a 100644 --- a/extensions/algebra/transpiler/src/lib.rs +++ b/extensions/algebra/transpiler/src/lib.rs @@ -3,7 +3,8 @@ use openvm_algebra_guest::{ MODULAR_ARITHMETIC_FUNCT3, OPCODE, }; use openvm_instructions::{ - instruction::Instruction, riscv::RV32_REGISTER_NUM_LIMBS, LocalOpcode, VmOpcode, + instruction::Instruction, riscv::RV32_REGISTER_NUM_LIMBS, LocalOpcode, PhantomDiscriminant, + VmOpcode, }; use openvm_instructions_derive::LocalOpcode; use openvm_stark_backend::p3_field::PrimeField32; @@ -28,6 +29,13 @@ pub enum Rv32ModularArithmeticOpcode { SETUP_ISEQ, } +#[derive(Copy, Clone, Debug, PartialEq, Eq, FromRepr)] +#[repr(u16)] +pub enum ModularPhantom { + HintNonQr = 0x50, + HintSqrt = 0x51, +} + #[derive( Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, EnumCount, EnumIter, FromRepr, LocalOpcode, )] @@ -73,10 +81,10 @@ impl TranspilerExtension for ModularTranspilerExtension { Rv32ModularArithmeticOpcode::COUNT <= ModArithBaseFunct7::MODULAR_ARITHMETIC_MAX_KINDS as usize ); - let mod_idx_shift = ((dec_insn.funct7 as u8) + let mod_idx = ((dec_insn.funct7 as u8) / ModArithBaseFunct7::MODULAR_ARITHMETIC_MAX_KINDS) - as usize - * Rv32ModularArithmeticOpcode::COUNT; + as usize; + let mod_idx_shift = mod_idx * Rv32ModularArithmeticOpcode::COUNT; if base_funct7 == ModArithBaseFunct7::SetupMod as u8 { let local_opcode = match dec_insn.rs2 { 0 => Rv32ModularArithmeticOpcode::SETUP_ADDSUB, @@ -100,6 +108,25 @@ impl TranspilerExtension for ModularTranspilerExtension { F::ZERO, )) } + } else if base_funct7 == ModArithBaseFunct7::HintNonQr as u8 { + assert_eq!(dec_insn.rd, 0); + assert_eq!(dec_insn.rs1, 0); + assert_eq!(dec_insn.rs2, 0); + Some(Instruction::phantom( + PhantomDiscriminant(ModularPhantom::HintNonQr as u16), + F::ZERO, + F::ZERO, + mod_idx as u16, + )) + } else if base_funct7 == ModArithBaseFunct7::HintSqrt as u8 { + assert_eq!(dec_insn.rd, 0); + assert_eq!(dec_insn.rs2, 0); + Some(Instruction::phantom( + PhantomDiscriminant(ModularPhantom::HintSqrt as u16), + F::from_canonical_usize(RV32_REGISTER_NUM_LIMBS * dec_insn.rs1), + F::ZERO, + mod_idx as u16, + )) } else { let global_opcode = match ModArithBaseFunct7::from_repr(base_funct7) { Some(ModArithBaseFunct7::AddMod) => { diff --git a/extensions/ecc/circuit/src/weierstrass_extension.rs b/extensions/ecc/circuit/src/weierstrass_extension.rs index 64d01a2711..91653d4913 100644 --- a/extensions/ecc/circuit/src/weierstrass_extension.rs +++ b/extensions/ecc/circuit/src/weierstrass_extension.rs @@ -248,9 +248,10 @@ pub(crate) mod phantom { }; use eyre::bail; - use num_bigint::{BigUint, RandBigInt}; + use num_bigint::BigUint; use num_integer::Integer; - use num_traits::{FromPrimitive, One}; + use num_traits::One; + use openvm_algebra_circuit::{find_non_qr, mod_sqrt}; use openvm_circuit::{ arch::{PhantomSubExecutor, Streams}, system::memory::MemoryController, @@ -259,7 +260,6 @@ pub(crate) mod phantom { use openvm_instructions::{riscv::RV32_MEMORY_AS, PhantomDiscriminant}; use openvm_rv32im_circuit::adapters::unsafe_read_rv32_register; use openvm_stark_backend::p3_field::PrimeField32; - use rand::{rngs::StdRng, SeedableRng}; use super::CurveConfig; @@ -382,61 +382,6 @@ pub(crate) mod phantom { } } - /// Find the square root of `x` modulo `modulus` with `non_qr` a - /// quadratic nonresidue of the field. - pub fn mod_sqrt(x: &BigUint, modulus: &BigUint, non_qr: &BigUint) -> Option { - if modulus % 4u32 == BigUint::from_u8(3).unwrap() { - // x^(1/2) = x^((p+1)/4) when p = 3 mod 4 - let exponent = (modulus + BigUint::one()) >> 2; - let ret = x.modpow(&exponent, modulus); - if &ret * &ret % modulus == x % modulus { - Some(ret) - } else { - None - } - } else { - // Tonelli-Shanks algorithm - // https://en.wikipedia.org/wiki/Tonelli%E2%80%93Shanks_algorithm#The_algorithm - let mut q = modulus - BigUint::one(); - let mut s = 0; - while &q % 2u32 == BigUint::ZERO { - s += 1; - q /= 2u32; - } - let z = non_qr; - let mut m = s; - let mut c = z.modpow(&q, modulus); - let mut t = x.modpow(&q, modulus); - let mut r = x.modpow(&((q + BigUint::one()) >> 1), modulus); - loop { - if t == BigUint::ZERO { - return Some(BigUint::ZERO); - } - if t == BigUint::one() { - return Some(r); - } - let mut i = 0; - let mut tmp = t.clone(); - while tmp != BigUint::one() && i < m { - tmp = &tmp * &tmp % modulus; - i += 1; - } - if i == m { - // self is not a quadratic residue - return None; - } - for _ in 0..m - i - 1 { - c = &c * &c % modulus; - } - let b = c; - m = i; - c = &b * &b % modulus; - t = ((t * &b % modulus) * &b) % modulus; - r = (r * b) % modulus; - } - } - } - #[derive(Clone)] pub struct NonQrHintSubEx { pub supported_curves: Vec, @@ -494,33 +439,4 @@ pub(crate) mod phantom { Ok(()) } } - - // Returns a non-quadratic residue in the field - fn find_non_qr(modulus: &BigUint) -> BigUint { - if modulus % 4u32 == BigUint::from(3u8) { - // p = 3 mod 4 then -1 is a quadratic residue - modulus - BigUint::one() - } else if modulus % 8u32 == BigUint::from(5u8) { - // p = 5 mod 8 then 2 is a non-quadratic residue - // since 2^((p-1)/2) = (-1)^((p^2-1)/8) - BigUint::from_u8(2u8).unwrap() - } else { - let mut rng = StdRng::from_entropy(); - let mut non_qr = rng.gen_biguint_range( - &BigUint::from_u8(2).unwrap(), - &(modulus - BigUint::from_u8(1).unwrap()), - ); - // To check if non_qr is a quadratic nonresidue, we compute non_qr^((p-1)/2) - // If the result is p-1, then non_qr is a quadratic nonresidue - // Otherwise, non_qr is a quadratic residue - let exponent = (modulus - BigUint::one()) >> 1; - while non_qr.modpow(&exponent, modulus) != modulus - BigUint::one() { - non_qr = rng.gen_biguint_range( - &BigUint::from_u8(2).unwrap(), - &(modulus - BigUint::from_u8(1).unwrap()), - ); - } - non_qr - } - } } From d0187c44c064dbc0fbc1d989ff3603c5dc6853c3 Mon Sep 17 00:00:00 2001 From: Avaneesh Kulkarni Date: Fri, 25 Apr 2025 15:25:18 -0400 Subject: [PATCH 06/41] Fix rebase mistakes --- .../algebra/circuit/src/fp2_extension.rs | 2 +- .../algebra/circuit/src/modular_extension.rs | 2 +- extensions/algebra/guest/src/lib.rs | 1 - extensions/algebra/moduli-macros/src/lib.rs | 43 ++++++++++++++++++- .../algebra/tests/programs/examples/sqrt.rs | 6 +-- extensions/algebra/tests/src/lib.rs | 4 +- 6 files changed, 47 insertions(+), 11 deletions(-) diff --git a/extensions/algebra/circuit/src/fp2_extension.rs b/extensions/algebra/circuit/src/fp2_extension.rs index 30a55cf015..20ce310d23 100644 --- a/extensions/algebra/circuit/src/fp2_extension.rs +++ b/extensions/algebra/circuit/src/fp2_extension.rs @@ -36,7 +36,7 @@ impl Fp2Extension { pub fn generate_complex_init(&self, modular_config: &ModularExtension) -> String { fn get_index_of_modulus(modulus: &BigUint, modular_config: &ModularExtension) -> usize { modular_config - .supported_modulus + .supported_moduli .iter() .position(|m| m == modulus) .expect("Modulus used in Fp2Extension not found in ModularExtension") diff --git a/extensions/algebra/circuit/src/modular_extension.rs b/extensions/algebra/circuit/src/modular_extension.rs index 1ff331566d..679c0655ab 100644 --- a/extensions/algebra/circuit/src/modular_extension.rs +++ b/extensions/algebra/circuit/src/modular_extension.rs @@ -36,7 +36,7 @@ impl ModularExtension { // Generates a call to the moduli_init! macro with moduli in the correct order pub fn generate_moduli_init(&self) -> String { let supported_moduli = self - .supported_modulus + .supported_moduli .iter() .map(|modulus| format!("\"{}\"", modulus)) .collect::>() diff --git a/extensions/algebra/guest/src/lib.rs b/extensions/algebra/guest/src/lib.rs index 448035987c..2c7b24bfb8 100644 --- a/extensions/algebra/guest/src/lib.rs +++ b/extensions/algebra/guest/src/lib.rs @@ -54,7 +54,6 @@ use core::{ pub use field::Field; #[cfg(not(target_os = "zkvm"))] use num_bigint::BigUint; -pub use once_cell; pub use openvm_algebra_complex_macros as complex_macros; pub use openvm_algebra_moduli_macros as moduli_macros; #[cfg(target_os = "zkvm")] diff --git a/extensions/algebra/moduli-macros/src/lib.rs b/extensions/algebra/moduli-macros/src/lib.rs index 1b64b125e7..f7c71cbce7 100644 --- a/extensions/algebra/moduli-macros/src/lib.rs +++ b/extensions/algebra/moduli-macros/src/lib.rs @@ -1041,9 +1041,50 @@ pub fn moduli_init(input: TokenStream) -> TokenStream { } }); + let hint_non_qr_extern_func = syn::Ident::new( + &format!("hint_non_qr_extern_func_{}", modulus_hex), + span.into(), + ); + externs.push(quote::quote_spanned! { span.into() => + #[no_mangle] + extern "C" fn #hint_non_qr_extern_func() { + openvm::platform::custom_insn_r!( + opcode = ::openvm_algebra_guest::OPCODE, + funct3 = ::openvm_algebra_guest::MODULAR_ARITHMETIC_FUNCT3 as usize, + funct7 = ::openvm_algebra_guest::ModArithBaseFunct7::HintNonQr as usize + #mod_idx * (::openvm_algebra_guest::ModArithBaseFunct7::MODULAR_ARITHMETIC_MAX_KINDS as usize), + rd = Const "x0", + rs1 = Const "x0", + rs2 = Const "x0" + ); + } + + + }); + + // This function will be defined regardless of whether the modulus is prime or not. + // But it will be called only if the modulus is prime (i.e. moduli_declare has implemented + // the Field trait). + let hint_sqrt_extern_func = syn::Ident::new( + &format!("hint_sqrt_extern_func_{}", modulus_hex), + span.into(), + ); + externs.push(quote::quote_spanned! { span.into() => + #[no_mangle] + extern "C" fn #hint_sqrt_extern_func(rs1: usize) { + openvm::platform::custom_insn_r!( + opcode = ::openvm_algebra_guest::OPCODE, + funct3 = ::openvm_algebra_guest::MODULAR_ARITHMETIC_FUNCT3 as usize, + funct7 = ::openvm_algebra_guest::ModArithBaseFunct7::HintSqrt as usize + #mod_idx * (::openvm_algebra_guest::ModArithBaseFunct7::MODULAR_ARITHMETIC_MAX_KINDS as usize), + rd = Const "x0", + rs1 = In rs1, + rs2 = Const "x0" + ); + } + }); + externs.push(quote::quote_spanned! { span.into() => #[no_mangle] - extern "C" fn #setup_extern_func() { + extern "C" fn #setup_function() { #[cfg(target_os = "zkvm")] { let mut ptr = 0; diff --git a/extensions/algebra/tests/programs/examples/sqrt.rs b/extensions/algebra/tests/programs/examples/sqrt.rs index ee995ae81a..194e522b97 100644 --- a/extensions/algebra/tests/programs/examples/sqrt.rs +++ b/extensions/algebra/tests/programs/examples/sqrt.rs @@ -14,13 +14,9 @@ openvm_algebra_moduli_macros::moduli_declare! { } } -openvm_algebra_moduli_macros::moduli_init!( - "0xFFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE FFFFFC2F" -); +openvm::init!(); pub fn main() { - setup_all_moduli(); - let a = Secp256k1Coord::from_u32(4); let sqrt = a.sqrt(); assert_eq!(sqrt, Some(Secp256k1Coord::from_u32(2))); diff --git a/extensions/algebra/tests/src/lib.rs b/extensions/algebra/tests/src/lib.rs index c7668ed45e..181f592544 100644 --- a/extensions/algebra/tests/src/lib.rs +++ b/extensions/algebra/tests/src/lib.rs @@ -168,7 +168,8 @@ mod tests { #[test] fn test_sqrt() -> Result<()> { - let elf = build_example_program_at_path(get_programs_dir!(), "sqrt")?; + let config = Rv32ModularConfig::new(vec![SECP256K1_CONFIG.modulus.clone()]); + let elf = build_example_program_at_path(get_programs_dir!(), "sqrt", &config)?; let openvm_exe = VmExe::from_elf( elf, Transpiler::::default() @@ -177,7 +178,6 @@ mod tests { .with_extension(Rv32IoTranspilerExtension) .with_extension(ModularTranspilerExtension), )?; - let config = Rv32ModularConfig::new(vec![SECP256K1_CONFIG.modulus.clone()]); air_test(config, openvm_exe); Ok(()) } From 66d0b47c4de6c933439c4df604c87b9480478008 Mon Sep 17 00:00:00 2001 From: Avaneesh Kulkarni Date: Fri, 25 Apr 2025 18:45:15 -0400 Subject: [PATCH 07/41] Fix bug --- .../algebra/circuit/src/modular_extension.rs | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/extensions/algebra/circuit/src/modular_extension.rs b/extensions/algebra/circuit/src/modular_extension.rs index 679c0655ab..b51696841b 100644 --- a/extensions/algebra/circuit/src/modular_extension.rs +++ b/extensions/algebra/circuit/src/modular_extension.rs @@ -231,19 +231,18 @@ impl VmExtension for ModularExtension { } else { panic!("Modulus too large"); } - - let non_qr_hint_sub_ex = phantom::NonQrHintSubEx::new(self.supported_moduli.clone()); - builder.add_phantom_sub_executor( - non_qr_hint_sub_ex.clone(), - PhantomDiscriminant(ModularPhantom::HintNonQr as u16), - )?; - - let sqrt_hint_sub_ex = phantom::SqrtHintSubEx::new(non_qr_hint_sub_ex); - builder.add_phantom_sub_executor( - sqrt_hint_sub_ex, - PhantomDiscriminant(ModularPhantom::HintSqrt as u16), - )?; } + let non_qr_hint_sub_ex = phantom::NonQrHintSubEx::new(self.supported_moduli.clone()); + builder.add_phantom_sub_executor( + non_qr_hint_sub_ex.clone(), + PhantomDiscriminant(ModularPhantom::HintNonQr as u16), + )?; + + let sqrt_hint_sub_ex = phantom::SqrtHintSubEx::new(non_qr_hint_sub_ex); + builder.add_phantom_sub_executor( + sqrt_hint_sub_ex, + PhantomDiscriminant(ModularPhantom::HintSqrt as u16), + )?; Ok(inventory) } From 33ba8f24cab4aa704312ba9dd5f46791074be6b8 Mon Sep 17 00:00:00 2001 From: Avaneesh Kulkarni Date: Mon, 28 Apr 2025 12:29:36 -0400 Subject: [PATCH 08/41] Disambiguate ecc point x() method call --- extensions/ecc/guest/src/weierstrass.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/extensions/ecc/guest/src/weierstrass.rs b/extensions/ecc/guest/src/weierstrass.rs index e1783e429d..276c2045e5 100644 --- a/extensions/ecc/guest/src/weierstrass.rs +++ b/extensions/ecc/guest/src/weierstrass.rs @@ -426,7 +426,7 @@ macro_rules! impl_sw_group_ops { p2.clone() } else if p2.is_identity() { self.clone() - } else if self.x() == p2.x() { + } else if WeierstrassPoint::x(self) == WeierstrassPoint::x(p2) { if self.y() + p2.y() == <$field as openvm_algebra_guest::Field>::ZERO { <$struct_name as WeierstrassPoint>::IDENTITY } else { @@ -444,7 +444,7 @@ macro_rules! impl_sw_group_ops { *self = p2.clone(); } else if p2.is_identity() { // do nothing - } else if self.x() == p2.x() { + } else if WeierstrassPoint::x(self) == WeierstrassPoint::x(p2) { if self.y() + p2.y() == <$field as openvm_algebra_guest::Field>::ZERO { *self = <$struct_name as WeierstrassPoint>::IDENTITY; } else { @@ -486,7 +486,7 @@ macro_rules! impl_sw_group_ops { self.clone() } else if self.is_identity() { core::ops::Neg::neg(p2) - } else if self.x() == p2.x() { + } else if WeierstrassPoint::x(self) == WeierstrassPoint::x(p2) { if self.y() == p2.y() { <$struct_name as WeierstrassPoint>::IDENTITY } else { @@ -504,7 +504,7 @@ macro_rules! impl_sw_group_ops { // do nothing } else if self.is_identity() { *self = core::ops::Neg::neg(p2); - } else if self.x() == p2.x() { + } else if WeierstrassPoint::x(self) == WeierstrassPoint::x(p2) { if self.y() == p2.y() { *self = <$struct_name as WeierstrassPoint>::IDENTITY } else { From 86ebe4addc54e29da0e1dab985ee3e6f324d099c Mon Sep 17 00:00:00 2001 From: Avaneesh Kulkarni Date: Mon, 28 Apr 2025 12:37:19 -0400 Subject: [PATCH 09/41] Remove re-export of halo2curves in ecc guest --- Cargo.lock | 2 ++ extensions/algebra/guest/src/lib.rs | 4 ++-- extensions/ecc/guest/src/lib.rs | 2 -- extensions/ecc/tests/Cargo.toml | 2 ++ extensions/ecc/tests/src/lib.rs | 4 ++-- extensions/pairing/circuit/Cargo.toml | 1 + .../pairing/circuit/src/pairing_extension.rs | 7 ++++--- extensions/pairing/tests/Cargo.toml | 1 + extensions/pairing/tests/src/lib.rs | 16 ++++++++-------- 9 files changed, 22 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4364c9ee91..2628f2406d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4319,6 +4319,7 @@ name = "openvm-ecc-integration-tests" version = "1.1.2" dependencies = [ "eyre", + "halo2curves-axiom", "hex-literal", "num-bigint 0.4.6", "openvm-algebra-circuit", @@ -4623,6 +4624,7 @@ name = "openvm-pairing-integration-tests" version = "1.1.2" dependencies = [ "eyre", + "halo2curves-axiom", "num-bigint 0.4.6", "num-traits", "openvm", diff --git a/extensions/algebra/guest/src/lib.rs b/extensions/algebra/guest/src/lib.rs index 2c7b24bfb8..4bb4aaec5b 100644 --- a/extensions/algebra/guest/src/lib.rs +++ b/extensions/algebra/guest/src/lib.rs @@ -63,8 +63,6 @@ pub use openvm_rv32im_guest; pub use serde_big_array::BigArray; use strum_macros::FromRepr; -/// Field traits -pub mod field; /// Implementation of this library's traits on halo2curves types. /// Used for testing and also VM runtime execution. /// These should **only** be importable on a host machine. @@ -73,6 +71,8 @@ mod halo2curves; /// Exponentiation by bytes mod exp_bytes; +/// Field traits +pub mod field; pub use exp_bytes::*; pub use once_cell; diff --git a/extensions/ecc/guest/src/lib.rs b/extensions/ecc/guest/src/lib.rs index e780ccf891..823c87261f 100644 --- a/extensions/ecc/guest/src/lib.rs +++ b/extensions/ecc/guest/src/lib.rs @@ -3,8 +3,6 @@ extern crate self as openvm_ecc_guest; #[macro_use] extern crate alloc; -#[cfg(feature = "halo2curves")] -pub use halo2curves_axiom as halo2curves; pub use once_cell; pub use openvm_algebra_guest as algebra; pub use openvm_ecc_sw_macros as sw_macros; diff --git a/extensions/ecc/tests/Cargo.toml b/extensions/ecc/tests/Cargo.toml index 9a743ac00b..b877277da6 100644 --- a/extensions/ecc/tests/Cargo.toml +++ b/extensions/ecc/tests/Cargo.toml @@ -23,6 +23,8 @@ openvm-sdk.workspace = true eyre.workspace = true hex-literal.workspace = true num-bigint.workspace = true +halo2curves-axiom = { workspace = true } + [features] default = ["parallel"] parallel = ["openvm-circuit/parallel"] diff --git a/extensions/ecc/tests/src/lib.rs b/extensions/ecc/tests/src/lib.rs index dd3f885e90..37a71c9e3b 100644 --- a/extensions/ecc/tests/src/lib.rs +++ b/extensions/ecc/tests/src/lib.rs @@ -97,7 +97,7 @@ mod tests { #[test] fn test_decompress() -> Result<()> { - use openvm_ecc_guest::halo2curves::{group::Curve, secp256k1::Secp256k1Affine}; + use halo2curves_axiom::{group::Curve, secp256k1::Secp256k1Affine}; let config = Rv32WeierstrassConfig::new(vec![SECP256K1_CONFIG.clone(), @@ -162,7 +162,7 @@ mod tests { } fn test_decompress_invalid_specific_test(test_type: &str) -> Result<()> { - use openvm_ecc_guest::halo2curves::{group::Curve, secp256k1::Secp256k1Affine}; + use halo2curves_axiom::{group::Curve, secp256k1::Secp256k1Affine}; let config = Rv32WeierstrassConfig::new(vec![SECP256K1_CONFIG.clone(), diff --git a/extensions/pairing/circuit/Cargo.toml b/extensions/pairing/circuit/Cargo.toml index 6ec8891541..af16f7eeab 100644 --- a/extensions/pairing/circuit/Cargo.toml +++ b/extensions/pairing/circuit/Cargo.toml @@ -36,6 +36,7 @@ rand = { workspace = true } itertools = { workspace = true } eyre = { workspace = true } serde = { workspace = true, features = ["derive", "std"] } +halo2curves-axiom = { workspace = true } [target.'cfg(not(target_os = "zkvm"))'.dependencies] openvm-pairing-guest = { workspace = true } diff --git a/extensions/pairing/circuit/src/pairing_extension.rs b/extensions/pairing/circuit/src/pairing_extension.rs index b1e6afe38d..c75687f404 100644 --- a/extensions/pairing/circuit/src/pairing_extension.rs +++ b/extensions/pairing/circuit/src/pairing_extension.rs @@ -103,11 +103,12 @@ pub(crate) mod phantom { use std::collections::VecDeque; use eyre::bail; + use halo2curves_axiom::ff; use openvm_circuit::{ arch::{PhantomSubExecutor, Streams}, system::memory::MemoryController, }; - use openvm_ecc_guest::{algebra::field::FieldExtension, halo2curves::ff, AffinePoint}; + use openvm_ecc_guest::{algebra::field::FieldExtension, AffinePoint}; use openvm_instructions::{ riscv::{RV32_MEMORY_AS, RV32_REGISTER_NUM_LIMBS}, PhantomDiscriminant, @@ -168,7 +169,7 @@ pub(crate) mod phantom { match PairingCurve::from_repr(c_upper as usize) { Some(PairingCurve::Bn254) => { - use openvm_ecc_guest::halo2curves::bn256::{Fq, Fq12, Fq2}; + use halo2curves_axiom::bn256::{Fq, Fq12, Fq2}; use openvm_pairing_guest::halo2curves_shims::bn254::Bn254; const N: usize = BN254_NUM_LIMBS; if p_len != q_len { @@ -210,7 +211,7 @@ pub(crate) mod phantom { ); } Some(PairingCurve::Bls12_381) => { - use openvm_ecc_guest::halo2curves::bls12_381::{Fq, Fq12, Fq2}; + use halo2curves_axiom::bls12_381::{Fq, Fq12, Fq2}; use openvm_pairing_guest::halo2curves_shims::bls12_381::Bls12_381; const N: usize = BLS12_381_NUM_LIMBS; if p_len != q_len { diff --git a/extensions/pairing/tests/Cargo.toml b/extensions/pairing/tests/Cargo.toml index a88caa0de3..6a337bfab0 100644 --- a/extensions/pairing/tests/Cargo.toml +++ b/extensions/pairing/tests/Cargo.toml @@ -27,6 +27,7 @@ eyre.workspace = true rand.workspace = true num-bigint.workspace = true num-traits.workspace = true +halo2curves-axiom = { workspace = true } [features] default = ["parallel"] diff --git a/extensions/pairing/tests/src/lib.rs b/extensions/pairing/tests/src/lib.rs index f14956f024..7f12f01241 100644 --- a/extensions/pairing/tests/src/lib.rs +++ b/extensions/pairing/tests/src/lib.rs @@ -5,6 +5,10 @@ mod bn254 { use std::iter; use eyre::Result; + use halo2curves_axiom::{ + bn256::{Fq12, Fq2, Fr, G1Affine, G2Affine}, + ff::Field, + }; use openvm_algebra_circuit::{Fp2Extension, ModularExtension}; use openvm_algebra_transpiler::{Fp2TranspilerExtension, ModularTranspilerExtension}; use openvm_circuit::{ @@ -14,10 +18,6 @@ mod bn254 { use openvm_ecc_circuit::WeierstrassExtension; use openvm_ecc_guest::{ algebra::{field::FieldExtension, IntMod}, - halo2curves::{ - bn256::{Fq12, Fq2, Fr, G1Affine, G2Affine}, - ff::Field, - }, AffinePoint, }; use openvm_instructions::exe::VmExe; @@ -425,6 +425,10 @@ mod bn254 { #[cfg(test)] mod bls12_381 { use eyre::Result; + use halo2curves_axiom::{ + bls12_381::{Fq12, Fq2, Fr, G1Affine, G2Affine}, + ff::Field, + }; use num_bigint::BigUint; use num_traits::{self, FromPrimitive}; use openvm_algebra_circuit::{Fp2Extension, ModularExtension}; @@ -436,10 +440,6 @@ mod bls12_381 { use openvm_ecc_circuit::{CurveConfig, Rv32WeierstrassConfig, WeierstrassExtension}; use openvm_ecc_guest::{ algebra::{field::FieldExtension, IntMod}, - halo2curves::{ - bls12_381::{Fq12, Fq2, Fr, G1Affine, G2Affine}, - ff::Field, - }, AffinePoint, }; use openvm_ecc_transpiler::EccTranspilerExtension; From 6b65a74e1a7b0500014b1ee3e19dd1acddc14ec4 Mon Sep 17 00:00:00 2001 From: Avaneesh Kulkarni Date: Mon, 28 Apr 2025 21:53:16 -0400 Subject: [PATCH 10/41] Make sha256 rust binding public --- extensions/sha256/guest/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/sha256/guest/src/lib.rs b/extensions/sha256/guest/src/lib.rs index c1f8ee0649..1c51a272fd 100644 --- a/extensions/sha256/guest/src/lib.rs +++ b/extensions/sha256/guest/src/lib.rs @@ -17,6 +17,6 @@ pub const SHA256_FUNCT7: u8 = 0x1; #[cfg(target_os = "zkvm")] #[inline(always)] #[no_mangle] -extern "C" fn zkvm_sha256_impl(bytes: *const u8, len: usize, output: *mut u8) { +pub extern "C" fn zkvm_sha256_impl(bytes: *const u8, len: usize, output: *mut u8) { openvm_platform::custom_insn_r!(opcode = OPCODE, funct3 = SHA256_FUNCT3, funct7 = SHA256_FUNCT7, rd = In output, rs1 = In bytes, rs2 = In len); } From b1f1abbd0169276af42f52ef4117544fceb1a0f7 Mon Sep 17 00:00:00 2001 From: Avaneesh Kulkarni Date: Mon, 28 Apr 2025 22:30:04 -0400 Subject: [PATCH 11/41] Make keccak256 binding public --- extensions/keccak256/guest/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/keccak256/guest/src/lib.rs b/extensions/keccak256/guest/src/lib.rs index 22d91e0154..7e2bb3da54 100644 --- a/extensions/keccak256/guest/src/lib.rs +++ b/extensions/keccak256/guest/src/lib.rs @@ -20,7 +20,7 @@ pub const KECCAK256_FUNCT7: u8 = 0; #[cfg(target_os = "zkvm")] #[inline(always)] #[no_mangle] -extern "C" fn native_keccak256(bytes: *const u8, len: usize, output: *mut u8) { +pub extern "C" fn native_keccak256(bytes: *const u8, len: usize, output: *mut u8) { openvm_platform::custom_insn_r!( opcode = OPCODE, funct3 = KECCAK256_FUNCT3, From 328ec8e095e4be8916d7ac1e95bdf24d65d043a4 Mon Sep 17 00:00:00 2001 From: Avaneesh Kulkarni Date: Mon, 28 Apr 2025 23:04:05 -0400 Subject: [PATCH 12/41] Replace blanket implementation of traits with macro-based manual impl This is to avoid conflict with traits impl'd in moduli_declare! --- extensions/algebra/guest/src/halo2curves.rs | 79 +++++++++++++++------ 1 file changed, 57 insertions(+), 22 deletions(-) diff --git a/extensions/algebra/guest/src/halo2curves.rs b/extensions/algebra/guest/src/halo2curves.rs index a9a6513ac6..7b913e0d42 100644 --- a/extensions/algebra/guest/src/halo2curves.rs +++ b/extensions/algebra/guest/src/halo2curves.rs @@ -4,37 +4,72 @@ use halo2curves_axiom::ff; use crate::{field::Field, DivAssignUnsafe, DivUnsafe}; -impl<'a, F: ff::Field> DivUnsafe<&'a F> for F { - type Output = F; +macro_rules! div_unsafe_impl { + ($($t:ty),*) => { + $( + impl DivUnsafe for $t { + type Output = $t; + + fn div_unsafe(self, other: Self) -> Self::Output { + self * other.invert().unwrap() + } + } - fn div_unsafe(self, other: &'a F) -> Self::Output { - self * other.invert().unwrap() - } -} + impl<'a> DivUnsafe<&'a $t> for $t { + type Output = $t; -impl<'a, F: ff::Field> DivUnsafe<&'a F> for &'a F { - type Output = F; + fn div_unsafe(self, other: &'a $t) -> Self::Output { + self * other.invert().unwrap() + } + } - fn div_unsafe(self, other: &'a F) -> Self::Output { - *self * other.invert().unwrap() - } -} + impl<'a> DivUnsafe<&'a $t> for &'a $t { + type Output = $t; -impl DivAssignUnsafe for F { - fn div_assign_unsafe(&mut self, other: Self) { - *self *= other.invert().unwrap(); - } -} + fn div_unsafe(self, other: &'a $t) -> Self::Output { + *self * other.invert().unwrap() + } + } -impl<'a, F: ff::Field> DivAssignUnsafe<&'a F> for F { - fn div_assign_unsafe(&mut self, other: &'a F) { - *self *= other.invert().unwrap(); - } + impl DivAssignUnsafe for $t { + fn div_assign_unsafe(&mut self, other: Self) { + *self *= other.invert().unwrap(); + } + } + + impl<'a> DivAssignUnsafe<&'a $t> for $t { + fn div_assign_unsafe(&mut self, other: &'a $t) { + *self *= other.invert().unwrap(); + } + } + )* + }; } +div_unsafe_impl!( + halo2curves_axiom::bls12_381::Fq, + halo2curves_axiom::bls12_381::Fq12, + halo2curves_axiom::bls12_381::Fq2 +); +div_unsafe_impl!( + halo2curves_axiom::bn256::Fq, + halo2curves_axiom::bn256::Fq12, + halo2curves_axiom::bn256::Fq2 +); + impl Field for F where - for<'a> &'a F: Add<&'a F, Output = F> + Sub<&'a F, Output = F> + Mul<&'a F, Output = F>, + for<'a> &'a F: Add<&'a F, Output = F> + + Sub<&'a F, Output = F> + + Mul<&'a F, Output = F> + + DivUnsafe<&'a F, Output = F>, + for<'a> F: Add<&'a F, Output = F> + + Sub<&'a F, Output = F> + + Mul<&'a F, Output = F> + + DivAssignUnsafe + + DivUnsafe + + DivAssignUnsafe<&'a F> + + DivUnsafe<&'a F, Output = F>, { const ZERO: Self = ::ZERO; const ONE: Self = ::ONE; From 81d7ff8b25a5e06247a5e02e95f8474aa16160b8 Mon Sep 17 00:00:00 2001 From: Avaneesh Kulkarni Date: Tue, 29 Apr 2025 15:32:23 -0400 Subject: [PATCH 13/41] Rename moduli_declare's "prime" argument to "impl_field" --- extensions/algebra/moduli-macros/src/lib.rs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/extensions/algebra/moduli-macros/src/lib.rs b/extensions/algebra/moduli-macros/src/lib.rs index f7c71cbce7..cc0c065fca 100644 --- a/extensions/algebra/moduli-macros/src/lib.rs +++ b/extensions/algebra/moduli-macros/src/lib.rs @@ -34,7 +34,7 @@ pub fn moduli_declare(input: TokenStream) -> TokenStream { let struct_name = item.name.to_string(); let struct_name = syn::Ident::new(&struct_name, span.into()); let mut modulus: Option = None; - let mut prime: Option = None; + let mut impl_field: Option = None; for param in item.params { match param.name.to_string().as_str() { "modulus" => { @@ -53,17 +53,17 @@ pub fn moduli_declare(input: TokenStream) -> TokenStream { .into(); } } - "prime" => { + "impl_field" => { if let syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Bool(value), .. }) = param.value { - prime = Some(value.value()); + impl_field = Some(value.value()); } else { return syn::Error::new_spanned( param.value, - "Expected a boolean literal for macro argument `prime`", + "Expected a boolean literal for macro argument `impl_field`", ) .to_compile_error() .into(); @@ -84,7 +84,7 @@ pub fn moduli_declare(input: TokenStream) -> TokenStream { let mut limbs = modulus_bytes.len(); let mut block_size = 32; - let prime = prime.unwrap_or(false); + let impl_field = impl_field.unwrap_or(false); if limbs <= 32 { limbs = 32; @@ -755,7 +755,7 @@ pub fn moduli_declare(input: TokenStream) -> TokenStream { output.push(result); - if prime { + if impl_field { // implement Field and Sqrt traits for prime moduli let field_and_sqrt_impl = TokenStream::from(quote::quote_spanned! { span.into() => impl ::openvm_algebra_guest::Field for #struct_name { @@ -1061,9 +1061,8 @@ pub fn moduli_init(input: TokenStream) -> TokenStream { }); - // This function will be defined regardless of whether the modulus is prime or not. - // But it will be called only if the modulus is prime (i.e. moduli_declare has implemented - // the Field trait). + // This function will be defined regardless of whether impl_field is true or false, + // but it will be called only if the impl_field is true. let hint_sqrt_extern_func = syn::Ident::new( &format!("hint_sqrt_extern_func_{}", modulus_hex), span.into(), From b08dda99ea8b060ad52fc752ca980a6eeae820f9 Mon Sep 17 00:00:00 2001 From: Avaneesh Kulkarni Date: Tue, 29 Apr 2025 15:32:35 -0400 Subject: [PATCH 14/41] Update docs and book about impl_field --- book/src/custom-extensions/algebra.md | 11 +++++++++-- extensions/algebra/moduli-macros/README.md | 10 ++++++++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/book/src/custom-extensions/algebra.md b/book/src/custom-extensions/algebra.md index 315cff2373..6224413ddc 100644 --- a/book/src/custom-extensions/algebra.md +++ b/book/src/custom-extensions/algebra.md @@ -17,6 +17,9 @@ The functional part is provided by the `openvm-algebra-guest` crate, which is a - `Field` trait: Provides constants `ZERO` and `ONE` and methods for basic arithmetic operations within a field. +- `Sqrt` trait: + Implements square root in a field using hinting. + ## Modular arithmetic To [leverage](./overview.md) compile-time known moduli for performance, you declare, initialize, and then set up the arithmetic structures: @@ -26,11 +29,15 @@ To [leverage](./overview.md) compile-time known moduli for performance, you decl ```rust moduli_declare! { Bls12_381Fp { modulus = "0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab" }, - Bn254Fp { modulus = "21888242871839275222246405745257275088696311157297823662689037894645226208583" }, + Bn254Fp { + modulus = "21888242871839275222246405745257275088696311157297823662689037894645226208583", impl_field = true + }, } ``` -This creates `Bls12_381Fp` and `Bn254Fp` structs, each implementing the `IntMod` trait. The modulus parameter must be a string literal in decimal or hexadecimal format. +This creates `Bls12_381Fp` and `Bn254Fp` structs, each implementing the `IntMod` trait. +Since `impl_field = true` is specified for `Bn254Fp`, it also implements the `Field` and `Sqrt` traits. +The modulus parameter must be a string literal in decimal or hexadecimal format. 2. **Init**: Use the `init!` macro exactly once in the final binary: diff --git a/extensions/algebra/moduli-macros/README.md b/extensions/algebra/moduli-macros/README.md index a7a3e27eba..78e561bc43 100644 --- a/extensions/algebra/moduli-macros/README.md +++ b/extensions/algebra/moduli-macros/README.md @@ -6,7 +6,10 @@ Procedural macros for use in guest program to generate modular arithmetic struct ```rust openvm_algebra_moduli_macros::moduli_declare! { - Bls12381 { modulus = "4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787" }, + Bls12381 { + modulus = "4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787", + impl_field = true + }, Mod1e18 { modulus = "1000000000000000003" }, } @@ -28,7 +31,10 @@ openvm_algebra_moduli_macros::moduli_init! { The crate provides two macros: `moduli_declare!` and `moduli_init!`. The signatures are: -- `moduli_declare!` receives comma-separated list of moduli classes descriptions. Each description looks like `ModulusName { modulus = "modulus_value" }`. Here `ModulusName` is the name of the struct, and `modulus_value` is the modulus value in decimal or hex format. +- `moduli_declare!` receives comma-separated list of moduli classes descriptions. Each description looks like `ModulusName { modulus = "modulus_value", impl_field = }`. Here `ModulusName` is the name of the struct, and `modulus_value` is the modulus value in decimal or hex format. + - The `impl_field` argument indicates whether or not the `Field` and `Sqrt` traits should be automatically implemented on the resulting struct. + It should only be set to `true` if the modulus is prime. + If unspecified, it defaults to `false`. - `moduli_init!` receives comma-separated list of modulus values in decimal or hex format. From 7afa23517de49f573b221d607ea3f79acdefc9ae Mon Sep 17 00:00:00 2001 From: Avaneesh Kulkarni Date: Wed, 30 Apr 2025 16:56:16 -0400 Subject: [PATCH 15/41] Remove U256 and I256 from bigint guest library --- extensions/bigint/guest/src/externs.rs | 28 +-- extensions/bigint/guest/src/i256.rs | 285 ------------------------- extensions/bigint/guest/src/lib.rs | 9 - extensions/bigint/guest/src/u256.rs | 276 ------------------------ extensions/bigint/guest/src/utils.rs | 115 ---------- 5 files changed, 16 insertions(+), 697 deletions(-) delete mode 100644 extensions/bigint/guest/src/i256.rs delete mode 100644 extensions/bigint/guest/src/u256.rs delete mode 100644 extensions/bigint/guest/src/utils.rs diff --git a/extensions/bigint/guest/src/externs.rs b/extensions/bigint/guest/src/externs.rs index c0ed0272c5..462ff21ff7 100644 --- a/extensions/bigint/guest/src/externs.rs +++ b/extensions/bigint/guest/src/externs.rs @@ -5,7 +5,7 @@ use openvm_platform::custom_insn_r; use super::{Int256Funct7, BEQ256_FUNCT3, INT256_FUNCT3, OPCODE}; #[no_mangle] -unsafe extern "C" fn zkvm_u256_wrapping_add_impl(result: *mut u8, a: *const u8, b: *const u8) { +pub unsafe extern "C" fn zkvm_u256_wrapping_add_impl(result: *mut u8, a: *const u8, b: *const u8) { custom_insn_r!( opcode = OPCODE, funct3 = INT256_FUNCT3, @@ -17,7 +17,7 @@ unsafe extern "C" fn zkvm_u256_wrapping_add_impl(result: *mut u8, a: *const u8, } #[no_mangle] -unsafe extern "C" fn zkvm_u256_wrapping_sub_impl(result: *mut u8, a: *const u8, b: *const u8) { +pub unsafe extern "C" fn zkvm_u256_wrapping_sub_impl(result: *mut u8, a: *const u8, b: *const u8) { custom_insn_r!( opcode = OPCODE, funct3 = INT256_FUNCT3, @@ -29,7 +29,7 @@ unsafe extern "C" fn zkvm_u256_wrapping_sub_impl(result: *mut u8, a: *const u8, } #[no_mangle] -unsafe extern "C" fn zkvm_u256_wrapping_mul_impl(result: *mut u8, a: *const u8, b: *const u8) { +pub unsafe extern "C" fn zkvm_u256_wrapping_mul_impl(result: *mut u8, a: *const u8, b: *const u8) { custom_insn_r!( opcode = OPCODE, funct3 = INT256_FUNCT3, @@ -41,7 +41,7 @@ unsafe extern "C" fn zkvm_u256_wrapping_mul_impl(result: *mut u8, a: *const u8, } #[no_mangle] -unsafe extern "C" fn zkvm_u256_bitxor_impl(result: *mut u8, a: *const u8, b: *const u8) { +pub unsafe extern "C" fn zkvm_u256_bitxor_impl(result: *mut u8, a: *const u8, b: *const u8) { custom_insn_r!( opcode = OPCODE, funct3 = INT256_FUNCT3, @@ -53,7 +53,7 @@ unsafe extern "C" fn zkvm_u256_bitxor_impl(result: *mut u8, a: *const u8, b: *co } #[no_mangle] -unsafe extern "C" fn zkvm_u256_bitand_impl(result: *mut u8, a: *const u8, b: *const u8) { +pub unsafe extern "C" fn zkvm_u256_bitand_impl(result: *mut u8, a: *const u8, b: *const u8) { custom_insn_r!( opcode = OPCODE, funct3 = INT256_FUNCT3, @@ -65,7 +65,7 @@ unsafe extern "C" fn zkvm_u256_bitand_impl(result: *mut u8, a: *const u8, b: *co } #[no_mangle] -unsafe extern "C" fn zkvm_u256_bitor_impl(result: *mut u8, a: *const u8, b: *const u8) { +pub unsafe extern "C" fn zkvm_u256_bitor_impl(result: *mut u8, a: *const u8, b: *const u8) { custom_insn_r!( opcode = OPCODE, funct3 = INT256_FUNCT3, @@ -77,7 +77,7 @@ unsafe extern "C" fn zkvm_u256_bitor_impl(result: *mut u8, a: *const u8, b: *con } #[no_mangle] -unsafe extern "C" fn zkvm_u256_wrapping_shl_impl(result: *mut u8, a: *const u8, b: *const u8) { +pub unsafe extern "C" fn zkvm_u256_wrapping_shl_impl(result: *mut u8, a: *const u8, b: *const u8) { custom_insn_r!( opcode = OPCODE, funct3 = INT256_FUNCT3, @@ -89,7 +89,7 @@ unsafe extern "C" fn zkvm_u256_wrapping_shl_impl(result: *mut u8, a: *const u8, } #[no_mangle] -unsafe extern "C" fn zkvm_u256_wrapping_shr_impl(result: *mut u8, a: *const u8, b: *const u8) { +pub unsafe extern "C" fn zkvm_u256_wrapping_shr_impl(result: *mut u8, a: *const u8, b: *const u8) { custom_insn_r!( opcode = OPCODE, funct3 = INT256_FUNCT3, @@ -101,7 +101,11 @@ unsafe extern "C" fn zkvm_u256_wrapping_shr_impl(result: *mut u8, a: *const u8, } #[no_mangle] -unsafe extern "C" fn zkvm_u256_arithmetic_shr_impl(result: *mut u8, a: *const u8, b: *const u8) { +pub unsafe extern "C" fn zkvm_u256_arithmetic_shr_impl( + result: *mut u8, + a: *const u8, + b: *const u8, +) { custom_insn_r!( opcode = OPCODE, funct3 = INT256_FUNCT3, @@ -113,7 +117,7 @@ unsafe extern "C" fn zkvm_u256_arithmetic_shr_impl(result: *mut u8, a: *const u8 } #[no_mangle] -unsafe extern "C" fn zkvm_u256_eq_impl(a: *const u8, b: *const u8) -> bool { +pub unsafe extern "C" fn zkvm_u256_eq_impl(a: *const u8, b: *const u8) -> bool { let mut is_equal: u32; asm!("li {res}, 1", ".insn b {opcode}, {func3}, {rs1}, {rs2}, 8", @@ -128,7 +132,7 @@ unsafe extern "C" fn zkvm_u256_eq_impl(a: *const u8, b: *const u8) -> bool { } #[no_mangle] -unsafe extern "C" fn zkvm_u256_cmp_impl(a: *const u8, b: *const u8) -> Ordering { +pub unsafe extern "C" fn zkvm_u256_cmp_impl(a: *const u8, b: *const u8) -> Ordering { let mut cmp_result = MaybeUninit::::uninit(); custom_insn_r!( opcode = OPCODE, @@ -157,7 +161,7 @@ unsafe extern "C" fn zkvm_u256_cmp_impl(a: *const u8, b: *const u8) -> Ordering } #[no_mangle] -unsafe extern "C" fn zkvm_u256_clone_impl(result: *mut u8, a: *const u8) { +pub unsafe extern "C" fn zkvm_u256_clone_impl(result: *mut u8, a: *const u8) { let zero = &crate::U256::ZERO as *const _ as *const u8; custom_insn_r!( opcode = OPCODE, diff --git a/extensions/bigint/guest/src/i256.rs b/extensions/bigint/guest/src/i256.rs deleted file mode 100644 index 2cb4078b8c..0000000000 --- a/extensions/bigint/guest/src/i256.rs +++ /dev/null @@ -1,285 +0,0 @@ -use core::{ - cmp::{Eq, Ord, Ordering, PartialEq, PartialOrd}, - ops::{ - Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Mul, - MulAssign, Shl, ShlAssign, Shr, ShrAssign, Sub, SubAssign, - }, -}; - -use serde::{Deserialize, Serialize}; -use serde_big_array::BigArray; -#[cfg(not(target_os = "zkvm"))] -use {super::bigint_to_limbs, num_bigint::BigInt}; -#[cfg(target_os = "zkvm")] -use { - super::{Int256Funct7, BEQ256_FUNCT3, INT256_FUNCT3, OPCODE}, - core::{arch::asm, mem::MaybeUninit}, - openvm_platform::custom_insn_r, -}; - -use crate::impl_bin_op; - -/// A 256-bit signed integer type. -#[derive(Debug, Serialize, Deserialize)] -#[repr(align(32), C)] -pub struct I256 { - #[serde(with = "BigArray")] - limbs: [u8; 32], -} - -impl I256 { - /// The minimum value of an I256. - pub const MIN: Self = Self::generate_min(); - - /// The maximum value of an I256. - pub const MAX: Self = Self::generate_max(); - - /// The zero constant. - pub const ZERO: Self = Self { limbs: [0u8; 32] }; - - /// Construct [I256] from little-endian bytes. - pub const fn from_le_bytes(bytes: [u8; 32]) -> Self { - Self { limbs: bytes } - } - - /// Value of this I256 as a BigInt. - #[cfg(not(target_os = "zkvm"))] - pub fn as_bigint(&self) -> BigInt { - BigInt::from_signed_bytes_le(&self.limbs) - } - - /// Creates a new I256 from a BigInt. - #[cfg(not(target_os = "zkvm"))] - pub fn from_bigint(value: &BigInt) -> Self { - Self { - limbs: bigint_to_limbs(value), - } - } - - /// Creates a new I256 that equals to the given i8 value. - pub fn from_i8(value: i8) -> Self { - let mut limbs = if value < 0 { [u8::MAX; 32] } else { [0u8; 32] }; - limbs[0] = value as u8; - Self { limbs } - } - - /// Creates a new I256 that equals to the given i32 value. - pub fn from_i32(value: i32) -> Self { - let mut limbs = if value < 0 { [u8::MAX; 32] } else { [0u8; 32] }; - let value = value as u32; - limbs[..4].copy_from_slice(&value.to_le_bytes()); - Self { limbs } - } - - /// Creates a new I256 that equals to the given i64 value. - pub fn from_i64(value: i64) -> Self { - let mut limbs = if value < 0 { [u8::MAX; 32] } else { [0u8; 32] }; - let value = value as u64; - limbs[..8].copy_from_slice(&value.to_le_bytes()); - Self { limbs } - } - - /// A constant private helper function to generate the minimum value of an I256. - const fn generate_min() -> Self { - let mut limbs = [0u8; 32]; - limbs[31] = i8::MIN as u8; - Self { limbs } - } - - /// A constant private helper function to generate the maximum value of an I256. - const fn generate_max() -> Self { - let mut limbs = [u8::MAX; 32]; - limbs[31] = i8::MAX as u8; - Self { limbs } - } -} - -impl_bin_op!( - I256, - Add, - AddAssign, - add, - add_assign, - OPCODE, - INT256_FUNCT3, - Int256Funct7::Add as u8, - +=, - |lhs: &I256, rhs: &I256| -> I256 {I256::from_bigint(&(lhs.as_bigint() + rhs.as_bigint()))} -); - -impl_bin_op!( - I256, - Sub, - SubAssign, - sub, - sub_assign, - OPCODE, - INT256_FUNCT3, - Int256Funct7::Sub as u8, - -=, - |lhs: &I256, rhs: &I256| -> I256 {I256::from_bigint(&(lhs.as_bigint() - rhs.as_bigint()))} -); - -impl_bin_op!( - I256, - Mul, - MulAssign, - mul, - mul_assign, - OPCODE, - INT256_FUNCT3, - Int256Funct7::Mul as u8, - *=, - |lhs: &I256, rhs: &I256| -> I256 {I256::from_bigint(&(lhs.as_bigint() * rhs.as_bigint()))} -); - -impl_bin_op!( - I256, - BitXor, - BitXorAssign, - bitxor, - bitxor_assign, - OPCODE, - INT256_FUNCT3, - Int256Funct7::Xor as u8, - ^=, - |lhs: &I256, rhs: &I256| -> I256 {I256::from_bigint(&(lhs.as_bigint() ^ rhs.as_bigint()))} -); - -impl_bin_op!( - I256, - BitAnd, - BitAndAssign, - bitand, - bitand_assign, - OPCODE, - INT256_FUNCT3, - Int256Funct7::And as u8, - &=, - |lhs: &I256, rhs: &I256| -> I256 {I256::from_bigint(&(lhs.as_bigint() & rhs.as_bigint()))} -); - -impl_bin_op!( - I256, - BitOr, - BitOrAssign, - bitor, - bitor_assign, - OPCODE, - INT256_FUNCT3, - Int256Funct7::Or as u8, - |=, - |lhs: &I256, rhs: &I256| -> I256 {I256::from_bigint(&(lhs.as_bigint() | rhs.as_bigint()))} -); - -impl_bin_op!( - I256, - Shl, - ShlAssign, - shl, - shl_assign, - OPCODE, - INT256_FUNCT3, - Int256Funct7::Sll as u8, - <<=, - |lhs: &I256, rhs: &I256| -> I256 {I256::from_bigint(&(lhs.as_bigint() << rhs.limbs[0] as usize))} -); - -impl_bin_op!( - I256, - Shr, - ShrAssign, - shr, - shr_assign, - OPCODE, - INT256_FUNCT3, - Int256Funct7::Sra as u8, - >>=, - |lhs: &I256, rhs: &I256| -> I256 {I256::from_bigint(&(lhs.as_bigint() >> rhs.limbs[0] as usize))} -); - -impl PartialEq for I256 { - fn eq(&self, other: &Self) -> bool { - #[cfg(target_os = "zkvm")] - { - let mut is_equal: u32; - unsafe { - asm!("li {res}, 1", - ".insn b {opcode}, {func3}, {rs1}, {rs2}, 8", - "li {res}, 0", - opcode = const OPCODE, - func3 = const BEQ256_FUNCT3, - rs1 = in(reg) self as *const Self, - rs2 = in(reg) other as *const Self, - res = out(reg) is_equal - ); - } - return is_equal == 1; - } - #[cfg(not(target_os = "zkvm"))] - return self.as_bigint() == other.as_bigint(); - } -} - -impl Eq for I256 {} - -impl PartialOrd for I256 { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for I256 { - fn cmp(&self, other: &Self) -> Ordering { - #[cfg(target_os = "zkvm")] - { - let mut cmp_result = MaybeUninit::::uninit(); - custom_insn_r!( - opcode = OPCODE, - funct3 = INT256_FUNCT3, - funct7 = Int256Funct7::Slt as u8, - rd = In cmp_result.as_mut_ptr(), - rs1 = In self as *const Self, - rs2 = In other as *const Self - ); - let mut cmp_result = unsafe { cmp_result.assume_init() }; - if cmp_result.limbs[0] != 0 { - return Ordering::Less; - } - custom_insn_r!( - opcode = OPCODE, - funct3 = INT256_FUNCT3, - funct7 = Int256Funct7::Slt as u8, - rd = In &mut cmp_result as *mut I256, - rs1 = In other as *const Self, - rs2 = In self as *const Self - ); - if cmp_result.limbs[0] != 0 { - return Ordering::Greater; - } - return Ordering::Equal; - } - #[cfg(not(target_os = "zkvm"))] - return self.as_bigint().cmp(&other.as_bigint()); - } -} - -impl Clone for I256 { - fn clone(&self) -> Self { - #[cfg(target_os = "zkvm")] - { - let mut uninit: MaybeUninit = MaybeUninit::uninit(); - custom_insn_r!( - opcode = OPCODE, - funct3 = INT256_FUNCT3, - funct7 = Int256Funct7::Add as u8, - rd = In uninit.as_mut_ptr(), - rs1 = In self as *const Self, - rs2 = In &Self::ZERO as *const Self - ); - unsafe { uninit.assume_init() } - } - #[cfg(not(target_os = "zkvm"))] - return Self { limbs: self.limbs }; - } -} diff --git a/extensions/bigint/guest/src/lib.rs b/extensions/bigint/guest/src/lib.rs index d10a131e74..66bb5fa6a5 100644 --- a/extensions/bigint/guest/src/lib.rs +++ b/extensions/bigint/guest/src/lib.rs @@ -1,15 +1,6 @@ #![no_std] -mod i256; -mod u256; - -pub use i256::*; use strum_macros::FromRepr; -pub use u256::*; - -mod utils; -#[allow(unused)] -pub use utils::*; /// This is custom-0 defined in RISC-V spec document pub const OPCODE: u8 = 0x0b; diff --git a/extensions/bigint/guest/src/u256.rs b/extensions/bigint/guest/src/u256.rs deleted file mode 100644 index c15b36be9e..0000000000 --- a/extensions/bigint/guest/src/u256.rs +++ /dev/null @@ -1,276 +0,0 @@ -use core::{ - cmp::{Eq, Ord, Ordering, PartialEq, PartialOrd}, - ops::{ - Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Mul, - MulAssign, Shl, ShlAssign, Shr, ShrAssign, Sub, SubAssign, - }, -}; - -use serde::{Deserialize, Serialize}; -use serde_big_array::BigArray; -#[cfg(target_os = "zkvm")] -use { - super::{Int256Funct7, BEQ256_FUNCT3, INT256_FUNCT3, OPCODE}, - core::{arch::asm, mem::MaybeUninit}, - openvm_platform::custom_insn_r, -}; -#[cfg(not(target_os = "zkvm"))] -use {num_bigint::BigUint, num_traits::One, openvm::utils::biguint_to_limbs}; - -use crate::impl_bin_op; - -/// A 256-bit unsigned integer type. -#[derive(Debug, Serialize, Deserialize)] -#[repr(align(32), C)] -pub struct U256 { - #[serde(with = "BigArray")] - limbs: [u8; 32], -} - -impl U256 { - /// The maximum value of a U256. - pub const MAX: Self = Self { - limbs: [u8::MAX; 32], - }; - - /// The minimum value of a U256. - pub const MIN: Self = Self { limbs: [0u8; 32] }; - - /// The zero constant. - pub const ZERO: Self = Self { limbs: [0u8; 32] }; - - /// Construct [U256] from little-endian bytes. - pub const fn from_le_bytes(bytes: [u8; 32]) -> Self { - Self { limbs: bytes } - } - - /// Value of this U256 as a BigUint. - #[cfg(not(target_os = "zkvm"))] - pub fn as_biguint(&self) -> BigUint { - BigUint::from_bytes_le(&self.limbs) - } - - /// Creates a new U256 from a BigUint. - #[cfg(not(target_os = "zkvm"))] - pub fn from_biguint(value: &BigUint) -> Self { - Self { - limbs: biguint_to_limbs(value), - } - } - - /// Creates a new U256 that equals to the given u8 value. - pub fn from_u8(value: u8) -> Self { - let mut limbs = [0u8; 32]; - limbs[0] = value; - Self { limbs } - } - - /// Creates a new U256 that equals to the given u32 value. - pub fn from_u32(value: u32) -> Self { - let mut limbs = [0u8; 32]; - limbs[..4].copy_from_slice(&value.to_le_bytes()); - Self { limbs } - } - - /// Creates a new U256 that equals to the given u64 value. - pub fn from_u64(value: u64) -> Self { - let mut limbs = [0u8; 32]; - limbs[..8].copy_from_slice(&value.to_le_bytes()); - Self { limbs } - } - - /// The little-endian byte representation of this U256. - pub fn as_le_bytes(&self) -> &[u8; 32] { - &self.limbs - } -} - -impl_bin_op!( - U256, - Add, - AddAssign, - add, - add_assign, - OPCODE, - INT256_FUNCT3, - Int256Funct7::Add as u8, - +=, - |lhs: &U256, rhs: &U256| -> U256 {U256::from_biguint(&(lhs.as_biguint() + rhs.as_biguint()))} -); - -impl_bin_op!( - U256, - Sub, - SubAssign, - sub, - sub_assign, - OPCODE, - INT256_FUNCT3, - Int256Funct7::Sub as u8, - -=, - |lhs: &U256, rhs: &U256| -> U256 {U256::from_biguint(&(U256::MAX.as_biguint() + BigUint::one() + lhs.as_biguint() - rhs.as_biguint()))} -); - -impl_bin_op!( - U256, - Mul, - MulAssign, - mul, - mul_assign, - OPCODE, - INT256_FUNCT3, - Int256Funct7::Mul as u8, - *=, - |lhs: &U256, rhs: &U256| -> U256 {U256::from_biguint(&(lhs.as_biguint() * rhs.as_biguint()))} -); - -impl_bin_op!( - U256, - BitXor, - BitXorAssign, - bitxor, - bitxor_assign, - OPCODE, - INT256_FUNCT3, - Int256Funct7::Xor as u8, - ^=, - |lhs: &U256, rhs: &U256| -> U256 {U256::from_biguint(&(lhs.as_biguint() ^ rhs.as_biguint()))} -); - -impl_bin_op!( - U256, - BitAnd, - BitAndAssign, - bitand, - bitand_assign, - OPCODE, - INT256_FUNCT3, - Int256Funct7::And as u8, - &=, - |lhs: &U256, rhs: &U256| -> U256 {U256::from_biguint(&(lhs.as_biguint() & rhs.as_biguint()))} -); - -impl_bin_op!( - U256, - BitOr, - BitOrAssign, - bitor, - bitor_assign, - OPCODE, - INT256_FUNCT3, - Int256Funct7::Or as u8, - |=, - |lhs: &U256, rhs: &U256| -> U256 {U256::from_biguint(&(lhs.as_biguint() | rhs.as_biguint()))} -); - -impl_bin_op!( - U256, - Shl, - ShlAssign, - shl, - shl_assign, - OPCODE, - INT256_FUNCT3, - Int256Funct7::Sll as u8, - <<=, - |lhs: &U256, rhs: &U256| -> U256 {U256::from_biguint(&(lhs.as_biguint() << rhs.limbs[0] as usize))} -); - -impl_bin_op!( - U256, - Shr, - ShrAssign, - shr, - shr_assign, - OPCODE, - INT256_FUNCT3, - Int256Funct7::Srl as u8, - >>=, - |lhs: &U256, rhs: &U256| -> U256 {U256::from_biguint(&(lhs.as_biguint() >> rhs.limbs[0] as usize))} -); - -impl PartialEq for U256 { - fn eq(&self, other: &Self) -> bool { - #[cfg(target_os = "zkvm")] - { - let mut is_equal: u32; - unsafe { - asm!("li {res}, 1", - ".insn b {opcode}, {func3}, {rs1}, {rs2}, 8", - "li {res}, 0", - opcode = const OPCODE, - func3 = const BEQ256_FUNCT3, - rs1 = in(reg) self as *const Self, - rs2 = in(reg) other as *const Self, - res = out(reg) is_equal - ); - } - return is_equal == 1; - } - #[cfg(not(target_os = "zkvm"))] - return self.as_biguint() == other.as_biguint(); - } -} - -impl Eq for U256 {} - -impl PartialOrd for U256 { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for U256 { - fn cmp(&self, other: &Self) -> Ordering { - #[cfg(target_os = "zkvm")] - { - let mut cmp_result = MaybeUninit::::uninit(); - custom_insn_r!( - opcode = OPCODE, - funct3 = INT256_FUNCT3, - funct7 = Int256Funct7::Sltu as u8, - rd = In cmp_result.as_mut_ptr(), - rs1 = In self as *const Self, - rs2 = In other as *const Self - ); - let mut cmp_result = unsafe { cmp_result.assume_init() }; - if cmp_result.limbs[0] != 0 { - return Ordering::Less; - } - custom_insn_r!( - opcode = OPCODE, - funct3 = INT256_FUNCT3, - funct7 = Int256Funct7::Sltu as u8, - rd = In &mut cmp_result as *mut U256, - rs1 = In other as *const Self, - rs2 = In self as *const Self - ); - if cmp_result.limbs[0] != 0 { - return Ordering::Greater; - } - return Ordering::Equal; - } - #[cfg(not(target_os = "zkvm"))] - return self.as_biguint().cmp(&other.as_biguint()); - } -} - -impl Clone for U256 { - fn clone(&self) -> Self { - #[cfg(target_os = "zkvm")] - { - let mut uninit: MaybeUninit = MaybeUninit::uninit(); - custom_insn_r!( - opcode = OPCODE, - funct3 = INT256_FUNCT3, - funct7 = Int256Funct7::Add as u8, - rd = In uninit.as_mut_ptr(), - rs1 = In self as *const Self, - rs2 = In &Self::ZERO as *const Self - ); - unsafe { uninit.assume_init() } - } - #[cfg(not(target_os = "zkvm"))] - return Self { limbs: self.limbs }; - } -} diff --git a/extensions/bigint/guest/src/utils.rs b/extensions/bigint/guest/src/utils.rs deleted file mode 100644 index 5eebb8b90a..0000000000 --- a/extensions/bigint/guest/src/utils.rs +++ /dev/null @@ -1,115 +0,0 @@ -#[cfg(not(target_os = "zkvm"))] -use num_bigint::BigInt; - -#[inline] -#[cfg(not(target_os = "zkvm"))] -#[allow(dead_code)] -/// Convert a `BigInt` to a `[u8; NUM_LIMBS]` in two's complement little-endian format. -pub(super) fn bigint_to_limbs(x: &BigInt) -> [u8; NUM_LIMBS] { - let mut sm = x.to_signed_bytes_le(); - let mut ext = 0; - if let Some(last) = sm.last() { - if (*last as i8) < 0 { - ext = u8::MAX; - } - } - sm.resize(NUM_LIMBS, ext); - sm.try_into().unwrap() -} - -/// A macro that implements all the following for the given struct and operation: -/// a op= b, a op= &b, a op b, a op &b, &a op b, &a op &b -/// Description of the parameters (see [u256.rs] for an example): -/// - $struct_name: The struct to implement the operation for. -/// - $trait_name: The trait name of the operation to implement. -/// - $trait_assign_name: The trait name of the assignment operation to implement. -/// - $trait_fn: The trait function name to implement. -/// - $trait_assign_fn: The assignment trait function name to implement. -/// - $opcode: The custom opcode of the operation in openvm. -/// - $func3: The func3 of the operation in openvm. -/// - $func7: The func7 of the operation in openvm. -/// - $op_sym: The symbol to use for the operation. -/// - $rust_expr: A closure to get the result of the operation if target is non-zkvm. -#[macro_export] -macro_rules! impl_bin_op { - ($struct_name:ty, $trait_name:ident, - $trait_assign_name:ident, $trait_fn:ident, - $trait_assign_fn:ident, $opcode:expr, - $func3:expr, $func7:expr, $op_sym:tt, - $rust_expr:expr) => { - impl<'a> $trait_assign_name<&'a $struct_name> for $struct_name { - #[inline(always)] - fn $trait_assign_fn(&mut self, rhs: &'a $struct_name) { - #[cfg(target_os = "zkvm")] - custom_insn_r!( - opcode = $opcode, - funct3 = $func3, - funct7 = $func7, - rd = In self as *mut Self, - rs1 = In self as *const Self, - rs2 = In rhs as *const Self - ); - #[cfg(not(target_os = "zkvm"))] - { - *self = $rust_expr(self, rhs); - } - } - } - - impl $trait_assign_name<$struct_name> for $struct_name { - #[inline(always)] - fn $trait_assign_fn(&mut self, rhs: $struct_name) { - *self $op_sym &rhs; - } - } - - impl<'a> $trait_name<&'a $struct_name> for &$struct_name { - type Output = $struct_name; - #[inline(always)] - fn $trait_fn(self, rhs: &'a $struct_name) -> Self::Output { - #[cfg(target_os = "zkvm")] - { - let mut uninit: MaybeUninit<$struct_name> = MaybeUninit::uninit(); - custom_insn_r!( - opcode = $opcode, - funct3 = $func3, - funct7 = $func7, - rd = In uninit.as_mut_ptr(), - rs1 = In self as *const $struct_name, - rs2 = In rhs as *const $struct_name - ); - unsafe { uninit.assume_init() } - } - #[cfg(not(target_os = "zkvm"))] - return $rust_expr(self, rhs); - } - } - - impl<'a> $trait_name<&'a $struct_name> for $struct_name { - type Output = $struct_name; - #[inline(always)] - fn $trait_fn(mut self, rhs: &'a $struct_name) -> Self::Output { - self $op_sym rhs; - self - } - } - - impl $trait_name<$struct_name> for $struct_name { - type Output = $struct_name; - #[inline(always)] - fn $trait_fn(mut self, rhs: $struct_name) -> Self::Output { - self $op_sym &rhs; - self - } - } - - impl $trait_name<$struct_name> for &$struct_name { - type Output = $struct_name; - #[inline(always)] - fn $trait_fn(self, mut rhs: $struct_name) -> Self::Output { - rhs $op_sym self; - rhs - } - } - }; -} From 16144887d03016018238a6152128e15ae4636294 Mon Sep 17 00:00:00 2001 From: Avaneesh Kulkarni Date: Wed, 30 Apr 2025 17:33:37 -0400 Subject: [PATCH 16/41] Use impl_field = true in k256 and p256 modules --- extensions/ecc/guest/src/k256.rs | 19 ++----------------- extensions/ecc/guest/src/p256.rs | 19 ++----------------- 2 files changed, 4 insertions(+), 34 deletions(-) diff --git a/extensions/ecc/guest/src/k256.rs b/extensions/ecc/guest/src/k256.rs index eed5f69be7..1b8b406eee 100644 --- a/extensions/ecc/guest/src/k256.rs +++ b/extensions/ecc/guest/src/k256.rs @@ -36,8 +36,8 @@ const fn seven_le() -> [u8; 32] { } moduli_declare! { - Secp256k1Coord { modulus = "0xFFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE FFFFFC2F" }, - Secp256k1Scalar { modulus = "0xFFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE BAAEDCE6 AF48A03B BFD25E8C D0364141" }, + Secp256k1Coord { modulus = "0xFFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE FFFFFC2F", impl_field = true }, + Secp256k1Scalar { modulus = "0xFFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE BAAEDCE6 AF48A03B BFD25E8C D0364141", impl_field = true }, } sw_declare! { @@ -48,21 +48,6 @@ sw_declare! { // Used in WeierstrassExtension config pub const SECP256K1_ECC_STRUCT_NAME: &str = "Secp256k1Point"; -impl Field for Secp256k1Coord { - const ZERO: Self = ::ZERO; - const ONE: Self = ::ONE; - - type SelfRef<'a> = &'a Self; - - fn double_assign(&mut self) { - IntMod::double_assign(self); - } - - fn square_assign(&mut self) { - IntMod::square_assign(self); - } -} - impl CyclicGroup for Secp256k1Point { // The constants are taken from: https://en.bitcoin.it/wiki/Secp256k1 const GENERATOR: Self = Secp256k1Point { diff --git a/extensions/ecc/guest/src/p256.rs b/extensions/ecc/guest/src/p256.rs index 2892b9a200..01af6e4b17 100644 --- a/extensions/ecc/guest/src/p256.rs +++ b/extensions/ecc/guest/src/p256.rs @@ -22,8 +22,8 @@ lazy_static! { } openvm_algebra_moduli_macros::moduli_declare! { - P256Coord { modulus = "0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff" }, - P256Scalar { modulus = "0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551" }, + P256Coord { modulus = "0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff", impl_field = true }, + P256Scalar { modulus = "0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551", impl_field = true }, } pub const P256_NUM_LIMBS: usize = 32; @@ -45,21 +45,6 @@ openvm_ecc_sw_macros::sw_declare! { // Used in WeierstrassExtension config pub const P256_ECC_STRUCT_NAME: &str = "P256Point"; -impl Field for P256Coord { - const ZERO: Self = ::ZERO; - const ONE: Self = ::ONE; - - type SelfRef<'a> = &'a Self; - - fn double_assign(&mut self) { - IntMod::double_assign(self); - } - - fn square_assign(&mut self) { - IntMod::square_assign(self); - } -} - impl CyclicGroup for P256Point { // The constants are taken from: https://neuromancer.sk/std/secg/secp256r1 const GENERATOR: Self = P256Point { From 4aebb8efd8a24e67e13438eec95d763bc3a8b489 Mon Sep 17 00:00:00 2001 From: Avaneesh Kulkarni Date: Wed, 30 Apr 2025 19:22:19 -0400 Subject: [PATCH 17/41] Remove pairing guest library and tests (moved to new repo) --- Cargo.lock | 35 +- .../pairing/guest/src/bls12_381/fp12.rs | 214 ----- extensions/pairing/guest/src/bls12_381/fp2.rs | 76 -- extensions/pairing/guest/src/bls12_381/mod.rs | 682 -------------- .../pairing/guest/src/bls12_381/pairing.rs | 347 ------- .../pairing/guest/src/bls12_381/tests.rs | 317 ------- .../pairing/guest/src/bls12_381/utils.rs | 49 - extensions/pairing/guest/src/bn254/fp12.rs | 215 ----- extensions/pairing/guest/src/bn254/fp2.rs | 76 -- extensions/pairing/guest/src/bn254/mod.rs | 744 --------------- extensions/pairing/guest/src/bn254/pairing.rs | 379 -------- extensions/pairing/guest/src/bn254/tests.rs | 325 ------- extensions/pairing/guest/src/bn254/utils.rs | 49 - extensions/pairing/guest/src/lib.rs | 16 - extensions/pairing/tests/Cargo.toml | 34 - extensions/pairing/tests/programs/Cargo.toml | 51 - .../pairing/tests/programs/examples/bls_ec.rs | 11 - .../programs/examples/bls_final_exp_hint.rs | 24 - .../programs/examples/bn_final_exp_hint.rs | 24 - .../tests/programs/examples/fp12_mul.rs | 82 -- .../tests/programs/examples/pairing_check.rs | 90 -- .../examples/pairing_check_fallback.rs | 241 ----- .../tests/programs/examples/pairing_line.rs | 147 --- .../programs/examples/pairing_miller_loop.rs | 97 -- .../programs/examples/pairing_miller_step.rs | 166 ---- .../programs/openvm_init_bls_ec_bls12_381.rs | 3 - ...penvm_init_bls_final_exp_hint_bls12_381.rs | 4 - .../openvm_init_bn_final_exp_hint_bn254.rs | 4 - .../openvm_init_fp12_mul_bls12_381.rs | 4 - .../programs/openvm_init_fp12_mul_bn254.rs | 4 - .../openvm_init_pairing_check_bls12_381.rs | 4 - .../openvm_init_pairing_check_bn254.rs | 4 - ...m_init_pairing_check_fallback_bls12_381.rs | 4 - ...penvm_init_pairing_check_fallback_bn254.rs | 4 - .../openvm_init_pairing_line_bls12_381.rs | 4 - .../openvm_init_pairing_line_bn254.rs | 4 - ...envm_init_pairing_miller_loop_bls12_381.rs | 4 - .../openvm_init_pairing_miller_loop_bn254.rs | 4 - ...envm_init_pairing_miller_step_bls12_381.rs | 4 - .../openvm_init_pairing_miller_step_bn254.rs | 4 - extensions/pairing/tests/src/lib.rs | 884 ------------------ 41 files changed, 30 insertions(+), 5404 deletions(-) delete mode 100644 extensions/pairing/guest/src/bls12_381/fp12.rs delete mode 100644 extensions/pairing/guest/src/bls12_381/fp2.rs delete mode 100644 extensions/pairing/guest/src/bls12_381/mod.rs delete mode 100644 extensions/pairing/guest/src/bls12_381/pairing.rs delete mode 100644 extensions/pairing/guest/src/bls12_381/tests.rs delete mode 100644 extensions/pairing/guest/src/bls12_381/utils.rs delete mode 100644 extensions/pairing/guest/src/bn254/fp12.rs delete mode 100644 extensions/pairing/guest/src/bn254/fp2.rs delete mode 100644 extensions/pairing/guest/src/bn254/mod.rs delete mode 100644 extensions/pairing/guest/src/bn254/pairing.rs delete mode 100644 extensions/pairing/guest/src/bn254/tests.rs delete mode 100644 extensions/pairing/guest/src/bn254/utils.rs delete mode 100644 extensions/pairing/tests/Cargo.toml delete mode 100644 extensions/pairing/tests/programs/Cargo.toml delete mode 100644 extensions/pairing/tests/programs/examples/bls_ec.rs delete mode 100644 extensions/pairing/tests/programs/examples/bls_final_exp_hint.rs delete mode 100644 extensions/pairing/tests/programs/examples/bn_final_exp_hint.rs delete mode 100644 extensions/pairing/tests/programs/examples/fp12_mul.rs delete mode 100644 extensions/pairing/tests/programs/examples/pairing_check.rs delete mode 100644 extensions/pairing/tests/programs/examples/pairing_check_fallback.rs delete mode 100644 extensions/pairing/tests/programs/examples/pairing_line.rs delete mode 100644 extensions/pairing/tests/programs/examples/pairing_miller_loop.rs delete mode 100644 extensions/pairing/tests/programs/examples/pairing_miller_step.rs delete mode 100644 extensions/pairing/tests/programs/openvm_init_bls_ec_bls12_381.rs delete mode 100644 extensions/pairing/tests/programs/openvm_init_bls_final_exp_hint_bls12_381.rs delete mode 100644 extensions/pairing/tests/programs/openvm_init_bn_final_exp_hint_bn254.rs delete mode 100644 extensions/pairing/tests/programs/openvm_init_fp12_mul_bls12_381.rs delete mode 100644 extensions/pairing/tests/programs/openvm_init_fp12_mul_bn254.rs delete mode 100644 extensions/pairing/tests/programs/openvm_init_pairing_check_bls12_381.rs delete mode 100644 extensions/pairing/tests/programs/openvm_init_pairing_check_bn254.rs delete mode 100644 extensions/pairing/tests/programs/openvm_init_pairing_check_fallback_bls12_381.rs delete mode 100644 extensions/pairing/tests/programs/openvm_init_pairing_check_fallback_bn254.rs delete mode 100644 extensions/pairing/tests/programs/openvm_init_pairing_line_bls12_381.rs delete mode 100644 extensions/pairing/tests/programs/openvm_init_pairing_line_bn254.rs delete mode 100644 extensions/pairing/tests/programs/openvm_init_pairing_miller_loop_bls12_381.rs delete mode 100644 extensions/pairing/tests/programs/openvm_init_pairing_miller_loop_bn254.rs delete mode 100644 extensions/pairing/tests/programs/openvm_init_pairing_miller_step_bls12_381.rs delete mode 100644 extensions/pairing/tests/programs/openvm_init_pairing_miller_step_bn254.rs delete mode 100644 extensions/pairing/tests/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 2628f2406d..7031868f8f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3932,7 +3932,6 @@ version = "1.1.2" dependencies = [ "derive-new 0.6.0", "derive_more 1.0.0", - "eyre", "halo2curves-axiom", "itertools 0.14.0", "num-bigint 0.4.6", @@ -3974,8 +3973,6 @@ dependencies = [ "once_cell", "openvm-algebra-complex-macros", "openvm-algebra-moduli-macros", - "openvm-custom-insn", - "openvm-rv32im-guest", "serde-big-array", "strum_macros", ] @@ -4319,7 +4316,6 @@ name = "openvm-ecc-integration-tests" version = "1.1.2" dependencies = [ "eyre", - "halo2curves-axiom", "hex-literal", "num-bigint 0.4.6", "openvm-algebra-circuit", @@ -4423,6 +4419,21 @@ dependencies = [ "tiny-keccak", ] +[[package]] +name = "openvm-keccak256-integration-tests" +version = "1.1.2" +dependencies = [ + "eyre", + "openvm-circuit", + "openvm-instructions", + "openvm-keccak256-circuit", + "openvm-keccak256-transpiler", + "openvm-rv32im-transpiler", + "openvm-stark-sdk", + "openvm-toolchain-tests", + "openvm-transpiler", +] + [[package]] name = "openvm-keccak256-transpiler" version = "1.1.2" @@ -4624,7 +4635,6 @@ name = "openvm-pairing-integration-tests" version = "1.1.2" dependencies = [ "eyre", - "halo2curves-axiom", "num-bigint 0.4.6", "num-traits", "openvm", @@ -4878,6 +4888,21 @@ dependencies = [ "sha2", ] +[[package]] +name = "openvm-sha256-integration-tests" +version = "1.1.2" +dependencies = [ + "eyre", + "openvm-circuit", + "openvm-instructions", + "openvm-rv32im-transpiler", + "openvm-sha256-circuit", + "openvm-sha256-transpiler", + "openvm-stark-sdk", + "openvm-toolchain-tests", + "openvm-transpiler", +] + [[package]] name = "openvm-sha256-transpiler" version = "1.1.2" diff --git a/extensions/pairing/guest/src/bls12_381/fp12.rs b/extensions/pairing/guest/src/bls12_381/fp12.rs deleted file mode 100644 index 413f9377af..0000000000 --- a/extensions/pairing/guest/src/bls12_381/fp12.rs +++ /dev/null @@ -1,214 +0,0 @@ -use alloc::vec::Vec; -use core::ops::{Mul, MulAssign, Neg}; - -use openvm_algebra_guest::{ - field::{ComplexConjugate, FieldExtension}, - DivAssignUnsafe, DivUnsafe, Field, -}; - -use super::{Bls12_381, Fp, Fp2}; -use crate::pairing::{fp12_invert_assign, PairingIntrinsics, SexticExtField}; - -pub type Fp12 = SexticExtField; - -impl Fp12 { - pub fn invert(&self) -> Self { - let mut s = self.clone(); - fp12_invert_assign::(&mut s.c, &Bls12_381::XI); - s - } -} - -impl Field for Fp12 { - type SelfRef<'a> = &'a Self; - const ZERO: Self = Self::new([Fp2::ZERO; 6]); - const ONE: Self = Self::new([ - Fp2::ONE, - Fp2::ZERO, - Fp2::ZERO, - Fp2::ZERO, - Fp2::ZERO, - Fp2::ZERO, - ]); - - fn double_assign(&mut self) { - *self += self.clone(); - } - - fn square_assign(&mut self) { - *self *= self.clone(); - } -} - -impl FieldExtension for Fp12 { - const D: usize = 6; - type Coeffs = [Fp2; 6]; - - fn from_coeffs(coeffs: Self::Coeffs) -> Self { - Self::new(coeffs) - } - - fn from_bytes(bytes: &[u8]) -> Self { - assert_eq!(bytes.len(), 576); - Self::from_coeffs([ - Fp2::from_bytes(&bytes[0..96]), - Fp2::from_bytes(&bytes[96..192]), - Fp2::from_bytes(&bytes[192..288]), - Fp2::from_bytes(&bytes[288..384]), - Fp2::from_bytes(&bytes[384..480]), - Fp2::from_bytes(&bytes[480..576]), - ]) - } - - fn to_coeffs(self) -> Self::Coeffs { - self.c - } - - fn to_bytes(&self) -> Vec { - let mut bytes = Vec::with_capacity(576); - for coeff in self.clone().to_coeffs() { - bytes.extend_from_slice(&coeff.to_bytes()); - } - bytes - } - - fn embed(c0: Fp2) -> Self { - Self::new([c0, Fp2::ZERO, Fp2::ZERO, Fp2::ZERO, Fp2::ZERO, Fp2::ZERO]) - } - - /// We assume that the frobenius map power is < 12 - fn frobenius_map(&self, power: usize) -> Self { - if power & 1 != 0 { - let c0 = self.c[0].clone().conjugate(); - let c1 = self.c[1].clone().conjugate() * &Bls12_381::FROBENIUS_COEFFS[power][0]; - let c2 = self.c[2].clone().conjugate() * &Bls12_381::FROBENIUS_COEFFS[power][1]; - let c3 = self.c[3].clone().conjugate() * &Bls12_381::FROBENIUS_COEFFS[power][2]; - let c4 = self.c[4].clone().conjugate() * &Bls12_381::FROBENIUS_COEFFS[power][3]; - let c5 = self.c[5].clone().conjugate() * &Bls12_381::FROBENIUS_COEFFS[power][4]; - Self::new([c0, c1, c2, c3, c4, c5]) - } else { - let c0 = self.c[0].clone(); - let c1 = &self.c[1] * &Bls12_381::FROBENIUS_COEFFS[power][0]; - let c2 = &self.c[2] * &Bls12_381::FROBENIUS_COEFFS[power][1]; - let c3 = &self.c[3] * &Bls12_381::FROBENIUS_COEFFS[power][2]; - let c4 = &self.c[4] * &Bls12_381::FROBENIUS_COEFFS[power][3]; - let c5 = &self.c[5] * &Bls12_381::FROBENIUS_COEFFS[power][4]; - Self::new([c0, c1, c2, c3, c4, c5]) - } - } - - fn mul_base(&self, rhs: &Fp2) -> Self { - Self::new([ - &self.c[0] * rhs, - &self.c[1] * rhs, - &self.c[2] * rhs, - &self.c[3] * rhs, - &self.c[4] * rhs, - &self.c[5] * rhs, - ]) - } -} - -// This is ambiguous. It is conjugation for Fp12 over Fp6. -impl ComplexConjugate for Fp12 { - fn conjugate(self) -> Self { - let [c0, c1, c2, c3, c4, c5] = self.c; - Self::new([c0, -c1, c2, -c3, c4, -c5]) - } - - fn conjugate_assign(&mut self) { - self.c[1].neg_assign(); - self.c[3].neg_assign(); - self.c[5].neg_assign(); - } -} - -impl<'a> MulAssign<&'a Fp12> for Fp12 { - #[inline(always)] - fn mul_assign(&mut self, other: &'a Fp12) { - *self = crate::pairing::sextic_tower_mul(self, other, &Bls12_381::XI); - } -} - -impl<'a> Mul<&'a Fp12> for &'a Fp12 { - type Output = Fp12; - #[inline(always)] - fn mul(self, other: &'a Fp12) -> Self::Output { - crate::pairing::sextic_tower_mul(self, other, &Bls12_381::XI) - } -} - -impl MulAssign for Fp12 { - #[inline(always)] - fn mul_assign(&mut self, other: Self) { - self.mul_assign(&other); - } -} - -impl Mul for Fp12 { - type Output = Self; - #[inline(always)] - fn mul(mut self, other: Self) -> Self::Output { - self *= other; - self - } -} - -impl<'a> Mul<&'a Fp12> for Fp12 { - type Output = Self; - #[inline(always)] - fn mul(mut self, other: &'a Fp12) -> Self::Output { - self *= other; - self - } -} - -impl<'a> DivAssignUnsafe<&'a Fp12> for Fp12 { - #[inline(always)] - fn div_assign_unsafe(&mut self, other: &'a Fp12) { - *self *= other.invert(); - } -} - -impl<'a> DivUnsafe<&'a Fp12> for &'a Fp12 { - type Output = Fp12; - #[inline(always)] - fn div_unsafe(self, other: &'a Fp12) -> Self::Output { - let mut res = self.clone(); - res.div_assign_unsafe(other); - res - } -} - -impl DivAssignUnsafe for Fp12 { - #[inline(always)] - fn div_assign_unsafe(&mut self, other: Self) { - *self *= other.invert(); - } -} - -impl DivUnsafe for Fp12 { - type Output = Self; - #[inline(always)] - fn div_unsafe(mut self, other: Self) -> Self::Output { - self.div_assign_unsafe(other); - self - } -} - -impl<'a> DivUnsafe<&'a Fp12> for Fp12 { - type Output = Self; - #[inline(always)] - fn div_unsafe(mut self, other: &'a Fp12) -> Self::Output { - self.div_assign_unsafe(other); - self - } -} - -impl Neg for Fp12 { - type Output = Fp12; - #[inline(always)] - fn neg(self) -> Self::Output { - Self::ZERO - &self - } -} diff --git a/extensions/pairing/guest/src/bls12_381/fp2.rs b/extensions/pairing/guest/src/bls12_381/fp2.rs deleted file mode 100644 index 20de223962..0000000000 --- a/extensions/pairing/guest/src/bls12_381/fp2.rs +++ /dev/null @@ -1,76 +0,0 @@ -use alloc::vec::Vec; -use core::ops::Neg; - -use openvm_algebra_complex_macros::{complex_declare, complex_impl_field}; -use openvm_algebra_guest::{field::FieldExtension, Field, IntMod}; - -use super::Fp; - -#[cfg(not(target_os = "zkvm"))] -// Used in Fp2Extension config -pub const BLS12_381_COMPLEX_STRUCT_NAME: &str = "Bls12_381Fp2"; - -// The struct name needs to be globally unique for linking purposes. -// The mod_type is a path used only in the struct definition. -complex_declare! { - Bls12_381Fp2 { mod_type = Fp } -} - -complex_impl_field! { - Bls12_381Fp2, -} - -pub type Fp2 = Bls12_381Fp2; - -impl FieldExtension for Fp2 { - const D: usize = 2; - type Coeffs = [Fp; 2]; - - fn from_coeffs([c0, c1]: Self::Coeffs) -> Self { - Self { c0, c1 } - } - - fn from_bytes(bytes: &[u8]) -> Self { - assert_eq!(bytes.len(), 96); - Self::from_coeffs([ - Fp::from_const_bytes(bytes[0..48].try_into().unwrap()), - Fp::from_const_bytes(bytes[48..96].try_into().unwrap()), - ]) - } - - fn to_coeffs(self) -> Self::Coeffs { - [self.c0, self.c1] - } - - fn to_bytes(&self) -> Vec { - let mut bytes = Vec::with_capacity(96); - bytes.extend_from_slice(self.c0.as_le_bytes()); - bytes.extend_from_slice(self.c1.as_le_bytes()); - bytes - } - - fn embed(base_elem: Fp) -> Self { - Self { - c0: base_elem, - c1: ::ZERO, - } - } - - fn frobenius_map(&self, power: usize) -> Self { - if power % 2 == 0 { - self.clone() - } else { - Self { - c0: self.c0.clone(), - c1: (&self.c1).neg(), - } - } - } - - fn mul_base(&self, rhs: &Fp) -> Self { - Self { - c0: &self.c0 * rhs, - c1: &self.c1 * rhs, - } - } -} diff --git a/extensions/pairing/guest/src/bls12_381/mod.rs b/extensions/pairing/guest/src/bls12_381/mod.rs deleted file mode 100644 index f1940ebe21..0000000000 --- a/extensions/pairing/guest/src/bls12_381/mod.rs +++ /dev/null @@ -1,682 +0,0 @@ -use core::ops::Neg; - -use openvm_algebra_guest::{Field, IntMod}; -use openvm_algebra_moduli_macros::moduli_declare; -use openvm_ecc_guest::{weierstrass::IntrinsicCurve, CyclicGroup, Group}; - -mod fp12; -mod fp2; -mod pairing; -#[cfg(all(feature = "halo2curves", not(target_os = "zkvm")))] -pub(crate) mod utils; - -pub use fp12::*; -pub use fp2::*; -use hex_literal::hex; -#[cfg(not(target_os = "zkvm"))] -use lazy_static::lazy_static; -#[cfg(not(target_os = "zkvm"))] -use num_bigint::BigUint; -use openvm_ecc_sw_macros::sw_declare; - -use crate::pairing::PairingIntrinsics; - -#[cfg(all(test, feature = "halo2curves", not(target_os = "zkvm")))] -mod tests; - -#[cfg(not(target_os = "zkvm"))] -lazy_static! { - pub static ref BLS12_381_MODULUS: BigUint = BigUint::from_bytes_be(&hex!( - "1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab" - )); - pub static ref BLS12_381_ORDER: BigUint = BigUint::from_bytes_be(&hex!( - "73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001" - )); -} - -pub const BLS12_381_XI_ISIZE: [isize; 2] = [1, 1]; -pub const BLS12_381_NUM_LIMBS: usize = 48; -pub const BLS12_381_LIMB_BITS: usize = 8; -pub const BLS12_381_BLOCK_SIZE: usize = 16; - -pub const BLS12_381_SEED_ABS: u64 = 0xd201000000010000; -// Encodes the Bls12_381 seed, x. -// x = sum_i BLS12_381_PSEUDO_BINARY_ENCODING[i] * 2^i -// where BLS12_381_PSEUDO_BINARY_ENCODING[i] is in {-1, 0, 1} -// Validated against BLS12_381_SEED_ABS by a test in tests.rs -pub const BLS12_381_PSEUDO_BINARY_ENCODING: [i8; 64] = [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, -]; - -moduli_declare! { - Bls12_381Fp { modulus = "0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab" }, - Bls12_381Scalar { modulus = "0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001" }, -} - -const CURVE_B: Bls12_381Fp = Bls12_381Fp::from_const_u8(4); - -sw_declare! { - Bls12_381G1Affine { mod_type = Bls12_381Fp, b = CURVE_B }, -} - -#[cfg(not(target_os = "zkvm"))] -// Used in WeierstrassExtension config -pub const BLS12_381_ECC_STRUCT_NAME: &str = "Bls12_381G1Affine"; - -pub type Fp = Bls12_381Fp; -pub type Scalar = Bls12_381Scalar; -/// Affine point representation of `Fp` points of BLS12-381. -/// **Note**: an instance of this type may be constructed that lies -/// on the curve but not necessarily in the prime order subgroup -/// because the group has cofactors. -pub type G1Affine = Bls12_381G1Affine; -pub use g2::G2Affine; - -impl Field for Fp { - type SelfRef<'a> = &'a Self; - const ZERO: Self = ::ZERO; - const ONE: Self = ::ONE; - - fn double_assign(&mut self) { - IntMod::double_assign(self); - } - - fn square_assign(&mut self) { - IntMod::square_assign(self); - } -} - -impl Field for Scalar { - type SelfRef<'a> = &'a Self; - const ZERO: Self = ::ZERO; - const ONE: Self = ::ONE; - - fn double_assign(&mut self) { - IntMod::double_assign(self); - } - - fn square_assign(&mut self) { - IntMod::square_assign(self); - } -} - -// https://hackmd.io/@benjaminion/bls12-381#Cofactor -// BLS12-381: The from_xy function will allow constructing elements that lie on the curve -// but aren't actually in the cyclic subgroup of prime order that is usually called G1. -impl CyclicGroup for G1Affine { - // https://github.com/zcash/librustzcash/blob/6e0364cd42a2b3d2b958a54771ef51a8db79dd29/pairing/src/bls12_381/README.md#generators - const GENERATOR: Self = G1Affine { - x: Bls12_381Fp::from_const_bytes(hex!( - "BBC622DB0AF03AFBEF1A7AF93FE8556C58AC1B173F3A4EA105B974974F8C68C30FACA94F8C63952694D79731A7D3F117" - )), - y: Bls12_381Fp::from_const_bytes(hex!( - "E1E7C5462923AA0CE48A88A244C73CD0EDB3042CCB18DB00F60AD0D595E0F5FCE48A1D74ED309EA0F1A0AAE381F4B308" - )), - }; - const NEG_GENERATOR: Self = G1Affine { - x: Bls12_381Fp::from_const_bytes(hex!( - "BBC622DB0AF03AFBEF1A7AF93FE8556C58AC1B173F3A4EA105B974974F8C68C30FACA94F8C63952694D79731A7D3F117" - )), - y: Bls12_381Fp::from_const_bytes(hex!( - "CAC239B9D6DC54AD1B75CB0EBA386F4E3642ACCAD5B95566C907B51DEF6A8167F2212ECFC8767DAAA845D555681D4D11" - )), - }; -} - -pub struct Bls12_381; - -impl IntrinsicCurve for Bls12_381 { - type Scalar = Scalar; - type Point = G1Affine; - - fn msm(coeffs: &[Self::Scalar], bases: &[Self::Point]) -> Self::Point { - openvm_ecc_guest::msm(coeffs, bases) - } -} - -// Define a G2Affine struct that implements curve operations using `Fp2` intrinsics -// but not special E(Fp2) intrinsics. -mod g2 { - use openvm_algebra_guest::Field; - use openvm_ecc_guest::{ - impl_sw_affine, impl_sw_group_ops, weierstrass::WeierstrassPoint, AffinePoint, Group, - }; - - use super::{Fp, Fp2}; - - const THREE: Fp2 = Fp2::new(Fp::from_const_u8(3), Fp::ZERO); - const B: Fp2 = Fp2::new(Fp::from_const_u8(4), Fp::from_const_u8(4)); - impl_sw_affine!(G2Affine, Fp2, THREE, B); - impl_sw_group_ops!(G2Affine, Fp2); -} - -impl PairingIntrinsics for Bls12_381 { - type Fp = Fp; - type Fp2 = Fp2; - type Fp12 = Fp12; - - const PAIRING_IDX: usize = 1; - // The sextic extension `Fp12` is `Fp2[X] / (X^6 - \xi)`, where `\xi` is a non-residue. - const XI: Fp2 = Fp2::new(Fp::from_const_u8(1), Fp::from_const_u8(1)); - const FP2_TWO: Fp2 = Fp2::new(Fp::from_const_u8(2), Fp::from_const_u8(0)); - const FP2_THREE: Fp2 = Fp2::new(Fp::from_const_u8(3), Fp::from_const_u8(0)); - - // Multiplication constants for the Frobenius map for coefficients in Fp2 c1..=c5 for powers - // 0..12 FROBENIUS_COEFFS\[i\]\[j\] = \xi^{(j + 1) * (p^i - 1)/6} when p = 1 (mod 6) - // These are validated against `halo2curves::bls12_381::FROBENIUS_COEFF_FQ12_C1` in tests.rs - const FROBENIUS_COEFFS: [[Self::Fp2; 5]; 12] = [ - [ - Fp2 { - c0: Bls12_381Fp(hex!( - "010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 - ")), - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 - ")), - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 - ")), - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 - ")), - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 - ")), - }, - ], - [ - Fp2 { - c0: Bls12_381Fp(hex!( - "b85f2392ed75078d3d81e7633da57ef6c4b9ba84d743247b4f5fbd3cfd03d60f1f0d2c20b4be31c26706bb02bfd30419" - )), - c1: Bls12_381Fp(hex!( - "f34adc6d128af72cc27e6c4dc15a2d285f3cf671c98e0cec6fb3c7b68747a154b89f1f2302e9e98832e0c4362b3efc00" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )), - c1: Bls12_381Fp(hex!( - "acaa00000000fd8bfdff494feb2794409b5fb80f65297d89d49a75897d850daa85ded463864002ec99e67f39ea11011a" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "09cce3edfb8410c8f405ec722f9967eec5419200176ef7775e43d3c2ab5d3948fe7fd16b6de331680b40ff37040eaf06" - )), - c1: Bls12_381Fp(hex!( - "09cce3edfb8410c8f405ec722f9967eec5419200176ef7775e43d3c2ab5d3948fe7fd16b6de331680b40ff37040eaf06" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "adaa00000000fd8bfdff494feb2794409b5fb80f65297d89d49a75897d850daa85ded463864002ec99e67f39ea11011a" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "16810780e9fa189b32877f256e3e3ac666059c8e4ddfea8bee8f0b0c241698f345e0b1486bfa47dfd85f3a01d9cfb205" - )), - c1: Bls12_381Fp(hex!( - "9529f87f1605e61ecd78d48b90c17158bdf0146853f345dbd08279e76035df7091cc99fa4aadd36bc186453811424e14" - )) - }, - ], - [ - Fp2 { - c0: Bls12_381Fp(hex!( - "fffffeffffff012e02000a6213d817de8896f8e63ba9b3ddea770f6a07c669ba51ce76df2f67195f0000000000000000" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "fefffeffffff012e02000a6213d817de8896f8e63ba9b3ddea770f6a07c669ba51ce76df2f67195f0000000000000000" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "aaaafffffffffeb9ffff53b1feffab1e24f6b0f6a0d23067bf1285f3844b7764d7ac4b43b6a71b4b9ae67f39ea11011a" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "acaa00000000fd8bfdff494feb2794409b5fb80f65297d89d49a75897d850daa85ded463864002ec99e67f39ea11011a" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "adaa00000000fd8bfdff494feb2794409b5fb80f65297d89d49a75897d850daa85ded463864002ec99e67f39ea11011a" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )) - }, - ], - [ - Fp2 { - c0: Bls12_381Fp(hex!( - "a2de1b12047beef10afa673ecf6644305eb41ef6896439ef60cfb130d9ed3d1cd92c7ad748c4e9e28ea68001e6035213" - )), - c1: Bls12_381Fp(hex!( - "09cce3edfb8410c8f405ec722f9967eec5419200176ef7775e43d3c2ab5d3948fe7fd16b6de331680b40ff37040eaf06" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )), - c1: Bls12_381Fp(hex!( - "010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "a2de1b12047beef10afa673ecf6644305eb41ef6896439ef60cfb130d9ed3d1cd92c7ad748c4e9e28ea68001e6035213" - )), - c1: Bls12_381Fp(hex!( - "a2de1b12047beef10afa673ecf6644305eb41ef6896439ef60cfb130d9ed3d1cd92c7ad748c4e9e28ea68001e6035213" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "aaaafffffffffeb9ffff53b1feffab1e24f6b0f6a0d23067bf1285f3844b7764d7ac4b43b6a71b4b9ae67f39ea11011a" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "09cce3edfb8410c8f405ec722f9967eec5419200176ef7775e43d3c2ab5d3948fe7fd16b6de331680b40ff37040eaf06" - )), - c1: Bls12_381Fp(hex!( - "a2de1b12047beef10afa673ecf6644305eb41ef6896439ef60cfb130d9ed3d1cd92c7ad748c4e9e28ea68001e6035213" - )) - }, - ], - [ - Fp2 { - c0: Bls12_381Fp(hex!( - "fefffeffffff012e02000a6213d817de8896f8e63ba9b3ddea770f6a07c669ba51ce76df2f67195f0000000000000000" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "acaa00000000fd8bfdff494feb2794409b5fb80f65297d89d49a75897d850daa85ded463864002ec99e67f39ea11011a" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "fefffeffffff012e02000a6213d817de8896f8e63ba9b3ddea770f6a07c669ba51ce76df2f67195f0000000000000000" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "acaa00000000fd8bfdff494feb2794409b5fb80f65297d89d49a75897d850daa85ded463864002ec99e67f39ea11011a" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )) - }, - ], - [ - Fp2 { - c0: Bls12_381Fp(hex!( - "9529f87f1605e61ecd78d48b90c17158bdf0146853f345dbd08279e76035df7091cc99fa4aadd36bc186453811424e14" - )), - c1: Bls12_381Fp(hex!( - "16810780e9fa189b32877f256e3e3ac666059c8e4ddfea8bee8f0b0c241698f345e0b1486bfa47dfd85f3a01d9cfb205" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )), - c1: Bls12_381Fp(hex!( - "fefffeffffff012e02000a6213d817de8896f8e63ba9b3ddea770f6a07c669ba51ce76df2f67195f0000000000000000" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "09cce3edfb8410c8f405ec722f9967eec5419200176ef7775e43d3c2ab5d3948fe7fd16b6de331680b40ff37040eaf06" - )), - c1: Bls12_381Fp(hex!( - "09cce3edfb8410c8f405ec722f9967eec5419200176ef7775e43d3c2ab5d3948fe7fd16b6de331680b40ff37040eaf06" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "fffffeffffff012e02000a6213d817de8896f8e63ba9b3ddea770f6a07c669ba51ce76df2f67195f0000000000000000" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "f34adc6d128af72cc27e6c4dc15a2d285f3cf671c98e0cec6fb3c7b68747a154b89f1f2302e9e98832e0c4362b3efc00" - )), - c1: Bls12_381Fp(hex!( - "b85f2392ed75078d3d81e7633da57ef6c4b9ba84d743247b4f5fbd3cfd03d60f1f0d2c20b4be31c26706bb02bfd30419" - )) - }, - ], - [ - Fp2 { - c0: Bls12_381Fp(hex!( - "aaaafffffffffeb9ffff53b1feffab1e24f6b0f6a0d23067bf1285f3844b7764d7ac4b43b6a71b4b9ae67f39ea11011a" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "aaaafffffffffeb9ffff53b1feffab1e24f6b0f6a0d23067bf1285f3844b7764d7ac4b43b6a71b4b9ae67f39ea11011a" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "aaaafffffffffeb9ffff53b1feffab1e24f6b0f6a0d23067bf1285f3844b7764d7ac4b43b6a71b4b9ae67f39ea11011a" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )) - }, - ], - [ - Fp2 { - c0: Bls12_381Fp(hex!( - "f34adc6d128af72cc27e6c4dc15a2d285f3cf671c98e0cec6fb3c7b68747a154b89f1f2302e9e98832e0c4362b3efc00" - )), - c1: Bls12_381Fp(hex!( - "b85f2392ed75078d3d81e7633da57ef6c4b9ba84d743247b4f5fbd3cfd03d60f1f0d2c20b4be31c26706bb02bfd30419" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )), - c1: Bls12_381Fp(hex!( - "acaa00000000fd8bfdff494feb2794409b5fb80f65297d89d49a75897d850daa85ded463864002ec99e67f39ea11011a" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "a2de1b12047beef10afa673ecf6644305eb41ef6896439ef60cfb130d9ed3d1cd92c7ad748c4e9e28ea68001e6035213" - )), - c1: Bls12_381Fp(hex!( - "a2de1b12047beef10afa673ecf6644305eb41ef6896439ef60cfb130d9ed3d1cd92c7ad748c4e9e28ea68001e6035213" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "adaa00000000fd8bfdff494feb2794409b5fb80f65297d89d49a75897d850daa85ded463864002ec99e67f39ea11011a" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "9529f87f1605e61ecd78d48b90c17158bdf0146853f345dbd08279e76035df7091cc99fa4aadd36bc186453811424e14" - )), - c1: Bls12_381Fp(hex!( - "16810780e9fa189b32877f256e3e3ac666059c8e4ddfea8bee8f0b0c241698f345e0b1486bfa47dfd85f3a01d9cfb205" - )) - }, - ], - [ - Fp2 { - c0: Bls12_381Fp(hex!( - "acaa00000000fd8bfdff494feb2794409b5fb80f65297d89d49a75897d850daa85ded463864002ec99e67f39ea11011a" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "fefffeffffff012e02000a6213d817de8896f8e63ba9b3ddea770f6a07c669ba51ce76df2f67195f0000000000000000" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "acaa00000000fd8bfdff494feb2794409b5fb80f65297d89d49a75897d850daa85ded463864002ec99e67f39ea11011a" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "fefffeffffff012e02000a6213d817de8896f8e63ba9b3ddea770f6a07c669ba51ce76df2f67195f0000000000000000" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )) - }, - ], - [ - Fp2 { - c0: Bls12_381Fp(hex!( - "09cce3edfb8410c8f405ec722f9967eec5419200176ef7775e43d3c2ab5d3948fe7fd16b6de331680b40ff37040eaf06" - )), - c1: Bls12_381Fp(hex!( - "a2de1b12047beef10afa673ecf6644305eb41ef6896439ef60cfb130d9ed3d1cd92c7ad748c4e9e28ea68001e6035213" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )), - c1: Bls12_381Fp(hex!( - "010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "09cce3edfb8410c8f405ec722f9967eec5419200176ef7775e43d3c2ab5d3948fe7fd16b6de331680b40ff37040eaf06" - )), - c1: Bls12_381Fp(hex!( - "09cce3edfb8410c8f405ec722f9967eec5419200176ef7775e43d3c2ab5d3948fe7fd16b6de331680b40ff37040eaf06" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "aaaafffffffffeb9ffff53b1feffab1e24f6b0f6a0d23067bf1285f3844b7764d7ac4b43b6a71b4b9ae67f39ea11011a" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "a2de1b12047beef10afa673ecf6644305eb41ef6896439ef60cfb130d9ed3d1cd92c7ad748c4e9e28ea68001e6035213" - )), - c1: Bls12_381Fp(hex!( - "09cce3edfb8410c8f405ec722f9967eec5419200176ef7775e43d3c2ab5d3948fe7fd16b6de331680b40ff37040eaf06" - )) - }, - ], - [ - Fp2 { - c0: Bls12_381Fp(hex!( - "adaa00000000fd8bfdff494feb2794409b5fb80f65297d89d49a75897d850daa85ded463864002ec99e67f39ea11011a" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "acaa00000000fd8bfdff494feb2794409b5fb80f65297d89d49a75897d850daa85ded463864002ec99e67f39ea11011a" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "aaaafffffffffeb9ffff53b1feffab1e24f6b0f6a0d23067bf1285f3844b7764d7ac4b43b6a71b4b9ae67f39ea11011a" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "fefffeffffff012e02000a6213d817de8896f8e63ba9b3ddea770f6a07c669ba51ce76df2f67195f0000000000000000" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "fffffeffffff012e02000a6213d817de8896f8e63ba9b3ddea770f6a07c669ba51ce76df2f67195f0000000000000000" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )) - }, - ], - [ - Fp2 { - c0: Bls12_381Fp(hex!( - "16810780e9fa189b32877f256e3e3ac666059c8e4ddfea8bee8f0b0c241698f345e0b1486bfa47dfd85f3a01d9cfb205" - )), - c1: Bls12_381Fp(hex!( - "9529f87f1605e61ecd78d48b90c17158bdf0146853f345dbd08279e76035df7091cc99fa4aadd36bc186453811424e14" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )), - c1: Bls12_381Fp(hex!( - "fefffeffffff012e02000a6213d817de8896f8e63ba9b3ddea770f6a07c669ba51ce76df2f67195f0000000000000000" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "a2de1b12047beef10afa673ecf6644305eb41ef6896439ef60cfb130d9ed3d1cd92c7ad748c4e9e28ea68001e6035213" - )), - c1: Bls12_381Fp(hex!( - "a2de1b12047beef10afa673ecf6644305eb41ef6896439ef60cfb130d9ed3d1cd92c7ad748c4e9e28ea68001e6035213" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "fffffeffffff012e02000a6213d817de8896f8e63ba9b3ddea770f6a07c669ba51ce76df2f67195f0000000000000000" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "b85f2392ed75078d3d81e7633da57ef6c4b9ba84d743247b4f5fbd3cfd03d60f1f0d2c20b4be31c26706bb02bfd30419" - )), - c1: Bls12_381Fp(hex!( - "f34adc6d128af72cc27e6c4dc15a2d285f3cf671c98e0cec6fb3c7b68747a154b89f1f2302e9e98832e0c4362b3efc00" - )) - }, - ], - ]; -} - -impl Bls12_381 { - // FINAL_EXPONENT = (p^12 - 1) / r in big-endian - // Validated by a test in test.rs - pub const FINAL_EXPONENT: [u8; 540] = hex!( - "02ee1db5dcc825b7e1bda9c0496a1c0a89ee0193d4977b3f7d4507d07363baa13f8d14a917848517badc3a43d1073776ab353f2c30698e8cc7deada9c0aadff5e9cfee9a074e43b9a660835cc872ee83ff3a0f0f1c0ad0d6106feaf4e347aa68ad49466fa927e7bb9375331807a0dce2630d9aa4b113f414386b0e8819328148978e2b0dd39099b86e1ab656d2670d93e4d7acdd350da5359bc73ab61a0c5bf24c374693c49f570bcd2b01f3077ffb10bf24dde41064837f27611212596bc293c8d4c01f25118790f4684d0b9c40a68eb74bb22a40ee7169cdc1041296532fef459f12438dfc8e2886ef965e61a474c5c85b0129127a1b5ad0463434724538411d1676a53b5a62eb34c05739334f46c02c3f0bd0c55d3109cd15948d0a1fad20044ce6ad4c6bec3ec03ef19592004cedd556952c6d8823b19dadd7c2498345c6e5308f1c511291097db60b1749bf9b71a9f9e0100418a3ef0bc627751bbd81367066bca6a4c1b6dcfc5cceb73fc56947a403577dfa9e13c24ea820b09c1d9f7c31759c3635de3f7a3639991708e88adce88177456c49637fd7961be1a4c7e79fb02faa732e2f3ec2bea83d196283313492caa9d4aff1c910e9622d2a73f62537f2701aaef6539314043f7bbce5b78c7869aeb2181a67e49eeed2161daf3f881bd88592d767f67c4717489119226c2f011d4cab803e9d71650a6f80698e2f8491d12191a04406fbc8fbd5f48925f98630e68bfb24c0bcb9b55df57510" - ); -} diff --git a/extensions/pairing/guest/src/bls12_381/pairing.rs b/extensions/pairing/guest/src/bls12_381/pairing.rs deleted file mode 100644 index 9cd7ade4a5..0000000000 --- a/extensions/pairing/guest/src/bls12_381/pairing.rs +++ /dev/null @@ -1,347 +0,0 @@ -use alloc::vec::Vec; - -use itertools::izip; -use openvm_algebra_guest::{ - field::{ComplexConjugate, FieldExtension}, - DivUnsafe, Field, -}; -use openvm_ecc_guest::AffinePoint; -#[cfg(target_os = "zkvm")] -use { - crate::{PairingBaseFunct7, OPCODE, PAIRING_FUNCT3}, - core::mem::MaybeUninit, - openvm_platform::custom_insn_r, - openvm_rv32im_guest, - openvm_rv32im_guest::hint_buffer_u32, -}; - -use super::{Bls12_381, Fp, Fp12, Fp2, BLS12_381_PSEUDO_BINARY_ENCODING, BLS12_381_SEED_ABS}; -use crate::pairing::{ - exp_check_fallback, Evaluatable, EvaluatedLine, FromLineMType, LineMulMType, MillerStep, - MultiMillerLoop, PairingCheck, PairingCheckError, PairingIntrinsics, UnevaluatedLine, -}; -#[cfg(all(feature = "halo2curves", not(target_os = "zkvm")))] -use crate::{ - bls12_381::utils::{ - convert_bls12381_fp2_to_halo2_fq2, convert_bls12381_fp_to_halo2_fq, - convert_bls12381_halo2_fq12_to_fp12, - }, - halo2curves_shims::bls12_381::Bls12_381 as Halo2CurvesBls12_381, - pairing::FinalExp, -}; - -impl Evaluatable for UnevaluatedLine { - fn evaluate(&self, xy_frac: &(Fp, Fp)) -> EvaluatedLine { - let (x_over_y, y_inv) = xy_frac; - // Represents the line L(x,y) = 1 + b (x/y) w^-1 + c (1/y) w^-3 - EvaluatedLine { - b: self.b.mul_base(x_over_y), - c: self.c.mul_base(y_inv), - } - } -} - -impl FromLineMType for Fp12 { - // Since multiplying by w^3 doesn't change the miller loop result, we transform the line - // into L_new(x,y) = w^3 L(x,y) = w^3 + b (x/y) w^2 + c (1/y) - fn from_evaluated_line_m_type(line: EvaluatedLine) -> Fp12 { - Fp12::from_coeffs([line.c, Fp2::ZERO, line.b, Fp2::ONE, Fp2::ZERO, Fp2::ZERO]) - } -} - -// TODO[jpw]: make this into a macro depending on P::PAIRING_IDX when we have more curves -impl LineMulMType for Bls12_381 { - /// Multiplies two lines in 023-form to get an element in 02345-form - fn mul_023_by_023(l0: &EvaluatedLine, l1: &EvaluatedLine) -> [Fp2; 5] { - // l0 = c0 + b0 w^2 + w^3 - let b0 = &l0.b; - let c0 = &l0.c; - // l1 = c1 + b1 w^2 + w^3 - let b1 = &l1.b; - let c1 = &l1.c; - - // where w⁶ = xi - // l0 * l1 = c0c1 + (c0b1 + c1b0)w² + (c0 + c1)w³ + (b0b1)w⁴ + (b0 +b1)w⁵ + w⁶ - // = (c0c1 + xi) + (c0b1 + c1b0)w² + (c0 + c1)w³ + (b0b1)w⁴ + (b0 + b1)w⁵ - let x0 = c0 * c1 + Bls12_381::XI; - let x2 = c0 * b1 + c1 * b0; - let x3 = c0 + c1; - let x4 = b0 * b1; - let x5 = b0 + b1; - - [x0, x2, x3, x4, x5] - } - - /// Multiplies a line in 02345-form with a Fp12 element to get an Fp12 element - fn mul_by_023(f: &Fp12, l: &EvaluatedLine) -> Fp12 { - // this is only used if the number of lines is odd, which doesn't happen for our - // applications right now, so we can use this suboptimal implementation - Fp12::from_evaluated_line_m_type(l.clone()) * f - } - - /// Multiplies a line in 02345-form with a Fp12 element to get an Fp12 element - fn mul_by_02345(f: &Fp12, x: &[Fp2; 5]) -> Fp12 { - // we update the order of the coefficients to match the Fp12 coefficient ordering: - // Fp12 { - // c0: Fp6 { - // c0: x0, - // c1: x2, - // c2: x4, - // }, - // c1: Fp6 { - // c0: x1, - // c1: x3, - // c2: x5, - // }, - // } - let o0 = &x[0]; // coeff x0 - let o1 = &x[1]; // coeff x2 - let o2 = &x[3]; // coeff x4 - let o4 = &x[2]; // coeff x3 - let o5 = &x[4]; // coeff x5 - - let xi = &Bls12_381::XI; - - let self_coeffs = &f.c; - let s0 = &self_coeffs[0]; - let s1 = &self_coeffs[2]; - let s2 = &self_coeffs[4]; - let s3 = &self_coeffs[1]; - let s4 = &self_coeffs[3]; - let s5 = &self_coeffs[5]; - - // NOTE[yj]: Hand-calculated multiplication for Fp12 * 02345 ∈ Fp2; this is likely not the - // most efficient implementation c00 = cs0co0 + xi(cs1co2 + cs2co1 + cs3co5 + - // cs4co4) c01 = cs0co1 + cs1co0 + xi(cs2co2 + cs4co5 + cs5co4) - // c02 = cs0co2 + cs1co1 + cs2co0 + cs3co4 + xi(cs5co5) - // c10 = cs3co0 + xi(cs1co5 + cs2co4 + cs4co2 + cs5co1) - // c11 = cs0co4 + cs3co1 + cs4co0 + xi(cs2co5 + cs5co2) - // c12 = cs0co5 + cs1co4 + cs3co2 + cs4co1 + cs5co0 - // where cs*: self.c* - let c00 = s0 * o0 + xi * &(s1 * o2 + s2 * o1 + s3 * o5 + s4 * o4); - let c01 = s0 * o1 + s1 * o0 + xi * &(s2 * o2 + s4 * o5 + s5 * o4); - let c02 = s0 * o2 + s1 * o1 + s2 * o0 + s3 * o4 + xi * &(s5 * o5); - let c10 = s3 * o0 + xi * &(s1 * o5 + s2 * o4 + s4 * o2 + s5 * o1); - let c11 = s0 * o4 + s3 * o1 + s4 * o0 + xi * &(s2 * o5 + s5 * o2); - let c12 = s0 * o5 + s1 * o4 + s3 * o2 + s4 * o1 + s5 * o0; - - Fp12::from_coeffs([c00, c10, c01, c11, c02, c12]) - } -} - -#[allow(non_snake_case)] -impl MultiMillerLoop for Bls12_381 { - type Fp = Fp; - type Fp12 = Fp12; - - const SEED_ABS: u64 = BLS12_381_SEED_ABS; - const PSEUDO_BINARY_ENCODING: &[i8] = &BLS12_381_PSEUDO_BINARY_ENCODING; - - fn evaluate_lines_vec(f: Self::Fp12, lines: Vec>) -> Self::Fp12 { - let mut f = f; - let mut lines = lines; - if lines.len() % 2 == 1 { - f = Self::mul_by_023(&f, &lines.pop().unwrap()); - } - for chunk in lines.chunks(2) { - if let [line0, line1] = chunk { - let prod = Self::mul_023_by_023(line0, line1); - f = Self::mul_by_02345(&f, &prod); - } else { - panic!("lines.len() % 2 should be 0 at this point"); - } - } - f - } - - /// The expected output of this function when running the Miller loop with embedded exponent is - /// c^3 * l_{3Q} - fn pre_loop( - Q_acc: Vec>, - Q: &[AffinePoint], - c: Option, - xy_fracs: &[(Self::Fp, Self::Fp)], - ) -> (Self::Fp12, Vec>) { - let mut f = if let Some(mut c) = c { - // for the miller loop with embedded exponent, f will be set to c at the beginning of - // the function, and we will multiply by c again due to the last two values - // of the pseudo-binary encoding (BLS12_381_PSEUDO_BINARY_ENCODING) being 1. - // Therefore, the final value of f at the end of this block is c^3. - let mut c3 = c.clone(); - c.square_assign(); - c3 *= &c; - c3 - } else { - Self::Fp12::ONE - }; - - let mut Q_acc = Q_acc; - - // Special case the first iteration of the Miller loop with pseudo_binary_encoding = 1: - // this means that the first step is a double and add, but we need to separate the two steps - // since the optimized `miller_double_and_add_step` will fail because Q_acc is equal - // to Q_signed on the first iteration - let (Q_out_double, lines_2S) = Q_acc - .into_iter() - .map(|Q| Self::miller_double_step(&Q)) - .unzip::<_, _, Vec<_>, Vec<_>>(); - Q_acc = Q_out_double; - - let mut initial_lines = Vec::>::new(); - - let lines_iter = izip!(lines_2S.iter(), xy_fracs.iter()); - for (line_2S, xy_frac) in lines_iter { - let line = line_2S.evaluate(xy_frac); - initial_lines.push(line); - } - - let (Q_out_add, lines_S_plus_Q) = Q_acc - .iter() - .zip(Q.iter()) - .map(|(Q_acc, Q)| Self::miller_add_step(Q_acc, Q)) - .unzip::<_, _, Vec<_>, Vec<_>>(); - Q_acc = Q_out_add; - - let lines_iter = izip!(lines_S_plus_Q.iter(), xy_fracs.iter()); - for (lines_S_plus_Q, xy_frac) in lines_iter { - let line = lines_S_plus_Q.evaluate(xy_frac); - initial_lines.push(line); - } - - f = Self::evaluate_lines_vec(f, initial_lines); - - (f, Q_acc) - } - - /// After running the main body of the Miller loop, we conjugate f due to the curve seed x being - /// negative. - fn post_loop( - f: &Self::Fp12, - Q_acc: Vec>, - _Q: &[AffinePoint], - _c: Option, - _xy_fracs: &[(Self::Fp, Self::Fp)], - ) -> (Self::Fp12, Vec>) { - // Conjugate for negative component of the seed - // By Lemma 1 from https://www.iacr.org/archive/eurocrypt2011/66320047/66320047.pdf f_{x,Q} = conjugate( f_{|x|,Q} ) - let mut f = f.clone(); - f.conjugate_assign(); - (f, Q_acc) - } -} - -#[allow(non_snake_case)] -impl PairingCheck for Bls12_381 { - type Fp = Fp; - type Fp2 = Fp2; - type Fp12 = Fp12; - - #[allow(unused_variables)] - fn pairing_check_hint( - P: &[AffinePoint], - Q: &[AffinePoint], - ) -> (Self::Fp12, Self::Fp12) { - #[cfg(not(target_os = "zkvm"))] - { - #[cfg(not(feature = "halo2curves"))] - panic!("`halo2curves` feature must be enabled to use pairing check hint on host"); - - #[cfg(feature = "halo2curves")] - { - let p_halo2 = P - .iter() - .map(|p| { - AffinePoint::new( - convert_bls12381_fp_to_halo2_fq(p.x.clone()), - convert_bls12381_fp_to_halo2_fq(p.y.clone()), - ) - }) - .collect::>(); - let q_halo2 = Q - .iter() - .map(|q| { - AffinePoint::new( - convert_bls12381_fp2_to_halo2_fq2(q.x.clone()), - convert_bls12381_fp2_to_halo2_fq2(q.y.clone()), - ) - }) - .collect::>(); - let fq12 = Halo2CurvesBls12_381::multi_miller_loop(&p_halo2, &q_halo2); - let (c_fq12, s_fq12) = Halo2CurvesBls12_381::final_exp_hint(&fq12); - let c = convert_bls12381_halo2_fq12_to_fp12(c_fq12); - let s = convert_bls12381_halo2_fq12_to_fp12(s_fq12); - (c, s) - } - } - #[cfg(target_os = "zkvm")] - { - let hint = MaybeUninit::<(Fp12, Fp12)>::uninit(); - // We do not rely on the slice P's memory layout since rust does not guarantee it across - // compiler versions. - let p_fat_ptr = (P.as_ptr() as u32, P.len() as u32); - let q_fat_ptr = (Q.as_ptr() as u32, Q.len() as u32); - unsafe { - custom_insn_r!( - opcode = OPCODE, - funct3 = PAIRING_FUNCT3, - funct7 = ((Bls12_381::PAIRING_IDX as u8) * PairingBaseFunct7::PAIRING_MAX_KINDS + PairingBaseFunct7::HintFinalExp as u8), - rd = Const "x0", - rs1 = In &p_fat_ptr, - rs2 = In &q_fat_ptr - ); - let ptr = hint.as_ptr() as *const u8; - hint_buffer_u32!(ptr, (48 * 12 * 2) / 4); - hint.assume_init() - } - } - } - - fn pairing_check( - P: &[AffinePoint], - Q: &[AffinePoint], - ) -> Result<(), PairingCheckError> { - Self::try_honest_pairing_check(P, Q).unwrap_or_else(|| { - let f = Self::multi_miller_loop(P, Q); - exp_check_fallback(&f, &Self::FINAL_EXPONENT) - }) - } -} - -#[allow(non_snake_case)] -impl Bls12_381 { - // The paper only describes the implementation for Bn254, so we use the gnark implementation for - // Bls12_381. Adapted from the gnark implementation: - // https://github.com/Consensys/gnark/blob/af754dd1c47a92be375930ae1abfbd134c5310d8/std/algebra/emulated/fields_bls12381/e12_pairing.go#L394C1-L395C1 - fn try_honest_pairing_check( - P: &[AffinePoint<::Fp>], - Q: &[AffinePoint<::Fp2>], - ) -> Option> { - let (c, s) = Self::pairing_check_hint(P, Q); - - // The gnark implementation checks that f * s = c^{q - x} where x is the curve seed. - // We check an equivalent condition: f * c^x * s = c^q. - // This is because we can compute f * c^x by embedding the c^x computation in the miller - // loop. - - // We compute c^q before c is consumed by conjugate() below - let c_q = FieldExtension::frobenius_map(&c, 1); - - // Since the Bls12_381 curve has a negative seed, the miller loop for Bls12_381 is computed - // as f_{Miller,x,Q}(P) = conjugate( f_{Miller,-x,Q}(P) * c^{-x} ). - // We will pass in the conjugate inverse of c into the miller loop so that we compute - // fc = conjugate( f_{Miller,-x,Q}(P) * c'^{-x} ) (where c' is the conjugate inverse of c) - // = f_{Miller,x,Q}(P) * c^x - let c_conj = c.conjugate(); - if c_conj == Fp12::ZERO { - return None; - } - let c_conj_inv = Fp12::ONE.div_unsafe(&c_conj); - let fc = Self::multi_miller_loop_embedded_exp(P, Q, Some(c_conj_inv)); - - if fc * s == c_q { - Some(Ok(())) - } else { - None - } - } -} diff --git a/extensions/pairing/guest/src/bls12_381/tests.rs b/extensions/pairing/guest/src/bls12_381/tests.rs deleted file mode 100644 index 9ca38d8586..0000000000 --- a/extensions/pairing/guest/src/bls12_381/tests.rs +++ /dev/null @@ -1,317 +0,0 @@ -use group::ff::Field; -use halo2curves_axiom::bls12_381::{ - Fq, Fq12, Fq2, Fq6, G1Affine, G2Affine, G2Prepared, MillerLoopResult, FROBENIUS_COEFF_FQ12_C1, -}; -use num_bigint::BigUint; -use num_traits::One; -use openvm_algebra_guest::{field::FieldExtension, IntMod}; -use openvm_ecc_guest::{weierstrass::WeierstrassPoint, AffinePoint}; -use rand::{rngs::StdRng, SeedableRng}; - -use super::{Fp, Fp12, Fp2, BLS12_381_MODULUS, BLS12_381_ORDER}; -use crate::{ - bls12_381::{ - utils::{ - convert_bls12381_fp12_to_halo2_fq12, convert_bls12381_halo2_fq12_to_fp12, - convert_bls12381_halo2_fq2_to_fp2, convert_bls12381_halo2_fq_to_fp, - convert_g2_affine_halo2_to_openvm, - }, - Bls12_381, G2Affine as OpenVmG2Affine, BLS12_381_PSEUDO_BINARY_ENCODING, - BLS12_381_SEED_ABS, - }, - pairing::{ - fp2_invert_assign, fp6_invert_assign, fp6_square_assign, FinalExp, MultiMillerLoop, - PairingCheck, PairingIntrinsics, - }, -}; - -#[test] -fn test_bls12381_frobenius_coeffs() { - #[allow(clippy::needless_range_loop)] - for i in 0..12 { - for j in 0..5 { - assert_eq!( - Bls12_381::FROBENIUS_COEFFS[i][j], - convert_bls12381_halo2_fq2_to_fp2(FROBENIUS_COEFF_FQ12_C1[i].pow([j as u64 + 1])), - "FROBENIUS_COEFFS[{}][{}] failed", - i, - j - ) - } - } -} - -#[test] -fn test_bls12381_frobenius() { - let mut rng = StdRng::seed_from_u64(15); - for pow in 0..12 { - let fq = Fq12::random(&mut rng); - let mut fq_frob = fq; - for _ in 0..pow { - fq_frob = fq_frob.frobenius_map(); - } - - let fp = convert_bls12381_halo2_fq12_to_fp12(fq); - let fp_frob = fp.frobenius_map(pow); - - assert_eq!(fp_frob, convert_bls12381_halo2_fq12_to_fp12(fq_frob)); - } -} - -#[test] -fn test_fp12_invert() { - let mut rng = StdRng::seed_from_u64(15); - let fq = Fq12::random(&mut rng); - let fq_inv = fq.invert().unwrap(); - - let fp = convert_bls12381_halo2_fq12_to_fp12(fq); - let fp_inv = fp.invert(); - assert_eq!(fp_inv, convert_bls12381_halo2_fq12_to_fp12(fq_inv)); -} - -#[test] -fn test_fp6_invert() { - let mut rng = StdRng::seed_from_u64(20); - let fq6 = Fq6 { - c0: Fq2::random(&mut rng), - c1: Fq2::random(&mut rng), - c2: Fq2::random(&mut rng), - }; - let fq6_inv = fq6.invert().unwrap(); - - let fp6c0 = convert_bls12381_halo2_fq2_to_fp2(fq6.c0); - let fp6c1 = convert_bls12381_halo2_fq2_to_fp2(fq6.c1); - let fp6c2 = convert_bls12381_halo2_fq2_to_fp2(fq6.c2); - let mut fp6 = [fp6c0, fp6c1, fp6c2]; - fp6_invert_assign::(&mut fp6, &Bls12_381::XI); - - let fq6_invc0 = convert_bls12381_halo2_fq2_to_fp2(fq6_inv.c0); - let fq6_invc1 = convert_bls12381_halo2_fq2_to_fp2(fq6_inv.c1); - let fq6_invc2 = convert_bls12381_halo2_fq2_to_fp2(fq6_inv.c2); - let fq6_inv = [fq6_invc0, fq6_invc1, fq6_invc2]; - assert_eq!(fp6, fq6_inv); -} - -#[test] -fn test_fp2_invert() { - let mut rng = StdRng::seed_from_u64(25); - let fq2 = Fq2::random(&mut rng); - let fq2_inv = fq2.invert().unwrap(); - - let mut fp2 = convert_bls12381_halo2_fq2_to_fp2(fq2).to_coeffs(); - fp2_invert_assign::(&mut fp2); - assert_eq!(fp2, convert_bls12381_halo2_fq2_to_fp2(fq2_inv).to_coeffs()); -} - -#[test] -fn test_fp6_square() { - let mut rng = StdRng::seed_from_u64(45); - let fq6 = Fq6 { - c0: Fq2::random(&mut rng), - c1: Fq2::random(&mut rng), - c2: Fq2::random(&mut rng), - }; - let fq6_sq = fq6.square(); - - let fp6c0 = convert_bls12381_halo2_fq2_to_fp2(fq6.c0); - let fp6c1 = convert_bls12381_halo2_fq2_to_fp2(fq6.c1); - let fp6c2 = convert_bls12381_halo2_fq2_to_fp2(fq6.c2); - let mut fp6 = [fp6c0, fp6c1, fp6c2]; - fp6_square_assign::(&mut fp6, &Bls12_381::XI); - - let fq6_sqc0 = convert_bls12381_halo2_fq2_to_fp2(fq6_sq.c0); - let fq6_sqc1 = convert_bls12381_halo2_fq2_to_fp2(fq6_sq.c1); - let fq6_sqc2 = convert_bls12381_halo2_fq2_to_fp2(fq6_sq.c2); - let fq6_sq = [fq6_sqc0, fq6_sqc1, fq6_sqc2]; - assert_eq!(fp6, fq6_sq); -} - -#[test] -fn test_fp2_square() { - let mut rng = StdRng::seed_from_u64(55); - let fq2 = Fq2::random(&mut rng); - let fq2_sq = fq2.square(); - - let fp2 = convert_bls12381_halo2_fq2_to_fp2(fq2); - let fp2_sq = &fp2 * &fp2; - assert_eq!(fp2_sq, convert_bls12381_halo2_fq2_to_fp2(fq2_sq)); -} - -#[test] -fn test_fp_add() { - let mut rng = StdRng::seed_from_u64(65); - let fq = Fq::random(&mut rng); - let fq_res = fq + Fq::one(); - - let fp = convert_bls12381_halo2_fq_to_fp(fq); - let fp_res = fp + Fp::ONE; - assert_eq!(fp_res, convert_bls12381_halo2_fq_to_fp(fq_res)); -} - -#[test] -fn test_fp_one() { - let fp_one = Fp::ONE; - let fq_one = Fq::ONE; - assert_eq!(fp_one, convert_bls12381_halo2_fq_to_fp(fq_one)); -} - -// Gt(Fq12) is not public -fn assert_miller_results_eq(a: MillerLoopResult, b: Fp12) { - let b = convert_bls12381_fp12_to_halo2_fq12(b); - crate::halo2curves_shims::bls12_381::tests::assert_miller_results_eq(a, b); -} - -#[test] -fn test_bls12381_miller_loop() { - let mut rng = StdRng::seed_from_u64(65); - let h2c_p = G1Affine::random(&mut rng); - let h2c_q = G2Affine::random(&mut rng); - - let p = AffinePoint { - x: convert_bls12381_halo2_fq_to_fp(h2c_p.x), - y: convert_bls12381_halo2_fq_to_fp(h2c_p.y), - }; - let q = AffinePoint { - x: convert_bls12381_halo2_fq2_to_fp2(h2c_q.x), - y: convert_bls12381_halo2_fq2_to_fp2(h2c_q.y), - }; - - // Compare against halo2curves implementation - let h2c_q_prepared = G2Prepared::from(h2c_q); - let compare_miller = - halo2curves_axiom::bls12_381::multi_miller_loop(&[(&h2c_p, &h2c_q_prepared)]); - let f = Bls12_381::multi_miller_loop(&[p], &[q]); - assert_miller_results_eq(compare_miller, f); -} - -#[test] -fn test_bls12381_miller_loop_identity() { - let mut rng = StdRng::seed_from_u64(33); - let h2c_p = G1Affine::identity(); - let h2c_q = G2Affine::random(&mut rng); - - let p = AffinePoint { - x: convert_bls12381_halo2_fq_to_fp(Fq::ZERO), - y: convert_bls12381_halo2_fq_to_fp(Fq::ZERO), - }; - let q = AffinePoint { - x: convert_bls12381_halo2_fq2_to_fp2(h2c_q.x), - y: convert_bls12381_halo2_fq2_to_fp2(h2c_q.y), - }; - - let f = Bls12_381::multi_miller_loop(&[p], &[q]); - // halo2curves implementation - let h2c_q_prepared = G2Prepared::from(h2c_q); - let compare_miller = - halo2curves_axiom::bls12_381::multi_miller_loop(&[(&h2c_p, &h2c_q_prepared)]); - assert_miller_results_eq(compare_miller, f); -} - -#[test] -fn test_bls12381_miller_loop_identity_2() { - let mut rng = StdRng::seed_from_u64(33); - let h2c_p = G1Affine::random(&mut rng); - let h2c_q = G2Affine::identity(); - let p = AffinePoint { - x: convert_bls12381_halo2_fq_to_fp(h2c_p.x), - y: convert_bls12381_halo2_fq_to_fp(h2c_p.y), - }; - let q = AffinePoint { - x: convert_bls12381_halo2_fq2_to_fp2(Fq2::ZERO), - y: convert_bls12381_halo2_fq2_to_fp2(Fq2::ZERO), - }; - - let f = Bls12_381::multi_miller_loop(&[p], &[q]); - // halo2curves implementation - let h2c_q_prepared = G2Prepared::from(h2c_q); - let compare_miller = - halo2curves_axiom::bls12_381::multi_miller_loop(&[(&h2c_p, &h2c_q_prepared)]); - assert_miller_results_eq(compare_miller, f); -} - -// test on host is enough since we are testing the curve formulas and not anything -// about intrinsic functions -#[test] -fn test_bls12381_g2_affine() { - let mut rng = StdRng::seed_from_u64(34); - for _ in 0..10 { - let p = G2Affine::random(&mut rng); - let q = G2Affine::random(&mut rng); - let expected_add = G2Affine::from(p + q); - let expected_sub = G2Affine::from(p - q); - let expected_neg = -p; - let expected_double = G2Affine::from(p + p); - let [p, q] = [p, q].map(|p| { - let x = convert_bls12381_halo2_fq2_to_fp2(p.x); - let y = convert_bls12381_halo2_fq2_to_fp2(p.y); - // check on curve - OpenVmG2Affine::from_xy(x, y).unwrap() - }); - let r_add = &p + &q; - let r_sub = &p - &q; - let r_neg = -&p; - let r_double = &p + &p; - - for (expected, actual) in [ - (expected_add, r_add), - (expected_sub, r_sub), - (expected_neg, r_neg), - (expected_double, r_double), - ] { - assert_eq!(convert_g2_affine_halo2_to_openvm(expected), actual); - } - } -} - -#[test] -fn test_bls12381_pairing_check_hint_host() { - let mut rng = StdRng::seed_from_u64(83); - let h2c_p = G1Affine::random(&mut rng); - let h2c_q = G2Affine::random(&mut rng); - - let p = AffinePoint { - x: convert_bls12381_halo2_fq_to_fp(h2c_p.x), - y: convert_bls12381_halo2_fq_to_fp(h2c_p.y), - }; - let q = AffinePoint { - x: convert_bls12381_halo2_fq2_to_fp2(h2c_q.x), - y: convert_bls12381_halo2_fq2_to_fp2(h2c_q.y), - }; - - let (c, s) = Bls12_381::pairing_check_hint(&[p], &[q]); - - let p_cmp = AffinePoint { - x: h2c_p.x, - y: h2c_p.y, - }; - let q_cmp = AffinePoint { - x: h2c_q.x, - y: h2c_q.y, - }; - - let f_cmp = - crate::halo2curves_shims::bls12_381::Bls12_381::multi_miller_loop(&[p_cmp], &[q_cmp]); - let (c_cmp, s_cmp) = crate::halo2curves_shims::bls12_381::Bls12_381::final_exp_hint(&f_cmp); - let c_cmp = convert_bls12381_halo2_fq12_to_fp12(c_cmp); - let s_cmp = convert_bls12381_halo2_fq12_to_fp12(s_cmp); - - assert_eq!(c, c_cmp); - assert_eq!(s, s_cmp); -} - -#[test] -fn test_bls12381_final_exponent() { - let final_exp = (BLS12_381_MODULUS.pow(12) - BigUint::one()) / BLS12_381_ORDER.clone(); - assert_eq!(Bls12_381::FINAL_EXPONENT.to_vec(), final_exp.to_bytes_be()); -} - -#[test] -fn test_bls12381_pseudo_binary_encoding() { - let mut x: i128 = 0; - let mut power_of_2 = 1; - for b in BLS12_381_PSEUDO_BINARY_ENCODING.iter() { - x += (*b as i128) * power_of_2; - power_of_2 *= 2; - } - assert_eq!(x.unsigned_abs(), BLS12_381_SEED_ABS as u128); -} diff --git a/extensions/pairing/guest/src/bls12_381/utils.rs b/extensions/pairing/guest/src/bls12_381/utils.rs deleted file mode 100644 index 51c749c596..0000000000 --- a/extensions/pairing/guest/src/bls12_381/utils.rs +++ /dev/null @@ -1,49 +0,0 @@ -use halo2curves_axiom::bls12_381::{Fq, Fq12, Fq2, G2Affine}; -use openvm_algebra_guest::{field::FieldExtension, IntMod}; -use openvm_ecc_guest::weierstrass::WeierstrassPoint; - -use super::{Fp, Fp12, Fp2}; -use crate::bls12_381::G2Affine as OpenVmG2Affine; - -pub(crate) fn convert_bls12381_halo2_fq_to_fp(x: Fq) -> Fp { - let bytes = x.to_bytes(); - Fp::from_le_bytes(&bytes) -} - -pub(crate) fn convert_bls12381_halo2_fq2_to_fp2(x: Fq2) -> Fp2 { - Fp2::new( - convert_bls12381_halo2_fq_to_fp(x.c0), - convert_bls12381_halo2_fq_to_fp(x.c1), - ) -} - -pub(crate) fn convert_bls12381_halo2_fq12_to_fp12(x: Fq12) -> Fp12 { - Fp12 { - c: x.to_coeffs().map(convert_bls12381_halo2_fq2_to_fp2), - } -} - -pub(crate) fn convert_bls12381_fp_to_halo2_fq(x: Fp) -> Fq { - Fq::from_bytes(&x.0).unwrap() -} - -pub(crate) fn convert_bls12381_fp2_to_halo2_fq2(x: Fp2) -> Fq2 { - Fq2 { - c0: convert_bls12381_fp_to_halo2_fq(x.c0.clone()), - c1: convert_bls12381_fp_to_halo2_fq(x.c1.clone()), - } -} - -#[allow(unused)] -pub(crate) fn convert_bls12381_fp12_to_halo2_fq12(x: Fp12) -> Fq12 { - let c = x.to_coeffs(); - Fq12::from_coeffs(c.map(convert_bls12381_fp2_to_halo2_fq2)) -} - -#[allow(unused)] -pub(crate) fn convert_g2_affine_halo2_to_openvm(p: G2Affine) -> OpenVmG2Affine { - OpenVmG2Affine::from_xy_unchecked( - convert_bls12381_halo2_fq2_to_fp2(p.x), - convert_bls12381_halo2_fq2_to_fp2(p.y), - ) -} diff --git a/extensions/pairing/guest/src/bn254/fp12.rs b/extensions/pairing/guest/src/bn254/fp12.rs deleted file mode 100644 index d8b9b07415..0000000000 --- a/extensions/pairing/guest/src/bn254/fp12.rs +++ /dev/null @@ -1,215 +0,0 @@ -use alloc::vec::Vec; -use core::ops::{Mul, MulAssign, Neg}; - -use openvm_algebra_guest::{ - field::{ComplexConjugate, FieldExtension}, - DivAssignUnsafe, DivUnsafe, Field, -}; - -use super::{Bn254, Fp, Fp2}; -use crate::pairing::{fp12_invert_assign, PairingIntrinsics, SexticExtField}; - -pub type Fp12 = SexticExtField; - -impl Fp12 { - pub fn invert(&self) -> Self { - let mut s = self.clone(); - fp12_invert_assign::(&mut s.c, &Bn254::XI); - s - } -} - -impl Field for Fp12 { - type SelfRef<'a> = &'a Self; - const ZERO: Self = Self::new([Fp2::ZERO; 6]); - const ONE: Self = Self::new([ - Fp2::ONE, - Fp2::ZERO, - Fp2::ZERO, - Fp2::ZERO, - Fp2::ZERO, - Fp2::ZERO, - ]); - - fn double_assign(&mut self) { - *self += self.clone(); - } - - fn square_assign(&mut self) { - *self *= self.clone(); - } -} - -impl FieldExtension for Fp12 { - const D: usize = 6; - type Coeffs = [Fp2; 6]; - - fn from_coeffs(coeffs: Self::Coeffs) -> Self { - Self::new(coeffs) - } - - fn from_bytes(bytes: &[u8]) -> Self { - assert_eq!(bytes.len(), 384); - Self::from_coeffs([ - Fp2::from_bytes(&bytes[0..64]), - Fp2::from_bytes(&bytes[64..128]), - Fp2::from_bytes(&bytes[128..192]), - Fp2::from_bytes(&bytes[192..256]), - Fp2::from_bytes(&bytes[256..320]), - Fp2::from_bytes(&bytes[320..384]), - ]) - } - - fn to_coeffs(self) -> Self::Coeffs { - self.c - } - - fn to_bytes(&self) -> Vec { - let mut bytes = Vec::with_capacity(384); - for coeff in self.clone().to_coeffs() { - bytes.extend_from_slice(&coeff.to_bytes()); - } - bytes - } - - fn embed(c0: Fp2) -> Self { - Self::new([c0, Fp2::ZERO, Fp2::ZERO, Fp2::ZERO, Fp2::ZERO, Fp2::ZERO]) - } - - /// We assume that the frobenius map power is < 12 - fn frobenius_map(&self, power: usize) -> Self { - if power & 1 != 0 { - let c0 = self.c[0].clone().conjugate(); - let c1 = self.c[1].clone().conjugate() * &Bn254::FROBENIUS_COEFFS[power][0]; - let c2 = self.c[2].clone().conjugate() * &Bn254::FROBENIUS_COEFFS[power][1]; - let c3 = self.c[3].clone().conjugate() * &Bn254::FROBENIUS_COEFFS[power][2]; - let c4 = self.c[4].clone().conjugate() * &Bn254::FROBENIUS_COEFFS[power][3]; - let c5 = self.c[5].clone().conjugate() * &Bn254::FROBENIUS_COEFFS[power][4]; - Self::new([c0, c1, c2, c3, c4, c5]) - } else { - let c0 = self.c[0].clone(); - let c1 = &self.c[1] * &Bn254::FROBENIUS_COEFFS[power][0]; - let c2 = &self.c[2] * &Bn254::FROBENIUS_COEFFS[power][1]; - let c3 = &self.c[3] * &Bn254::FROBENIUS_COEFFS[power][2]; - let c4 = &self.c[4] * &Bn254::FROBENIUS_COEFFS[power][3]; - let c5 = &self.c[5] * &Bn254::FROBENIUS_COEFFS[power][4]; - Self::new([c0, c1, c2, c3, c4, c5]) - } - } - - fn mul_base(&self, rhs: &Fp2) -> Self { - Self::new([ - &self.c[0] * rhs, - &self.c[1] * rhs, - &self.c[2] * rhs, - &self.c[3] * rhs, - &self.c[4] * rhs, - &self.c[5] * rhs, - ]) - } -} - -// This is ambiguous. It is conjugation for Fp12 over Fp6. -impl ComplexConjugate for Fp12 { - #[inline(always)] - fn conjugate(self) -> Self { - let [c0, c1, c2, c3, c4, c5] = self.c; - Self::new([c0, -c1, c2, -c3, c4, -c5]) - } - - fn conjugate_assign(&mut self) { - self.c[1].neg_assign(); - self.c[3].neg_assign(); - self.c[5].neg_assign(); - } -} - -impl<'a> MulAssign<&'a Fp12> for Fp12 { - #[inline(always)] - fn mul_assign(&mut self, other: &'a Fp12) { - *self = crate::pairing::sextic_tower_mul(self, other, &Bn254::XI); - } -} - -impl<'a> Mul<&'a Fp12> for &'a Fp12 { - type Output = Fp12; - #[inline(always)] - fn mul(self, other: &'a Fp12) -> Self::Output { - crate::pairing::sextic_tower_mul(self, other, &Bn254::XI) - } -} - -impl MulAssign for Fp12 { - #[inline(always)] - fn mul_assign(&mut self, other: Self) { - self.mul_assign(&other); - } -} - -impl Mul for Fp12 { - type Output = Self; - #[inline(always)] - fn mul(mut self, other: Self) -> Self::Output { - self *= other; - self - } -} - -impl<'a> Mul<&'a Fp12> for Fp12 { - type Output = Self; - #[inline(always)] - fn mul(mut self, other: &'a Fp12) -> Fp12 { - self *= other; - self - } -} - -impl<'a> DivAssignUnsafe<&'a Fp12> for Fp12 { - #[inline(always)] - fn div_assign_unsafe(&mut self, other: &'a Fp12) { - *self *= other.invert(); - } -} - -impl<'a> DivUnsafe<&'a Fp12> for &'a Fp12 { - type Output = Fp12; - #[inline(always)] - fn div_unsafe(self, other: &'a Fp12) -> Self::Output { - let mut res = self.clone(); - res.div_assign_unsafe(other); - res - } -} - -impl DivAssignUnsafe for Fp12 { - #[inline(always)] - fn div_assign_unsafe(&mut self, other: Self) { - *self *= other.invert(); - } -} - -impl DivUnsafe for Fp12 { - type Output = Self; - #[inline(always)] - fn div_unsafe(mut self, other: Self) -> Self::Output { - self.div_assign_unsafe(other); - self - } -} - -impl<'a> DivUnsafe<&'a Fp12> for Fp12 { - type Output = Self; - #[inline(always)] - fn div_unsafe(mut self, other: &'a Fp12) -> Self::Output { - self.div_assign_unsafe(other); - self - } -} - -impl Neg for Fp12 { - type Output = Fp12; - #[inline(always)] - fn neg(self) -> Self::Output { - Self::ZERO - &self - } -} diff --git a/extensions/pairing/guest/src/bn254/fp2.rs b/extensions/pairing/guest/src/bn254/fp2.rs deleted file mode 100644 index b086b7d6aa..0000000000 --- a/extensions/pairing/guest/src/bn254/fp2.rs +++ /dev/null @@ -1,76 +0,0 @@ -use alloc::vec::Vec; -use core::ops::Neg; - -use openvm_algebra_complex_macros::{complex_declare, complex_impl_field}; -use openvm_algebra_guest::{field::FieldExtension, Field, IntMod}; - -use super::Fp; - -#[cfg(not(target_os = "zkvm"))] -// Used in Fp2Extension config -pub const BN254_COMPLEX_STRUCT_NAME: &str = "Bn254Fp2"; - -// The struct name needs to be globally unique for linking purposes. -// The mod_type is a path used only in the struct definition. -complex_declare! { - Bn254Fp2 { mod_type = Fp } -} - -complex_impl_field! { - Bn254Fp2, -} - -pub type Fp2 = Bn254Fp2; - -impl FieldExtension for Fp2 { - const D: usize = 2; - type Coeffs = [Fp; 2]; - - fn from_coeffs([c0, c1]: Self::Coeffs) -> Self { - Self { c0, c1 } - } - - fn from_bytes(bytes: &[u8]) -> Self { - assert_eq!(bytes.len(), 64); - Self::from_coeffs([ - Fp::from_const_bytes(bytes[0..32].try_into().unwrap()), - Fp::from_const_bytes(bytes[32..64].try_into().unwrap()), - ]) - } - - fn to_coeffs(self) -> Self::Coeffs { - [self.c0, self.c1] - } - - fn to_bytes(&self) -> Vec { - let mut bytes = Vec::with_capacity(64); - bytes.extend_from_slice(self.c0.as_le_bytes()); - bytes.extend_from_slice(self.c1.as_le_bytes()); - bytes - } - - fn embed(c0: Fp) -> Self { - Self { - c0, - c1: ::ZERO, - } - } - - fn frobenius_map(&self, power: usize) -> Self { - if power % 2 == 0 { - self.clone() - } else { - Self { - c0: self.c0.clone(), - c1: (&self.c1).neg(), - } - } - } - - fn mul_base(&self, rhs: &Fp) -> Self { - Self { - c0: &self.c0 * rhs, - c1: &self.c1 * rhs, - } - } -} diff --git a/extensions/pairing/guest/src/bn254/mod.rs b/extensions/pairing/guest/src/bn254/mod.rs deleted file mode 100644 index 676a0adabd..0000000000 --- a/extensions/pairing/guest/src/bn254/mod.rs +++ /dev/null @@ -1,744 +0,0 @@ -use core::ops::{Add, Neg}; - -use hex_literal::hex; -#[cfg(not(target_os = "zkvm"))] -use lazy_static::lazy_static; -#[cfg(not(target_os = "zkvm"))] -use num_bigint::BigUint; -use openvm_algebra_guest::{Field, IntMod}; -use openvm_algebra_moduli_macros::moduli_declare; -use openvm_ecc_guest::{ - weierstrass::{CachedMulTable, IntrinsicCurve}, - CyclicGroup, Group, -}; -use openvm_ecc_sw_macros::sw_declare; - -use crate::pairing::PairingIntrinsics; - -mod fp12; -mod fp2; -pub mod pairing; -#[cfg(all(feature = "halo2curves", not(target_os = "zkvm")))] -pub(crate) mod utils; - -pub use fp12::*; -pub use fp2::*; - -#[cfg(all(test, feature = "halo2curves", not(target_os = "zkvm")))] -pub mod tests; - -#[cfg(not(target_os = "zkvm"))] -lazy_static! { - pub static ref BN254_MODULUS: BigUint = BigUint::from_bytes_be(&hex!( - "30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47" - )); - pub static ref BN254_ORDER: BigUint = BigUint::from_bytes_be(&hex!( - "30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001" - )); -} - -pub const BN254_XI_ISIZE: [isize; 2] = [9, 1]; -pub const BN254_NUM_LIMBS: usize = 32; -pub const BN254_LIMB_BITS: usize = 8; -pub const BN254_BLOCK_SIZE: usize = 32; - -pub const BN254_SEED: u64 = 0x44e992b44a6909f1; -// Encodes 6x+2 where x is the BN254 seed. -// 6*x+2 = sum_i BN254_PSEUDO_BINARY_ENCODING[i] * 2^i -// where BN254_PSEUDO_BINARY_ENCODING[i] is in {-1, 0, 1} -// Validated against BN254_SEED_ABS by a test in tests.rs -pub const BN254_PSEUDO_BINARY_ENCODING: [i8; 66] = [ - 0, 0, 0, 1, 0, 1, 0, -1, 0, 0, -1, 0, 0, 0, 1, 0, 0, -1, 0, -1, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0, - -1, 0, 0, 1, 0, -1, 0, 0, 1, 0, 0, 0, 0, 0, -1, 0, 0, -1, 0, 1, 0, -1, 0, 0, 0, -1, 0, -1, 0, - 0, 0, 1, 0, -1, 0, 1, -]; - -moduli_declare! { - Bn254Fp { modulus = "21888242871839275222246405745257275088696311157297823662689037894645226208583" }, - Bn254Scalar { modulus = "21888242871839275222246405745257275088548364400416034343698204186575808495617" }, -} - -const CURVE_B: Bn254Fp = Bn254Fp::from_const_bytes(hex!( - "0300000000000000000000000000000000000000000000000000000000000000" -)); - -sw_declare! { - Bn254G1Affine { mod_type = Bn254Fp, b = CURVE_B }, -} - -#[cfg(not(target_os = "zkvm"))] -// Used in WeierstrassExtension config -pub const BN254_ECC_STRUCT_NAME: &str = "Bn254G1Affine"; - -pub type Fp = Bn254Fp; -pub type Scalar = Bn254Scalar; -pub type G1Affine = Bn254G1Affine; -pub use g2::G2Affine; - -impl Field for Fp { - type SelfRef<'a> = &'a Self; - const ZERO: Self = ::ZERO; - const ONE: Self = ::ONE; - - fn double_assign(&mut self) { - IntMod::double_assign(self); - } - - fn square_assign(&mut self) { - IntMod::square_assign(self); - } -} - -impl Field for Scalar { - type SelfRef<'a> = &'a Self; - const ZERO: Self = ::ZERO; - const ONE: Self = ::ONE; - - fn double_assign(&mut self) { - IntMod::double_assign(self); - } - - fn square_assign(&mut self) { - IntMod::square_assign(self); - } -} - -impl CyclicGroup for G1Affine { - // https://eips.ethereum.org/EIPS/eip-197 - const GENERATOR: Self = G1Affine { - x: Bn254Fp::from_const_u8(1), - y: Bn254Fp::from_const_u8(2), - }; - const NEG_GENERATOR: Self = G1Affine { - x: Bn254Fp::from_const_u8(1), - y: Bn254Fp::from_const_bytes(hex!( - "45FD7CD8168C203C8DCA7168916A81975D588181B64550B829A031E1724E6430" - )), - }; -} - -// Define a G2Affine struct that implements curve operations using `Fp2` intrinsics -// but not special E(Fp2) intrinsics. -mod g2 { - use hex_literal::hex; - use openvm_algebra_guest::Field; - use openvm_ecc_guest::{ - impl_sw_affine, impl_sw_group_ops, weierstrass::WeierstrassPoint, AffinePoint, Group, - }; - - use super::{Fp, Fp2}; - - const THREE: Fp2 = Fp2::new(Fp::from_const_u8(3), Fp::ZERO); - // 3 / (9 + u) - // validated by a test below - const B: Fp2 = Fp2::new( - Fp::from_const_bytes(hex!( - "e538a124dce66732a3efdb59e5c5b4b5c36ae01b9918be81aeaab8ce409d142b" - )), - Fp::from_const_bytes(hex!( - "d215c38506bda2e452182de584a04fa7f4fdd8eeadaf2ccdd4fef03ab0139700" - )), - ); - impl_sw_affine!(G2Affine, Fp2, THREE, B); - impl_sw_group_ops!(G2Affine, Fp2); - - #[test] - fn test_g2_curve_equation_b() { - use openvm_algebra_guest::DivUnsafe; - let b = Fp2::new(Fp::from_const_u8(3), Fp::ZERO) - .div_unsafe(Fp2::new(Fp::from_const_u8(9), Fp::ONE)); - assert_eq!(b, B); - } -} - -pub struct Bn254; - -impl Bn254 { - // Same as the values from halo2curves_shims - // Validated by a test in tests.rs - pub const FROBENIUS_COEFF_FQ6_C1: [Fp2; 3] = [ - Fp2 { - c0: Bn254Fp(hex!( - "0100000000000000000000000000000000000000000000000000000000000000" - )), - c1: Bn254Fp(hex!( - "0000000000000000000000000000000000000000000000000000000000000000" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "3d556f175795e3990c33c3c210c38cb743b159f53cec0b4cf711794f9847b32f" - )), - c1: Bn254Fp(hex!( - "a2cb0f641cd56516ce9d7c0b1d2aae3294075ad78bcca44b20aeeb6150e5c916" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "48fd7c60e544bde43d6e96bb9f068fc2b0ccace0e7d96d5e29a031e1724e6430" - )), - c1: Bn254Fp(hex!( - "0000000000000000000000000000000000000000000000000000000000000000" - )), - }, - ]; - - // Same as the values from halo2curves_shims - // Validated by a test in tests.rs - pub const XI_TO_Q_MINUS_1_OVER_2: Fp2 = Fp2 { - c0: Bn254Fp(hex!( - "5a13a071460154dc9859c9a9ede0aadbb9f9e2b698c65edcdcf59a4805f33c06" - )), - c1: Bn254Fp(hex!( - "e3b02326637fd382d25ba28fc97d80212b6f79eca7b504079a0441acbc3cc007" - )), - }; - - // FINAL_EXPONENT = (p^12 - 1) / r in big-endian - // Validated by a test in test.rs - pub const FINAL_EXPONENT: [u8; 349] = hex!( - "2f4b6dc97020fddadf107d20bc842d43bf6369b1ff6a1c71015f3f7be2e1e30a73bb94fec0daf15466b2383a5d3ec3d15ad524d8f70c54efee1bd8c3b21377e563a09a1b705887e72eceaddea3790364a61f676baaf977870e88d5c6c8fef0781361e443ae77f5b63a2a2264487f2940a8b1ddb3d15062cd0fb2015dfc6668449aed3cc48a82d0d602d268c7daab6a41294c0cc4ebe5664568dfc50e1648a45a4a1e3a5195846a3ed011a337a02088ec80e0ebae8755cfe107acf3aafb40494e406f804216bb10cf430b0f37856b42db8dc5514724ee93dfb10826f0dd4a0364b9580291d2cd65664814fde37ca80bb4ea44eacc5e641bbadf423f9a2cbf813b8d145da90029baee7ddadda71c7f3811c4105262945bba1668c3be69a3c230974d83561841d766f9c9d570bb7fbe04c7e8a6c3c760c0de81def35692da361102b6b9b2b918837fa97896e84abb40a4efb7e54523a486964b64ca86f120" - ); -} - -impl IntrinsicCurve for Bn254 { - type Scalar = Scalar; - type Point = G1Affine; - - fn msm(coeffs: &[Self::Scalar], bases: &[Self::Point]) -> Self::Point - where - for<'a> &'a Self::Point: Add<&'a Self::Point, Output = Self::Point>, - { - // heuristic - if coeffs.len() < 25 { - // BN254(Fp) is of prime order by Weil conjecture: - // - let table = CachedMulTable::::new_with_prime_order(bases, 4); - table.windowed_mul(coeffs) - } else { - openvm_ecc_guest::msm(coeffs, bases) - } - } -} - -impl PairingIntrinsics for Bn254 { - type Fp = Fp; - type Fp2 = Fp2; - type Fp12 = Fp12; - - const PAIRING_IDX: usize = 0; - // The sextic extension `Fp12` is `Fp2[X] / (X^6 - \xi)`, where `\xi` is a non-residue. - const XI: Fp2 = Fp2::new(Fp::from_const_u8(9), Fp::from_const_u8(1)); - const FP2_TWO: Fp2 = Fp2::new(Fp::from_const_u8(2), Fp::from_const_u8(0)); - const FP2_THREE: Fp2 = Fp2::new(Fp::from_const_u8(3), Fp::from_const_u8(0)); - // Multiplication constants for the Frobenius map for coefficients in Fp2 c1..=c5 for powers - // 0..12 FROBENIUS_COEFFS\[i\]\[j\] = \xi^{(j + 1) * (p^i - 1)/6} when p = 1 (mod 6) - // These are validated against `halo2curves::bn256::FROBENIUS_COEFF_FQ12_C1` in tests.rs - // (Note that bn256 here is another name for bn254) - const FROBENIUS_COEFFS: [[Self::Fp2; 5]; 12] = [ - [ - Fp2 { - c0: Bn254Fp(hex!( - "0100000000000000000000000000000000000000000000000000000000000000" - )), - c1: Bn254Fp(hex!( - "0000000000000000000000000000000000000000000000000000000000000000" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "0100000000000000000000000000000000000000000000000000000000000000" - )), - c1: Bn254Fp(hex!( - "0000000000000000000000000000000000000000000000000000000000000000" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "0100000000000000000000000000000000000000000000000000000000000000" - )), - c1: Bn254Fp(hex!( - "0000000000000000000000000000000000000000000000000000000000000000" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "0100000000000000000000000000000000000000000000000000000000000000" - )), - c1: Bn254Fp(hex!( - "0000000000000000000000000000000000000000000000000000000000000000" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "0100000000000000000000000000000000000000000000000000000000000000" - )), - c1: Bn254Fp(hex!( - "0000000000000000000000000000000000000000000000000000000000000000" - )), - }, - ], - [ - Fp2 { - c0: Bn254Fp(hex!( - "70e4c9dcda350bd676212f29081e525c608be676dd9fb9e8dfa765281cb78412" - )), - c1: Bn254Fp(hex!( - "ac62f3805ff05ccae5c7ee8e779279748e0b1512fe7c32a6e6e7fab4f3966924" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "3d556f175795e3990c33c3c210c38cb743b159f53cec0b4cf711794f9847b32f" - )), - c1: Bn254Fp(hex!( - "a2cb0f641cd56516ce9d7c0b1d2aae3294075ad78bcca44b20aeeb6150e5c916" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "5a13a071460154dc9859c9a9ede0aadbb9f9e2b698c65edcdcf59a4805f33c06" - )), - c1: Bn254Fp(hex!( - "e3b02326637fd382d25ba28fc97d80212b6f79eca7b504079a0441acbc3cc007" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "62a71e92551f8a8472ec94bef76533d3841e185ab7c0f38001a8ee645e4fb505" - )), - c1: Bn254Fp(hex!( - "26812bcd11473bc163c7de1bead28536921c0b3bb0803a9fee8afde7db5e142c" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "2f69b7ea10c8a22ed31baa559b455c42f43f35a461363ae94986794fe7c18301" - )), - c1: Bn254Fp(hex!( - "4b2c0c6eeeb8c624c02a8e6799cb80b07d9f72c746b27fa27506fd76caf2ac12" - )), - }, - ], - [ - Fp2 { - c0: Bn254Fp(hex!( - "49fd7c60e544bde43d6e96bb9f068fc2b0ccace0e7d96d5e29a031e1724e6430" - )), - c1: Bn254Fp(hex!( - "0000000000000000000000000000000000000000000000000000000000000000" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "48fd7c60e544bde43d6e96bb9f068fc2b0ccace0e7d96d5e29a031e1724e6430" - )), - c1: Bn254Fp(hex!( - "0000000000000000000000000000000000000000000000000000000000000000" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "46fd7cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e6430" - )), - c1: Bn254Fp(hex!( - "0000000000000000000000000000000000000000000000000000000000000000" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "feffff77314763574f5cdbacf163f2d4ac8bd4a0ce6be2590000000000000000" - )), - c1: Bn254Fp(hex!( - "0000000000000000000000000000000000000000000000000000000000000000" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "ffffff77314763574f5cdbacf163f2d4ac8bd4a0ce6be2590000000000000000" - )), - c1: Bn254Fp(hex!( - "0000000000000000000000000000000000000000000000000000000000000000" - )), - }, - ], - [ - Fp2 { - c0: Bn254Fp(hex!( - "7fa6d41e397d6fe84ad255be8db34c8990aaacd08c60e9efbbe482cccf81dc19" - )), - c1: Bn254Fp(hex!( - "01c1c0f42baa9476ec39d497e3a5037f9d137635e3eecb06737de70bb6f8ab00" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "6dfbdc7be86e747bd342695d3dfd5f80ac259f95771cffba0aef55b778e05608" - )), - c1: Bn254Fp(hex!( - "de86a5aa2bab0c383126ff98bf31df0f4f0926ec6d0ef3a96f76d1b341def104" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "ede9dc66d08acc5ff470a8bea389d6bba35e9eca1d7ff1db4caa96986d5b272a" - )), - c1: Bn254Fp(hex!( - "644c59b2b30c4db9ba6ecfd8c7ec007632e907950e904bb18f9bf034b611a428" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "66f0cb3cbc921a0ecb6bb075450933e64e44b2b5f7e0be19ab8dc011668cc50b" - )), - c1: Bn254Fp(hex!( - "9f230c739dede35fe5967f73089e4aa4041dd20ceff6b0fe120a91e199e9d523" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "431b26767084deeba5847c969880d62e693f4d3bfa99167105092c954490c413" - )), - c1: Bn254Fp(hex!( - "992428841304251f21800220eada2d3e3d63482a28b2b19f0bddb1596a36db16" - )), - }, - ], - [ - Fp2 { - c0: Bn254Fp(hex!( - "48fd7c60e544bde43d6e96bb9f068fc2b0ccace0e7d96d5e29a031e1724e6430" - )), - c1: Bn254Fp(hex!( - "0000000000000000000000000000000000000000000000000000000000000000" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "feffff77314763574f5cdbacf163f2d4ac8bd4a0ce6be2590000000000000000" - )), - c1: Bn254Fp(hex!( - "0000000000000000000000000000000000000000000000000000000000000000" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "0100000000000000000000000000000000000000000000000000000000000000" - )), - c1: Bn254Fp(hex!( - "0000000000000000000000000000000000000000000000000000000000000000" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "48fd7c60e544bde43d6e96bb9f068fc2b0ccace0e7d96d5e29a031e1724e6430" - )), - c1: Bn254Fp(hex!( - "0000000000000000000000000000000000000000000000000000000000000000" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "feffff77314763574f5cdbacf163f2d4ac8bd4a0ce6be2590000000000000000" - )), - c1: Bn254Fp(hex!( - "0000000000000000000000000000000000000000000000000000000000000000" - )), - }, - ], - [ - Fp2 { - c0: Bn254Fp(hex!( - "0fc20a425e476412d4b026958595fa2c301fc659afc02f07dc3c1da4b3ca5707" - )), - c1: Bn254Fp(hex!( - "9c5b4a4ce34558e8933c5771fd7d0ba26c60e2a49bb7e918b6351e3835b0a60c" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "e4a9ad1dee13e9623a1fb7b0d41416f7cad90978b8829569513f94bbd474be28" - )), - c1: Bn254Fp(hex!( - "c7aac7c9ce0baeed8d06f6c3b40ef4547a4701bebc6ab8c2997b74cbe08aa814" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "5a13a071460154dc9859c9a9ede0aadbb9f9e2b698c65edcdcf59a4805f33c06" - )), - c1: Bn254Fp(hex!( - "e3b02326637fd382d25ba28fc97d80212b6f79eca7b504079a0441acbc3cc007" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "7f65920905da7ba94f722c3454fb1ade89f5b67107a49d1d7d6a826aae72e91e" - )), - c1: Bn254Fp(hex!( - "c955c2707ee32157d136854130643254247725bbcd13b5d251abd4f86f54de10" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "14b26e8b5fbc3bbdd268d240fd3a7aec74ff17979863dc87bb82b2455dce4012" - )), - c1: Bn254Fp(hex!( - "4ef81b16254b5efa605574b8500fad8dbfc3d562e1ff31fd95d6b4e29f432e04" - )), - }, - ], - [ - Fp2 { - c0: Bn254Fp(hex!( - "46fd7cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e6430" - )), - c1: Bn254Fp(hex!( - "0000000000000000000000000000000000000000000000000000000000000000" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "0100000000000000000000000000000000000000000000000000000000000000" - )), - c1: Bn254Fp(hex!( - "0000000000000000000000000000000000000000000000000000000000000000" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "46fd7cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e6430" - )), - c1: Bn254Fp(hex!( - "0000000000000000000000000000000000000000000000000000000000000000" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "0100000000000000000000000000000000000000000000000000000000000000" - )), - c1: Bn254Fp(hex!( - "0000000000000000000000000000000000000000000000000000000000000000" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "46fd7cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e6430" - )), - c1: Bn254Fp(hex!( - "0000000000000000000000000000000000000000000000000000000000000000" - )), - }, - ], - [ - Fp2 { - c0: Bn254Fp(hex!( - "d718b3fb3b56156616a9423f894c2f3bfdcc9a0ad9a596cf49f8cbb85697df1d" - )), - c1: Bn254Fp(hex!( - "9b9a8957b79bc371a70283d919d80723cf4c6c6fb8c81d1243b8362c7fb7fa0b" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "3d556f175795e3990c33c3c210c38cb743b159f53cec0b4cf711794f9847b32f" - )), - c1: Bn254Fp(hex!( - "a2cb0f641cd56516ce9d7c0b1d2aae3294075ad78bcca44b20aeeb6150e5c916" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "ede9dc66d08acc5ff470a8bea389d6bba35e9eca1d7ff1db4caa96986d5b272a" - )), - c1: Bn254Fp(hex!( - "644c59b2b30c4db9ba6ecfd8c7ec007632e907950e904bb18f9bf034b611a428" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "62a71e92551f8a8472ec94bef76533d3841e185ab7c0f38001a8ee645e4fb505" - )), - c1: Bn254Fp(hex!( - "26812bcd11473bc163c7de1bead28536921c0b3bb0803a9fee8afde7db5e142c" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "1894c5ed05c47d0dbaaec712f624255569184cdd540f16cfdf19b8918b8ce02e" - )), - c1: Bn254Fp(hex!( - "fcd0706a28d35917cd9fe300f89e00e7dfb80eba6f93d015b499346aa85bb71d" - )), - }, - ], - [ - Fp2 { - c0: Bn254Fp(hex!( - "feffff77314763574f5cdbacf163f2d4ac8bd4a0ce6be2590000000000000000" - )), - c1: Bn254Fp(hex!( - "0000000000000000000000000000000000000000000000000000000000000000" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "48fd7c60e544bde43d6e96bb9f068fc2b0ccace0e7d96d5e29a031e1724e6430" - )), - c1: Bn254Fp(hex!( - "0000000000000000000000000000000000000000000000000000000000000000" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "0100000000000000000000000000000000000000000000000000000000000000" - )), - c1: Bn254Fp(hex!( - "0000000000000000000000000000000000000000000000000000000000000000" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "feffff77314763574f5cdbacf163f2d4ac8bd4a0ce6be2590000000000000000" - )), - c1: Bn254Fp(hex!( - "0000000000000000000000000000000000000000000000000000000000000000" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "48fd7c60e544bde43d6e96bb9f068fc2b0ccace0e7d96d5e29a031e1724e6430" - )), - c1: Bn254Fp(hex!( - "0000000000000000000000000000000000000000000000000000000000000000" - )), - }, - ], - [ - Fp2 { - c0: Bn254Fp(hex!( - "c856a8b9dd0eb15342f81baa03b7340ecdadd4b029e566c86dbbae14a3cc8716" - )), - c1: Bn254Fp(hex!( - "463cbce3eae18bc5a0909dd0adc47d18c0440b4cd35684b1b6224ad5bc55b82f" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "6dfbdc7be86e747bd342695d3dfd5f80ac259f95771cffba0aef55b778e05608" - )), - c1: Bn254Fp(hex!( - "de86a5aa2bab0c383126ff98bf31df0f4f0926ec6d0ef3a96f76d1b341def104" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "5a13a071460154dc9859c9a9ede0aadbb9f9e2b698c65edcdcf59a4805f33c06" - )), - c1: Bn254Fp(hex!( - "e3b02326637fd382d25ba28fc97d80212b6f79eca7b504079a0441acbc3cc007" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "66f0cb3cbc921a0ecb6bb075450933e64e44b2b5f7e0be19ab8dc011668cc50b" - )), - c1: Bn254Fp(hex!( - "9f230c739dede35fe5967f73089e4aa4041dd20ceff6b0fe120a91e199e9d523" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "04e25662a6074250e745f5d1f8e9aa68f4183446bcab39472497054c2ebe9f1c" - )), - c1: Bn254Fp(hex!( - "aed854540388fb1c6c4a6f48a78f535920f538578e939e181ec37f8708188919" - )), - }, - ], - [ - Fp2 { - c0: Bn254Fp(hex!( - "ffffff77314763574f5cdbacf163f2d4ac8bd4a0ce6be2590000000000000000" - )), - c1: Bn254Fp(hex!( - "0000000000000000000000000000000000000000000000000000000000000000" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "feffff77314763574f5cdbacf163f2d4ac8bd4a0ce6be2590000000000000000" - )), - c1: Bn254Fp(hex!( - "0000000000000000000000000000000000000000000000000000000000000000" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "46fd7cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e6430" - )), - c1: Bn254Fp(hex!( - "0000000000000000000000000000000000000000000000000000000000000000" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "48fd7c60e544bde43d6e96bb9f068fc2b0ccace0e7d96d5e29a031e1724e6430" - )), - c1: Bn254Fp(hex!( - "0000000000000000000000000000000000000000000000000000000000000000" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "49fd7c60e544bde43d6e96bb9f068fc2b0ccace0e7d96d5e29a031e1724e6430" - )), - c1: Bn254Fp(hex!( - "0000000000000000000000000000000000000000000000000000000000000000" - )), - }, - ], - [ - Fp2 { - c0: Bn254Fp(hex!( - "383b7296b844bc29b9194bd30bd5866a2d39bb27078520b14d63143dbf830c29" - )), - c1: Bn254Fp(hex!( - "aba1328c3346c853f98d1af793ec75f5f0f79edc1a8e669f736a13a93d9ebd23" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "e4a9ad1dee13e9623a1fb7b0d41416f7cad90978b8829569513f94bbd474be28" - )), - c1: Bn254Fp(hex!( - "c7aac7c9ce0baeed8d06f6c3b40ef4547a4701bebc6ab8c2997b74cbe08aa814" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "ede9dc66d08acc5ff470a8bea389d6bba35e9eca1d7ff1db4caa96986d5b272a" - )), - c1: Bn254Fp(hex!( - "644c59b2b30c4db9ba6ecfd8c7ec007632e907950e904bb18f9bf034b611a428" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "7f65920905da7ba94f722c3454fb1ade89f5b67107a49d1d7d6a826aae72e91e" - )), - c1: Bn254Fp(hex!( - "c955c2707ee32157d136854130643254247725bbcd13b5d251abd4f86f54de10" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "334b0e4db7cfe47eba619f27942f07abe85869ea1de273306e1d7f9b1580231e" - )), - c1: Bn254Fp(hex!( - "f90461c2f140c2412c75fdaf405bd4099e94ab1ed5451ebb93c97cfed20a362c" - )), - }, - ], - ]; -} diff --git a/extensions/pairing/guest/src/bn254/pairing.rs b/extensions/pairing/guest/src/bn254/pairing.rs deleted file mode 100644 index 25a0d6b7fe..0000000000 --- a/extensions/pairing/guest/src/bn254/pairing.rs +++ /dev/null @@ -1,379 +0,0 @@ -use alloc::vec::Vec; - -use itertools::izip; -use openvm_algebra_guest::{field::FieldExtension, DivUnsafe, Field}; -use openvm_ecc_guest::AffinePoint; -#[cfg(target_os = "zkvm")] -use { - crate::{PairingBaseFunct7, OPCODE, PAIRING_FUNCT3}, - core::mem::MaybeUninit, - openvm_platform::custom_insn_r, - openvm_rv32im_guest::hint_buffer_u32, -}; - -use super::{Bn254, Fp, Fp12, Fp2, BN254_PSEUDO_BINARY_ENCODING, BN254_SEED}; -use crate::pairing::{ - exp_check_fallback, Evaluatable, EvaluatedLine, FromLineDType, LineMulDType, MillerStep, - MultiMillerLoop, PairingCheck, PairingCheckError, PairingIntrinsics, UnevaluatedLine, -}; -#[cfg(all(feature = "halo2curves", not(target_os = "zkvm")))] -use crate::{ - bn254::utils::{ - convert_bn254_fp2_to_halo2_fq2, convert_bn254_fp_to_halo2_fq, - convert_bn254_halo2_fq12_to_fp12, - }, - halo2curves_shims::bn254::Bn254 as Halo2CurvesBn254, - pairing::FinalExp, -}; - -impl Evaluatable for UnevaluatedLine { - fn evaluate(&self, xy_frac: &(Fp, Fp)) -> EvaluatedLine { - let (x_over_y, y_inv) = xy_frac; - // Represents the line L(x,y) = 1 + b (x/y) w^1 + c (1/y) w^3 - EvaluatedLine { - b: self.b.mul_base(x_over_y), - c: self.c.mul_base(y_inv), - } - } -} - -impl FromLineDType for Fp12 { - fn from_evaluated_line_d_type(line: EvaluatedLine) -> Fp12 { - FieldExtension::::from_coeffs([ - Fp2::ONE, - line.b, - Fp2::ZERO, - line.c, - Fp2::ZERO, - Fp2::ZERO, - ]) - } -} - -// TODO[jpw]: make this into a macro depending on P::PAIRING_IDX when we have more curves -impl LineMulDType for Bn254 { - /// Multiplies two lines in 013-form to get an element in 01234-form - fn mul_013_by_013(l0: &EvaluatedLine, l1: &EvaluatedLine) -> [Fp2; 5] { - let b0 = &l0.b; - let c0 = &l0.c; - let b1 = &l1.b; - let c1 = &l1.c; - - // where w⁶ = xi - // l0 * l1 = 1 + (b0 + b1)w + (b0b1)w² + (c0 + c1)w³ + (b0c1 + b1c0)w⁴ + (c0c1)w⁶ - // = (1 + c0c1 * xi) + (b0 + b1)w + (b0b1)w² + (c0 + c1)w³ + (b0c1 + b1c0)w⁴ - let x0 = Fp2::ONE + c0 * c1 * &Bn254::XI; - let x1 = b0 + b1; - let x2 = b0 * b1; - let x3 = c0 + c1; - let x4 = b0 * c1 + b1 * c0; - - [x0, x1, x2, x3, x4] - } - - /// Multiplies a line in 013-form with a Fp12 element to get an Fp12 element - fn mul_by_013(f: &Fp12, l: &EvaluatedLine) -> Fp12 { - Fp12::from_evaluated_line_d_type(l.clone()) * f - } - - /// Multiplies a line in 01234-form with a Fp12 element to get an Fp12 element - fn mul_by_01234(f: &Fp12, x: &[Fp2; 5]) -> Fp12 { - // we update the order of the coefficients to match the Fp12 coefficient ordering: - // Fp12 { - // c0: Fp6 { - // c0: x0, - // c1: x2, - // c2: x4, - // }, - // c1: Fp6 { - // c0: x1, - // c1: x3, - // c2: x5, - // }, - // } - let o0 = &x[0]; - let o1 = &x[2]; - let o2 = &x[4]; - let o3 = &x[1]; - let o4 = &x[3]; - - let xi = &Bn254::XI; - - let self_coeffs = &f.c; - let s0 = &self_coeffs[0]; - let s1 = &self_coeffs[2]; - let s2 = &self_coeffs[4]; - let s3 = &self_coeffs[1]; - let s4 = &self_coeffs[3]; - let s5 = &self_coeffs[5]; - - // NOTE[yj]: Hand-calculated multiplication for Fp12 * 01234 ∈ Fp2; this is likely not the - // most efficient implementation c00 = cs0co0 + xi(cs1co2 + cs2co1 + cs4co4 + - // cs5co3) c01 = cs0co1 + cs1co0 + cs3co3 + xi(cs2co2 + cs5co4) - // c02 = cs0co2 + cs1co1 + cs2co0 + cs3co4 + cs4co3 - // c10 = cs0co3 + cs3co0 + xi(cs2co4 + cs4co2 + cs5co1) - // c11 = cs0co4 + cs1co3 + cs3co1 + cs4co0 + xi(cs5co2) - // c12 = cs1co4 + cs2co3 + cs3co2 + cs4co1 + cs5co0 - let c00 = s0 * o0 + xi * &(s1 * o2 + s2 * o1 + s4 * o4 + s5 * o3); - let c01 = s0 * o1 + s1 * o0 + s3 * o3 + xi * &(s2 * o2 + s5 * o4); - let c02 = s0 * o2 + s1 * o1 + s2 * o0 + s3 * o4 + s4 * o3; - let c10 = s0 * o3 + s3 * o0 + xi * &(s2 * o4 + s4 * o2 + s5 * o1); - let c11 = s0 * o4 + s1 * o3 + s3 * o1 + s4 * o0 + xi * &(s5 * o2); - let c12 = s1 * o4 + s2 * o3 + s3 * o2 + s4 * o1 + s5 * o0; - - Fp12::from_coeffs([c00, c10, c01, c11, c02, c12]) - } -} - -#[allow(non_snake_case)] -impl MultiMillerLoop for Bn254 { - type Fp = Fp; - type Fp12 = Fp12; - - const SEED_ABS: u64 = BN254_SEED; - const PSEUDO_BINARY_ENCODING: &[i8] = &BN254_PSEUDO_BINARY_ENCODING; - - fn evaluate_lines_vec(f: Self::Fp12, lines: Vec>) -> Self::Fp12 { - let mut f = f; - let mut lines = lines; - if lines.len() % 2 == 1 { - f = Self::mul_by_013(&f, &lines.pop().unwrap()); - } - for chunk in lines.chunks(2) { - if let [line0, line1] = chunk { - let prod = Self::mul_013_by_013(line0, line1); - f = Self::mul_by_01234(&f, &prod); - } else { - panic!("lines.len() % 2 should be 0 at this point"); - } - } - f - } - - /// The expected output of this function when running the Miller loop with embedded exponent is - /// c^2 * l_{2Q} - fn pre_loop( - Q_acc: Vec>, - _Q: &[AffinePoint], - c: Option, - xy_fracs: &[(Self::Fp, Self::Fp)], - ) -> (Self::Fp12, Vec>) { - let mut f = if let Some(mut c) = c { - // for the miller loop with embedded exponent, f will be set to c at the beginning of - // the function, and we will square c due to the last two values of the - // pseudo-binary encoding (BN254_PSEUDO_BINARY_ENCODING) being 0 and 1. - // Therefore, the final value of f at the end of this block is c^2. - c.square_assign(); - c - } else { - Self::Fp12::ONE - }; - - let mut Q_acc = Q_acc; - let mut initial_lines = Vec::>::new(); - - // We don't need to special case the first iteration for Bn254, but since we are using the - // same Miller loop implementation for both Bn254 and Bls12_381, we need to do the - // first iteration separately here. - let (Q_out_double, lines_2S) = Q_acc - .into_iter() - .map(|Q| Self::miller_double_step(&Q)) - .unzip::<_, _, Vec<_>, Vec<_>>(); - Q_acc = Q_out_double; - - let lines_iter = izip!(lines_2S.iter(), xy_fracs.iter()); - for (line_2S, xy_frac) in lines_iter { - let line = line_2S.evaluate(xy_frac); - initial_lines.push(line); - } - - f = Self::evaluate_lines_vec(f, initial_lines); - - (f, Q_acc) - } - - /// Compute f_{Miller,Q}(P) from f_{6x+2,Q}(P) - fn post_loop( - f: &Self::Fp12, - Q_acc: Vec>, // at this point, Q_acc = (6x+2)Q - Q: &[AffinePoint], - _c: Option, - xy_fracs: &[(Self::Fp, Self::Fp)], - ) -> (Self::Fp12, Vec>) { - let mut Q_acc = Q_acc; - let mut lines = Vec::>::new(); - - let x_to_q_minus_1_over_3 = &Self::FROBENIUS_COEFF_FQ6_C1[1]; - let x_to_q_sq_minus_1_over_3 = &Self::FROBENIUS_COEFF_FQ6_C1[2]; - - // For each q, compute q1 such that `frob_p(twist(q)) = twist(q1)` - let q1_vec = Q - .iter() - .map(|Q| { - let x = Q.x.frobenius_map(1); - let x = x * x_to_q_minus_1_over_3; - let y = Q.y.frobenius_map(1); - let y = y * &Self::XI_TO_Q_MINUS_1_OVER_2; - AffinePoint { x, y } - }) - .collect::>(); - - // compute l_{(6x+2)\Psi(Q), \phi_p(\Psi(Q))} where \phi_p is the Frobenius map - let (Q_out_add, lines_S_plus_Q) = Q_acc - .iter() - .zip(q1_vec.iter()) - .map(|(Q_acc, q1)| Self::miller_add_step(Q_acc, q1)) - .unzip::<_, _, Vec<_>, Vec<_>>(); - Q_acc = Q_out_add; - - let lines_iter = izip!(lines_S_plus_Q.iter(), xy_fracs.iter()); - for (lines_S_plus_Q, xy_frac) in lines_iter { - let line = lines_S_plus_Q.evaluate(xy_frac); - lines.push(line); - } - - // For each q, compute q2 such that `-frob_p^2(twist(q)) = twist(q2)` - let q2_vec = Q - .iter() - .map(|Q| { - // There is a frobenius mapping π²(Q) that we skip here since it is equivalent to - // the identity mapping - let x = &Q.x * x_to_q_sq_minus_1_over_3; - AffinePoint { x, y: Q.y.clone() } - }) - .collect::>(); - - // compute l_{(6x+2)\Psi(Q) + \phi_p(\Psi(Q)), -(\phi_p)^2(\Psi(Q))} where \phi_p is the - // Frobenius map - let (Q_out_add, lines_S_plus_Q) = Q_acc - .iter() - .zip(q2_vec.iter()) - .map(|(Q_acc, q2)| Self::miller_add_step(Q_acc, q2)) - .unzip::<_, _, Vec<_>, Vec<_>>(); - Q_acc = Q_out_add; - - let lines_iter = izip!(lines_S_plus_Q.iter(), xy_fracs.iter()); - for (lines_S_plus_Q, xy_frac) in lines_iter { - let line = lines_S_plus_Q.evaluate(xy_frac); - lines.push(line); - } - - let mut f = f.clone(); - f = Self::evaluate_lines_vec(f, lines); - - (f, Q_acc) - } -} - -#[allow(non_snake_case)] -impl PairingCheck for Bn254 { - type Fp = Fp; - type Fp2 = Fp2; - type Fp12 = Fp12; - - #[allow(unused_variables)] - fn pairing_check_hint( - P: &[AffinePoint], - Q: &[AffinePoint], - ) -> (Self::Fp12, Self::Fp12) { - #[cfg(not(target_os = "zkvm"))] - { - #[cfg(not(feature = "halo2curves"))] - panic!("`halo2curves` feature must be enabled to use pairing check hint on host"); - - #[cfg(feature = "halo2curves")] - { - let p_halo2 = P - .iter() - .map(|p| { - AffinePoint::new( - convert_bn254_fp_to_halo2_fq(p.x.clone()), - convert_bn254_fp_to_halo2_fq(p.y.clone()), - ) - }) - .collect::>(); - let q_halo2 = Q - .iter() - .map(|q| { - AffinePoint::new( - convert_bn254_fp2_to_halo2_fq2(q.x.clone()), - convert_bn254_fp2_to_halo2_fq2(q.y.clone()), - ) - }) - .collect::>(); - let fq12 = Halo2CurvesBn254::multi_miller_loop(&p_halo2, &q_halo2); - let (c_fq12, s_fq12) = Halo2CurvesBn254::final_exp_hint(&fq12); - let c = convert_bn254_halo2_fq12_to_fp12(c_fq12); - let s = convert_bn254_halo2_fq12_to_fp12(s_fq12); - (c, s) - } - } - #[cfg(target_os = "zkvm")] - { - let hint = MaybeUninit::<(Fp12, Fp12)>::uninit(); - // We do not rely on the slice P's memory layout since rust does not guarantee it across - // compiler versions. - let p_fat_ptr = (P.as_ptr() as u32, P.len() as u32); - let q_fat_ptr = (Q.as_ptr() as u32, Q.len() as u32); - unsafe { - custom_insn_r!( - opcode = OPCODE, - funct3 = PAIRING_FUNCT3, - funct7 = ((Bn254::PAIRING_IDX as u8) * PairingBaseFunct7::PAIRING_MAX_KINDS + PairingBaseFunct7::HintFinalExp as u8), - rd = Const "x0", - rs1 = In &p_fat_ptr, - rs2 = In &q_fat_ptr - ); - let ptr = hint.as_ptr() as *const u8; - hint_buffer_u32!(ptr, (32 * 12 * 2) / 4); - hint.assume_init() - } - } - } - - fn pairing_check( - P: &[AffinePoint], - Q: &[AffinePoint], - ) -> Result<(), PairingCheckError> { - Self::try_honest_pairing_check(P, Q).unwrap_or_else(|| { - let f = Self::multi_miller_loop(P, Q); - exp_check_fallback(&f, &Self::FINAL_EXPONENT) - }) - } -} - -#[allow(non_snake_case)] -impl Bn254 { - fn try_honest_pairing_check( - P: &[AffinePoint<::Fp>], - Q: &[AffinePoint<::Fp2>], - ) -> Option> { - let (c, u) = Self::pairing_check_hint(P, Q); - if c == Fp12::ZERO { - return None; - } - let c_inv = Fp12::ONE.div_unsafe(&c); - - // We follow Theorem 3 of https://eprint.iacr.org/2024/640.pdf to check that the pairing equals 1 - // By the theorem, it suffices to provide c and u such that f * u == c^λ. - // Since λ = 6x + 2 + q^3 - q^2 + q, we will check the equivalent condition: - // f * c^-{6x + 2} * u * c^-{q^3 - q^2 + q} == 1 - // This is because we can compute f * c^-{6x+2} by embedding the c^-{6x+2} computation in - // the miller loop. - - // c_mul = c^-{q^3 - q^2 + q} - let c_q3_inv = FieldExtension::frobenius_map(&c_inv, 3); - let c_q2 = FieldExtension::frobenius_map(&c, 2); - let c_q_inv = FieldExtension::frobenius_map(&c_inv, 1); - let c_mul = c_q3_inv * c_q2 * c_q_inv; - - // Pass c inverse into the miller loop so that we compute fc == f * c^-{6x + 2} - let fc = Self::multi_miller_loop_embedded_exp(P, Q, Some(c_inv)); - - if fc * c_mul * u == Fp12::ONE { - Some(Ok(())) - } else { - None - } - } -} diff --git a/extensions/pairing/guest/src/bn254/tests.rs b/extensions/pairing/guest/src/bn254/tests.rs deleted file mode 100644 index 331bb64692..0000000000 --- a/extensions/pairing/guest/src/bn254/tests.rs +++ /dev/null @@ -1,325 +0,0 @@ -use group::{ff::Field, prime::PrimeCurveAffine}; -use halo2curves_axiom::bn256::{ - Fq, Fq12, Fq2, Fq6, G1Affine, G2Affine, G2Prepared, Gt, FROBENIUS_COEFF_FQ12_C1, - FROBENIUS_COEFF_FQ6_C1, XI_TO_Q_MINUS_1_OVER_2, -}; -use num_bigint::BigUint; -use num_traits::One; -use openvm_algebra_guest::{field::FieldExtension, IntMod}; -use openvm_ecc_guest::{weierstrass::WeierstrassPoint, AffinePoint}; -use rand::{rngs::StdRng, SeedableRng}; - -use super::{Fp, Fp12, Fp2}; -use crate::{ - bn254::{ - utils::{ - convert_bn254_fp12_to_halo2_fq12, convert_bn254_halo2_fq12_to_fp12, - convert_bn254_halo2_fq2_to_fp2, convert_bn254_halo2_fq_to_fp, - convert_g2_affine_halo2_to_openvm, - }, - Bn254, G2Affine as OpenVmG2Affine, BN254_MODULUS, BN254_ORDER, - BN254_PSEUDO_BINARY_ENCODING, BN254_SEED, - }, - pairing::{ - fp2_invert_assign, fp6_invert_assign, fp6_square_assign, FinalExp, MultiMillerLoop, - PairingCheck, PairingIntrinsics, - }, -}; - -#[test] -fn test_bn254_frobenius_coeffs() { - #[allow(clippy::needless_range_loop)] - for i in 0..12 { - for j in 0..5 { - assert_eq!( - Bn254::FROBENIUS_COEFFS[i][j], - convert_bn254_halo2_fq2_to_fp2(FROBENIUS_COEFF_FQ12_C1[i].pow([j as u64 + 1])), - "FROBENIUS_COEFFS[{}][{}] failed", - i, - j - ) - } - } -} - -#[test] -fn test_bn254_frobenius() { - let mut rng = StdRng::seed_from_u64(15); - for pow in 0..12 { - let fq = Fq12::random(&mut rng); - let fq_frob = fq.frobenius_map(pow); - - let fp = convert_bn254_halo2_fq12_to_fp12(fq); - let fp_frob = fp.frobenius_map(pow); - - assert_eq!(fp_frob, convert_bn254_halo2_fq12_to_fp12(fq_frob)); - } -} - -#[test] -fn test_fp12_invert() { - let mut rng = StdRng::seed_from_u64(15); - let fq = Fq12::random(&mut rng); - let fq_inv = fq.invert().unwrap(); - - let fp = convert_bn254_halo2_fq12_to_fp12(fq); - let fp_inv = fp.invert(); - assert_eq!(fp_inv, convert_bn254_halo2_fq12_to_fp12(fq_inv)); -} - -#[test] -fn test_fp6_invert() { - let mut rng = StdRng::seed_from_u64(20); - let fq6 = Fq6::random(&mut rng); - let fq6_inv = fq6.invert().unwrap(); - - let fp6c0 = convert_bn254_halo2_fq2_to_fp2(fq6.c0); - let fp6c1 = convert_bn254_halo2_fq2_to_fp2(fq6.c1); - let fp6c2 = convert_bn254_halo2_fq2_to_fp2(fq6.c2); - let mut fp6 = [fp6c0, fp6c1, fp6c2]; - fp6_invert_assign::(&mut fp6, &Bn254::XI); - - let fq6_invc0 = convert_bn254_halo2_fq2_to_fp2(fq6_inv.c0); - let fq6_invc1 = convert_bn254_halo2_fq2_to_fp2(fq6_inv.c1); - let fq6_invc2 = convert_bn254_halo2_fq2_to_fp2(fq6_inv.c2); - let fq6_inv = [fq6_invc0, fq6_invc1, fq6_invc2]; - assert_eq!(fp6, fq6_inv); -} - -#[test] -fn test_fp2_invert() { - let mut rng = StdRng::seed_from_u64(25); - let fq2 = Fq2::random(&mut rng); - let fq2_inv = fq2.invert().unwrap(); - - let mut fp2 = convert_bn254_halo2_fq2_to_fp2(fq2).to_coeffs(); - fp2_invert_assign::(&mut fp2); - assert_eq!(fp2, convert_bn254_halo2_fq2_to_fp2(fq2_inv).to_coeffs()); -} - -#[test] -fn test_fp6_square() { - let mut rng = StdRng::seed_from_u64(45); - let fq6 = Fq6::random(&mut rng); - let fq6_sq = fq6.square(); - - let fp6c0 = convert_bn254_halo2_fq2_to_fp2(fq6.c0); - let fp6c1 = convert_bn254_halo2_fq2_to_fp2(fq6.c1); - let fp6c2 = convert_bn254_halo2_fq2_to_fp2(fq6.c2); - let mut fp6 = [fp6c0, fp6c1, fp6c2]; - fp6_square_assign::(&mut fp6, &Bn254::XI); - - let fq6_sqc0 = convert_bn254_halo2_fq2_to_fp2(fq6_sq.c0); - let fq6_sqc1 = convert_bn254_halo2_fq2_to_fp2(fq6_sq.c1); - let fq6_sqc2 = convert_bn254_halo2_fq2_to_fp2(fq6_sq.c2); - let fq6_sq = [fq6_sqc0, fq6_sqc1, fq6_sqc2]; - assert_eq!(fp6, fq6_sq); -} - -#[test] -fn test_fp2_square() { - let mut rng = StdRng::seed_from_u64(55); - let fq2 = Fq2::random(&mut rng); - let fq2_sq = fq2.square(); - - let fp2 = convert_bn254_halo2_fq2_to_fp2(fq2); - let fp2_sq = &fp2 * &fp2; - assert_eq!(fp2_sq, convert_bn254_halo2_fq2_to_fp2(fq2_sq)); -} - -#[test] -fn test_fp_add() { - let mut rng = StdRng::seed_from_u64(65); - let fq = Fq::random(&mut rng); - let fq_res = fq + Fq::one(); - - let fp = convert_bn254_halo2_fq_to_fp(fq); - let fp_res = fp + Fp::ONE; - assert_eq!(fp_res, convert_bn254_halo2_fq_to_fp(fq_res)); -} - -#[test] -fn test_fp_one() { - let fp_one = Fp::ONE; - let fq_one = Fq::ONE; - assert_eq!(fp_one, convert_bn254_halo2_fq_to_fp(fq_one)); -} - -// Gt(Fq12) is not public -fn assert_miller_results_eq(a: Gt, b: Fp12) { - let b = convert_bn254_fp12_to_halo2_fq12(b); - crate::halo2curves_shims::bn254::tests::assert_miller_results_eq(a, b); -} - -#[test] -fn test_bn254_miller_loop() { - let mut rng = StdRng::seed_from_u64(53); - let h2c_p = G1Affine::random(&mut rng); - let h2c_q = G2Affine::random(&mut rng); - - let p = AffinePoint { - x: convert_bn254_halo2_fq_to_fp(h2c_p.x), - y: convert_bn254_halo2_fq_to_fp(h2c_p.y), - }; - let q = AffinePoint { - x: convert_bn254_halo2_fq2_to_fp2(h2c_q.x), - y: convert_bn254_halo2_fq2_to_fp2(h2c_q.y), - }; - - // Compare against halo2curves implementation - let h2c_q_prepared = G2Prepared::from(h2c_q); - let compare_miller = halo2curves_axiom::bn256::multi_miller_loop(&[(&h2c_p, &h2c_q_prepared)]); - let f = Bn254::multi_miller_loop(&[p], &[q]); - assert_miller_results_eq(compare_miller, f); -} - -#[test] -fn test_bn254_miller_loop_identity() { - let mut rng = StdRng::seed_from_u64(33); - let h2c_p = G1Affine::identity(); - let h2c_q = G2Affine::random(&mut rng); - - let p = AffinePoint { - x: convert_bn254_halo2_fq_to_fp(Fq::ZERO), - y: convert_bn254_halo2_fq_to_fp(Fq::ZERO), - }; - let q = AffinePoint { - x: convert_bn254_halo2_fq2_to_fp2(h2c_q.x), - y: convert_bn254_halo2_fq2_to_fp2(h2c_q.y), - }; - - let f = Bn254::multi_miller_loop(&[p], &[q]); - // halo2curves implementation - let h2c_q_prepared = G2Prepared::from(h2c_q); - let compare_miller = halo2curves_axiom::bn256::multi_miller_loop(&[(&h2c_p, &h2c_q_prepared)]); - assert_miller_results_eq(compare_miller, f); -} - -#[test] -fn test_bn254_miller_loop_identity_2() { - let mut rng = StdRng::seed_from_u64(33); - let h2c_p = G1Affine::random(&mut rng); - let h2c_q = G2Affine::identity(); - let p = AffinePoint { - x: convert_bn254_halo2_fq_to_fp(h2c_p.x), - y: convert_bn254_halo2_fq_to_fp(h2c_p.y), - }; - let q = AffinePoint { - x: convert_bn254_halo2_fq2_to_fp2(Fq2::ZERO), - y: convert_bn254_halo2_fq2_to_fp2(Fq2::ZERO), - }; - - let f = Bn254::multi_miller_loop(&[p], &[q]); - // halo2curves implementation - let h2c_q_prepared = G2Prepared::from(h2c_q); - let compare_miller = halo2curves_axiom::bn256::multi_miller_loop(&[(&h2c_p, &h2c_q_prepared)]); - assert_miller_results_eq(compare_miller, f); -} - -// test on host is enough since we are testing the curve formulas and not anything -// about intrinsic functions -#[test] -fn test_bn254_g2_affine() { - let mut rng = StdRng::seed_from_u64(34); - for _ in 0..10 { - let p = G2Affine::random(&mut rng); - let q = G2Affine::random(&mut rng); - let expected_add = G2Affine::from(p + q); - let expected_sub = G2Affine::from(p - q); - let expected_neg = -p; - let expected_double = G2Affine::from(p + p); - let [p, q] = [p, q].map(|p| { - let x = convert_bn254_halo2_fq2_to_fp2(p.x); - let y = convert_bn254_halo2_fq2_to_fp2(p.y); - // check on curve - OpenVmG2Affine::from_xy(x, y).unwrap() - }); - let r_add = &p + &q; - let r_sub = &p - &q; - let r_neg = -&p; - let r_double = &p + &p; - - for (expected, actual) in [ - (expected_add, r_add), - (expected_sub, r_sub), - (expected_neg, r_neg), - (expected_double, r_double), - ] { - assert_eq!(convert_g2_affine_halo2_to_openvm(expected), actual); - } - } -} - -#[test] -fn test_bn254_pairing_check_hint_host() { - let mut rng = StdRng::seed_from_u64(83); - let h2c_p = G1Affine::random(&mut rng); - let h2c_q = G2Affine::random(&mut rng); - - let p = AffinePoint { - x: convert_bn254_halo2_fq_to_fp(h2c_p.x), - y: convert_bn254_halo2_fq_to_fp(h2c_p.y), - }; - let q = AffinePoint { - x: convert_bn254_halo2_fq2_to_fp2(h2c_q.x), - y: convert_bn254_halo2_fq2_to_fp2(h2c_q.y), - }; - - let (c, u) = Bn254::pairing_check_hint(&[p], &[q]); - - let p_cmp = AffinePoint { - x: h2c_p.x, - y: h2c_p.y, - }; - let q_cmp = AffinePoint { - x: h2c_q.x, - y: h2c_q.y, - }; - - let f_cmp = crate::halo2curves_shims::bn254::Bn254::multi_miller_loop(&[p_cmp], &[q_cmp]); - let (c_cmp, u_cmp) = crate::halo2curves_shims::bn254::Bn254::final_exp_hint(&f_cmp); - let c_cmp = convert_bn254_halo2_fq12_to_fp12(c_cmp); - let u_cmp = convert_bn254_halo2_fq12_to_fp12(u_cmp); - - assert_eq!(c, c_cmp); - assert_eq!(u, u_cmp); -} - -#[test] -fn test_bn254_final_exponent() { - let final_exp = (BN254_MODULUS.pow(12) - BigUint::one()) / BN254_ORDER.clone(); - assert_eq!(Bn254::FINAL_EXPONENT.to_vec(), final_exp.to_bytes_be()); -} - -#[test] -fn test_bn254_frobenius_coeffs_fq6() { - #[allow(clippy::needless_range_loop)] - for i in 0..3 { - assert_eq!( - Bn254::FROBENIUS_COEFF_FQ6_C1[i], - convert_bn254_halo2_fq2_to_fp2(FROBENIUS_COEFF_FQ6_C1[i]), - "FROBENIUS_COEFFS_FQ6_C1[{}] failed", - i, - ) - } -} - -#[test] -fn test_bn254_pseudo_binary_encoding() { - let mut x: i128 = 0; - let mut power_of_2 = 1; - for b in BN254_PSEUDO_BINARY_ENCODING.iter() { - x += (*b as i128) * power_of_2; - power_of_2 *= 2; - } - assert_eq!(x.unsigned_abs(), 6 * (BN254_SEED as u128) + 2); -} - -#[test] -fn test_bn254_xi_to_q_minus_1_over_2() { - assert_eq!( - Bn254::XI_TO_Q_MINUS_1_OVER_2, - convert_bn254_halo2_fq2_to_fp2(XI_TO_Q_MINUS_1_OVER_2), - "XI_TO_Q_MINUS_1_OVER_2 failed", - ) -} diff --git a/extensions/pairing/guest/src/bn254/utils.rs b/extensions/pairing/guest/src/bn254/utils.rs deleted file mode 100644 index 9102e980db..0000000000 --- a/extensions/pairing/guest/src/bn254/utils.rs +++ /dev/null @@ -1,49 +0,0 @@ -use halo2curves_axiom::bn256::{Fq, Fq12, Fq2, G2Affine}; -use openvm_algebra_guest::{field::FieldExtension, IntMod}; -use openvm_ecc_guest::weierstrass::WeierstrassPoint; - -use super::{Fp, Fp12, Fp2}; -use crate::bn254::G2Affine as OpenVmG2Affine; - -pub(crate) fn convert_bn254_halo2_fq_to_fp(x: Fq) -> Fp { - let bytes = x.to_bytes(); - Fp::from_le_bytes(&bytes) -} - -pub(crate) fn convert_bn254_halo2_fq2_to_fp2(x: Fq2) -> Fp2 { - Fp2::new( - convert_bn254_halo2_fq_to_fp(x.c0), - convert_bn254_halo2_fq_to_fp(x.c1), - ) -} - -pub(crate) fn convert_bn254_halo2_fq12_to_fp12(x: Fq12) -> Fp12 { - Fp12 { - c: x.to_coeffs().map(convert_bn254_halo2_fq2_to_fp2), - } -} - -pub(crate) fn convert_bn254_fp_to_halo2_fq(x: Fp) -> Fq { - Fq::from_bytes(&x.0).unwrap() -} - -pub(crate) fn convert_bn254_fp2_to_halo2_fq2(x: Fp2) -> Fq2 { - Fq2 { - c0: convert_bn254_fp_to_halo2_fq(x.c0.clone()), - c1: convert_bn254_fp_to_halo2_fq(x.c1.clone()), - } -} - -#[allow(unused)] -pub(crate) fn convert_bn254_fp12_to_halo2_fq12(x: Fp12) -> Fq12 { - let c = x.to_coeffs(); - Fq12::from_coeffs(c.map(convert_bn254_fp2_to_halo2_fq2)) -} - -#[allow(unused)] -pub(crate) fn convert_g2_affine_halo2_to_openvm(p: G2Affine) -> OpenVmG2Affine { - OpenVmG2Affine::from_xy_unchecked( - convert_bn254_halo2_fq2_to_fp2(p.x), - convert_bn254_halo2_fq2_to_fp2(p.y), - ) -} diff --git a/extensions/pairing/guest/src/lib.rs b/extensions/pairing/guest/src/lib.rs index d0fdcf5eab..9cf0042076 100644 --- a/extensions/pairing/guest/src/lib.rs +++ b/extensions/pairing/guest/src/lib.rs @@ -16,24 +16,8 @@ impl PairingBaseFunct7 { pub const PAIRING_MAX_KINDS: u8 = 16; } -extern crate alloc; -extern crate self as openvm_ecc; - -#[cfg(feature = "halo2curves")] -pub use halo2curves_axiom as halo2curves; -pub use openvm_algebra_guest as algebra; - /// Implementation of this library's traits on halo2curves types. /// Used for testing and also VM runtime execution. /// These should **only** be importable on a host machine. #[cfg(all(feature = "halo2curves", not(target_os = "zkvm")))] pub mod halo2curves_shims; -/// Traits for optimal Ate pairing check using intrinsic functions. -pub mod pairing; - -/// Types for BLS12-381 curve with intrinsic functions. -#[cfg(feature = "bls12_381")] -pub mod bls12_381; -/// Types for BN254 curve with intrinsic functions. -#[cfg(feature = "bn254")] -pub mod bn254; diff --git a/extensions/pairing/tests/Cargo.toml b/extensions/pairing/tests/Cargo.toml deleted file mode 100644 index 6a337bfab0..0000000000 --- a/extensions/pairing/tests/Cargo.toml +++ /dev/null @@ -1,34 +0,0 @@ -[package] -name = "openvm-pairing-integration-tests" -description = "Integration tests for the OpenVM pairing extension" -version.workspace = true -authors.workspace = true -edition.workspace = true -homepage.workspace = true -repository.workspace = true - -[dependencies] -openvm-instructions = { workspace = true } -openvm-stark-sdk.workspace = true -openvm-circuit = { workspace = true, features = ["test-utils"] } -openvm-transpiler.workspace = true -openvm-algebra-circuit.workspace = true -openvm-algebra-transpiler.workspace = true -openvm-pairing-circuit.workspace = true -openvm-pairing-transpiler.workspace = true -openvm-pairing-guest.workspace = true -openvm-ecc-circuit.workspace = true -openvm-ecc-guest.workspace = true -openvm-ecc-transpiler.workspace = true -openvm-rv32im-transpiler.workspace = true -openvm = { workspace = true } -openvm-toolchain-tests = { workspace = true } -eyre.workspace = true -rand.workspace = true -num-bigint.workspace = true -num-traits.workspace = true -halo2curves-axiom = { workspace = true } - -[features] -default = ["parallel"] -parallel = ["openvm-circuit/parallel"] diff --git a/extensions/pairing/tests/programs/Cargo.toml b/extensions/pairing/tests/programs/Cargo.toml deleted file mode 100644 index 6e5cd66984..0000000000 --- a/extensions/pairing/tests/programs/Cargo.toml +++ /dev/null @@ -1,51 +0,0 @@ -[workspace] -[package] -name = "openvm-pairing-test-programs" -version = "0.0.0" -edition = "2021" - -[dependencies] -openvm = { path = "../../../../crates/toolchain/openvm" } -openvm-platform = { path = "../../../../crates/toolchain/platform" } - -openvm-algebra-guest = { path = "../../../../extensions/algebra/guest", default-features = false } -openvm-algebra-moduli-macros = { path = "../../../../extensions/algebra/moduli-macros", default-features = false } -openvm-algebra-complex-macros = { path = "../../../../extensions/algebra/complex-macros", default-features = false } -openvm-ecc-guest = { path = "../../../../extensions/ecc/guest", default-features = false } -openvm-ecc-sw-macros = { path = "../../../../extensions/ecc/sw-macros", default-features = false } -openvm-pairing-guest = { path = "../../../../extensions/pairing/guest", default-features = false } - -serde = { version = "1.0", default-features = false, features = [ - "alloc", - "derive", -] } -hex = { version = "0.4.3", default-features = false, features = ["alloc"] } -hex-literal = { version = "0.4.1", default-features = false } -k256 = { version = "0.13.3", default-features = false, features = [ - "ecdsa-core", - "ecdsa", -], optional = true } - -[features] -default = [] -std = ["serde/std", "openvm/std"] - -bn254 = ["openvm-pairing-guest/bn254"] -bls12_381 = ["openvm-pairing-guest/bls12_381"] - -[profile.release] -panic = "abort" -lto = "thin" # turn on lto = fat to decrease binary size, but this optimizes out some missing extern links so we shouldn't use it for testing -# strip = "symbols" - -[[example]] -name = "bn_final_exp_hint" -required-features = ["bn254"] - -[[example]] -name = "bls_final_exp_hint" -required-features = ["bls12_381"] - -[[example]] -name = "bls_ec" -required-features = ["bls12_381"] diff --git a/extensions/pairing/tests/programs/examples/bls_ec.rs b/extensions/pairing/tests/programs/examples/bls_ec.rs deleted file mode 100644 index 93f72115cf..0000000000 --- a/extensions/pairing/tests/programs/examples/bls_ec.rs +++ /dev/null @@ -1,11 +0,0 @@ -#![cfg_attr(not(feature = "std"), no_main)] -#![cfg_attr(not(feature = "std"), no_std)] - -#[allow(unused_imports)] -use openvm_pairing_guest::bls12_381::Bls12_381G1Affine; - -openvm::init!("openvm_init_bls_ec_bls12_381.rs"); - -openvm::entry!(main); - -pub fn main() {} diff --git a/extensions/pairing/tests/programs/examples/bls_final_exp_hint.rs b/extensions/pairing/tests/programs/examples/bls_final_exp_hint.rs deleted file mode 100644 index 33660e0951..0000000000 --- a/extensions/pairing/tests/programs/examples/bls_final_exp_hint.rs +++ /dev/null @@ -1,24 +0,0 @@ -#![cfg_attr(not(feature = "std"), no_main)] -#![cfg_attr(not(feature = "std"), no_std)] - -extern crate alloc; - -use alloc::vec::Vec; - -use openvm::io::read; -use openvm_ecc_guest::AffinePoint; -use openvm_pairing_guest::{ - bls12_381::{Bls12_381, Fp, Fp12, Fp2}, - pairing::PairingCheck, -}; - -openvm::entry!(main); - -openvm::init!("openvm_init_bls_final_exp_hint_bls12_381.rs"); - -pub fn main() { - #[allow(clippy::type_complexity)] - let (p, q, expected): (Vec>, Vec>, (Fp12, Fp12)) = read(); - let actual = Bls12_381::pairing_check_hint(&p, &q); - assert_eq!(actual, expected); -} diff --git a/extensions/pairing/tests/programs/examples/bn_final_exp_hint.rs b/extensions/pairing/tests/programs/examples/bn_final_exp_hint.rs deleted file mode 100644 index 347afa72bb..0000000000 --- a/extensions/pairing/tests/programs/examples/bn_final_exp_hint.rs +++ /dev/null @@ -1,24 +0,0 @@ -#![cfg_attr(not(feature = "std"), no_main)] -#![cfg_attr(not(feature = "std"), no_std)] - -extern crate alloc; - -use alloc::vec::Vec; - -use openvm::io::read; -use openvm_ecc_guest::AffinePoint; -use openvm_pairing_guest::{ - bn254::{Bn254, Fp, Fp12, Fp2}, - pairing::PairingCheck, -}; - -openvm::entry!(main); - -openvm::init!("openvm_init_bn_final_exp_hint_bn254.rs"); - -pub fn main() { - #[allow(clippy::type_complexity)] - let (p, q, expected): (Vec>, Vec>, (Fp12, Fp12)) = read(); - let actual = Bn254::pairing_check_hint(&p, &q); - assert_eq!(actual, expected); -} diff --git a/extensions/pairing/tests/programs/examples/fp12_mul.rs b/extensions/pairing/tests/programs/examples/fp12_mul.rs deleted file mode 100644 index aeec2fb63d..0000000000 --- a/extensions/pairing/tests/programs/examples/fp12_mul.rs +++ /dev/null @@ -1,82 +0,0 @@ -#![cfg_attr(not(feature = "std"), no_main)] -#![cfg_attr(not(feature = "std"), no_std)] -#![allow(unused_imports)] - -use openvm::io::read_vec; -use openvm_algebra_guest::{field::FieldExtension, IntMod}; - -openvm::entry!(main); - -#[cfg(feature = "bn254")] -mod bn254 { - use openvm_pairing_guest::bn254::{Fp, Fp12}; - - use super::*; - - openvm::init!("openvm_init_fp12_mul_bn254.rs"); - - pub fn test_fp12_mul(io: &[u8]) { - assert_eq!(io.len(), 32 * 36); - - let f0 = &io[0..32 * 12]; - let f1 = &io[32 * 12..32 * 24]; - let r_cmp = &io[32 * 24..32 * 36]; - - let f0_cast = Fp12::from_bytes(f0); - let f1_cast = Fp12::from_bytes(f1); - - let r = f0_cast * f1_cast; - let mut r_bytes = [0u8; 32 * 12]; - r.to_coeffs() - .iter() - .flat_map(|fp2| fp2.clone().to_coeffs()) - .enumerate() - .for_each(|(i, fp)| r_bytes[i * 32..(i + 1) * 32].copy_from_slice(fp.as_le_bytes())); - - assert_eq!(r_bytes, r_cmp); - } -} - -#[cfg(feature = "bls12_381")] -mod bls12_381 { - use openvm_pairing_guest::bls12_381::{Fp, Fp12}; - - use super::*; - - openvm::init!("openvm_init_fp12_mul_bls12_381.rs"); - - pub fn test_fp12_mul(io: &[u8]) { - assert_eq!(io.len(), 48 * 36); - - let f0 = &io[0..48 * 12]; - let f1 = &io[48 * 12..48 * 24]; - let r_cmp = &io[48 * 24..48 * 36]; - - let f0_cast = Fp12::from_bytes(f0); - let f1_cast = Fp12::from_bytes(f1); - - let r = f0_cast * f1_cast; - let mut r_bytes = [0u8; 48 * 12]; - r.to_coeffs() - .iter() - .flat_map(|fp2| fp2.clone().to_coeffs()) - .enumerate() - .for_each(|(i, fp)| r_bytes[i * 48..(i + 1) * 48].copy_from_slice(fp.as_le_bytes())); - - assert_eq!(r_bytes, r_cmp); - } -} - -pub fn main() { - #[allow(unused_variables)] - let io = read_vec(); - - #[cfg(feature = "bn254")] - { - bn254::test_fp12_mul(&io) - } - #[cfg(feature = "bls12_381")] - { - bls12_381::test_fp12_mul(&io) - } -} diff --git a/extensions/pairing/tests/programs/examples/pairing_check.rs b/extensions/pairing/tests/programs/examples/pairing_check.rs deleted file mode 100644 index c01caded79..0000000000 --- a/extensions/pairing/tests/programs/examples/pairing_check.rs +++ /dev/null @@ -1,90 +0,0 @@ -#![cfg_attr(not(feature = "std"), no_main)] -#![cfg_attr(not(feature = "std"), no_std)] -#![allow(unused_imports)] - -extern crate alloc; - -use openvm::io::read_vec; -use openvm_ecc_guest::AffinePoint; -use openvm_pairing_guest::pairing::PairingCheck; - -openvm::entry!(main); - -#[cfg(feature = "bn254")] -mod bn254 { - use alloc::format; - - use openvm_algebra_guest::{field::FieldExtension, IntMod}; - use openvm_pairing_guest::bn254::{Bn254, Fp, Fp2}; - - use super::*; - - openvm::init!("openvm_init_pairing_check_bn254.rs"); - - pub fn test_pairing_check(io: &[u8]) { - let s0 = &io[0..32 * 2]; - let s1 = &io[32 * 2..32 * 4]; - let q0 = &io[32 * 4..32 * 8]; - let q1 = &io[32 * 8..32 * 12]; - - let s0_cast = - AffinePoint::new(Fp::from_le_bytes(&s0[..32]), Fp::from_le_bytes(&s0[32..64])); - let s1_cast = - AffinePoint::new(Fp::from_le_bytes(&s1[..32]), Fp::from_le_bytes(&s1[32..64])); - let q0_cast = AffinePoint::new(Fp2::from_bytes(&q0[..64]), Fp2::from_bytes(&q0[64..128])); - let q1_cast = AffinePoint::new(Fp2::from_bytes(&q1[..64]), Fp2::from_bytes(&q1[64..128])); - - let f = Bn254::pairing_check( - &[s0_cast.clone(), s1_cast.clone()], - &[q0_cast.clone(), q1_cast.clone()], - ); - assert_eq!(f, Ok(())); - } -} - -#[cfg(feature = "bls12_381")] -mod bls12_381 { - - use alloc::format; - - use openvm_algebra_guest::{field::FieldExtension, IntMod}; - use openvm_pairing_guest::bls12_381::{Bls12_381, Fp, Fp2}; - - use super::*; - - openvm::init!("openvm_init_pairing_check_bls12_381.rs"); - - pub fn test_pairing_check(io: &[u8]) { - let s0 = &io[0..48 * 2]; - let s1 = &io[48 * 2..48 * 4]; - let q0 = &io[48 * 4..48 * 8]; - let q1 = &io[48 * 8..48 * 12]; - - let s0_cast = - AffinePoint::new(Fp::from_le_bytes(&s0[..48]), Fp::from_le_bytes(&s0[48..96])); - let s1_cast = - AffinePoint::new(Fp::from_le_bytes(&s1[..48]), Fp::from_le_bytes(&s1[48..96])); - let q0_cast = AffinePoint::new(Fp2::from_bytes(&q0[..96]), Fp2::from_bytes(&q0[96..192])); - let q1_cast = AffinePoint::new(Fp2::from_bytes(&q1[..96]), Fp2::from_bytes(&q1[96..192])); - - let f = Bls12_381::pairing_check( - &[s0_cast.clone(), s1_cast.clone()], - &[q0_cast.clone(), q1_cast.clone()], - ); - assert_eq!(f, Ok(())); - } -} - -pub fn main() { - #[allow(unused_variables)] - let io = read_vec(); - - #[cfg(feature = "bn254")] - { - bn254::test_pairing_check(&io); - } - #[cfg(feature = "bls12_381")] - { - bls12_381::test_pairing_check(&io); - } -} diff --git a/extensions/pairing/tests/programs/examples/pairing_check_fallback.rs b/extensions/pairing/tests/programs/examples/pairing_check_fallback.rs deleted file mode 100644 index da3bcbb16f..0000000000 --- a/extensions/pairing/tests/programs/examples/pairing_check_fallback.rs +++ /dev/null @@ -1,241 +0,0 @@ -#![cfg_attr(not(feature = "std"), no_main)] -#![cfg_attr(not(feature = "std"), no_std)] -#![allow(unused_imports)] - -extern crate alloc; - -use openvm::io::read_vec; -use openvm_algebra_guest::{ - field::{ComplexConjugate, FieldExtension}, - DivUnsafe, Field, IntMod, -}; -use openvm_ecc_guest::AffinePoint; -use openvm_pairing_guest::pairing::{ - exp_check_fallback, MultiMillerLoop, PairingCheck, PairingCheckError, -}; - -openvm::entry!(main); - -#[cfg(feature = "bn254")] -mod bn254 { - use alloc::format; - - use openvm_pairing_guest::bn254::{Bn254, Fp, Fp12, Fp2}; - - use super::*; - - openvm::init!("openvm_init_pairing_check_fallback_bn254.rs"); - - // Wrapper so that we can override `pairing_check_hint` - struct Bn254Wrapper(Bn254); - - #[allow(non_snake_case)] - impl PairingCheck for Bn254Wrapper { - type Fp = Fp; - type Fp2 = Fp2; - type Fp12 = Fp12; - - fn pairing_check_hint( - _P: &[AffinePoint], - _Q: &[AffinePoint], - ) -> (Self::Fp12, Self::Fp12) { - // return dummy values - (Fp12::ZERO, Fp12::ZERO) - } - - // copied from Bn254::pairing_check - fn pairing_check( - P: &[AffinePoint], - Q: &[AffinePoint], - ) -> Result<(), PairingCheckError> { - Self::try_honest_pairing_check(P, Q).unwrap_or_else(|| { - let f = Bn254::multi_miller_loop(P, Q); - exp_check_fallback(&f, &Bn254::FINAL_EXPONENT) - }) - } - } - - #[allow(non_snake_case)] - impl Bn254Wrapper { - // copied from Bn254::try_honest_pairing_check - fn try_honest_pairing_check( - P: &[AffinePoint<::Fp>], - Q: &[AffinePoint<::Fp2>], - ) -> Option> { - let (c, s) = Self::pairing_check_hint(P, Q); - - // f * s = c^{q - x} - // f * s = c^q * c^-x - // f * c^x * c^-q * s = 1, - // where fc = f * c'^x (embedded Miller loop with c conjugate inverse), - // and the curve seed x = -0xd201000000010000 - // the miller loop computation includes a conjugation at the end because the value of - // the seed is negative, so we need to conjugate the miller loop input c - // as c'. We then substitute y = -x to get c^-y and finally compute c'^-y - // as input to the miller loop: f * c'^-y * c^-q * s = 1 - let c_q = FieldExtension::frobenius_map(&c, 1); - let c_conj = c.conjugate(); - if c_conj == Fp12::ZERO { - return None; - } - let c_conj_inv = Fp12::ONE.div_unsafe(&c_conj); - - // fc = f_{Miller,x,Q}(P) * c^{x} - // where - // fc = conjugate( f_{Miller,-x,Q}(P) * c'^{-x} ), with c' denoting the conjugate of c - let fc = Bn254::multi_miller_loop_embedded_exp(P, Q, Some(c_conj_inv)); - - if fc * s == c_q { - Some(Ok(())) - } else { - None - } - } - } - - pub fn test_pairing_check(io: &[u8]) { - let s0 = &io[0..32 * 2]; - let s1 = &io[32 * 2..32 * 4]; - let q0 = &io[32 * 4..32 * 8]; - let q1 = &io[32 * 8..32 * 12]; - - let s0_cast = - AffinePoint::new(Fp::from_le_bytes(&s0[..32]), Fp::from_le_bytes(&s0[32..64])); - let s1_cast = - AffinePoint::new(Fp::from_le_bytes(&s1[..32]), Fp::from_le_bytes(&s1[32..64])); - let q0_cast = AffinePoint::new(Fp2::from_bytes(&q0[..64]), Fp2::from_bytes(&q0[64..128])); - let q1_cast = AffinePoint::new(Fp2::from_bytes(&q1[..64]), Fp2::from_bytes(&q1[64..128])); - - let f = Bn254Wrapper::pairing_check( - &[s0_cast.clone(), s1_cast.clone()], - &[q0_cast.clone(), q1_cast.clone()], - ); - assert_eq!(f, Ok(())); - - let f = Bn254Wrapper::pairing_check( - &[-s0_cast.clone(), s1_cast.clone()], - &[q0_cast.clone(), q1_cast.clone()], - ); - assert_eq!(f, Err(PairingCheckError)); - } -} - -#[cfg(feature = "bls12_381")] -mod bls12_381 { - - use alloc::format; - - use openvm_pairing_guest::bls12_381::{Bls12_381, Fp, Fp12, Fp2}; - - use super::*; - - openvm::init!("openvm_init_pairing_check_fallback_bls12_381.rs"); - - // Wrapper so that we can override `pairing_check_hint` - struct Bls12_381Wrapper(Bls12_381); - - #[allow(non_snake_case)] - impl PairingCheck for Bls12_381Wrapper { - type Fp = Fp; - type Fp2 = Fp2; - type Fp12 = Fp12; - - #[allow(unused_variables)] - fn pairing_check_hint( - _P: &[AffinePoint], - _Q: &[AffinePoint], - ) -> (Self::Fp12, Self::Fp12) { - // return dummy values - (Fp12::ZERO, Fp12::ZERO) - } - - // copied from Bls12_381::pairing_check - fn pairing_check( - P: &[AffinePoint], - Q: &[AffinePoint], - ) -> Result<(), PairingCheckError> { - Self::try_honest_pairing_check(P, Q).unwrap_or_else(|| { - let f = Bls12_381::multi_miller_loop(P, Q); - exp_check_fallback(&f, &Bls12_381::FINAL_EXPONENT) - }) - } - } - - #[allow(non_snake_case)] - impl Bls12_381Wrapper { - // copied from Bls12_381::try_honest_pairing_check - fn try_honest_pairing_check( - P: &[AffinePoint<::Fp>], - Q: &[AffinePoint<::Fp2>], - ) -> Option> { - let (c, s) = Self::pairing_check_hint(P, Q); - - // f * s = c^{q - x} - // f * s = c^q * c^-x - // f * c^x * c^-q * s = 1, - // where fc = f * c'^x (embedded Miller loop with c conjugate inverse), - // and the curve seed x = -0xd201000000010000 - // the miller loop computation includes a conjugation at the end because the value of - // the seed is negative, so we need to conjugate the miller loop input c - // as c'. We then substitute y = -x to get c^-y and finally compute c'^-y - // as input to the miller loop: f * c'^-y * c^-q * s = 1 - let c_q = FieldExtension::frobenius_map(&c, 1); - let c_conj = c.conjugate(); - if c_conj == Fp12::ZERO { - return None; - } - let c_conj_inv = Fp12::ONE.div_unsafe(&c_conj); - - // fc = f_{Miller,x,Q}(P) * c^{x} - // where - // fc = conjugate( f_{Miller,-x,Q}(P) * c'^{-x} ), with c' denoting the conjugate of c - let fc = Bls12_381::multi_miller_loop_embedded_exp(P, Q, Some(c_conj_inv)); - - if fc * s == c_q { - Some(Ok(())) - } else { - None - } - } - } - - pub fn test_pairing_check(io: &[u8]) { - let s0 = &io[0..48 * 2]; - let s1 = &io[48 * 2..48 * 4]; - let q0 = &io[48 * 4..48 * 8]; - let q1 = &io[48 * 8..48 * 12]; - - let s0_cast = - AffinePoint::new(Fp::from_le_bytes(&s0[..48]), Fp::from_le_bytes(&s0[48..96])); - let s1_cast = - AffinePoint::new(Fp::from_le_bytes(&s1[..48]), Fp::from_le_bytes(&s1[48..96])); - let q0_cast = AffinePoint::new(Fp2::from_bytes(&q0[..96]), Fp2::from_bytes(&q0[96..192])); - let q1_cast = AffinePoint::new(Fp2::from_bytes(&q1[..96]), Fp2::from_bytes(&q1[96..192])); - - let f = Bls12_381Wrapper::pairing_check( - &[s0_cast.clone(), s1_cast.clone()], - &[q0_cast.clone(), q1_cast.clone()], - ); - assert_eq!(f, Ok(())); - - let f = Bls12_381Wrapper::pairing_check( - &[-s0_cast.clone(), s1_cast.clone()], - &[q0_cast.clone(), q1_cast.clone()], - ); - assert_eq!(f, Err(PairingCheckError)); - } -} - -pub fn main() { - #[allow(unused_variables)] - let io = read_vec(); - - #[cfg(feature = "bn254")] - { - bn254::test_pairing_check(&io); - } - #[cfg(feature = "bls12_381")] - { - bls12_381::test_pairing_check(&io); - } -} diff --git a/extensions/pairing/tests/programs/examples/pairing_line.rs b/extensions/pairing/tests/programs/examples/pairing_line.rs deleted file mode 100644 index b36c200391..0000000000 --- a/extensions/pairing/tests/programs/examples/pairing_line.rs +++ /dev/null @@ -1,147 +0,0 @@ -#![allow(unused_imports)] -#![cfg_attr(not(feature = "std"), no_main)] -#![cfg_attr(not(feature = "std"), no_std)] - -use openvm::io::read_vec; -use openvm_algebra_guest::{field::FieldExtension, IntMod}; -use openvm_pairing_guest::pairing::{EvaluatedLine, LineMulDType, LineMulMType}; - -openvm::entry!(main); - -#[cfg(feature = "bn254")] -mod bn254 { - use openvm_pairing_guest::bn254::{Bn254, Fp, Fp12, Fp2}; - - use super::*; - - openvm::init!("openvm_init_pairing_line_bn254.rs"); - - pub fn test_mul_013_by_013(io: &[u8]) { - assert_eq!(io.len(), 32 * 18); - let l0 = &io[..32 * 4]; - let l1 = &io[32 * 4..32 * 8]; - let expected = &io[32 * 8..32 * 18]; - - let l0_cast = EvaluatedLine { - b: Fp2::from_bytes(&l0[..64]), - c: Fp2::from_bytes(&l0[64..128]), - }; - let l1_cast = EvaluatedLine { - b: Fp2::from_bytes(&l1[..64]), - c: Fp2::from_bytes(&l1[64..128]), - }; - - let r = Bn254::mul_013_by_013(&l0_cast, &l1_cast); - let mut r_bytes = [0u8; 32 * 10]; - let mut i = 0; - for x in r { - r_bytes[i..i + 32].copy_from_slice(x.c0.as_le_bytes()); - r_bytes[i + 32..i + 64].copy_from_slice(x.c1.as_le_bytes()); - i += 64; - } - assert_eq!(r_bytes, expected); - } - - pub fn test_mul_by_01234(io: &[u8]) { - assert_eq!(io.len(), 32 * 34); - let f = &io[..32 * 12]; - let x = &io[32 * 12..32 * 22]; - let expected = &io[32 * 22..32 * 34]; - - let f_cast = Fp12::from_bytes(f); - let x_cast = [ - Fp2::from_bytes(&x[..64]), - Fp2::from_bytes(&x[64..128]), - Fp2::from_bytes(&x[128..192]), - Fp2::from_bytes(&x[192..256]), - Fp2::from_bytes(&x[256..320]), - ]; - - let r = Bn254::mul_by_01234(&f_cast, &x_cast); - let mut r_bytes = [0u8; 32 * 12]; - let mut i = 0; - for x in r.to_coeffs() { - r_bytes[i..i + 32].copy_from_slice(x.c0.as_le_bytes()); - r_bytes[i + 32..i + 64].copy_from_slice(x.c1.as_le_bytes()); - i += 64; - } - assert_eq!(r_bytes, expected); - } -} - -#[cfg(feature = "bls12_381")] -mod bls12_381 { - use openvm_pairing_guest::bls12_381::{Bls12_381, Fp, Fp12, Fp2}; - - use super::*; - - openvm::init!("openvm_init_pairing_line_bls12_381.rs"); - - pub fn test_mul_023_by_023(io: &[u8]) { - assert_eq!(io.len(), 48 * 18); - let l0 = &io[..48 * 4]; - let l1 = &io[48 * 4..48 * 8]; - let expected = &io[48 * 8..48 * 18]; - - let l0_cast = EvaluatedLine { - b: Fp2::from_bytes(&l0[..48 * 2]), - c: Fp2::from_bytes(&l0[48 * 2..48 * 4]), - }; - let l1_cast = EvaluatedLine { - b: Fp2::from_bytes(&l1[..48 * 2]), - c: Fp2::from_bytes(&l1[48 * 2..48 * 4]), - }; - - let r = Bls12_381::mul_023_by_023(&l0_cast, &l1_cast); - let mut r_bytes = [0u8; 48 * 10]; - let mut i = 0; - for x in r { - r_bytes[i..i + 48].copy_from_slice(x.c0.as_le_bytes()); - r_bytes[i + 48..i + 96].copy_from_slice(x.c1.as_le_bytes()); - i += 96; - } - assert_eq!(r_bytes, expected); - } - - pub fn test_mul_by_02345(io: &[u8]) { - assert_eq!(io.len(), 48 * 34); - let f = &io[..48 * 12]; - let x = &io[48 * 12..48 * 22]; - let expected = &io[48 * 22..48 * 34]; - - let f_cast = Fp12::from_bytes(f); - let x_cast = [ - Fp2::from_bytes(&x[..48 * 2]), - Fp2::from_bytes(&x[48 * 2..48 * 4]), - Fp2::from_bytes(&x[48 * 4..48 * 6]), - Fp2::from_bytes(&x[48 * 6..48 * 8]), - Fp2::from_bytes(&x[48 * 8..48 * 10]), - ]; - - let r = Bls12_381::mul_by_02345(&f_cast, &x_cast); - let mut r_bytes = [0u8; 48 * 12]; - let mut i = 0; - for x in r.to_coeffs() { - r_bytes[i..i + 48].copy_from_slice(x.c0.as_le_bytes()); - r_bytes[i + 48..i + 96].copy_from_slice(x.c1.as_le_bytes()); - i += 96; - } - assert_eq!(r_bytes, expected); - } -} - -pub fn main() { - #[allow(unused_variables)] - let io = read_vec(); - - #[cfg(feature = "bn254")] - { - bn254::test_mul_013_by_013(&io[..32 * 18]); - bn254::test_mul_by_01234(&io[32 * 18..32 * 52]); - } - #[cfg(feature = "bls12_381")] - { - bls12_381::test_mul_023_by_023(&io[..48 * 18]); - bls12_381::test_mul_by_02345(&io[48 * 18..48 * 52]); - } -} diff --git a/extensions/pairing/tests/programs/examples/pairing_miller_loop.rs b/extensions/pairing/tests/programs/examples/pairing_miller_loop.rs deleted file mode 100644 index a9d8f09dbd..0000000000 --- a/extensions/pairing/tests/programs/examples/pairing_miller_loop.rs +++ /dev/null @@ -1,97 +0,0 @@ -#![allow(unused_imports)] -#![cfg_attr(not(feature = "std"), no_main)] -#![cfg_attr(not(feature = "std"), no_std)] - -extern crate alloc; - -use openvm::io::read_vec; -use openvm_algebra_guest::{field::FieldExtension, IntMod}; -use openvm_ecc_guest::AffinePoint; -use openvm_pairing_guest::pairing::MultiMillerLoop; - -openvm::entry!(main); - -#[cfg(feature = "bn254")] -mod bn254 { - use openvm_pairing_guest::bn254::{Bn254, Fp, Fp2}; - - use super::*; - - openvm::init!("openvm_init_pairing_miller_loop_bn254.rs"); - - pub fn test_miller_loop(io: &[u8]) { - let s0 = &io[0..32 * 2]; - let s1 = &io[32 * 2..32 * 4]; - let q0 = &io[32 * 4..32 * 8]; - let q1 = &io[32 * 8..32 * 12]; - let f_cmp = &io[32 * 12..32 * 24]; - - let s0_cast = - AffinePoint::new(Fp::from_le_bytes(&s0[..32]), Fp::from_le_bytes(&s0[32..64])); - let s1_cast = - AffinePoint::new(Fp::from_le_bytes(&s1[..32]), Fp::from_le_bytes(&s1[32..64])); - let q0_cast = AffinePoint::new(Fp2::from_bytes(&q0[..64]), Fp2::from_bytes(&q0[64..128])); - let q1_cast = AffinePoint::new(Fp2::from_bytes(&q1[..64]), Fp2::from_bytes(&q1[64..128])); - - let f = Bn254::multi_miller_loop(&[s0_cast, s1_cast], &[q0_cast, q1_cast]); - let mut f_bytes = [0u8; 32 * 12]; - f.to_coeffs() - .iter() - .flat_map(|fp2| fp2.clone().to_coeffs()) - .enumerate() - .for_each(|(i, fp)| f_bytes[i * 32..(i + 1) * 32].copy_from_slice(fp.as_le_bytes())); - - assert_eq!(f_bytes, f_cmp); - } -} - -#[cfg(feature = "bls12_381")] -mod bls12_381 { - use openvm_pairing_guest::bls12_381::{Bls12_381, Fp, Fp2}; - - use super::*; - - openvm::init!("openvm_init_pairing_miller_loop_bls12_381.rs"); - - pub fn test_miller_loop(io: &[u8]) { - let s0 = &io[0..48 * 2]; - let s1 = &io[48 * 2..48 * 4]; - let q0 = &io[48 * 4..48 * 8]; - let q1 = &io[48 * 8..48 * 12]; - let f_cmp = &io[48 * 12..48 * 24]; - - let s0_cast = - AffinePoint::new(Fp::from_le_bytes(&s0[..48]), Fp::from_le_bytes(&s0[48..96])); - let s1_cast = - AffinePoint::new(Fp::from_le_bytes(&s1[..48]), Fp::from_le_bytes(&s1[48..96])); - let q0_cast = AffinePoint::new(Fp2::from_bytes(&q0[..96]), Fp2::from_bytes(&q0[96..192])); - let q1_cast = AffinePoint::new(Fp2::from_bytes(&q1[..96]), Fp2::from_bytes(&q1[96..192])); - - let f = Bls12_381::multi_miller_loop( - &[s0_cast.clone(), s1_cast.clone()], - &[q0_cast.clone(), q1_cast.clone()], - ); - let mut f_bytes = [0u8; 48 * 12]; - f.to_coeffs() - .iter() - .flat_map(|fp2| fp2.clone().to_coeffs()) - .enumerate() - .for_each(|(i, fp)| f_bytes[i * 48..(i + 1) * 48].copy_from_slice(fp.as_le_bytes())); - - assert_eq!(f_bytes, f_cmp); - } -} - -pub fn main() { - #[allow(unused_variables)] - let io = read_vec(); - - #[cfg(feature = "bn254")] - { - bn254::test_miller_loop(&io); - } - #[cfg(feature = "bls12_381")] - { - bls12_381::test_miller_loop(&io); - } -} diff --git a/extensions/pairing/tests/programs/examples/pairing_miller_step.rs b/extensions/pairing/tests/programs/examples/pairing_miller_step.rs deleted file mode 100644 index a2d4662cf4..0000000000 --- a/extensions/pairing/tests/programs/examples/pairing_miller_step.rs +++ /dev/null @@ -1,166 +0,0 @@ -#![allow(unused_imports)] -#![cfg_attr(not(feature = "std"), no_main)] -#![cfg_attr(not(feature = "std"), no_std)] - -use openvm::io::read_vec; -use openvm_algebra_guest::IntMod; -use openvm_ecc_guest::AffinePoint; -use openvm_pairing_guest::pairing::MillerStep; - -openvm::entry!(main); - -#[cfg(feature = "bn254")] -mod bn254 { - use openvm_algebra_guest::field::FieldExtension; - use openvm_pairing_guest::bn254::{Bn254, Fp, Fp2}; - - use super::*; - - openvm::init!("openvm_init_pairing_miller_step_bn254.rs"); - - pub fn test_miller_step(io: &[u8]) { - assert_eq!(io.len(), 32 * 12); - let s = &io[..32 * 4]; - let pt = &io[32 * 4..32 * 8]; - let l = &io[32 * 8..32 * 12]; - - let s_cast = AffinePoint::new(Fp2::from_bytes(&s[..64]), Fp2::from_bytes(&s[64..128])); - - let (pt_cmp, l_cmp) = Bn254::miller_double_step(&s_cast); - let mut pt_bytes = [0u8; 32 * 4]; - let mut l_bytes = [0u8; 32 * 4]; - - // TODO: if we ever need to change this, we should switch to using `StdIn::write` to - // serialize for us and use `read()` instead of `read_vec()` - pt_bytes[0..32].copy_from_slice(pt_cmp.x.c0.as_le_bytes()); - pt_bytes[32..2 * 32].copy_from_slice(pt_cmp.x.c1.as_le_bytes()); - pt_bytes[2 * 32..3 * 32].copy_from_slice(pt_cmp.y.c0.as_le_bytes()); - pt_bytes[3 * 32..4 * 32].copy_from_slice(pt_cmp.y.c1.as_le_bytes()); - l_bytes[0..32].copy_from_slice(l_cmp.b.c0.as_le_bytes()); - l_bytes[32..2 * 32].copy_from_slice(l_cmp.b.c1.as_le_bytes()); - l_bytes[2 * 32..3 * 32].copy_from_slice(l_cmp.c.c0.as_le_bytes()); - l_bytes[3 * 32..4 * 32].copy_from_slice(l_cmp.c.c1.as_le_bytes()); - - assert_eq!(pt_bytes, pt); - assert_eq!(l_bytes, l); - } - - pub fn test_miller_double_and_add_step(io: &[u8]) { - assert_eq!(io.len(), 32 * 20); - let s = &io[0..32 * 4]; - let q = &io[32 * 4..32 * 8]; - let pt = &io[32 * 8..32 * 12]; - let l0 = &io[32 * 12..32 * 16]; - let l1 = &io[32 * 16..32 * 20]; - - let s_cast = AffinePoint::new(Fp2::from_bytes(&s[..64]), Fp2::from_bytes(&s[64..128])); - let q_cast = AffinePoint::new(Fp2::from_bytes(&q[..64]), Fp2::from_bytes(&q[64..128])); - let (pt_cmp, l0_cmp, l1_cmp) = Bn254::miller_double_and_add_step(&s_cast, &q_cast); - let mut pt_bytes = [0u8; 32 * 4]; - let mut l0_bytes = [0u8; 32 * 4]; - let mut l1_bytes = [0u8; 32 * 4]; - - // TODO: if we ever need to change this, we should switch to using `StdIn::write` to - // serialize for us and use `read()` instead of `read_vec()` - pt_bytes[0..32].copy_from_slice(pt_cmp.x.c0.as_le_bytes()); - pt_bytes[32..2 * 32].copy_from_slice(pt_cmp.x.c1.as_le_bytes()); - pt_bytes[2 * 32..3 * 32].copy_from_slice(pt_cmp.y.c0.as_le_bytes()); - pt_bytes[3 * 32..4 * 32].copy_from_slice(pt_cmp.y.c1.as_le_bytes()); - l0_bytes[0..32].copy_from_slice(l0_cmp.b.c0.as_le_bytes()); - l0_bytes[32..2 * 32].copy_from_slice(l0_cmp.b.c1.as_le_bytes()); - l0_bytes[2 * 32..3 * 32].copy_from_slice(l0_cmp.c.c0.as_le_bytes()); - l0_bytes[3 * 32..4 * 32].copy_from_slice(l0_cmp.c.c1.as_le_bytes()); - l1_bytes[0..32].copy_from_slice(l1_cmp.b.c0.as_le_bytes()); - l1_bytes[32..2 * 32].copy_from_slice(l1_cmp.b.c1.as_le_bytes()); - l1_bytes[2 * 32..3 * 32].copy_from_slice(l1_cmp.c.c0.as_le_bytes()); - l1_bytes[3 * 32..4 * 32].copy_from_slice(l1_cmp.c.c1.as_le_bytes()); - - assert_eq!(pt_bytes, pt); - assert_eq!(l0_bytes, l0); - assert_eq!(l1_bytes, l1); - } -} - -#[cfg(feature = "bls12_381")] -mod bls12_381 { - use openvm_algebra_guest::field::FieldExtension; - use openvm_pairing_guest::bls12_381::{Bls12_381, Fp, Fp2}; - - use super::*; - - openvm::init!("openvm_init_pairing_miller_step_bls12_381.rs"); - - pub fn test_miller_step(io: &[u8]) { - assert_eq!(io.len(), 48 * 12); - let s = &io[..48 * 4]; - let pt = &io[48 * 4..48 * 8]; - let l = &io[48 * 8..48 * 12]; - - let s_cast = AffinePoint::new(Fp2::from_bytes(&s[..96]), Fp2::from_bytes(&s[96..192])); - - let (pt_cmp, l_cmp) = Bls12_381::miller_double_step(&s_cast); - let mut pt_bytes = [0u8; 48 * 4]; - let mut l_bytes = [0u8; 48 * 4]; - - pt_bytes[0..48].copy_from_slice(pt_cmp.x.c0.as_le_bytes()); - pt_bytes[48..2 * 48].copy_from_slice(pt_cmp.x.c1.as_le_bytes()); - pt_bytes[2 * 48..3 * 48].copy_from_slice(pt_cmp.y.c0.as_le_bytes()); - pt_bytes[3 * 48..4 * 48].copy_from_slice(pt_cmp.y.c1.as_le_bytes()); - l_bytes[0..48].copy_from_slice(l_cmp.b.c0.as_le_bytes()); - l_bytes[48..2 * 48].copy_from_slice(l_cmp.b.c1.as_le_bytes()); - l_bytes[2 * 48..3 * 48].copy_from_slice(l_cmp.c.c0.as_le_bytes()); - l_bytes[3 * 48..4 * 48].copy_from_slice(l_cmp.c.c1.as_le_bytes()); - - assert_eq!(pt_bytes, pt); - assert_eq!(l_bytes, l); - } - - pub fn test_miller_double_and_add_step(io: &[u8]) { - assert_eq!(io.len(), 48 * 20); - let s = &io[0..48 * 4]; - let q = &io[48 * 4..48 * 8]; - let pt = &io[48 * 8..48 * 12]; - let l0 = &io[48 * 12..48 * 16]; - let l1 = &io[48 * 16..48 * 20]; - - let s_cast = AffinePoint::new(Fp2::from_bytes(&s[..96]), Fp2::from_bytes(&s[96..192])); - let q_cast = AffinePoint::new(Fp2::from_bytes(&q[..96]), Fp2::from_bytes(&q[96..192])); - let (pt_cmp, l0_cmp, l1_cmp) = Bls12_381::miller_double_and_add_step(&s_cast, &q_cast); - let mut pt_bytes = [0u8; 48 * 4]; - let mut l0_bytes = [0u8; 48 * 4]; - let mut l1_bytes = [0u8; 48 * 4]; - - pt_bytes[0..48].copy_from_slice(pt_cmp.x.c0.as_le_bytes()); - pt_bytes[48..2 * 48].copy_from_slice(pt_cmp.x.c1.as_le_bytes()); - pt_bytes[2 * 48..3 * 48].copy_from_slice(pt_cmp.y.c0.as_le_bytes()); - pt_bytes[3 * 48..4 * 48].copy_from_slice(pt_cmp.y.c1.as_le_bytes()); - l0_bytes[0..48].copy_from_slice(l0_cmp.b.c0.as_le_bytes()); - l0_bytes[48..2 * 48].copy_from_slice(l0_cmp.b.c1.as_le_bytes()); - l0_bytes[2 * 48..3 * 48].copy_from_slice(l0_cmp.c.c0.as_le_bytes()); - l0_bytes[3 * 48..4 * 48].copy_from_slice(l0_cmp.c.c1.as_le_bytes()); - l1_bytes[0..48].copy_from_slice(l1_cmp.b.c0.as_le_bytes()); - l1_bytes[48..2 * 48].copy_from_slice(l1_cmp.b.c1.as_le_bytes()); - l1_bytes[2 * 48..3 * 48].copy_from_slice(l1_cmp.c.c0.as_le_bytes()); - l1_bytes[3 * 48..4 * 48].copy_from_slice(l1_cmp.c.c1.as_le_bytes()); - - assert_eq!(pt_bytes, pt); - assert_eq!(l0_bytes, l0); - assert_eq!(l1_bytes, l1); - } -} - -pub fn main() { - #[allow(unused_variables)] - let io = read_vec(); - - #[cfg(feature = "bn254")] - { - bn254::test_miller_step(&io[..32 * 12]); - bn254::test_miller_double_and_add_step(&io[32 * 12..]); - } - #[cfg(feature = "bls12_381")] - { - bls12_381::test_miller_step(&io[..48 * 12]); - bls12_381::test_miller_double_and_add_step(&io[48 * 12..]); - } -} diff --git a/extensions/pairing/tests/programs/openvm_init_bls_ec_bls12_381.rs b/extensions/pairing/tests/programs/openvm_init_bls_ec_bls12_381.rs deleted file mode 100644 index 95a4e46fd3..0000000000 --- a/extensions/pairing/tests/programs/openvm_init_bls_ec_bls12_381.rs +++ /dev/null @@ -1,3 +0,0 @@ -// This file is automatically generated by cargo openvm. Do not rename or edit. -openvm_algebra_guest::moduli_macros::moduli_init! { "4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787", "52435875175126190479447740508185965837690552500527637822603658699938581184513" } -openvm_ecc_guest::sw_macros::sw_init! { Bls12_381G1Affine } diff --git a/extensions/pairing/tests/programs/openvm_init_bls_final_exp_hint_bls12_381.rs b/extensions/pairing/tests/programs/openvm_init_bls_final_exp_hint_bls12_381.rs deleted file mode 100644 index 00181b03ef..0000000000 --- a/extensions/pairing/tests/programs/openvm_init_bls_final_exp_hint_bls12_381.rs +++ /dev/null @@ -1,4 +0,0 @@ -// This file is automatically generated by cargo openvm. Do not rename or edit. -openvm_algebra_guest::moduli_macros::moduli_init! { "4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787" } -openvm_algebra_guest::complex_macros::complex_init! { Bls12_381Fp2 { mod_idx = 0 } } -openvm_ecc_guest::sw_macros::sw_init! { } diff --git a/extensions/pairing/tests/programs/openvm_init_bn_final_exp_hint_bn254.rs b/extensions/pairing/tests/programs/openvm_init_bn_final_exp_hint_bn254.rs deleted file mode 100644 index c130859ad8..0000000000 --- a/extensions/pairing/tests/programs/openvm_init_bn_final_exp_hint_bn254.rs +++ /dev/null @@ -1,4 +0,0 @@ -// This file is automatically generated by cargo openvm. Do not rename or edit. -openvm_algebra_guest::moduli_macros::moduli_init! { "21888242871839275222246405745257275088696311157297823662689037894645226208583" } -openvm_algebra_guest::complex_macros::complex_init! { Bn254Fp2 { mod_idx = 0 } } -openvm_ecc_guest::sw_macros::sw_init! { } diff --git a/extensions/pairing/tests/programs/openvm_init_fp12_mul_bls12_381.rs b/extensions/pairing/tests/programs/openvm_init_fp12_mul_bls12_381.rs deleted file mode 100644 index c130859ad8..0000000000 --- a/extensions/pairing/tests/programs/openvm_init_fp12_mul_bls12_381.rs +++ /dev/null @@ -1,4 +0,0 @@ -// This file is automatically generated by cargo openvm. Do not rename or edit. -openvm_algebra_guest::moduli_macros::moduli_init! { "21888242871839275222246405745257275088696311157297823662689037894645226208583" } -openvm_algebra_guest::complex_macros::complex_init! { Bn254Fp2 { mod_idx = 0 } } -openvm_ecc_guest::sw_macros::sw_init! { } diff --git a/extensions/pairing/tests/programs/openvm_init_fp12_mul_bn254.rs b/extensions/pairing/tests/programs/openvm_init_fp12_mul_bn254.rs deleted file mode 100644 index c130859ad8..0000000000 --- a/extensions/pairing/tests/programs/openvm_init_fp12_mul_bn254.rs +++ /dev/null @@ -1,4 +0,0 @@ -// This file is automatically generated by cargo openvm. Do not rename or edit. -openvm_algebra_guest::moduli_macros::moduli_init! { "21888242871839275222246405745257275088696311157297823662689037894645226208583" } -openvm_algebra_guest::complex_macros::complex_init! { Bn254Fp2 { mod_idx = 0 } } -openvm_ecc_guest::sw_macros::sw_init! { } diff --git a/extensions/pairing/tests/programs/openvm_init_pairing_check_bls12_381.rs b/extensions/pairing/tests/programs/openvm_init_pairing_check_bls12_381.rs deleted file mode 100644 index 00181b03ef..0000000000 --- a/extensions/pairing/tests/programs/openvm_init_pairing_check_bls12_381.rs +++ /dev/null @@ -1,4 +0,0 @@ -// This file is automatically generated by cargo openvm. Do not rename or edit. -openvm_algebra_guest::moduli_macros::moduli_init! { "4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787" } -openvm_algebra_guest::complex_macros::complex_init! { Bls12_381Fp2 { mod_idx = 0 } } -openvm_ecc_guest::sw_macros::sw_init! { } diff --git a/extensions/pairing/tests/programs/openvm_init_pairing_check_bn254.rs b/extensions/pairing/tests/programs/openvm_init_pairing_check_bn254.rs deleted file mode 100644 index c130859ad8..0000000000 --- a/extensions/pairing/tests/programs/openvm_init_pairing_check_bn254.rs +++ /dev/null @@ -1,4 +0,0 @@ -// This file is automatically generated by cargo openvm. Do not rename or edit. -openvm_algebra_guest::moduli_macros::moduli_init! { "21888242871839275222246405745257275088696311157297823662689037894645226208583" } -openvm_algebra_guest::complex_macros::complex_init! { Bn254Fp2 { mod_idx = 0 } } -openvm_ecc_guest::sw_macros::sw_init! { } diff --git a/extensions/pairing/tests/programs/openvm_init_pairing_check_fallback_bls12_381.rs b/extensions/pairing/tests/programs/openvm_init_pairing_check_fallback_bls12_381.rs deleted file mode 100644 index 00181b03ef..0000000000 --- a/extensions/pairing/tests/programs/openvm_init_pairing_check_fallback_bls12_381.rs +++ /dev/null @@ -1,4 +0,0 @@ -// This file is automatically generated by cargo openvm. Do not rename or edit. -openvm_algebra_guest::moduli_macros::moduli_init! { "4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787" } -openvm_algebra_guest::complex_macros::complex_init! { Bls12_381Fp2 { mod_idx = 0 } } -openvm_ecc_guest::sw_macros::sw_init! { } diff --git a/extensions/pairing/tests/programs/openvm_init_pairing_check_fallback_bn254.rs b/extensions/pairing/tests/programs/openvm_init_pairing_check_fallback_bn254.rs deleted file mode 100644 index c130859ad8..0000000000 --- a/extensions/pairing/tests/programs/openvm_init_pairing_check_fallback_bn254.rs +++ /dev/null @@ -1,4 +0,0 @@ -// This file is automatically generated by cargo openvm. Do not rename or edit. -openvm_algebra_guest::moduli_macros::moduli_init! { "21888242871839275222246405745257275088696311157297823662689037894645226208583" } -openvm_algebra_guest::complex_macros::complex_init! { Bn254Fp2 { mod_idx = 0 } } -openvm_ecc_guest::sw_macros::sw_init! { } diff --git a/extensions/pairing/tests/programs/openvm_init_pairing_line_bls12_381.rs b/extensions/pairing/tests/programs/openvm_init_pairing_line_bls12_381.rs deleted file mode 100644 index 00181b03ef..0000000000 --- a/extensions/pairing/tests/programs/openvm_init_pairing_line_bls12_381.rs +++ /dev/null @@ -1,4 +0,0 @@ -// This file is automatically generated by cargo openvm. Do not rename or edit. -openvm_algebra_guest::moduli_macros::moduli_init! { "4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787" } -openvm_algebra_guest::complex_macros::complex_init! { Bls12_381Fp2 { mod_idx = 0 } } -openvm_ecc_guest::sw_macros::sw_init! { } diff --git a/extensions/pairing/tests/programs/openvm_init_pairing_line_bn254.rs b/extensions/pairing/tests/programs/openvm_init_pairing_line_bn254.rs deleted file mode 100644 index c130859ad8..0000000000 --- a/extensions/pairing/tests/programs/openvm_init_pairing_line_bn254.rs +++ /dev/null @@ -1,4 +0,0 @@ -// This file is automatically generated by cargo openvm. Do not rename or edit. -openvm_algebra_guest::moduli_macros::moduli_init! { "21888242871839275222246405745257275088696311157297823662689037894645226208583" } -openvm_algebra_guest::complex_macros::complex_init! { Bn254Fp2 { mod_idx = 0 } } -openvm_ecc_guest::sw_macros::sw_init! { } diff --git a/extensions/pairing/tests/programs/openvm_init_pairing_miller_loop_bls12_381.rs b/extensions/pairing/tests/programs/openvm_init_pairing_miller_loop_bls12_381.rs deleted file mode 100644 index 00181b03ef..0000000000 --- a/extensions/pairing/tests/programs/openvm_init_pairing_miller_loop_bls12_381.rs +++ /dev/null @@ -1,4 +0,0 @@ -// This file is automatically generated by cargo openvm. Do not rename or edit. -openvm_algebra_guest::moduli_macros::moduli_init! { "4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787" } -openvm_algebra_guest::complex_macros::complex_init! { Bls12_381Fp2 { mod_idx = 0 } } -openvm_ecc_guest::sw_macros::sw_init! { } diff --git a/extensions/pairing/tests/programs/openvm_init_pairing_miller_loop_bn254.rs b/extensions/pairing/tests/programs/openvm_init_pairing_miller_loop_bn254.rs deleted file mode 100644 index c130859ad8..0000000000 --- a/extensions/pairing/tests/programs/openvm_init_pairing_miller_loop_bn254.rs +++ /dev/null @@ -1,4 +0,0 @@ -// This file is automatically generated by cargo openvm. Do not rename or edit. -openvm_algebra_guest::moduli_macros::moduli_init! { "21888242871839275222246405745257275088696311157297823662689037894645226208583" } -openvm_algebra_guest::complex_macros::complex_init! { Bn254Fp2 { mod_idx = 0 } } -openvm_ecc_guest::sw_macros::sw_init! { } diff --git a/extensions/pairing/tests/programs/openvm_init_pairing_miller_step_bls12_381.rs b/extensions/pairing/tests/programs/openvm_init_pairing_miller_step_bls12_381.rs deleted file mode 100644 index 00181b03ef..0000000000 --- a/extensions/pairing/tests/programs/openvm_init_pairing_miller_step_bls12_381.rs +++ /dev/null @@ -1,4 +0,0 @@ -// This file is automatically generated by cargo openvm. Do not rename or edit. -openvm_algebra_guest::moduli_macros::moduli_init! { "4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787" } -openvm_algebra_guest::complex_macros::complex_init! { Bls12_381Fp2 { mod_idx = 0 } } -openvm_ecc_guest::sw_macros::sw_init! { } diff --git a/extensions/pairing/tests/programs/openvm_init_pairing_miller_step_bn254.rs b/extensions/pairing/tests/programs/openvm_init_pairing_miller_step_bn254.rs deleted file mode 100644 index c130859ad8..0000000000 --- a/extensions/pairing/tests/programs/openvm_init_pairing_miller_step_bn254.rs +++ /dev/null @@ -1,4 +0,0 @@ -// This file is automatically generated by cargo openvm. Do not rename or edit. -openvm_algebra_guest::moduli_macros::moduli_init! { "21888242871839275222246405745257275088696311157297823662689037894645226208583" } -openvm_algebra_guest::complex_macros::complex_init! { Bn254Fp2 { mod_idx = 0 } } -openvm_ecc_guest::sw_macros::sw_init! { } diff --git a/extensions/pairing/tests/src/lib.rs b/extensions/pairing/tests/src/lib.rs deleted file mode 100644 index 7f12f01241..0000000000 --- a/extensions/pairing/tests/src/lib.rs +++ /dev/null @@ -1,884 +0,0 @@ -#![allow(non_snake_case)] - -#[cfg(test)] -mod bn254 { - use std::iter; - - use eyre::Result; - use halo2curves_axiom::{ - bn256::{Fq12, Fq2, Fr, G1Affine, G2Affine}, - ff::Field, - }; - use openvm_algebra_circuit::{Fp2Extension, ModularExtension}; - use openvm_algebra_transpiler::{Fp2TranspilerExtension, ModularTranspilerExtension}; - use openvm_circuit::{ - arch::SystemConfig, - utils::{air_test_impl, air_test_with_min_segments}, - }; - use openvm_ecc_circuit::WeierstrassExtension; - use openvm_ecc_guest::{ - algebra::{field::FieldExtension, IntMod}, - AffinePoint, - }; - use openvm_instructions::exe::VmExe; - use openvm_pairing_circuit::{PairingCurve, PairingExtension, Rv32PairingConfig}; - use openvm_pairing_guest::{ - bn254::{BN254_COMPLEX_STRUCT_NAME, BN254_MODULUS}, - halo2curves_shims::bn254::Bn254, - pairing::{EvaluatedLine, FinalExp, LineMulDType, MillerStep, MultiMillerLoop}, - }; - use openvm_pairing_transpiler::PairingTranspilerExtension; - use openvm_rv32im_transpiler::{ - Rv32ITranspilerExtension, Rv32IoTranspilerExtension, Rv32MTranspilerExtension, - }; - use openvm_stark_sdk::{openvm_stark_backend::p3_field::FieldAlgebra, p3_baby_bear::BabyBear}; - use openvm_toolchain_tests::{build_example_program_at_path_with_features, get_programs_dir}; - use openvm_transpiler::{transpiler::Transpiler, FromElf}; - use rand::SeedableRng; - - type F = BabyBear; - - pub fn get_testing_config() -> Rv32PairingConfig { - let primes = [BN254_MODULUS.clone()]; - let complex_struct_names = [BN254_COMPLEX_STRUCT_NAME.to_string()]; - let primes_with_names = complex_struct_names - .into_iter() - .zip(primes.clone()) - .collect::>(); - Rv32PairingConfig { - system: SystemConfig::default().with_continuations(), - base: Default::default(), - mul: Default::default(), - io: Default::default(), - modular: ModularExtension::new(primes.to_vec()), - fp2: Fp2Extension::new(primes_with_names), - weierstrass: WeierstrassExtension::new(vec![]), - pairing: PairingExtension::new(vec![PairingCurve::Bn254]), - } - } - - #[test] - fn test_bn254_fp12_mul() -> Result<()> { - let config = get_testing_config(); - let elf = build_example_program_at_path_with_features( - get_programs_dir!(), - "fp12_mul", - ["bn254"], - &config, - )?; - let openvm_exe = VmExe::from_elf( - elf, - Transpiler::::default() - .with_extension(Rv32ITranspilerExtension) - .with_extension(Rv32MTranspilerExtension) - .with_extension(Rv32IoTranspilerExtension) - .with_extension(PairingTranspilerExtension) - .with_extension(ModularTranspilerExtension) - .with_extension(Fp2TranspilerExtension), - )?; - - let mut rng = rand::rngs::StdRng::seed_from_u64(2); - let f0 = Fq12::random(&mut rng); - let f1 = Fq12::random(&mut rng); - let r = f0 * f1; - - let io = [f0, f1, r] - .into_iter() - .flat_map(|fp12| fp12.to_coeffs()) - .flat_map(|fp2| fp2.to_bytes()) - .map(FieldAlgebra::from_canonical_u8) - .collect::>(); - - air_test_with_min_segments(config, openvm_exe, vec![io], 1); - Ok(()) - } - - #[test] - fn test_bn254_line_functions() -> Result<()> { - let config = get_testing_config(); - let elf = build_example_program_at_path_with_features( - get_programs_dir!(), - "pairing_line", - ["bn254"], - &config, - )?; - let openvm_exe = VmExe::from_elf( - elf, - Transpiler::::default() - .with_extension(Rv32ITranspilerExtension) - .with_extension(Rv32MTranspilerExtension) - .with_extension(Rv32IoTranspilerExtension) - .with_extension(PairingTranspilerExtension) - .with_extension(ModularTranspilerExtension) - .with_extension(Fp2TranspilerExtension), - )?; - - let mut rng = rand::rngs::StdRng::seed_from_u64(2); - let a = G2Affine::random(&mut rng); - let b = G2Affine::random(&mut rng); - let c = G2Affine::random(&mut rng); - - let f = Fq12::random(&mut rng); - let l0 = EvaluatedLine:: { b: a.x, c: a.y }; - let l1 = EvaluatedLine:: { b: b.x, c: b.y }; - - // Test mul_013_by_013 - let r0 = Bn254::mul_013_by_013(&l0, &l1); - let io0 = [l0, l1] - .into_iter() - .flat_map(|fp2| fp2.into_iter()) - .chain(r0) - .flat_map(|fp2| fp2.to_coeffs()) - .flat_map(|fp| fp.to_bytes()) - .map(FieldAlgebra::from_canonical_u8) - .collect::>(); - - // Test mul_by_01234 - let x = [c.x, c.y, b.x, b.y, a.x]; - let r1 = Bn254::mul_by_01234(&f, &x); - let io1 = iter::empty() - .chain(f.to_coeffs()) - .chain(x) - .chain(r1.to_coeffs()) - .flat_map(|fp2| fp2.to_coeffs()) - .flat_map(|fp| fp.to_bytes()) - .map(FieldAlgebra::from_canonical_u8) - .collect::>(); - - let io_all = io0.into_iter().chain(io1).collect::>(); - - air_test_with_min_segments(config, openvm_exe, vec![io_all], 1); - Ok(()) - } - - #[test] - fn test_bn254_miller_step() -> Result<()> { - let config = get_testing_config(); - let elf = build_example_program_at_path_with_features( - get_programs_dir!(), - "pairing_miller_step", - ["bn254"], - &config, - )?; - let openvm_exe = VmExe::from_elf( - elf, - Transpiler::::default() - .with_extension(Rv32ITranspilerExtension) - .with_extension(Rv32MTranspilerExtension) - .with_extension(Rv32IoTranspilerExtension) - .with_extension(PairingTranspilerExtension) - .with_extension(ModularTranspilerExtension) - .with_extension(Fp2TranspilerExtension), - )?; - - let mut rng = rand::rngs::StdRng::seed_from_u64(20); - let S = G2Affine::random(&mut rng); - let Q = G2Affine::random(&mut rng); - - let s = AffinePoint::new(S.x, S.y); - let q = AffinePoint::new(Q.x, Q.y); - - // Test miller_double_step - let (pt, l) = Bn254::miller_double_step(&s); - let io0 = [s.x, s.y, pt.x, pt.y, l.b, l.c] - .into_iter() - .flat_map(|fp| fp.to_bytes()) - .map(FieldAlgebra::from_canonical_u8) - .collect::>(); - - // Test miller_double_and_add_step - let (pt, l0, l1) = Bn254::miller_double_and_add_step(&s, &q); - let io1 = [s.x, s.y, q.x, q.y, pt.x, pt.y, l0.b, l0.c, l1.b, l1.c] - .into_iter() - .flat_map(|fp| fp.to_bytes()) - .map(FieldAlgebra::from_canonical_u8) - .collect::>(); - - let io_all = io0.into_iter().chain(io1).collect::>(); - - air_test_with_min_segments(config, openvm_exe, vec![io_all], 1); - Ok(()) - } - - #[test] - fn test_bn254_miller_loop() -> Result<()> { - let config = get_testing_config(); - let elf = build_example_program_at_path_with_features( - get_programs_dir!(), - "pairing_miller_loop", - ["bn254"], - &config, - )?; - let openvm_exe = VmExe::from_elf( - elf, - Transpiler::::default() - .with_extension(Rv32ITranspilerExtension) - .with_extension(Rv32MTranspilerExtension) - .with_extension(Rv32IoTranspilerExtension) - .with_extension(PairingTranspilerExtension) - .with_extension(ModularTranspilerExtension) - .with_extension(Fp2TranspilerExtension), - )?; - - let S = G1Affine::generator(); - let Q = G2Affine::generator(); - - let mut S_mul = [S * Fr::from(1), S * Fr::from(2)]; - S_mul[1].y = -S_mul[1].y; - let Q_mul = [Q * Fr::from(2), Q * Fr::from(1)]; - - let s = S_mul.map(|s| AffinePoint::new(s.x, s.y)); - let q = Q_mul.map(|p| AffinePoint::new(p.x, p.y)); - - // Test miller_loop - let f = Bn254::multi_miller_loop(&s, &q); - let io0 = s - .into_iter() - .flat_map(|pt| [pt.x, pt.y].into_iter().flat_map(|fp| fp.to_bytes())) - .map(FieldAlgebra::from_canonical_u8) - .collect::>(); - - let io1 = q - .into_iter() - .flat_map(|pt| [pt.x, pt.y].into_iter()) - .chain(f.to_coeffs()) - .flat_map(|fp2| fp2.to_coeffs()) - .flat_map(|fp| fp.to_bytes()) - .map(FieldAlgebra::from_canonical_u8) - .collect::>(); - - let io_all = io0.into_iter().chain(io1).collect::>(); - - air_test_with_min_segments(config, openvm_exe, vec![io_all], 1); - Ok(()) - } - - #[test] - fn test_bn254_pairing_check() -> Result<()> { - let config = get_testing_config(); - let elf = build_example_program_at_path_with_features( - get_programs_dir!(), - "pairing_check", - ["bn254"], - &config, - )?; - let openvm_exe = VmExe::from_elf( - elf, - Transpiler::::default() - .with_extension(Rv32ITranspilerExtension) - .with_extension(Rv32MTranspilerExtension) - .with_extension(Rv32IoTranspilerExtension) - .with_extension(PairingTranspilerExtension) - .with_extension(ModularTranspilerExtension) - .with_extension(Fp2TranspilerExtension), - )?; - - let S = G1Affine::generator(); - let Q = G2Affine::generator(); - - let mut S_mul = [ - G1Affine::from(S * Fr::from(1)), - G1Affine::from(S * Fr::from(2)), - ]; - S_mul[1].y = -S_mul[1].y; - let Q_mul = [ - G2Affine::from(Q * Fr::from(2)), - G2Affine::from(Q * Fr::from(1)), - ]; - - let s = S_mul.map(|s| AffinePoint::new(s.x, s.y)); - let q = Q_mul.map(|p| AffinePoint::new(p.x, p.y)); - - // Gather inputs - let io0 = s - .into_iter() - .flat_map(|pt| [pt.x, pt.y].into_iter().flat_map(|fp| fp.to_bytes())) - .map(FieldAlgebra::from_canonical_u8) - .collect::>(); - - let io1 = q - .into_iter() - .flat_map(|pt| [pt.x, pt.y].into_iter()) - .flat_map(|fp2| fp2.to_coeffs()) - .flat_map(|fp| fp.to_bytes()) - .map(FieldAlgebra::from_canonical_u8) - .collect::>(); - - let io_all = io0.into_iter().chain(io1).collect::>(); - - air_test_with_min_segments(config, openvm_exe, vec![io_all], 1); - Ok(()) - } - - #[test] - fn test_bn254_pairing_check_fallback() -> Result<()> { - let config = get_testing_config(); - let elf = build_example_program_at_path_with_features( - get_programs_dir!(), - "pairing_check_fallback", - ["bn254"], - &config, - )?; - let openvm_exe = VmExe::from_elf( - elf, - Transpiler::::default() - .with_extension(Rv32ITranspilerExtension) - .with_extension(Rv32MTranspilerExtension) - .with_extension(Rv32IoTranspilerExtension) - .with_extension(PairingTranspilerExtension) - .with_extension(ModularTranspilerExtension) - .with_extension(Fp2TranspilerExtension), - )?; - - let S = G1Affine::generator(); - let Q = G2Affine::generator(); - - let mut S_mul = [ - G1Affine::from(S * Fr::from(1)), - G1Affine::from(S * Fr::from(2)), - ]; - S_mul[1].y = -S_mul[1].y; - let Q_mul = [ - G2Affine::from(Q * Fr::from(2)), - G2Affine::from(Q * Fr::from(1)), - ]; - - let s = S_mul.map(|s| AffinePoint::new(s.x, s.y)); - let q = Q_mul.map(|p| AffinePoint::new(p.x, p.y)); - - // Gather inputs - let io0 = s - .into_iter() - .flat_map(|pt| [pt.x, pt.y].into_iter().flat_map(|fp| fp.to_bytes())) - .map(FieldAlgebra::from_canonical_u8) - .collect::>(); - - let io1 = q - .into_iter() - .flat_map(|pt| [pt.x, pt.y].into_iter()) - .flat_map(|fp2| fp2.to_coeffs()) - .flat_map(|fp| fp.to_bytes()) - .map(FieldAlgebra::from_canonical_u8) - .collect::>(); - - let io_all = io0.into_iter().chain(io1).collect::>(); - // Don't run debugger because it's slow - air_test_impl(get_testing_config(), openvm_exe, vec![io_all], 1, false); - Ok(()) - } - - #[test] - fn test_bn254_final_exp_hint() -> Result<()> { - let config = get_testing_config(); - let elf = build_example_program_at_path_with_features( - get_programs_dir!(), - "bn_final_exp_hint", - ["bn254"], - &config, - )?; - let openvm_exe = VmExe::from_elf( - elf, - Transpiler::::default() - .with_extension(Rv32ITranspilerExtension) - .with_extension(Rv32MTranspilerExtension) - .with_extension(Rv32IoTranspilerExtension) - .with_extension(PairingTranspilerExtension) - .with_extension(ModularTranspilerExtension) - .with_extension(Fp2TranspilerExtension), - )?; - - let P = G1Affine::generator(); - let Q = G2Affine::generator(); - let ps = vec![AffinePoint::new(P.x, P.y), AffinePoint::new(P.x, -P.y)]; - let qs = vec![AffinePoint::new(Q.x, Q.y), AffinePoint::new(Q.x, Q.y)]; - let f = Bn254::multi_miller_loop(&ps, &qs); - let (c, s) = Bn254::final_exp_hint(&f); - let ps = ps - .into_iter() - .map(|pt| { - let [x, y] = [pt.x, pt.y] - .map(|x| openvm_pairing_guest::bn254::Fp::from_le_bytes(&x.to_bytes())); - AffinePoint::new(x, y) - }) - .collect::>(); - let qs = qs - .into_iter() - .map(|pt| { - let [x, y] = [pt.x, pt.y] - .map(|x| openvm_pairing_guest::bn254::Fp2::from_bytes(&x.to_bytes())); - AffinePoint::new(x, y) - }) - .collect::>(); - let [c, s] = [c, s].map(|x| openvm_pairing_guest::bn254::Fp12::from_bytes(&x.to_bytes())); - let io = (ps, qs, (c, s)); - let io = openvm::serde::to_vec(&io).unwrap(); - let io = io - .into_iter() - .flat_map(|w| w.to_le_bytes()) - .map(F::from_canonical_u8) - .collect(); - air_test_with_min_segments(config, openvm_exe, vec![io], 1); - Ok(()) - } -} - -#[cfg(test)] -mod bls12_381 { - use eyre::Result; - use halo2curves_axiom::{ - bls12_381::{Fq12, Fq2, Fr, G1Affine, G2Affine}, - ff::Field, - }; - use num_bigint::BigUint; - use num_traits::{self, FromPrimitive}; - use openvm_algebra_circuit::{Fp2Extension, ModularExtension}; - use openvm_algebra_transpiler::{Fp2TranspilerExtension, ModularTranspilerExtension}; - use openvm_circuit::{ - arch::{instructions::exe::VmExe, SystemConfig}, - utils::{air_test, air_test_impl, air_test_with_min_segments}, - }; - use openvm_ecc_circuit::{CurveConfig, Rv32WeierstrassConfig, WeierstrassExtension}; - use openvm_ecc_guest::{ - algebra::{field::FieldExtension, IntMod}, - AffinePoint, - }; - use openvm_ecc_transpiler::EccTranspilerExtension; - use openvm_pairing_circuit::{PairingCurve, PairingExtension, Rv32PairingConfig}; - use openvm_pairing_guest::{ - bls12_381::{ - BLS12_381_COMPLEX_STRUCT_NAME, BLS12_381_ECC_STRUCT_NAME, BLS12_381_MODULUS, - BLS12_381_ORDER, - }, - halo2curves_shims::bls12_381::Bls12_381, - pairing::{EvaluatedLine, FinalExp, LineMulMType, MillerStep, MultiMillerLoop}, - }; - use openvm_pairing_transpiler::PairingTranspilerExtension; - use openvm_rv32im_transpiler::{ - Rv32ITranspilerExtension, Rv32IoTranspilerExtension, Rv32MTranspilerExtension, - }; - use openvm_stark_sdk::{openvm_stark_backend::p3_field::FieldAlgebra, p3_baby_bear::BabyBear}; - use openvm_toolchain_tests::{build_example_program_at_path_with_features, get_programs_dir}; - use openvm_transpiler::{transpiler::Transpiler, FromElf}; - use rand::SeedableRng; - - type F = BabyBear; - - pub fn get_testing_config() -> Rv32PairingConfig { - let primes = [BLS12_381_MODULUS.clone()]; - let complex_struct_names = [BLS12_381_COMPLEX_STRUCT_NAME.to_string()]; - let primes_with_names = complex_struct_names - .into_iter() - .zip(primes.clone()) - .collect::>(); - Rv32PairingConfig { - system: SystemConfig::default().with_continuations(), - base: Default::default(), - mul: Default::default(), - io: Default::default(), - modular: ModularExtension::new(primes.to_vec()), - fp2: Fp2Extension::new(primes_with_names), - weierstrass: WeierstrassExtension::new(vec![]), - pairing: PairingExtension::new(vec![PairingCurve::Bls12_381]), - } - } - - #[test] - fn test_bls_ec() -> Result<()> { - let curve = CurveConfig { - struct_name: BLS12_381_ECC_STRUCT_NAME.to_string(), - modulus: BLS12_381_MODULUS.clone(), - scalar: BLS12_381_ORDER.clone(), - a: BigUint::ZERO, - b: BigUint::from_u8(4).unwrap(), - }; - let config = Rv32WeierstrassConfig::new(vec![curve]); - let elf = build_example_program_at_path_with_features( - get_programs_dir!(), - "bls_ec", - ["bls12_381"], - &config, - )?; - let openvm_exe = VmExe::from_elf( - elf, - Transpiler::::default() - .with_extension(Rv32ITranspilerExtension) - .with_extension(Rv32MTranspilerExtension) - .with_extension(Rv32IoTranspilerExtension) - .with_extension(EccTranspilerExtension) - .with_extension(ModularTranspilerExtension), - )?; - air_test(config, openvm_exe); - Ok(()) - } - - #[test] - fn test_bls12_381_fp12_mul() -> Result<()> { - let config = get_testing_config(); - let elf = build_example_program_at_path_with_features( - get_programs_dir!(), - "fp12_mul", - ["bls12_381"], - &config, - )?; - let openvm_exe = VmExe::from_elf( - elf, - Transpiler::::default() - .with_extension(Rv32ITranspilerExtension) - .with_extension(Rv32MTranspilerExtension) - .with_extension(Rv32IoTranspilerExtension) - .with_extension(PairingTranspilerExtension) - .with_extension(ModularTranspilerExtension) - .with_extension(Fp2TranspilerExtension), - )?; - - let mut rng = rand::rngs::StdRng::seed_from_u64(50); - let f0 = Fq12::random(&mut rng); - let f1 = Fq12::random(&mut rng); - let r = f0 * f1; - - let io = [f0, f1, r] - .into_iter() - .flat_map(|fp12| fp12.to_coeffs()) - .flat_map(|fp2| fp2.to_bytes()) - .map(FieldAlgebra::from_canonical_u8) - .collect::>(); - - air_test_with_min_segments(config, openvm_exe, vec![io], 1); - Ok(()) - } - - #[test] - fn test_bls12_381_line_functions() -> Result<()> { - let config = get_testing_config(); - let elf = build_example_program_at_path_with_features( - get_programs_dir!(), - "pairing_line", - ["bls12_381"], - &config, - )?; - let openvm_exe = VmExe::from_elf( - elf, - Transpiler::::default() - .with_extension(Rv32ITranspilerExtension) - .with_extension(Rv32MTranspilerExtension) - .with_extension(Rv32IoTranspilerExtension) - .with_extension(PairingTranspilerExtension) - .with_extension(ModularTranspilerExtension) - .with_extension(Fp2TranspilerExtension), - )?; - - let mut rng = rand::rngs::StdRng::seed_from_u64(5); - let a = G2Affine::random(&mut rng); - let b = G2Affine::random(&mut rng); - let c = G2Affine::random(&mut rng); - - let f = Fq12::random(&mut rng); - let l0 = EvaluatedLine:: { b: a.x, c: a.y }; - let l1 = EvaluatedLine:: { b: b.x, c: b.y }; - - // Test mul_023_by_023 - let r0 = Bls12_381::mul_023_by_023(&l0, &l1); - let io0 = [l0, l1] - .into_iter() - .flat_map(|fp2| fp2.into_iter()) - .chain(r0) - .flat_map(|fp2| fp2.to_coeffs()) - .flat_map(|fp| fp.to_bytes()) - .map(FieldAlgebra::from_canonical_u8) - .collect::>(); - - // Test mul_by_02345 - let x = [c.x, c.y, b.x, b.y, a.x]; - let r1 = Bls12_381::mul_by_02345(&f, &x); - let io1 = f - .to_coeffs() - .into_iter() - .chain(x) - .chain(r1.to_coeffs()) - .flat_map(|fp2| fp2.to_coeffs()) - .flat_map(|fp| fp.to_bytes()) - .map(FieldAlgebra::from_canonical_u8) - .collect::>(); - - let io_all = io0.into_iter().chain(io1).collect::>(); - - air_test_with_min_segments(config, openvm_exe, vec![io_all], 1); - Ok(()) - } - - #[test] - fn test_bls12_381_miller_step() -> Result<()> { - let config = get_testing_config(); - let elf = build_example_program_at_path_with_features( - get_programs_dir!(), - "pairing_miller_step", - ["bls12_381"], - &config, - )?; - let openvm_exe = VmExe::from_elf( - elf, - Transpiler::::default() - .with_extension(Rv32ITranspilerExtension) - .with_extension(Rv32MTranspilerExtension) - .with_extension(Rv32IoTranspilerExtension) - .with_extension(PairingTranspilerExtension) - .with_extension(ModularTranspilerExtension) - .with_extension(Fp2TranspilerExtension), - )?; - - let mut rng = rand::rngs::StdRng::seed_from_u64(88); - let S = G2Affine::random(&mut rng); - let Q = G2Affine::random(&mut rng); - - let s = AffinePoint::new(S.x, S.y); - let q = AffinePoint::new(Q.x, Q.y); - - // Test miller_double_step - let (pt, l) = Bls12_381::miller_double_step(&s); - let io0 = [s.x, s.y, pt.x, pt.y, l.b, l.c] - .into_iter() - .flat_map(|fp| fp.to_bytes()) - .map(FieldAlgebra::from_canonical_u8) - .collect::>(); - - // Test miller_double_and_add_step - let (pt, l0, l1) = Bls12_381::miller_double_and_add_step(&s, &q); - let io1 = [s.x, s.y, q.x, q.y, pt.x, pt.y, l0.b, l0.c, l1.b, l1.c] - .into_iter() - .flat_map(|fp| fp.to_bytes()) - .map(FieldAlgebra::from_canonical_u8) - .collect::>(); - - let io_all = io0.into_iter().chain(io1).collect::>(); - - air_test_with_min_segments(config, openvm_exe, vec![io_all], 1); - Ok(()) - } - - #[test] - fn test_bls12_381_miller_loop() -> Result<()> { - let config = get_testing_config(); - let elf = build_example_program_at_path_with_features( - get_programs_dir!(), - "pairing_miller_loop", - ["bls12_381"], - &config, - )?; - let openvm_exe = VmExe::from_elf( - elf, - Transpiler::::default() - .with_extension(Rv32ITranspilerExtension) - .with_extension(Rv32MTranspilerExtension) - .with_extension(Rv32IoTranspilerExtension) - .with_extension(PairingTranspilerExtension) - .with_extension(ModularTranspilerExtension) - .with_extension(Fp2TranspilerExtension), - )?; - - let S = G1Affine::generator(); - let Q = G2Affine::generator(); - - let mut S_mul = [ - G1Affine::from(S * Fr::from(1)), - G1Affine::from(S * Fr::from(2)), - ]; - S_mul[1].y = -S_mul[1].y; - let Q_mul = [ - G2Affine::from(Q * Fr::from(2)), - G2Affine::from(Q * Fr::from(1)), - ]; - - let s = S_mul.map(|s| AffinePoint::new(s.x, s.y)); - let q = Q_mul.map(|p| AffinePoint::new(p.x, p.y)); - - // Test miller_loop - let f = Bls12_381::multi_miller_loop(&s, &q); - let io0 = s - .into_iter() - .flat_map(|pt| [pt.x, pt.y].into_iter().flat_map(|fp| fp.to_bytes())) - .map(FieldAlgebra::from_canonical_u8) - .collect::>(); - - let io1 = q - .into_iter() - .flat_map(|pt| [pt.x, pt.y].into_iter()) - .chain(f.to_coeffs()) - .flat_map(|fp2| fp2.to_coeffs()) - .flat_map(|fp| fp.to_bytes()) - .map(FieldAlgebra::from_canonical_u8) - .collect::>(); - - let io_all = io0.into_iter().chain(io1).collect::>(); - - air_test_with_min_segments(config, openvm_exe, vec![io_all], 1); - Ok(()) - } - - #[test] - fn test_bls12_381_pairing_check() -> Result<()> { - let config = get_testing_config(); - let elf = build_example_program_at_path_with_features( - get_programs_dir!(), - "pairing_check", - ["bls12_381"], - &config, - )?; - let openvm_exe = VmExe::from_elf( - elf, - Transpiler::::default() - .with_extension(Rv32ITranspilerExtension) - .with_extension(Rv32MTranspilerExtension) - .with_extension(Rv32IoTranspilerExtension) - .with_extension(PairingTranspilerExtension) - .with_extension(ModularTranspilerExtension) - .with_extension(Fp2TranspilerExtension), - )?; - - let S = G1Affine::generator(); - let Q = G2Affine::generator(); - - let mut S_mul = [ - G1Affine::from(S * Fr::from(1)), - G1Affine::from(S * Fr::from(2)), - ]; - S_mul[1].y = -S_mul[1].y; - let Q_mul = [ - G2Affine::from(Q * Fr::from(2)), - G2Affine::from(Q * Fr::from(1)), - ]; - let s = S_mul.map(|s| AffinePoint::new(s.x, s.y)); - let q = Q_mul.map(|p| AffinePoint::new(p.x, p.y)); - - // Gather inputs - let io0 = s - .into_iter() - .flat_map(|pt| [pt.x, pt.y].into_iter().flat_map(|fp| fp.to_bytes())) - .map(FieldAlgebra::from_canonical_u8) - .collect::>(); - - let io1 = q - .into_iter() - .flat_map(|pt| [pt.x, pt.y].into_iter()) - .flat_map(|fp2| fp2.to_coeffs()) - .flat_map(|fp| fp.to_bytes()) - .map(FieldAlgebra::from_canonical_u8) - .collect::>(); - - let io_all = io0.into_iter().chain(io1).collect::>(); - - air_test_with_min_segments(config, openvm_exe, vec![io_all], 1); - Ok(()) - } - - #[ignore] - #[test] - fn test_bls12_381_pairing_check_fallback() -> Result<()> { - let config = get_testing_config(); - let elf = build_example_program_at_path_with_features( - get_programs_dir!(), - "pairing_check_fallback", - ["bls12_381"], - &config, - )?; - let openvm_exe = VmExe::from_elf( - elf, - Transpiler::::default() - .with_extension(Rv32ITranspilerExtension) - .with_extension(Rv32MTranspilerExtension) - .with_extension(Rv32IoTranspilerExtension) - .with_extension(PairingTranspilerExtension) - .with_extension(ModularTranspilerExtension) - .with_extension(Fp2TranspilerExtension), - )?; - - let S = G1Affine::generator(); - let Q = G2Affine::generator(); - - let mut S_mul = [ - G1Affine::from(S * Fr::from(1)), - G1Affine::from(S * Fr::from(2)), - ]; - S_mul[1].y = -S_mul[1].y; - let Q_mul = [ - G2Affine::from(Q * Fr::from(2)), - G2Affine::from(Q * Fr::from(1)), - ]; - let s = S_mul.map(|s| AffinePoint::new(s.x, s.y)); - let q = Q_mul.map(|p| AffinePoint::new(p.x, p.y)); - - // Gather inputs - let io0 = s - .into_iter() - .flat_map(|pt| [pt.x, pt.y].into_iter().flat_map(|fp| fp.to_bytes())) - .map(FieldAlgebra::from_canonical_u8) - .collect::>(); - - let io1 = q - .into_iter() - .flat_map(|pt| [pt.x, pt.y].into_iter()) - .flat_map(|fp2| fp2.to_coeffs()) - .flat_map(|fp| fp.to_bytes()) - .map(FieldAlgebra::from_canonical_u8) - .collect::>(); - - let io_all = io0.into_iter().chain(io1).collect::>(); - // Don't run debugger because it's slow - air_test_impl(get_testing_config(), openvm_exe, vec![io_all], 1, false); - Ok(()) - } - - #[test] - fn test_bls12_381_final_exp_hint() -> Result<()> { - let config = get_testing_config(); - let elf = build_example_program_at_path_with_features( - get_programs_dir!(), - "bls_final_exp_hint", - ["bls12_381"], - &config, - )?; - let openvm_exe = VmExe::from_elf( - elf, - Transpiler::::default() - .with_extension(Rv32ITranspilerExtension) - .with_extension(Rv32MTranspilerExtension) - .with_extension(Rv32IoTranspilerExtension) - .with_extension(PairingTranspilerExtension) - .with_extension(ModularTranspilerExtension) - .with_extension(Fp2TranspilerExtension), - )?; - - let P = G1Affine::generator(); - let Q = G2Affine::generator(); - let ps = vec![AffinePoint::new(P.x, P.y), AffinePoint::new(P.x, -P.y)]; - let qs = vec![AffinePoint::new(Q.x, Q.y), AffinePoint::new(Q.x, Q.y)]; - let f = Bls12_381::multi_miller_loop(&ps, &qs); - let (c, s) = Bls12_381::final_exp_hint(&f); - let ps = ps - .into_iter() - .map(|pt| { - let [x, y] = [pt.x, pt.y] - .map(|x| openvm_pairing_guest::bls12_381::Fp::from_le_bytes(&x.to_bytes())); - AffinePoint::new(x, y) - }) - .collect::>(); - let qs = qs - .into_iter() - .map(|pt| { - let [x, y] = [pt.x, pt.y] - .map(|x| openvm_pairing_guest::bls12_381::Fp2::from_bytes(&x.to_bytes())); - AffinePoint::new(x, y) - }) - .collect::>(); - let [c, s] = - [c, s].map(|x| openvm_pairing_guest::bls12_381::Fp12::from_bytes(&x.to_bytes())); - let io = (ps, qs, (c, s)); - let io = openvm::serde::to_vec(&io).unwrap(); - let io = io - .into_iter() - .flat_map(|w| w.to_le_bytes()) - .map(F::from_canonical_u8) - .collect(); - air_test_with_min_segments(config, openvm_exe, vec![io], 1); - Ok(()) - } -} From c385dd5b6df71e4bbfd56b151d09763754418c92 Mon Sep 17 00:00:00 2001 From: Avaneesh Kulkarni Date: Wed, 30 Apr 2025 17:36:14 -0400 Subject: [PATCH 18/41] Delete bigint tests --- extensions/bigint/tests/Cargo.toml | 23 --- extensions/bigint/tests/programs/Cargo.toml | 25 --- .../programs/examples/matrix-power-signed.rs | 142 ------------------ .../examples/matrix-power-unsigned.rs | 136 ----------------- extensions/bigint/tests/src/lib.rs | 50 ------ 5 files changed, 376 deletions(-) delete mode 100644 extensions/bigint/tests/Cargo.toml delete mode 100644 extensions/bigint/tests/programs/Cargo.toml delete mode 100644 extensions/bigint/tests/programs/examples/matrix-power-signed.rs delete mode 100644 extensions/bigint/tests/programs/examples/matrix-power-unsigned.rs delete mode 100644 extensions/bigint/tests/src/lib.rs diff --git a/extensions/bigint/tests/Cargo.toml b/extensions/bigint/tests/Cargo.toml deleted file mode 100644 index ae32b3e7ef..0000000000 --- a/extensions/bigint/tests/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -[package] -name = "openvm-bigint-integration-tests" -description = "Integration tests for the OpenVM bigint extension" -version.workspace = true -authors.workspace = true -edition.workspace = true -homepage.workspace = true -repository.workspace = true - -[dependencies] -openvm-instructions = { workspace = true } -openvm-stark-sdk.workspace = true -openvm-circuit = { workspace = true, features = ["test-utils"] } -openvm-transpiler.workspace = true -openvm-bigint-transpiler.workspace = true -openvm-bigint-circuit.workspace = true -openvm-rv32im-transpiler.workspace = true -openvm-toolchain-tests = { path = "../../../crates/toolchain/tests" } -eyre.workspace = true - -[features] -default = ["parallel"] -parallel = ["openvm-circuit/parallel"] diff --git a/extensions/bigint/tests/programs/Cargo.toml b/extensions/bigint/tests/programs/Cargo.toml deleted file mode 100644 index 5b7ee53bf7..0000000000 --- a/extensions/bigint/tests/programs/Cargo.toml +++ /dev/null @@ -1,25 +0,0 @@ -[workspace] -[package] -name = "openvm-bigint-test-programs" -version = "0.0.0" -edition = "2021" - -[dependencies] -openvm = { path = "../../../../crates/toolchain/openvm" } -openvm-platform = { path = "../../../../crates/toolchain/platform" } - -openvm-bigint-guest = { path = "../../guest" } -serde = { version = "1.0", default-features = false, features = [ - "alloc", - "derive", -] } - - -[features] -default = [] -std = ["serde/std", "openvm/std"] - -[profile.release] -panic = "abort" -lto = "thin" # turn on lto = fat to decrease binary size, but this optimizes out some missing extern links so we shouldn't use it for testing -# strip = "symbols" diff --git a/extensions/bigint/tests/programs/examples/matrix-power-signed.rs b/extensions/bigint/tests/programs/examples/matrix-power-signed.rs deleted file mode 100644 index 9684fe2a35..0000000000 --- a/extensions/bigint/tests/programs/examples/matrix-power-signed.rs +++ /dev/null @@ -1,142 +0,0 @@ -#![cfg_attr(not(feature = "std"), no_main)] -#![cfg_attr(not(feature = "std"), no_std)] -#![allow(clippy::needless_range_loop)] -#![allow(clippy::eq_op)] -use core::array; - -use openvm::io::print; -use openvm_bigint_guest::I256; -openvm::entry!(main); - -const N: usize = 16; -type Matrix = [[I256; N]; N]; - -pub fn get_matrix(val: i8) -> Matrix { - array::from_fn(|_| array::from_fn(|_| I256::from_i8(val))) -} - -pub fn mult(a: &Matrix, b: &Matrix) -> Matrix { - let mut c = get_matrix(0); - for i in 0..N { - for j in 0..N { - for k in 0..N { - c[i][j] += &a[i][k] * &b[k][j]; - } - } - } - c -} - -pub fn get_identity_matrix() -> Matrix { - let mut res = get_matrix(0); - for i in 0..N { - res[i][i] = I256::from_i8(1); - } - res -} - -/// Computes base^exp using binary exponentiation. -pub fn matrix_exp(mut base: Matrix, mut exp: I256) -> Matrix { - let mut result = get_identity_matrix(); - let one = I256::from_i8(1); - while exp > I256::from_i8(0) { - if (&exp & &one) == one { - result = mult(&result, &base); - } - base = mult(&base, &base); - exp >>= &one; - } - result -} - -pub fn main() { - let a: Matrix = get_identity_matrix(); - let c = matrix_exp(a, I256::from_i32(1234567)); - if c != get_identity_matrix() { - print("FAIL: the resulting matrix should have been the identity matrix"); - panic!(); - } - - let one = I256::from_i8(1); - let neg_one = I256::from_i8(-1); - let zero = I256::from_i8(0); - - let a: Matrix = get_matrix(-1); - let c = matrix_exp(a, I256::from_i8(51)); - let two_to_200 = &neg_one << &I256::from_i32(200); - - for i in 0..N { - for j in 0..N { - if c[i][j] != two_to_200 { - print("FAIL: the resulting matrix is incorrect"); - panic!(); - } - } - } - - // Shift right tests - if &two_to_200 >> &I256::from_i32(200) != neg_one { - print("FAIL: -2^200 >> 200 == -1 test failed"); - panic!(); - } - if &two_to_200 >> &I256::from_i32(201) != neg_one { - print("FAIL: -2^200 >> 201 == -1 test failed"); - panic!(); - } - - if &neg_one >> &I256::from_i32(200) != neg_one { - print("FAIL: -1 >> 200 == -1 test failed"); - panic!(); - } - - // Xor tests - if &two_to_200 ^ &two_to_200 != zero { - print("FAIL: -2^200 ^ -2^200 == 0 test failed"); - panic!(); - } - - if &two_to_200 ^ &one != &two_to_200 + &one { - print("FAIL: -2^200 ^ 1 == -2^200 + 1 test failed"); - panic!(); - } - - // Or tests - - if &one | &one != one { - print("FAIL: 1 | 1 == 1 test failed"); - panic!(); - } - - if &two_to_200 | &one != &two_to_200 + &one { - print("FAIL: -2^200 | 1 = -2^200 + 1 test failed"); - panic!(); - } - - // Other tests - if &zero - &one >= zero { - print("FAIL: 0 - 1 <= 0 test failed"); - panic!(); - } - - if neg_one >= zero { - print("FAIL: -1 <= 0 test failed"); - panic!(); - } - - if &zero - &one + &one != zero { - print("FAIL: 0 - 1 + 1 == 0 test failed (should have wrapped)"); - panic!(); - } - - if &one << &I256::from_i32(256) != one { - print("FAIL: 1 << 256 == 1 test failed"); - panic!(); - } - - if two_to_200.clone() != two_to_200 { - print("FAIL: 2^200 clone test failed"); - panic!(); - } - - print("PASS"); -} diff --git a/extensions/bigint/tests/programs/examples/matrix-power-unsigned.rs b/extensions/bigint/tests/programs/examples/matrix-power-unsigned.rs deleted file mode 100644 index b128dbe4f9..0000000000 --- a/extensions/bigint/tests/programs/examples/matrix-power-unsigned.rs +++ /dev/null @@ -1,136 +0,0 @@ -#![cfg_attr(not(feature = "std"), no_main)] -#![cfg_attr(not(feature = "std"), no_std)] -#![allow(clippy::needless_range_loop)] -#![allow(clippy::eq_op)] - -openvm::entry!(main); -use core::array; - -use openvm::io::print; -use openvm_bigint_guest::U256; - -const N: usize = 16; -type Matrix = [[U256; N]; N]; - -pub fn get_matrix(val: u8) -> Matrix { - array::from_fn(|_| array::from_fn(|_| U256::from_u8(val))) -} - -pub fn mult(a: &Matrix, b: &Matrix) -> Matrix { - let mut c = get_matrix(0); - for i in 0..N { - for j in 0..N { - for k in 0..N { - c[i][j] += &a[i][k] * &b[k][j]; - } - } - } - c -} - -pub fn get_identity_matrix() -> Matrix { - let mut res = get_matrix(0); - for i in 0..N { - res[i][i] = U256::from_u8(1); - } - res -} - -/// Computes base^exp using binary exponentiation. -pub fn bin_exp(mut base: Matrix, mut exp: U256) -> Matrix { - let mut result = get_identity_matrix(); - let one = U256::from_u8(1); - while exp > U256::from_u8(0) { - if (&exp & &one) == one { - result = mult(&result, &base); - } - base = mult(&base, &base); - exp >>= &one; - } - result -} - -pub fn main() { - let a: Matrix = get_identity_matrix(); - let c = bin_exp(a, U256::from_u32(1234567)); - if c != get_identity_matrix() { - print("FAIL: the resulting matrix should have been the identity matrix"); - panic!(); - } - - let one = U256::from_u8(1); - let zero = U256::from_u8(0); - - let a: Matrix = get_matrix(1); - let c = bin_exp(a, U256::from_u8(51)); - let two_to_200 = &one << &U256::from_u8(200); - - for i in 0..N { - for j in 0..N { - if c[i][j] != two_to_200 { - print("FAIL: the resulting matrix is incorrect"); - panic!(); - } - } - } - - // Shift right tests - if &two_to_200 >> &U256::from_u8(200) != one { - print("FAIL: 2^200 >> 200 == 1 test failed"); - panic!(); - } - if &two_to_200 >> &U256::from_u8(201) != zero { - print("FAIL: 2^200 >> 201 == 0 test failed"); - panic!(); - } - - // Xor tests - if &two_to_200 ^ &two_to_200 != zero { - print("FAIL: 2^200 ^ 2^200 == 0 test failed"); - panic!(); - } - - if &two_to_200 ^ &one != &two_to_200 + &one { - print("FAIL: 2^200 ^ 1 == 2^200 + 1 test failed"); - panic!(); - } - - // Or tests - if &one | &one != one { - print("FAIL: 1 | 1 == 1 test failed"); - panic!(); - } - - if &two_to_200 | &one != &two_to_200 + &one { - print("FAIL: 2^200 | 1 = 2^200 + 1 test failed"); - panic!(); - } - - // Other tests - if &zero - &one <= zero { - print("FAIL: 0 - 1 > 0 test failed (should have wrapped)"); - panic!(); - } - - if &zero - &one + &one != zero { - print("FAIL: 0 - 1 + 1 == 0 test failed (should have wrapped)"); - panic!(); - } - - if &one << &U256::from_u32(256) != one { - print("FAIL: 1 << 256 == 1 test failed"); - panic!(); - } - - if &one << &U256::from_u32(261) != U256::from_u8(32) { - print("FAIL: 1 << 261 == 32 test failed"); - panic!(); - } - - if two_to_200.clone() != two_to_200 { - print("FAIL: 2^200 clone test failed"); - panic!(); - } - - print("PASS"); -} diff --git a/extensions/bigint/tests/src/lib.rs b/extensions/bigint/tests/src/lib.rs deleted file mode 100644 index 25db54c2b7..0000000000 --- a/extensions/bigint/tests/src/lib.rs +++ /dev/null @@ -1,50 +0,0 @@ -#[cfg(test)] -mod tests { - use eyre::Result; - use openvm_bigint_circuit::Int256Rv32Config; - use openvm_bigint_transpiler::Int256TranspilerExtension; - use openvm_circuit::utils::air_test; - use openvm_instructions::exe::VmExe; - use openvm_rv32im_transpiler::{ - Rv32ITranspilerExtension, Rv32IoTranspilerExtension, Rv32MTranspilerExtension, - }; - use openvm_stark_sdk::p3_baby_bear::BabyBear; - use openvm_toolchain_tests::{build_example_program_at_path, get_programs_dir}; - use openvm_transpiler::{transpiler::Transpiler, FromElf}; - - type F = BabyBear; - - #[test] - fn test_matrix_power() -> Result<()> { - let config = Int256Rv32Config::default(); - let elf = - build_example_program_at_path(get_programs_dir!(), "matrix-power-unsigned", &config)?; - let openvm_exe = VmExe::from_elf( - elf, - Transpiler::::default() - .with_extension(Rv32ITranspilerExtension) - .with_extension(Rv32MTranspilerExtension) - .with_extension(Rv32IoTranspilerExtension) - .with_extension(Int256TranspilerExtension), - )?; - air_test(config, openvm_exe); - Ok(()) - } - - #[test] - fn test_matrix_power_signed() -> Result<()> { - let config = Int256Rv32Config::default(); - let elf = - build_example_program_at_path(get_programs_dir!(), "matrix-power-signed", &config)?; - let openvm_exe = VmExe::from_elf( - elf, - Transpiler::::default() - .with_extension(Rv32ITranspilerExtension) - .with_extension(Rv32MTranspilerExtension) - .with_extension(Rv32IoTranspilerExtension) - .with_extension(Int256TranspilerExtension), - )?; - air_test(config, openvm_exe); - Ok(()) - } -} From 7fc84ab1f63ac1f93899731501bd16e8ec871a07 Mon Sep 17 00:00:00 2001 From: Avaneesh Kulkarni Date: Wed, 30 Apr 2025 17:37:19 -0400 Subject: [PATCH 19/41] Remove pairing library's README --- extensions/pairing/README.md | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 extensions/pairing/README.md diff --git a/extensions/pairing/README.md b/extensions/pairing/README.md deleted file mode 100644 index 30ca7e1888..0000000000 --- a/extensions/pairing/README.md +++ /dev/null @@ -1,4 +0,0 @@ -Optimal Ate Pairing -== - -See [this HackMD](https://hackmd.io/@openvm/BkrrE_un1g) for details. From 5585dca004e97160d7afda82fe812eece247c32f Mon Sep 17 00:00:00 2001 From: Avaneesh Kulkarni Date: Wed, 30 Apr 2025 18:51:01 -0400 Subject: [PATCH 20/41] Remove pairing tests from Cargo.toml --- Cargo.lock | 3201 ++++++++++++++++++++++++++++++++++++++-------------- Cargo.toml | 2 - 2 files changed, 2380 insertions(+), 823 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7031868f8f..f35f40abd8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -51,11 +51,12 @@ dependencies = [ [[package]] name = "ahash" -version = "0.8.11" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" dependencies = [ "cfg-if", + "getrandom 0.3.3", "once_cell", "version_check", "zerocopy", @@ -76,28 +77,61 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" +[[package]] +name = "alloy-eip2124" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "741bdd7499908b3aa0b159bba11e71c8cddd009a2c2eb7a06e825f1ec87900a5" +dependencies = [ + "alloy-primitives 1.1.1", + "alloy-rlp", + "crc", + "serde", + "thiserror 2.0.12", +] + [[package]] name = "alloy-eip2930" -version = "0.1.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0069cf0642457f87a01a014f6dc29d5d893cd4fd8fddf0c3cdfad1bb3ebafc41" +checksum = "7b82752a889170df67bbb36d42ca63c531eb16274f0d7299ae2a680facba17bd" dependencies = [ - "alloy-primitives 0.8.25", + "alloy-primitives 1.1.1", "alloy-rlp", "serde", ] [[package]] name = "alloy-eip7702" -version = "0.4.2" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c986539255fb839d1533c128e190e557e52ff652c9ef62939e233a81dd93f7e" +checksum = "9d4769c6ffddca380b0070d71c8b7f30bed375543fe76bb2f74ec0acf4b7cd16" dependencies = [ - "alloy-primitives 0.8.25", + "alloy-primitives 1.1.1", "alloy-rlp", - "derive_more 1.0.0", "k256", "serde", + "thiserror 2.0.12", +] + +[[package]] +name = "alloy-eips" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "609515c1955b33af3d78d26357540f68c5551a90ef58fd53def04f2aa074ec43" +dependencies = [ + "alloy-eip2124", + "alloy-eip2930", + "alloy-eip7702", + "alloy-primitives 1.1.1", + "alloy-rlp", + "alloy-serde", + "auto_impl", + "c-kzg", + "derive_more 2.0.1", + "either", + "serde", + "sha2 0.10.9", ] [[package]] @@ -121,7 +155,7 @@ dependencies = [ "bytes", "cfg-if", "const-hex", - "derive_more 0.99.19", + "derive_more 0.99.20", "hex-literal", "itoa", "ruint", @@ -138,7 +172,7 @@ dependencies = [ "bytes", "cfg-if", "const-hex", - "derive_more 0.99.19", + "derive_more 0.99.20", "hex-literal", "itoa", "ruint", @@ -157,14 +191,41 @@ dependencies = [ "const-hex", "derive_more 2.0.1", "foldhash", - "hashbrown 0.15.2", - "indexmap 2.7.1", + "hashbrown 0.15.3", + "indexmap 2.9.0", "itoa", "k256", "keccak-asm", "paste", "proptest", - "rand", + "rand 0.8.5", + "ruint", + "rustc-hash 2.1.1", + "serde", + "sha3", + "tiny-keccak", +] + +[[package]] +name = "alloy-primitives" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd197479be097918475c992db778f9989d828b1c2efc37dcc7655409b1eaac7a" +dependencies = [ + "alloy-rlp", + "bytes", + "cfg-if", + "const-hex", + "derive_more 2.0.1", + "foldhash", + "hashbrown 0.15.3", + "indexmap 2.9.0", + "itoa", + "k256", + "keccak-asm", + "paste", + "proptest", + "rand 0.9.1", "ruint", "rustc-hash 2.1.1", "serde", @@ -174,9 +235,9 @@ dependencies = [ [[package]] name = "alloy-rlp" -version = "0.3.11" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6c1d995bff8d011f7cd6c81820d51825e6e06d6db73914c1630ecf544d83d6" +checksum = "5f70d83b765fdc080dbcd4f4db70d8d23fe4761f2f02ebfa9146b833900634b4" dependencies = [ "alloy-rlp-derive", "arrayvec", @@ -185,13 +246,24 @@ dependencies = [ [[package]] name = "alloy-rlp-derive" -version = "0.3.11" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a40e1ef334153322fd878d07e86af7a529bcb86b2439525920a88eba87bcf943" +checksum = "64b728d511962dda67c1bc7ea7c03736ec275ed2cf4c35d9585298ac9ccf3b73" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", +] + +[[package]] +name = "alloy-serde" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4dba6ff08916bc0a9cbba121ce21f67c0b554c39cf174bc7b9df6c651bd3c3b" +dependencies = [ + "alloy-primitives 1.1.1", + "serde", + "serde_json", ] [[package]] @@ -205,7 +277,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -218,11 +290,11 @@ dependencies = [ "alloy-sol-macro-input", "const-hex", "heck", - "indexmap 2.7.1", + "indexmap 2.9.0", "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", "syn-solidity", "tiny-keccak", ] @@ -241,7 +313,7 @@ dependencies = [ "proc-macro2", "quote", "serde_json", - "syn 2.0.98", + "syn 2.0.101", "syn-solidity", ] @@ -252,7 +324,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d162f8524adfdfb0e4bd0505c734c985f3e2474eb022af32eef0d52a4f3935c" dependencies = [ "serde", - "winnow 0.7.3", + "winnow 0.7.10", ] [[package]] @@ -350,9 +422,24 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.96" +version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b964d184e89d9b6b67dd2715bc8e74cf3107fb2b529990c90cf517326150bf4" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" + +[[package]] +name = "approx" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" +dependencies = [ + "num-traits", +] + +[[package]] +name = "arbitrary" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" [[package]] name = "ariadne" @@ -364,6 +451,104 @@ dependencies = [ "yansi 0.5.1", ] +[[package]] +name = "ark-bls12-381" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3df4dcc01ff89867cd86b0da835f23c3f02738353aaee7dde7495af71363b8d5" +dependencies = [ + "ark-ec 0.5.0", + "ark-ff 0.5.0", + "ark-serialize 0.5.0", + "ark-std 0.5.0", +] + +[[package]] +name = "ark-bn254" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea691771ebbb28aea556c044e2e5c5227398d840cee0c34d4d20fa8eb2689e8c" +dependencies = [ + "ark-ec 0.3.0", + "ark-ff 0.3.0", + "ark-std 0.3.0", +] + +[[package]] +name = "ark-bn254" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a22f4561524cd949590d78d7d4c5df8f592430d221f7f3c9497bbafd8972120f" +dependencies = [ + "ark-ec 0.4.2", + "ark-ff 0.4.2", + "ark-std 0.4.0", +] + +[[package]] +name = "ark-bn254" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d69eab57e8d2663efa5c63135b2af4f396d66424f88954c21104125ab6b3e6bc" +dependencies = [ + "ark-ec 0.5.0", + "ark-ff 0.5.0", + "ark-r1cs-std", + "ark-std 0.5.0", +] + +[[package]] +name = "ark-ec" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dea978406c4b1ca13c2db2373b05cc55429c3575b8b21f1b9ee859aa5b03dd42" +dependencies = [ + "ark-ff 0.3.0", + "ark-serialize 0.3.0", + "ark-std 0.3.0", + "derivative", + "num-traits", + "zeroize", +] + +[[package]] +name = "ark-ec" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defd9a439d56ac24968cca0571f598a61bc8c55f71d50a89cda591cb750670ba" +dependencies = [ + "ark-ff 0.4.2", + "ark-poly 0.4.2", + "ark-serialize 0.4.2", + "ark-std 0.4.0", + "derivative", + "hashbrown 0.13.2", + "itertools 0.10.5", + "num-traits", + "zeroize", +] + +[[package]] +name = "ark-ec" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d68f2d516162846c1238e755a7c4d131b892b70cc70c471a8e3ca3ed818fce" +dependencies = [ + "ahash", + "ark-ff 0.5.0", + "ark-poly 0.5.0", + "ark-serialize 0.5.0", + "ark-std 0.5.0", + "educe", + "fnv", + "hashbrown 0.15.3", + "itertools 0.13.0", + "num-bigint 0.4.6", + "num-integer", + "num-traits", + "zeroize", +] + [[package]] name = "ark-ff" version = "0.3.0" @@ -402,6 +587,26 @@ dependencies = [ "zeroize", ] +[[package]] +name = "ark-ff" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a177aba0ed1e0fbb62aa9f6d0502e9b46dad8c2eab04c14258a1212d2557ea70" +dependencies = [ + "ark-ff-asm 0.5.0", + "ark-ff-macros 0.5.0", + "ark-serialize 0.5.0", + "ark-std 0.5.0", + "arrayvec", + "digest 0.10.7", + "educe", + "itertools 0.13.0", + "num-bigint 0.4.6", + "num-traits", + "paste", + "zeroize", +] + [[package]] name = "ark-ff-asm" version = "0.3.0" @@ -422,6 +627,16 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "ark-ff-asm" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62945a2f7e6de02a31fe400aa489f0e0f5b2502e69f95f853adb82a96c7a6b60" +dependencies = [ + "quote", + "syn 2.0.101", +] + [[package]] name = "ark-ff-macros" version = "0.3.0" @@ -447,6 +662,76 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "ark-ff-macros" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09be120733ee33f7693ceaa202ca41accd5653b779563608f1234f78ae07c4b3" +dependencies = [ + "num-bigint 0.4.6", + "num-traits", + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "ark-poly" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d320bfc44ee185d899ccbadfa8bc31aab923ce1558716e1997a1e74057fe86bf" +dependencies = [ + "ark-ff 0.4.2", + "ark-serialize 0.4.2", + "ark-std 0.4.0", + "derivative", + "hashbrown 0.13.2", +] + +[[package]] +name = "ark-poly" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "579305839da207f02b89cd1679e50e67b4331e2f9294a57693e5051b7703fe27" +dependencies = [ + "ahash", + "ark-ff 0.5.0", + "ark-serialize 0.5.0", + "ark-std 0.5.0", + "educe", + "fnv", + "hashbrown 0.15.3", +] + +[[package]] +name = "ark-r1cs-std" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "941551ef1df4c7a401de7068758db6503598e6f01850bdb2cfdb614a1f9dbea1" +dependencies = [ + "ark-ec 0.5.0", + "ark-ff 0.5.0", + "ark-relations", + "ark-std 0.5.0", + "educe", + "num-bigint 0.4.6", + "num-integer", + "num-traits", + "tracing", +] + +[[package]] +name = "ark-relations" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec46ddc93e7af44bcab5230937635b06fb5744464dd6a7e7b083e80ebd274384" +dependencies = [ + "ark-ff 0.5.0", + "ark-std 0.5.0", + "tracing", + "tracing-subscriber 0.2.25", +] + [[package]] name = "ark-serialize" version = "0.3.0" @@ -463,11 +748,47 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" dependencies = [ + "ark-serialize-derive 0.4.2", "ark-std 0.4.0", "digest 0.10.7", "num-bigint 0.4.6", ] +[[package]] +name = "ark-serialize" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f4d068aaf107ebcd7dfb52bc748f8030e0fc930ac8e360146ca54c1203088f7" +dependencies = [ + "ark-serialize-derive 0.5.0", + "ark-std 0.5.0", + "arrayvec", + "digest 0.10.7", + "num-bigint 0.4.6", +] + +[[package]] +name = "ark-serialize-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-serialize-derive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213888f660fddcca0d257e88e54ac05bca01885f258ccdf695bafd77031bb69d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + [[package]] name = "ark-std" version = "0.3.0" @@ -475,7 +796,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1df2c09229cbc5a028b1d70e00fdb2acee28b1055dfb5ca73eea49c5a25c4e7c" dependencies = [ "num-traits", - "rand", + "rand 0.8.5", ] [[package]] @@ -485,7 +806,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" dependencies = [ "num-traits", - "rand", + "rand 0.8.5", +] + +[[package]] +name = "ark-std" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "246a225cc6131e9ee4f24619af0f19d67761fff15d7ccc22e42b80846e69449a" +dependencies = [ + "num-traits", + "rand 0.8.5", ] [[package]] @@ -511,13 +842,22 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.86" +version = "0.1.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "644dd749086bf3771a2fbc5f256fdb982d53f011c7d5d560304eafeecebce79d" +checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", +] + +[[package]] +name = "atoi" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528" +dependencies = [ + "num-traits", ] [[package]] @@ -529,6 +869,12 @@ dependencies = [ "bytemuck", ] +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + [[package]] name = "aurora-engine-modexp" version = "1.2.0" @@ -541,13 +887,13 @@ dependencies = [ [[package]] name = "auto_impl" -version = "1.2.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e12882f59de5360c748c4cbf569a042d5fb0eb515f7bea9c1f470b47f6ffbd73" +checksum = "ffdcb70bdbc4d478427380519163274ac86e52916e10f0a8889adf0f96d3fee7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -558,9 +904,9 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "aws-config" -version = "1.5.18" +version = "1.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90aff65e86db5fe300752551c1b015ef72b708ac54bded8ef43d0d53cb7cb0b1" +checksum = "02a18fd934af6ae7ca52410d4548b98eb895aab0f1ea417d168d85db1434a141" dependencies = [ "aws-credential-types", "aws-runtime", @@ -568,7 +914,7 @@ dependencies = [ "aws-sdk-ssooidc", "aws-sdk-sts", "aws-smithy-async", - "aws-smithy-http 0.61.1", + "aws-smithy-http", "aws-smithy-json", "aws-smithy-runtime", "aws-smithy-runtime-api", @@ -577,7 +923,7 @@ dependencies = [ "bytes", "fastrand", "hex", - "http 0.2.12", + "http 1.3.1", "ring", "time", "tokio", @@ -588,9 +934,9 @@ dependencies = [ [[package]] name = "aws-credential-types" -version = "1.2.1" +version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60e8f6b615cb5fc60a98132268508ad104310f0cfb25a1c22eee76efdf9154da" +checksum = "687bc16bc431a8533fe0097c7f0182874767f920989d7260950172ae8e3c4465" dependencies = [ "aws-smithy-async", "aws-smithy-runtime-api", @@ -598,17 +944,40 @@ dependencies = [ "zeroize", ] +[[package]] +name = "aws-lc-rs" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fcc8f365936c834db5514fc45aee5b1202d677e6b40e48468aaaa8183ca8c7" +dependencies = [ + "aws-lc-sys", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61b1d86e7705efe1be1b569bab41d4fa1e14e220b60a160f78de2db687add079" +dependencies = [ + "bindgen", + "cc", + "cmake", + "dunce", + "fs_extra", +] + [[package]] name = "aws-runtime" -version = "1.5.5" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76dd04d39cc12844c0994f2c9c5a6f5184c22e9188ec1ff723de41910a21dcad" +checksum = "6c4063282c69991e57faab9e5cb21ae557e59f5b0fb285c196335243df8dc25c" dependencies = [ "aws-credential-types", "aws-sigv4", "aws-smithy-async", "aws-smithy-eventstream", - "aws-smithy-http 0.60.12", + "aws-smithy-http", "aws-smithy-runtime", "aws-smithy-runtime-api", "aws-smithy-types", @@ -617,7 +986,6 @@ dependencies = [ "fastrand", "http 0.2.12", "http-body 0.4.6", - "once_cell", "percent-encoding", "pin-project-lite", "tracing", @@ -626,9 +994,9 @@ dependencies = [ [[package]] name = "aws-sdk-s3" -version = "1.78.0" +version = "1.86.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3038614b6cf7dd68d9a7b5b39563d04337eb3678d1d4173e356e927b0356158a" +checksum = "0303d28521eae1dbf1bf29e772029204cf9e126da7aa763e5391ff2131bcc2ac" dependencies = [ "aws-credential-types", "aws-runtime", @@ -636,7 +1004,7 @@ dependencies = [ "aws-smithy-async", "aws-smithy-checksums", "aws-smithy-eventstream", - "aws-smithy-http 0.61.1", + "aws-smithy-http", "aws-smithy-json", "aws-smithy-runtime", "aws-smithy-runtime-api", @@ -648,70 +1016,70 @@ dependencies = [ "hex", "hmac", "http 0.2.12", + "http 1.3.1", "http-body 0.4.6", "lru", - "once_cell", "percent-encoding", "regex-lite", - "sha2", + "sha2 0.10.9", "tracing", "url", ] [[package]] name = "aws-sdk-sso" -version = "1.61.0" +version = "1.68.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e65ff295979977039a25f5a0bf067a64bc5e6aa38f3cef4037cf42516265553c" +checksum = "bd5f01ea61fed99b5fe4877abff6c56943342a56ff145e9e0c7e2494419008be" dependencies = [ "aws-credential-types", "aws-runtime", "aws-smithy-async", - "aws-smithy-http 0.61.1", + "aws-smithy-http", "aws-smithy-json", "aws-smithy-runtime", "aws-smithy-runtime-api", "aws-smithy-types", "aws-types", "bytes", + "fastrand", "http 0.2.12", - "once_cell", "regex-lite", "tracing", ] [[package]] name = "aws-sdk-ssooidc" -version = "1.62.0" +version = "1.69.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91430a60f754f235688387b75ee798ef00cfd09709a582be2b7525ebb5306d4f" +checksum = "27454e4c55aaa4ef65647e3a1cf095cb834ca6d54e959e2909f1fef96ad87860" dependencies = [ "aws-credential-types", "aws-runtime", "aws-smithy-async", - "aws-smithy-http 0.61.1", + "aws-smithy-http", "aws-smithy-json", "aws-smithy-runtime", "aws-smithy-runtime-api", "aws-smithy-types", "aws-types", "bytes", + "fastrand", "http 0.2.12", - "once_cell", "regex-lite", "tracing", ] [[package]] name = "aws-sdk-sts" -version = "1.62.0" +version = "1.69.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9276e139d39fff5a0b0c984fc2d30f970f9a202da67234f948fda02e5bea1dbe" +checksum = "ffd6ef5d00c94215960fabcdf2d9fe7c090eed8be482d66d47b92d4aba1dd4aa" dependencies = [ "aws-credential-types", "aws-runtime", "aws-smithy-async", - "aws-smithy-http 0.61.1", + "aws-smithy-http", "aws-smithy-json", "aws-smithy-query", "aws-smithy-runtime", @@ -719,21 +1087,21 @@ dependencies = [ "aws-smithy-types", "aws-smithy-xml", "aws-types", + "fastrand", "http 0.2.12", - "once_cell", "regex-lite", "tracing", ] [[package]] name = "aws-sigv4" -version = "1.2.9" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bfe75fad52793ce6dec0dc3d4b1f388f038b5eb866c8d4d7f3a8e21b5ea5051" +checksum = "3734aecf9ff79aa401a6ca099d076535ab465ff76b46440cf567c8e70b65dc13" dependencies = [ "aws-credential-types", "aws-smithy-eventstream", - "aws-smithy-http 0.60.12", + "aws-smithy-http", "aws-smithy-runtime-api", "aws-smithy-types", "bytes", @@ -742,12 +1110,11 @@ dependencies = [ "hex", "hmac", "http 0.2.12", - "http 1.2.0", - "once_cell", + "http 1.3.1", "p256 0.11.1", "percent-encoding", "ring", - "sha2", + "sha2 0.10.9", "subtle", "time", "tracing", @@ -756,9 +1123,9 @@ dependencies = [ [[package]] name = "aws-smithy-async" -version = "1.2.4" +version = "1.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa59d1327d8b5053c54bf2eaae63bf629ba9e904434d0835a28ed3c0ed0a614e" +checksum = "1e190749ea56f8c42bf15dd76c65e14f8f765233e6df9b0506d9d934ebef867c" dependencies = [ "futures-util", "pin-project-lite", @@ -767,31 +1134,29 @@ dependencies = [ [[package]] name = "aws-smithy-checksums" -version = "0.63.0" +version = "0.63.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2dc8d842d872529355c72632de49ef8c5a2949a4472f10e802f28cf925770c" +checksum = "d2f77a921dbd2c78ebe70726799787c1d110a2245dd65e39b20923dfdfb2deee" dependencies = [ - "aws-smithy-http 0.60.12", + "aws-smithy-http", "aws-smithy-types", "bytes", - "crc32c", - "crc32fast", - "crc64fast-nvme", + "crc-fast", "hex", "http 0.2.12", "http-body 0.4.6", "md-5", "pin-project-lite", "sha1", - "sha2", + "sha2 0.10.9", "tracing", ] [[package]] name = "aws-smithy-eventstream" -version = "0.60.7" +version = "0.60.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "461e5e02f9864cba17cff30f007c2e37ade94d01e87cdb5204e44a84e6d38c17" +checksum = "7c45d3dddac16c5c59d553ece225a88870cf81b7b813c9cc17b78cf4685eac7a" dependencies = [ "aws-smithy-types", "bytes", @@ -800,18 +1165,19 @@ dependencies = [ [[package]] name = "aws-smithy-http" -version = "0.60.12" +version = "0.62.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7809c27ad8da6a6a68c454e651d4962479e81472aa19ae99e59f9aba1f9713cc" +checksum = "99335bec6cdc50a346fda1437f9fefe33abf8c99060739a546a16457f2862ca9" dependencies = [ + "aws-smithy-eventstream", "aws-smithy-runtime-api", "aws-smithy-types", "bytes", "bytes-utils", "futures-core", "http 0.2.12", + "http 1.3.1", "http-body 0.4.6", - "once_cell", "percent-encoding", "pin-project-lite", "pin-utils", @@ -819,35 +1185,51 @@ dependencies = [ ] [[package]] -name = "aws-smithy-http" -version = "0.61.1" +name = "aws-smithy-http-client" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6f276f21c7921fe902826618d1423ae5bf74cf8c1b8472aee8434f3dfd31824" +checksum = "7e44697a9bded898dcd0b1cb997430d949b87f4f8940d91023ae9062bf218250" dependencies = [ - "aws-smithy-eventstream", + "aws-smithy-async", "aws-smithy-runtime-api", "aws-smithy-types", - "bytes", - "bytes-utils", - "futures-core", + "h2 0.4.10", "http 0.2.12", + "http 1.3.1", "http-body 0.4.6", - "once_cell", - "percent-encoding", + "hyper 0.14.32", + "hyper 1.6.0", + "hyper-rustls 0.24.2", + "hyper-rustls 0.27.5", + "hyper-util", "pin-project-lite", - "pin-utils", + "rustls 0.21.12", + "rustls 0.23.27", + "rustls-native-certs 0.8.1", + "rustls-pki-types", + "tokio", + "tower", "tracing", ] [[package]] name = "aws-smithy-json" -version = "0.61.2" +version = "0.61.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "623a51127f24c30776c8b374295f2df78d92517386f77ba30773f15a30ce1422" +checksum = "92144e45819cae7dc62af23eac5a038a58aa544432d2102609654376a900bd07" dependencies = [ "aws-smithy-types", ] +[[package]] +name = "aws-smithy-observability" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9364d5989ac4dd918e5cc4c4bdcc61c9be17dcd2586ea7f69e348fc7c6cab393" +dependencies = [ + "aws-smithy-runtime-api", +] + [[package]] name = "aws-smithy-query" version = "0.60.7" @@ -860,42 +1242,39 @@ dependencies = [ [[package]] name = "aws-smithy-runtime" -version = "1.7.8" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d526a12d9ed61fadefda24abe2e682892ba288c2018bcb38b1b4c111d13f6d92" +checksum = "14302f06d1d5b7d333fd819943075b13d27c7700b414f574c3c35859bfb55d5e" dependencies = [ "aws-smithy-async", - "aws-smithy-http 0.60.12", + "aws-smithy-http", + "aws-smithy-http-client", + "aws-smithy-observability", "aws-smithy-runtime-api", "aws-smithy-types", "bytes", "fastrand", - "h2", "http 0.2.12", + "http 1.3.1", "http-body 0.4.6", "http-body 1.0.1", - "httparse", - "hyper", - "hyper-rustls", - "once_cell", "pin-project-lite", "pin-utils", - "rustls", "tokio", "tracing", ] [[package]] name = "aws-smithy-runtime-api" -version = "1.7.3" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92165296a47a812b267b4f41032ff8069ab7ff783696d217f0994a0d7ab585cd" +checksum = "a1e5d9e3a80a18afa109391fb5ad09c3daf887b516c6fd805a157c6ea7994a57" dependencies = [ "aws-smithy-async", "aws-smithy-types", "bytes", "http 0.2.12", - "http 1.2.0", + "http 1.3.1", "pin-project-lite", "tokio", "tracing", @@ -904,16 +1283,16 @@ dependencies = [ [[package]] name = "aws-smithy-types" -version = "1.2.13" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7b8a53819e42f10d0821f56da995e1470b199686a1809168db6ca485665f042" +checksum = "40076bd09fadbc12d5e026ae080d0930defa606856186e31d83ccc6a255eeaf3" dependencies = [ "base64-simd", "bytes", "bytes-utils", "futures-core", "http 0.2.12", - "http 1.2.0", + "http 1.3.1", "http-body 0.4.6", "http-body 1.0.1", "http-body-util", @@ -939,9 +1318,9 @@ dependencies = [ [[package]] name = "aws-types" -version = "1.3.5" +version = "1.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbd0a668309ec1f66c0f6bda4840dd6d4796ae26d699ebc266d7cc95c6d040f" +checksum = "8a322fec39e4df22777ed3ad8ea868ac2f94cd15e1a55f6ee8d8d6305057689a" dependencies = [ "aws-credential-types", "aws-smithy-async", @@ -953,9 +1332,9 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.74" +version = "0.3.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" dependencies = [ "addr2line", "cfg-if", @@ -1009,9 +1388,9 @@ dependencies = [ [[package]] name = "base64ct" -version = "1.6.0" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +checksum = "89e25b6adfb930f02d1981565a6e5d9c547ac15a96606256d3b59040e5cd4ca3" [[package]] name = "bincode" @@ -1022,13 +1401,45 @@ dependencies = [ "serde", ] +[[package]] +name = "bindgen" +version = "0.69.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" +dependencies = [ + "bitflags 2.9.1", + "cexpr", + "clang-sys", + "itertools 0.12.1", + "lazy_static", + "lazycell", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash 1.1.0", + "shlex", + "syn 2.0.101", + "which", +] + [[package]] name = "bit-set" version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" dependencies = [ - "bit-vec", + "bit-vec 0.6.3", +] + +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec 0.8.0", ] [[package]] @@ -1037,11 +1448,17 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + [[package]] name = "bitcode" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18c1406a27371b2f76232a2259df6ab607b91b5a0a7476a7729ff590df5a969a" +checksum = "cf300f4aa6e66f3bdff11f1236a88c622fe47ea814524792240b4d554d9858ee" dependencies = [ "arrayvec", "bitcode_derive", @@ -1058,7 +1475,23 @@ checksum = "42b6b4cb608b8282dc3b53d0f4c9ab404655d562674c682db7e6c0458cc83c23" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", +] + +[[package]] +name = "bitcoin-io" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b47c4ab7a93edb0c7198c5535ed9b52b63095f4e9b45279c6736cec4b856baf" + +[[package]] +name = "bitcoin_hashes" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb18c03d0db0247e147a21a6faafd5a7eb851c743db062de72018b6b7e8e4d16" +dependencies = [ + "bitcoin-io", + "hex-conservative", ] [[package]] @@ -1069,9 +1502,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.8.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" [[package]] name = "bitvec" @@ -1107,16 +1540,24 @@ dependencies = [ [[package]] name = "blake3" -version = "1.6.0" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1230237285e3e10cde447185e8975408ae24deaa67205ce684805c25bc0c7937" +checksum = "3888aaa89e4b2a40fca9848e400f6a658a5a3978de7be858e209cafa8be9a4a0" dependencies = [ "arrayref", "arrayvec", "cc", "cfg-if", "constant_time_eq 0.3.1", - "memmap2", +] + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", ] [[package]] @@ -1137,7 +1578,7 @@ dependencies = [ "ff 0.12.1", "group 0.12.1", "pairing 0.22.0", - "rand_core", + "rand_core 0.6.4", "subtle", ] @@ -1153,11 +1594,26 @@ dependencies = [ "zeroize", ] +[[package]] +name = "bn-rs" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34e20109dce74b02019885a01edc8ca485380a297ed8d6eb9e63e657774074b" +dependencies = [ + "getrandom 0.2.16", + "js-sys", + "primitive-types", + "rustc-hex", + "thiserror 1.0.69", + "uint", + "wasm-bindgen", +] + [[package]] name = "bon" -version = "3.3.2" +version = "3.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe7acc34ff59877422326db7d6f2d845a582b16396b6b08194942bf34c6528ab" +checksum = "ced38439e7a86a4761f7f7d5ded5ff009135939ecb464a24452eaa4c1696af7d" dependencies = [ "bon-macros", "rustversion", @@ -1165,9 +1621,9 @@ dependencies = [ [[package]] name = "bon-macros" -version = "3.3.2" +version = "3.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4159dd617a7fbc9be6a692fe69dc2954f8e6bb6bb5e4d7578467441390d77fd0" +checksum = "0ce61d2d3844c6b8d31b2353d9f66cf5e632b3e9549583fe3cac2f4f6136725e" dependencies = [ "darling", "ident_case", @@ -1175,7 +1631,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -1202,15 +1658,15 @@ checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" [[package]] name = "byte-slice-cast" -version = "1.2.2" +version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" +checksum = "7575182f7272186991736b70173b0ea045398f984bf5ebbb3804736ce1330c9d" [[package]] name = "bytemuck" -version = "1.21.0" +version = "1.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3" +checksum = "9134a6ef01ce4b366b50689c94f82c14bc72bc5d0386829828a2e2752ef7958c" [[package]] name = "byteorder" @@ -1220,9 +1676,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.10.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f61dac84819c6588b558454b194026eb1f09c293b9036ae9b159e74e73ab6cf9" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" dependencies = [ "serde", ] @@ -1259,9 +1715,9 @@ dependencies = [ [[package]] name = "c-kzg" -version = "1.0.3" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0307f72feab3300336fb803a57134159f6e20139af1357f36c54cb90d8e8928" +checksum = "7318cfa722931cb5fe0838b98d3ce5621e75f6a6408abc21721d80de9223f2e4" dependencies = [ "blst", "cc", @@ -1300,10 +1756,10 @@ dependencies = [ "openvm-transpiler", "serde", "serde_json", - "target-lexicon", + "target-lexicon 0.12.16", "tempfile", "tokio", - "toml 0.8.20", + "toml 0.8.22", "tracing", "vergen", ] @@ -1325,7 +1781,7 @@ checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" dependencies = [ "camino", "cargo-platform", - "semver 1.0.25", + "semver 1.0.26", "serde", "serde_json", "thiserror 1.0.69", @@ -1339,15 +1795,24 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.2.14" +version = "1.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c3d1b2e905a3a7b00a6141adb0e4c0bb941d11caf55349d863942a1cc44e3c9" +checksum = "5f4ac86a9e5bc1e2b3449ab9d7d3a6a405e3d1bb28d7b9be8614f55846ae3766" dependencies = [ "jobserver", "libc", "shlex", ] +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + [[package]] name = "cfg-if" version = "1.0.0" @@ -1356,15 +1821,15 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.39" +version = "0.4.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" +checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" dependencies = [ "android-tzdata", "iana-time-zone", "num-traits", "serde", - "windows-targets 0.52.6", + "windows-link", ] [[package]] @@ -1404,11 +1869,22 @@ dependencies = [ "inout", ] +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + [[package]] name = "clap" -version = "4.5.30" +version = "4.5.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92b7b18d71fad5313a1e320fa9897994228ce274b60faa4d694fe0ea89cd9e6d" +checksum = "ed93b9805f8ba930df42c2590f05453d5ec36cbb85d018868a5b24d31f6ac000" dependencies = [ "clap_builder", "clap_derive", @@ -1416,9 +1892,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.30" +version = "4.5.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a35db2071778a7344791a4fb4f95308b5673d219dee3ae348b86642574ecc90c" +checksum = "379026ff283facf611b0ea629334361c4211d1b12ee01024eec1591133b04120" dependencies = [ "anstream", "anstyle", @@ -1428,14 +1904,14 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.28" +version = "4.5.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4ced95c6f4a675af3da73304b9ac4ed991640c36374e4b46795c49e17cf1ed" +checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -1444,6 +1920,15 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" +[[package]] +name = "cmake" +version = "0.1.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" +dependencies = [ + "cc", +] + [[package]] name = "colorchoice" version = "1.0.3" @@ -1458,9 +1943,9 @@ checksum = "0b396d1f76d455557e1218ec8066ae14bba60b4b36ecd55577ba979f5db7ecaa" [[package]] name = "const-hex" -version = "1.14.0" +version = "1.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b0485bab839b018a8f1723fc5391819fea5f8f0f32288ef8a735fd096b6160c" +checksum = "83e22e0ed40b96a48d3db274f72fd365bd78f67af39b6bbd47e8a15e1c6207ff" dependencies = [ "cfg-if", "cpufeatures", @@ -1523,6 +2008,16 @@ dependencies = [ "libc", ] +[[package]] +name = "core-foundation" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -1540,9 +2035,9 @@ dependencies = [ [[package]] name = "crc" -version = "3.2.1" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" +checksum = "9710d3b3739c2e349eb44fe848ad0b7c8cb1e42bd87ee49371df2f7acaf3e675" dependencies = [ "crc-catalog", ] @@ -1554,12 +2049,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" [[package]] -name = "crc32c" -version = "0.6.8" +name = "crc-fast" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a47af21622d091a8f0fb295b88bc886ac74efcc613efc19f5d0b21de5c89e47" +checksum = "add8d3a4c789d77eeb0f0e3c1035f73610d017f9047e87db94f89c02a56d8fef" dependencies = [ - "rustc_version 0.4.1", + "cc", + "crc", + "digest 0.10.7", + "libc", + "rand 0.9.1", + "regex", ] [[package]] @@ -1571,15 +2071,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "crc64fast-nvme" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4955638f00a809894c947f85a024020a20815b65a5eea633798ea7924edab2b3" -dependencies = [ - "crc", -] - [[package]] name = "criterion" version = "0.5.1" @@ -1691,7 +2182,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" dependencies = [ "generic-array", - "rand_core", + "rand_core 0.6.4", "subtle", "zeroize", ] @@ -1703,7 +2194,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" dependencies = [ "generic-array", - "rand_core", + "rand_core 0.6.4", "subtle", "zeroize", ] @@ -1720,9 +2211,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.20.10" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" dependencies = [ "darling_core", "darling_macro", @@ -1730,27 +2221,27 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.10" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", "strsim", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] name = "darling_macro" -version = "0.20.10" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -1765,9 +2256,9 @@ dependencies = [ [[package]] name = "der" -version = "0.7.9" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" dependencies = [ "const-oid", "zeroize", @@ -1775,9 +2266,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.3.11" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" dependencies = [ "powerfmt", "serde", @@ -1802,7 +2293,7 @@ checksum = "d150dea618e920167e5973d70ae6ece4385b7164e0d799fe7c122dd0a5d912ad" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -1813,20 +2304,31 @@ checksum = "2cdc8d50f426189eef89dac62fabfa0abb27d5cc008f25bf4156a0203325becc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", +] + +[[package]] +name = "derive-where" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e73f2692d4bd3cac41dca28934a39894200c9fabf49586d77d0e5954af1d7902" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", ] [[package]] name = "derive_more" -version = "0.99.19" +version = "0.99.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3da29a38df43d6f156149c9b43ded5e018ddff2a855cf2cfd62e8cd7d079c69f" +checksum = "6edb4b64a43d977b8e99788fe3a04d483834fba1215a7e02caa415b626497f7f" dependencies = [ "convert_case", "proc-macro2", "quote", "rustc_version 0.4.1", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -1855,7 +2357,7 @@ checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", "unicode-xid", ] @@ -1867,7 +2369,7 @@ checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", "unicode-xid", ] @@ -1886,7 +2388,7 @@ version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer", + "block-buffer 0.10.4", "const-oid", "crypto-common", "subtle", @@ -1942,7 +2444,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -1957,12 +2459,6 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" -[[package]] -name = "dyn-clone" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "feeef44e73baff3a26d371801df019877a9866a8c493d315ab00177843314f35" - [[package]] name = "ecdsa" version = "0.14.8" @@ -1981,7 +2477,7 @@ version = "0.16.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" dependencies = [ - "der 0.7.9", + "der 0.7.10", "digest 0.10.7", "elliptic-curve 0.13.8", "rfc6979 0.4.0", @@ -1989,11 +2485,23 @@ dependencies = [ "spki 0.7.3", ] +[[package]] +name = "educe" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7bc049e1bd8cdeb31b68bbd586a9464ecf9f3944af3958a7a9d0f8b9799417" +dependencies = [ + "enum-ordinalize", + "proc-macro2", + "quote", + "syn 2.0.101", +] + [[package]] name = "either" -version = "1.13.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "elf" @@ -2015,7 +2523,7 @@ dependencies = [ "generic-array", "group 0.12.1", "pkcs8 0.9.0", - "rand_core", + "rand_core 0.6.4", "sec1 0.3.0", "subtle", "zeroize", @@ -2030,11 +2538,11 @@ dependencies = [ "base16ct 0.2.0", "crypto-bigint 0.5.5", "digest 0.10.7", - "ff 0.13.0", + "ff 0.13.1", "generic-array", "group 0.13.0", "pkcs8 0.10.2", - "rand_core", + "rand_core 0.6.4", "sec1 0.7.3", "subtle", "zeroize", @@ -2076,6 +2584,26 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" +[[package]] +name = "enum-ordinalize" +version = "4.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea0dcfa4e54eeb516fe454635a95753ddd39acda650ce703031c6973e315dd5" +dependencies = [ + "enum-ordinalize-derive", +] + +[[package]] +name = "enum-ordinalize-derive" +version = "4.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d28318a75d4aead5c4db25382e8ef717932d0346600cacae6357eb5941bc5ff" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + [[package]] name = "enum_dispatch" version = "0.3.13" @@ -2085,7 +2613,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -2096,7 +2624,7 @@ checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -2110,9 +2638,9 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.11.6" +version = "0.11.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcaee3d8e3cfc3fd92428d477bc97fc29ec8716d180c0d74c643bb26166660e0" +checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f" dependencies = [ "anstream", "anstyle", @@ -2128,9 +2656,9 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.10" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18" dependencies = [ "libc", "windows-sys 0.59.0", @@ -2184,6 +2712,17 @@ dependencies = [ "uint", ] +[[package]] +name = "ethereum_ssz" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d3627f83d8b87b432a5fad9934b4565260722a141a2c40f371f8080adec9425" +dependencies = [ + "ethereum-types", + "itertools 0.10.5", + "smallvec", +] + [[package]] name = "ethers-core" version = "2.0.14" @@ -2200,7 +2739,7 @@ dependencies = [ "k256", "num_enum", "open-fastrlp", - "rand", + "rand 0.8.5", "rlp", "serde", "serde_json", @@ -2220,7 +2759,7 @@ dependencies = [ "chrono", "ethers-core", "reqwest", - "semver 1.0.25", + "semver 1.0.26", "serde", "serde_json", "thiserror 1.0.69", @@ -2247,10 +2786,10 @@ dependencies = [ "path-slash", "rayon", "regex", - "semver 1.0.25", + "semver 1.0.26", "serde", "serde_json", - "sha2", + "sha2 0.10.9", "solang-parser", "svm-rs", "svm-rs-builds", @@ -2262,6 +2801,12 @@ dependencies = [ "yansi 0.5.1", ] +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + [[package]] name = "eyre" version = "0.6.12" @@ -2272,6 +2817,12 @@ dependencies = [ "once_cell", ] +[[package]] +name = "fallible-iterator" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" + [[package]] name = "fastrand" version = "2.3.0" @@ -2307,31 +2858,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" dependencies = [ "bitvec", - "rand_core", + "rand_core 0.6.4", "subtle", ] [[package]] name = "ff" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" dependencies = [ "bitvec", "byteorder", "ff_derive", - "rand_core", + "rand_core 0.6.4", "subtle", ] [[package]] name = "ff_derive" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9f54704be45ed286151c5e11531316eaef5b8f5af7d597b806fdb8af108d84a" +checksum = "f10d12652036b0e99197587c6ba87a8fc3031986499973c030d8b44fcc151b60" dependencies = [ "addchain", - "cfg-if", "num-bigint 0.3.3", "num-integer", "num-traits", @@ -2349,7 +2899,7 @@ dependencies = [ "atomic", "pear", "serde", - "toml 0.8.20", + "toml 0.8.22", "uncased", "version_check", ] @@ -2361,7 +2911,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" dependencies = [ "byteorder", - "rand", + "rand 0.8.5", "rustc-hex", "static_assertions", ] @@ -2374,9 +2924,9 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11faaf5a5236997af9848be0bef4db95824b1d534ebc64d0f0c6cf3e67bd38dc" +checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" dependencies = [ "crc32fast", "miniz_oxide", @@ -2390,9 +2940,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "foldhash" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" [[package]] name = "forge-fmt" @@ -2439,7 +2989,7 @@ dependencies = [ "regex", "reqwest", "revm-primitives 1.3.0", - "semver 1.0.25", + "semver 1.0.26", "serde", "serde_json", "serde_regex", @@ -2460,6 +3010,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + [[package]] name = "funty" version = "2.0.0" @@ -2473,6 +3029,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", + "futures-sink", ] [[package]] @@ -2481,6 +3038,17 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +[[package]] +name = "futures-intrusive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" +dependencies = [ + "futures-core", + "lock_api", + "parking_lot", +] + [[package]] name = "futures-io" version = "0.3.31" @@ -2495,7 +3063,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -2519,6 +3087,7 @@ dependencies = [ "futures-core", "futures-io", "futures-macro", + "futures-sink", "futures-task", "memchr", "pin-project-lite", @@ -2545,37 +3114,39 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi 0.11.0+wasi-snapshot-preview1", + "wasm-bindgen", ] [[package]] name = "getrandom" -version = "0.3.1" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" dependencies = [ "cfg-if", "libc", - "wasi 0.13.3+wasi-0.2.2", - "windows-targets 0.52.6", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", ] [[package]] name = "getset" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eded738faa0e88d3abc9d1a13cb11adc2073c400969eeb8793cf7132589959fc" +checksum = "f3586f256131df87204eb733da72e3d3eb4f343c639f4b7be279ac7c48baeafe" dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -2590,7 +3161,7 @@ version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b903b73e45dc0c6c596f2d37eccece7c1c8bb6e4407b001096387c63d0d93724" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.1", "libc", "libgit2-sys", "log", @@ -2599,9 +3170,9 @@ dependencies = [ [[package]] name = "glam" -version = "0.30.0" +version = "0.30.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17fcdf9683c406c2fc4d124afd29c0d595e22210d633cbdb8695ba9935ab1dc6" +checksum = "6b46b9ca4690308844c644e7c634d68792467260e051c8543e0c7871662b3ba7" [[package]] name = "glob" @@ -2630,7 +3201,7 @@ checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" dependencies = [ "ff 0.12.1", "memuse", - "rand_core", + "rand_core 0.6.4", "subtle", ] @@ -2640,8 +3211,8 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" dependencies = [ - "ff 0.13.0", - "rand_core", + "ff 0.13.1", + "rand_core 0.6.4", "subtle", ] @@ -2657,7 +3228,26 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.12", - "indexmap 2.7.1", + "indexmap 2.9.0", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "h2" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9421a676d1b147b16b82c9225157dc629087ef8ec4d5e2960f9437a90dac0a5" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http 1.3.1", + "indexmap 2.9.0", "slab", "tokio", "tokio-util", @@ -2666,9 +3256,9 @@ dependencies = [ [[package]] name = "half" -version = "2.4.1" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" dependencies = [ "cfg-if", "crunchy", @@ -2691,14 +3281,14 @@ checksum = "62f0ca78d12ac5c893f286d7cdfe3869290305ab8cac376e2592cdc8396da102" dependencies = [ "blake2b_simd", "crossbeam", - "ff 0.13.0", + "ff 0.13.1", "group 0.13.0", "halo2curves-axiom", "itertools 0.11.0", "maybe-rayon", "pairing 0.23.0", - "rand", - "rand_core", + "rand 0.8.5", + "rand_core 0.6.4", "rayon", "rustc-hash 1.1.0", "sha3", @@ -2719,7 +3309,7 @@ dependencies = [ "num-integer", "num-traits", "poseidon-primitives", - "rand_chacha", + "rand_chacha 0.3.1", "rayon", "rustc-hash 1.1.0", "serde", @@ -2737,9 +3327,9 @@ dependencies = [ "num-bigint 0.4.6", "num-integer", "num-traits", - "rand", - "rand_chacha", - "rand_core", + "rand 0.8.5", + "rand_chacha 0.3.1", + "rand_core 0.6.4", "rayon", "serde", "serde_json", @@ -2756,7 +3346,7 @@ dependencies = [ "ff 0.12.1", "group 0.12.1", "pasta_curves 0.4.1", - "rand_core", + "rand_core 0.6.4", "rayon", ] @@ -2768,7 +3358,7 @@ checksum = "b756596082144af6e57105a20403b7b80fe9dccd085700b74fae3af523b74dba" dependencies = [ "blake2", "digest 0.10.7", - "ff 0.13.0", + "ff 0.13.1", "group 0.13.0", "halo2derive", "hex", @@ -2778,12 +3368,12 @@ dependencies = [ "num-traits", "pairing 0.23.0", "paste", - "rand", - "rand_core", + "rand 0.8.5", + "rand_core 0.6.4", "rayon", "serde", "serde_arrays", - "sha2", + "sha2 0.10.9", "static_assertions", "subtle", "unroll", @@ -2797,7 +3387,7 @@ checksum = "dd8309e4638b4f1bcf6613d72265a84074d26034c35edc5d605b5688e580b8b8" dependencies = [ "blake2b_simd", "digest 0.10.7", - "ff 0.13.0", + "ff 0.13.1", "group 0.13.0", "hex", "lazy_static", @@ -2806,12 +3396,12 @@ dependencies = [ "pairing 0.23.0", "pasta_curves 0.5.1", "paste", - "rand", - "rand_core", + "rand 0.8.5", + "rand_core 0.6.4", "rayon", "serde", "serde_arrays", - "sha2", + "sha2 0.10.9", "static_assertions", "subtle", "unroll", @@ -2837,6 +3427,15 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] + [[package]] name = "hashbrown" version = "0.14.5" @@ -2849,9 +3448,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.2" +version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" dependencies = [ "allocator-api2", "equivalent", @@ -2859,6 +3458,15 @@ dependencies = [ "serde", ] +[[package]] +name = "hashlink" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" +dependencies = [ + "hashbrown 0.14.5", +] + [[package]] name = "heck" version = "0.5.0" @@ -2873,9 +3481,9 @@ checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "hermit-abi" -version = "0.4.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" +checksum = "f154ce46856750ed433c8649605bf7ed2de3bc35fd9d2a9f30cddd873c80cb08" [[package]] name = "hex" @@ -2886,6 +3494,15 @@ dependencies = [ "serde", ] +[[package]] +name = "hex-conservative" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5313b072ce3c597065a808dbf612c4c8e8590bdbf8b579508bf7a762c5eae6cd" +dependencies = [ + "arrayvec", +] + [[package]] name = "hex-literal" version = "0.4.1" @@ -2923,9 +3540,9 @@ dependencies = [ [[package]] name = "http" -version = "1.2.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" +checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" dependencies = [ "bytes", "fnv", @@ -2950,27 +3567,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http 1.2.0", + "http 1.3.1", ] [[package]] name = "http-body-util" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ "bytes", - "futures-util", - "http 1.2.0", + "futures-core", + "http 1.3.1", "http-body 1.0.1", "pin-project-lite", ] [[package]] name = "httparse" -version = "1.10.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2d708df4e7140240a16cd6ab0ab65c972d7433ab77819ea693fde9c43811e2a" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "httpdate" @@ -2988,17 +3605,37 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2", + "h2 0.3.26", "http 0.2.12", "http-body 0.4.6", "httparse", - "httpdate", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "h2 0.4.10", + "http 1.3.1", + "http-body 1.0.1", + "httparse", "itoa", "pin-project-lite", - "socket2", + "smallvec", "tokio", - "tower-service", - "tracing", "want", ] @@ -3010,24 +3647,63 @@ checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" dependencies = [ "futures-util", "http 0.2.12", - "hyper", + "hyper 0.14.32", "log", - "rustls", - "rustls-native-certs", + "rustls 0.21.12", + "rustls-native-certs 0.6.3", + "tokio", + "tokio-rustls 0.24.1", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" +dependencies = [ + "futures-util", + "http 1.3.1", + "hyper 1.6.0", + "hyper-util", + "rustls 0.23.27", + "rustls-native-certs 0.8.1", + "rustls-pki-types", + "tokio", + "tokio-rustls 0.26.2", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf9f1e950e0d9d1d3c47184416723cf29c0d1f93bd8cccf37e4beb6b44f31710" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http 1.3.1", + "http-body 1.0.1", + "hyper 1.6.0", + "libc", + "pin-project-lite", + "socket2", "tokio", - "tokio-rustls", + "tower-service", + "tracing", ] [[package]] name = "iana-time-zone" -version = "0.1.61" +version = "0.1.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", + "log", "wasm-bindgen", "windows-core", ] @@ -3043,21 +3719,22 @@ dependencies = [ [[package]] name = "icu_collections" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" dependencies = [ "displaydoc", + "potential_utf", "yoke", "zerofrom", "zerovec", ] [[package]] -name = "icu_locid" -version = "1.5.0" +name = "icu_locale_core" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" dependencies = [ "displaydoc", "litemap", @@ -3066,31 +3743,11 @@ dependencies = [ "zerovec", ] -[[package]] -name = "icu_locid_transform" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_locid_transform_data", - "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_locid_transform_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" - [[package]] name = "icu_normalizer" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" dependencies = [ "displaydoc", "icu_collections", @@ -3098,67 +3755,54 @@ dependencies = [ "icu_properties", "icu_provider", "smallvec", - "utf16_iter", - "utf8_iter", - "write16", "zerovec", ] [[package]] name = "icu_normalizer_data" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" +checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" [[package]] name = "icu_properties" -version = "1.5.1" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" dependencies = [ "displaydoc", "icu_collections", - "icu_locid_transform", + "icu_locale_core", "icu_properties_data", "icu_provider", - "tinystr", + "potential_utf", + "zerotrie", "zerovec", ] [[package]] name = "icu_properties_data" -version = "1.5.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" +checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" [[package]] name = "icu_provider" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" dependencies = [ "displaydoc", - "icu_locid", - "icu_provider_macros", + "icu_locale_core", "stable_deref_trait", "tinystr", "writeable", "yoke", "zerofrom", + "zerotrie", "zerovec", ] -[[package]] -name = "icu_provider_macros" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.98", -] - [[package]] name = "ident_case" version = "1.0.1" @@ -3178,9 +3822,9 @@ dependencies = [ [[package]] name = "idna_adapter" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" dependencies = [ "icu_normalizer", "icu_properties", @@ -3221,7 +3865,7 @@ checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -3243,12 +3887,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.7.1" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" dependencies = [ "equivalent", - "hashbrown 0.15.2", + "hashbrown 0.15.3", "serde", ] @@ -3275,11 +3919,11 @@ checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" [[package]] name = "is-terminal" -version = "0.4.15" +version = "0.4.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e19b23d53f35ce9f56aebc7d1bb4e6ac1e9c0db7ac85c8d1760c04379edced37" +checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" dependencies = [ - "hermit-abi 0.4.0", + "hermit-abi 0.5.1", "libc", "windows-sys 0.59.0", ] @@ -3308,6 +3952,24 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + [[package]] name = "itertools" version = "0.14.0" @@ -3319,16 +3981,17 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jobserver" -version = "0.1.32" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" +checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" dependencies = [ + "getrandom 0.3.3", "libc", ] @@ -3352,7 +4015,7 @@ dependencies = [ "bls12_381", "ff 0.12.1", "group 0.12.1", - "rand_core", + "rand_core 0.6.4", "subtle", ] @@ -3366,7 +4029,7 @@ dependencies = [ "ecdsa 0.16.9", "elliptic-curve 0.13.8", "once_cell", - "sha2", + "sha2 0.10.9", ] [[package]] @@ -3395,7 +4058,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55cb077ad656299f160924eb2912aa147d7339ea7d69e1b5517326fdcec3c1ca" dependencies = [ "ascii-canvas", - "bit-set", + "bit-set 0.5.3", "ena", "itertools 0.11.0", "lalrpop-util", @@ -3427,11 +4090,17 @@ dependencies = [ "spin", ] +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + [[package]] name = "libc" -version = "0.2.169" +version = "0.2.172" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" [[package]] name = "libgit2-sys" @@ -3445,17 +4114,27 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "libloading" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a793df0d7afeac54f95b471d3af7f0d4fb975699f972341a4b76988d49cdf0c" +dependencies = [ + "cfg-if", + "windows-targets 0.53.0", +] + [[package]] name = "libm" -version = "0.2.11" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" +checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" [[package]] name = "libmimalloc-sys" -version = "0.1.39" +version = "0.1.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23aa6811d3bd4deb8a84dde645f943476d13b248d818edcf8ce0b2f37f036b44" +checksum = "ec9d6fac27761dabcd4ee73571cdb06b7022dc99089acbe5435691edffaac0f4" dependencies = [ "cc", "libc", @@ -3467,15 +4146,61 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.1", "libc", ] +[[package]] +name = "libsecp256k1" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e79019718125edc905a079a70cfa5f3820bc76139fc91d6f9abc27ea2a887139" +dependencies = [ + "arrayref", + "base64 0.22.1", + "digest 0.9.0", + "libsecp256k1-core", + "libsecp256k1-gen-ecmult", + "libsecp256k1-gen-genmult", + "rand 0.8.5", + "serde", + "sha2 0.9.9", +] + +[[package]] +name = "libsecp256k1-core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be9b9bb642d8522a44d533eab56c16c738301965504753b03ad1de3425d5451" +dependencies = [ + "crunchy", + "digest 0.9.0", + "subtle", +] + +[[package]] +name = "libsecp256k1-gen-ecmult" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3038c808c55c87e8a172643a7d87187fc6c4174468159cb3090659d55bcb4809" +dependencies = [ + "libsecp256k1-core", +] + +[[package]] +name = "libsecp256k1-gen-genmult" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db8d6ba2cec9eacc40e6e8ccc98931840301f1006e95647ceb2dd5c3aa06f7c" +dependencies = [ + "libsecp256k1-core", +] + [[package]] name = "libz-sys" -version = "1.1.21" +version = "1.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df9b68e50e6e0b26f672573834882eb57759f6db9b3be2ea3c35c91188bb4eaa" +checksum = "8b70e7a7df205e92a1a4cd9aaae7898dac0aa555503cc0a649494d0d60e7651d" dependencies = [ "cc", "libc", @@ -3495,11 +4220,17 @@ version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" +[[package]] +name = "linux-raw-sys" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" + [[package]] name = "litemap" -version = "0.7.4" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" +checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" [[package]] name = "lock_api" @@ -3519,9 +4250,9 @@ checksum = "9374ef4228402d4b7e403e5838cb880d9ee663314b0a900d5a6aabf0c213552e" [[package]] name = "log" -version = "0.4.25" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" [[package]] name = "lru" @@ -3529,7 +4260,7 @@ version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" dependencies = [ - "hashbrown 0.15.2", + "hashbrown 0.15.3", ] [[package]] @@ -3540,7 +4271,7 @@ checksum = "1b27834086c65ec3f9387b096d66e99f221cf081c2b738042aa252bcd41204e3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -3587,6 +4318,15 @@ dependencies = [ "libc", ] +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + [[package]] name = "memuse" version = "0.2.2" @@ -3595,9 +4335,9 @@ checksum = "3d97bbf43eb4f088f8ca469930cde17fa036207c9a5e02ccc5107c4e8b17c964" [[package]] name = "metrics" -version = "0.23.0" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "884adb57038347dfbaf2d5065887b6cf4312330dc8e94bc30a1a839bd79d3261" +checksum = "3045b4193fbdc5b5681f32f11070da9be3609f189a79f3390706d42587f46bb5" dependencies = [ "ahash", "portable-atomic", @@ -3609,7 +4349,7 @@ version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62a6a1f7141f1d9bc7a886b87536bbfc97752e08b369e1e0453a9acfab5f5da4" dependencies = [ - "indexmap 2.7.1", + "indexmap 2.9.0", "itoa", "lockfree-object-pool", "metrics", @@ -3617,7 +4357,7 @@ dependencies = [ "once_cell", "tracing", "tracing-core", - "tracing-subscriber", + "tracing-subscriber 0.3.19", ] [[package]] @@ -3630,7 +4370,7 @@ dependencies = [ "crossbeam-epoch", "crossbeam-utils", "hashbrown 0.14.5", - "indexmap 2.7.1", + "indexmap 2.9.0", "metrics", "num_cpus", "ordered-float", @@ -3641,9 +4381,9 @@ dependencies = [ [[package]] name = "mimalloc" -version = "0.1.43" +version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68914350ae34959d83f732418d51e2427a794055d0b9529f48259ac07af65633" +checksum = "995942f432bbb4822a7e9c3faa87a695185b0d09273ba85f097b54f4e458f2af" dependencies = [ "libmimalloc-sys", ] @@ -3654,11 +4394,17 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" -version = "0.8.4" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3b1c9bd4fe1f0f8b387f6eb9eb3b4a1aa26185e5750efb9140301703f62cd1b" +checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" dependencies = [ "adler2", ] @@ -3689,6 +4435,16 @@ dependencies = [ "smallvec", ] +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -3732,7 +4488,7 @@ checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" dependencies = [ "num-integer", "num-traits", - "rand", + "rand 0.8.5", "serde", ] @@ -3781,6 +4537,33 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-modular" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64a5fe11d4135c3bcdf3a95b18b194afa9608a5f6ff034f5d857bc9a27fb0119" +dependencies = [ + "num-bigint 0.4.6", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-prime" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e238432a7881ec7164503ccc516c014bf009be7984cde1ba56837862543bdec3" +dependencies = [ + "bitvec", + "either", + "lru", + "num-bigint 0.4.6", + "num-integer", + "num-modular", + "num-traits", + "rand 0.8.5", +] + [[package]] name = "num-rational" version = "0.4.2" @@ -3830,7 +4613,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -3857,7 +4640,7 @@ dependencies = [ "num-bigint 0.4.6", "num-integer", "num-traits", - "rand", + "rand 0.8.5", ] [[package]] @@ -3871,15 +4654,21 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.20.3" +version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "oorandom" -version = "11.1.4" +version = "11.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" + +[[package]] +name = "opaque-debug" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "open-fastrlp" @@ -3918,7 +4707,7 @@ version = "1.1.2" dependencies = [ "bytemuck", "chrono", - "getrandom 0.3.1", + "getrandom 0.3.3", "num-bigint 0.4.6", "openvm-custom-insn", "openvm-platform", @@ -3932,6 +4721,7 @@ version = "1.1.2" dependencies = [ "derive-new 0.6.0", "derive_more 1.0.0", + "eyre", "halo2curves-axiom", "itertools 0.14.0", "num-bigint 0.4.6", @@ -3948,7 +4738,7 @@ dependencies = [ "openvm-rv32im-circuit", "openvm-stark-backend", "openvm-stark-sdk", - "rand", + "rand 0.8.5", "serde", "serde-big-array", "serde_with", @@ -3961,7 +4751,7 @@ version = "0.1.0" dependencies = [ "openvm-macros-common", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -3973,6 +4763,8 @@ dependencies = [ "once_cell", "openvm-algebra-complex-macros", "openvm-algebra-moduli-macros", + "openvm-custom-insn", + "openvm-rv32im-guest", "serde-big-array", "strum_macros", ] @@ -3981,9 +4773,11 @@ dependencies = [ name = "openvm-algebra-moduli-macros" version = "1.1.2" dependencies = [ + "num-bigint 0.4.6", + "num-prime", "openvm-macros-common", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -4035,7 +4829,7 @@ dependencies = [ "openvm-stark-sdk", "openvm-transpiler", "tracing", - "tracing-subscriber", + "tracing-subscriber 0.3.19", ] [[package]] @@ -4067,7 +4861,7 @@ dependencies = [ "openvm-stark-backend", "openvm-stark-sdk", "openvm-transpiler", - "rand_chacha", + "rand_chacha 0.3.1", "serde", "tiny-keccak", "tokio", @@ -4085,7 +4879,7 @@ dependencies = [ "openvm-transpiler", "tempfile", "tracing", - "tracing-subscriber", + "tracing-subscriber 0.3.19", ] [[package]] @@ -4105,7 +4899,7 @@ dependencies = [ "openvm-rv32im-transpiler", "openvm-stark-backend", "openvm-stark-sdk", - "rand", + "rand 0.8.5", "serde", ] @@ -4113,30 +4907,10 @@ dependencies = [ name = "openvm-bigint-guest" version = "1.1.2" dependencies = [ - "num-bigint 0.4.6", - "num-traits", - "openvm", "openvm-platform", - "serde", - "serde-big-array", "strum_macros", ] -[[package]] -name = "openvm-bigint-integration-tests" -version = "1.1.2" -dependencies = [ - "eyre", - "openvm-bigint-circuit", - "openvm-bigint-transpiler", - "openvm-circuit", - "openvm-instructions", - "openvm-rv32im-transpiler", - "openvm-stark-sdk", - "openvm-toolchain-tests", - "openvm-transpiler", -] - [[package]] name = "openvm-bigint-transpiler" version = "1.1.2" @@ -4188,7 +4962,7 @@ dependencies = [ "openvm-stark-backend", "openvm-stark-sdk", "p3-baby-bear", - "rand", + "rand 0.8.5", "rustc-hash 2.1.1", "serde", "serde-big-array", @@ -4204,7 +4978,7 @@ version = "1.1.2" dependencies = [ "itertools 0.14.0", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -4218,7 +4992,7 @@ dependencies = [ "openvm-circuit-primitives-derive", "openvm-stark-backend", "openvm-stark-sdk", - "rand", + "rand 0.8.5", "test-case", "tracing", ] @@ -4229,7 +5003,7 @@ version = "1.1.2" dependencies = [ "itertools 0.14.0", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -4252,7 +5026,7 @@ version = "0.1.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -4281,7 +5055,6 @@ dependencies = [ "openvm-rv32im-circuit", "openvm-stark-backend", "openvm-stark-sdk", - "rand", "serde", "serde_with", "strum", @@ -4316,13 +5089,13 @@ name = "openvm-ecc-integration-tests" version = "1.1.2" dependencies = [ "eyre", + "halo2curves-axiom", "hex-literal", "num-bigint 0.4.6", "openvm-algebra-circuit", "openvm-algebra-transpiler", "openvm-circuit", "openvm-ecc-circuit", - "openvm-ecc-guest", "openvm-ecc-transpiler", "openvm-keccak256-transpiler", "openvm-rv32im-transpiler", @@ -4338,7 +5111,7 @@ version = "1.1.2" dependencies = [ "openvm-macros-common", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -4354,6 +5127,29 @@ dependencies = [ "strum", ] +[[package]] +name = "openvm-ff-derive" +version = "1.1.2" +dependencies = [ + "addchain", + "eyre", + "num-bigint 0.3.3", + "num-bigint 0.4.6", + "num-integer", + "num-traits", + "openvm-algebra-circuit", + "openvm-algebra-transpiler", + "openvm-circuit", + "openvm-instructions", + "openvm-rv32im-transpiler", + "openvm-stark-sdk", + "openvm-toolchain-tests", + "openvm-transpiler", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "openvm-instructions" version = "1.1.2" @@ -4367,22 +5163,72 @@ dependencies = [ "num-traits", "openvm-instructions-derive", "openvm-stark-backend", - "p3-baby-bear", - "rand", + "p3-baby-bear", + "rand 0.8.5", + "serde", + "strum", + "strum_macros", +] + +[[package]] +name = "openvm-instructions-derive" +version = "1.1.2" +dependencies = [ + "openvm-instructions", + "quote", + "strum", + "strum_macros", + "syn 2.0.101", +] + +[[package]] +name = "openvm-k256" +version = "1.1.2" +dependencies = [ + "derive_more 1.0.0", + "ecdsa 0.16.9", + "elliptic-curve 0.13.8", + "eyre", + "ff 0.13.1", + "hex-literal", + "k256", + "num-bigint 0.4.6", + "openvm", + "openvm-algebra-circuit", + "openvm-algebra-guest", + "openvm-algebra-moduli-macros", + "openvm-algebra-transpiler", + "openvm-circuit", + "openvm-ecc-circuit", + "openvm-ecc-guest", + "openvm-ecc-sw-macros", + "openvm-ecc-transpiler", + "openvm-rv32im-circuit", + "openvm-rv32im-transpiler", + "openvm-sha256-circuit", + "openvm-sha256-transpiler", + "openvm-stark-backend", + "openvm-stark-sdk", + "openvm-toolchain-tests", + "openvm-transpiler", "serde", - "strum", - "strum_macros", ] [[package]] -name = "openvm-instructions-derive" +name = "openvm-keccak256" version = "1.1.2" dependencies = [ + "eyre", + "openvm-circuit", "openvm-instructions", - "quote", - "strum", - "strum_macros", - "syn 2.0.98", + "openvm-keccak256-circuit", + "openvm-keccak256-guest", + "openvm-keccak256-transpiler", + "openvm-rv32im-transpiler", + "openvm-stark-sdk", + "openvm-toolchain-tests", + "openvm-transpiler", + "tiny-keccak", ] [[package]] @@ -4403,7 +5249,7 @@ dependencies = [ "openvm-stark-backend", "openvm-stark-sdk", "p3-keccak-air", - "rand", + "rand 0.8.5", "serde", "serde-big-array", "strum", @@ -4416,22 +5262,6 @@ name = "openvm-keccak256-guest" version = "1.1.2" dependencies = [ "openvm-platform", - "tiny-keccak", -] - -[[package]] -name = "openvm-keccak256-integration-tests" -version = "1.1.2" -dependencies = [ - "eyre", - "openvm-circuit", - "openvm-instructions", - "openvm-keccak256-circuit", - "openvm-keccak256-transpiler", - "openvm-rv32im-transpiler", - "openvm-stark-sdk", - "openvm-toolchain-tests", - "openvm-transpiler", ] [[package]] @@ -4451,7 +5281,7 @@ dependencies = [ name = "openvm-macros-common" version = "1.1.2" dependencies = [ - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -4468,7 +5298,7 @@ dependencies = [ "openvm-pairing-guest", "openvm-stark-backend", "openvm-stark-sdk", - "rand", + "rand 0.8.5", "serde", "serde_with", "tracing", @@ -4492,7 +5322,7 @@ dependencies = [ "openvm-rv32im-circuit", "openvm-stark-backend", "openvm-stark-sdk", - "rand", + "rand 0.8.5", "serde", "serde-big-array", "static_assertions", @@ -4518,7 +5348,7 @@ dependencies = [ "openvm-stark-backend", "openvm-stark-sdk", "p3-symmetric", - "rand", + "rand 0.8.5", "serde", "snark-verifier-sdk", "strum", @@ -4531,7 +5361,7 @@ name = "openvm-native-compiler-derive" version = "1.1.2" dependencies = [ "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -4555,7 +5385,7 @@ dependencies = [ "p3-fri", "p3-merkle-tree", "p3-symmetric", - "rand", + "rand 0.8.5", "serde", "serde_json", "serde_with", @@ -4574,85 +5404,125 @@ dependencies = [ ] [[package]] -name = "openvm-pairing-circuit" +name = "openvm-p256" version = "1.1.2" dependencies = [ - "derive-new 0.6.0", "derive_more 1.0.0", + "ecdsa 0.16.9", + "elliptic-curve 0.13.8", "eyre", - "halo2curves-axiom", - "itertools 0.14.0", + "ff 0.13.1", + "hex-literal", "num-bigint 0.4.6", - "num-traits", + "openvm", "openvm-algebra-circuit", + "openvm-algebra-guest", + "openvm-algebra-moduli-macros", + "openvm-algebra-transpiler", "openvm-circuit", - "openvm-circuit-derive", - "openvm-circuit-primitives", - "openvm-circuit-primitives-derive", "openvm-ecc-circuit", "openvm-ecc-guest", - "openvm-instructions", - "openvm-mod-circuit-builder", - "openvm-pairing-guest", - "openvm-pairing-transpiler", - "openvm-rv32-adapters", + "openvm-ecc-sw-macros", + "openvm-ecc-transpiler", "openvm-rv32im-circuit", + "openvm-rv32im-transpiler", + "openvm-sha256-circuit", + "openvm-sha256-transpiler", "openvm-stark-backend", "openvm-stark-sdk", - "rand", + "openvm-toolchain-tests", + "openvm-transpiler", + "p256 0.13.2", "serde", - "strum", ] [[package]] -name = "openvm-pairing-guest" +name = "openvm-pairing" version = "1.1.2" dependencies = [ + "eyre", "group 0.13.0", "halo2curves-axiom", "hex-literal", "itertools 0.14.0", - "lazy_static", "num-bigint 0.4.6", "num-traits", "openvm", + "openvm-algebra-circuit", "openvm-algebra-complex-macros", "openvm-algebra-guest", "openvm-algebra-moduli-macros", + "openvm-algebra-transpiler", + "openvm-circuit", "openvm-custom-insn", + "openvm-ecc-circuit", "openvm-ecc-guest", "openvm-ecc-sw-macros", + "openvm-ecc-transpiler", + "openvm-instructions", + "openvm-pairing-circuit", + "openvm-pairing-guest", + "openvm-pairing-transpiler", "openvm-platform", "openvm-rv32im-guest", - "rand", + "openvm-rv32im-transpiler", + "openvm-stark-sdk", + "openvm-toolchain-tests", + "openvm-transpiler", + "rand 0.8.5", "serde", - "strum_macros", - "subtle", ] [[package]] -name = "openvm-pairing-integration-tests" +name = "openvm-pairing-circuit" version = "1.1.2" dependencies = [ + "derive-new 0.6.0", + "derive_more 1.0.0", "eyre", + "halo2curves-axiom", + "itertools 0.14.0", "num-bigint 0.4.6", "num-traits", - "openvm", "openvm-algebra-circuit", - "openvm-algebra-transpiler", "openvm-circuit", + "openvm-circuit-derive", + "openvm-circuit-primitives", + "openvm-circuit-primitives-derive", "openvm-ecc-circuit", "openvm-ecc-guest", - "openvm-ecc-transpiler", "openvm-instructions", - "openvm-pairing-circuit", + "openvm-mod-circuit-builder", "openvm-pairing-guest", "openvm-pairing-transpiler", - "openvm-rv32im-transpiler", + "openvm-rv32-adapters", + "openvm-rv32im-circuit", + "openvm-stark-backend", "openvm-stark-sdk", - "openvm-toolchain-tests", - "openvm-transpiler", - "rand", + "rand 0.8.5", + "serde", + "strum", +] + +[[package]] +name = "openvm-pairing-guest" +version = "1.1.2" +dependencies = [ + "halo2curves-axiom", + "hex-literal", + "itertools 0.14.0", + "lazy_static", + "num-bigint 0.4.6", + "num-traits", + "openvm", + "openvm-algebra-guest", + "openvm-algebra-moduli-macros", + "openvm-custom-insn", + "openvm-ecc-guest", + "rand 0.8.5", + "serde", + "strum_macros", + "subtle", ] [[package]] @@ -4691,7 +5561,7 @@ dependencies = [ "p3-poseidon2", "p3-poseidon2-air", "p3-symmetric", - "rand", + "rand 0.8.5", "zkhash", ] @@ -4708,6 +5578,62 @@ dependencies = [ "serde_json", ] +[[package]] +name = "openvm-ruint" +version = "1.1.2" +dependencies = [ + "alloy-rlp", + "approx", + "arbitrary", + "ark-bn254 0.3.0", + "ark-bn254 0.4.0", + "ark-ff 0.3.0", + "ark-ff 0.4.2", + "bincode", + "bn-rs", + "bytemuck", + "bytes", + "ethereum_ssz", + "eyre", + "fastrlp 0.3.1", + "hex-literal", + "num-bigint 0.4.6", + "num-traits", + "openvm-bigint-circuit", + "openvm-bigint-guest", + "openvm-bigint-transpiler", + "openvm-circuit", + "openvm-instructions", + "openvm-ruint", + "openvm-ruint-macro", + "openvm-rv32im-transpiler", + "openvm-stark-sdk", + "openvm-toolchain-tests", + "openvm-transpiler", + "parity-scale-codec", + "postgres", + "postgres-types", + "primitive-types", + "proptest", + "pyo3", + "quickcheck", + "rand 0.8.5", + "rlp", + "serde", + "serde_json", + "sqlx-core", + "thiserror 1.0.69", + "valuable", + "zeroize", +] + +[[package]] +name = "openvm-ruint-macro" +version = "1.2.1" +dependencies = [ + "openvm-ruint", +] + [[package]] name = "openvm-rv32-adapters" version = "1.1.2" @@ -4721,7 +5647,7 @@ dependencies = [ "openvm-rv32im-circuit", "openvm-stark-backend", "openvm-stark-sdk", - "rand", + "rand 0.8.5", "serde", "serde-big-array", "serde_with", @@ -4744,7 +5670,7 @@ dependencies = [ "openvm-rv32im-transpiler", "openvm-stark-backend", "openvm-stark-sdk", - "rand", + "rand 0.8.5", "serde", "serde-big-array", "strum", @@ -4755,7 +5681,6 @@ name = "openvm-rv32im-guest" version = "1.1.2" dependencies = [ "openvm-custom-insn", - "p3-field", "strum_macros", ] @@ -4768,7 +5693,6 @@ dependencies = [ "openvm-circuit", "openvm-instructions", "openvm-rv32im-circuit", - "openvm-rv32im-guest", "openvm-rv32im-transpiler", "openvm-stark-sdk", "openvm-toolchain-tests", @@ -4824,7 +5748,6 @@ dependencies = [ "openvm-native-circuit", "openvm-native-compiler", "openvm-native-recursion", - "openvm-native-transpiler", "openvm-pairing-circuit", "openvm-pairing-transpiler", "openvm-rv32im-circuit", @@ -4835,7 +5758,6 @@ dependencies = [ "openvm-stark-sdk", "openvm-transpiler", "p3-fri", - "rrs-lib", "serde", "serde_json", "serde_with", @@ -4846,6 +5768,23 @@ dependencies = [ "tracing", ] +[[package]] +name = "openvm-sha2" +version = "1.1.2" +dependencies = [ + "eyre", + "openvm-circuit", + "openvm-instructions", + "openvm-rv32im-transpiler", + "openvm-sha256-circuit", + "openvm-sha256-guest", + "openvm-sha256-transpiler", + "openvm-stark-sdk", + "openvm-toolchain-tests", + "openvm-transpiler", + "sha2 0.10.9", +] + [[package]] name = "openvm-sha256-air" version = "1.1.2" @@ -4854,8 +5793,8 @@ dependencies = [ "openvm-circuit-primitives", "openvm-stark-backend", "openvm-stark-sdk", - "rand", - "sha2", + "rand 0.8.5", + "sha2 0.10.9", ] [[package]] @@ -4874,9 +5813,9 @@ dependencies = [ "openvm-sha256-transpiler", "openvm-stark-backend", "openvm-stark-sdk", - "rand", + "rand 0.8.5", "serde", - "sha2", + "sha2 0.10.9", "strum", ] @@ -4885,22 +5824,6 @@ name = "openvm-sha256-guest" version = "1.1.2" dependencies = [ "openvm-platform", - "sha2", -] - -[[package]] -name = "openvm-sha256-integration-tests" -version = "1.1.2" -dependencies = [ - "eyre", - "openvm-circuit", - "openvm-instructions", - "openvm-rv32im-transpiler", - "openvm-sha256-circuit", - "openvm-sha256-transpiler", - "openvm-stark-sdk", - "openvm-toolchain-tests", - "openvm-transpiler", ] [[package]] @@ -4950,8 +5873,8 @@ version = "1.0.0" source = "git+https://github.com/openvm-org/stark-backend.git?tag=v1.0.1#e540dbdd09ef20db7207ad7f2674bece75a2b803" dependencies = [ "derivative", - "derive_more 0.99.19", - "ff 0.13.0", + "derive_more 0.99.20", + "ff 0.13.1", "itertools 0.14.0", "metrics", "metrics-tracing-context", @@ -4969,14 +5892,14 @@ dependencies = [ "p3-poseidon", "p3-poseidon2", "p3-symmetric", - "rand", + "rand 0.8.5", "serde", "serde_json", "static_assertions", - "toml 0.8.20", + "toml 0.8.22", "tracing", "tracing-forest", - "tracing-subscriber", + "tracing-subscriber 0.3.19", "zkhash", ] @@ -5019,20 +5942,6 @@ dependencies = [ "thiserror 1.0.69", ] -[[package]] -name = "openvm-verify-stark" -version = "1.1.2" -dependencies = [ - "eyre", - "openvm-circuit", - "openvm-native-compiler", - "openvm-native-recursion", - "openvm-rv32im-guest", - "openvm-sdk", - "openvm-stark-sdk", - "openvm-verify-stark", -] - [[package]] name = "option-ext" version = "0.2.0" @@ -5068,7 +5977,7 @@ checksum = "51f44edd08f51e2ade572f141051021c5af22677e42b7dd28a88155151c33594" dependencies = [ "ecdsa 0.14.8", "elliptic-curve 0.12.3", - "sha2", + "sha2 0.10.9", ] [[package]] @@ -5077,7 +5986,10 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" dependencies = [ + "ecdsa 0.16.9", "elliptic-curve 0.13.8", + "primeorder", + "sha2 0.10.9", ] [[package]] @@ -5099,7 +6011,7 @@ dependencies = [ "p3-monty-31", "p3-poseidon2", "p3-symmetric", - "rand", + "rand 0.8.5", "serde", ] @@ -5118,13 +6030,13 @@ name = "p3-bn254-fr" version = "0.1.0" source = "git+https://github.com/Plonky3/Plonky3.git?rev=1ba4e5c#1ba4e5c40417f4f7aae86bcca56b6484b4b2490b" dependencies = [ - "ff 0.13.0", + "ff 0.13.1", "halo2curves", "num-bigint 0.4.6", "p3-field", "p3-poseidon2", "p3-symmetric", - "rand", + "rand 0.8.5", "serde", ] @@ -5179,7 +6091,7 @@ dependencies = [ "nums", "p3-maybe-rayon", "p3-util", - "rand", + "rand 0.8.5", "serde", "tracing", ] @@ -5198,7 +6110,7 @@ dependencies = [ "p3-matrix", "p3-maybe-rayon", "p3-util", - "rand", + "rand 0.8.5", "serde", "tracing", ] @@ -5216,7 +6128,7 @@ dependencies = [ "p3-poseidon2", "p3-symmetric", "p3-util", - "rand", + "rand 0.8.5", "serde", ] @@ -5253,7 +6165,7 @@ dependencies = [ "p3-matrix", "p3-maybe-rayon", "p3-util", - "rand", + "rand 0.8.5", "tracing", ] @@ -5267,7 +6179,7 @@ dependencies = [ "p3-monty-31", "p3-poseidon2", "p3-symmetric", - "rand", + "rand 0.8.5", "serde", ] @@ -5280,7 +6192,7 @@ dependencies = [ "p3-field", "p3-maybe-rayon", "p3-util", - "rand", + "rand 0.8.5", "serde", "tracing", "transpose", @@ -5305,7 +6217,7 @@ dependencies = [ "p3-matrix", "p3-symmetric", "p3-util", - "rand", + "rand 0.8.5", ] [[package]] @@ -5320,7 +6232,7 @@ dependencies = [ "p3-maybe-rayon", "p3-symmetric", "p3-util", - "rand", + "rand 0.8.5", "serde", "tracing", ] @@ -5340,7 +6252,7 @@ dependencies = [ "p3-poseidon2", "p3-symmetric", "p3-util", - "rand", + "rand 0.8.5", "serde", "tracing", "transpose", @@ -5354,7 +6266,7 @@ dependencies = [ "p3-field", "p3-mds", "p3-symmetric", - "rand", + "rand 0.8.5", ] [[package]] @@ -5366,7 +6278,7 @@ dependencies = [ "p3-field", "p3-mds", "p3-symmetric", - "rand", + "rand 0.8.5", ] [[package]] @@ -5380,7 +6292,7 @@ dependencies = [ "p3-maybe-rayon", "p3-poseidon2", "p3-util", - "rand", + "rand 0.8.5", "tikv-jemallocator", "tracing", ] @@ -5464,7 +6376,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -5497,7 +6409,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700" dependencies = [ "base64ct", - "rand_core", + "rand_core 0.6.4", "subtle", ] @@ -5511,7 +6423,7 @@ dependencies = [ "ff 0.12.1", "group 0.12.1", "lazy_static", - "rand", + "rand 0.8.5", "static_assertions", "subtle", ] @@ -5523,10 +6435,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3e57598f73cc7e1b2ac63c79c517b31a0877cd7c402cdcaa311b5208de7a095" dependencies = [ "blake2b_simd", - "ff 0.13.0", + "ff 0.13.1", "group 0.13.0", "lazy_static", - "rand", + "rand 0.8.5", "static_assertions", "subtle", ] @@ -5552,7 +6464,7 @@ dependencies = [ "digest 0.10.7", "hmac", "password-hash", - "sha2", + "sha2 0.10.9", ] [[package]] @@ -5575,7 +6487,7 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -5586,12 +6498,12 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.7.15" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b7cafe60d6cf8e62e1b9b2ea516a089c008945bb5a275416789e7db0bc199dc" +checksum = "198db74531d58c70a361c42201efde7e2591e976d518caf7662a47dc5720e7b6" dependencies = [ "memchr", - "thiserror 2.0.11", + "thiserror 2.0.12", "ucd-trie", ] @@ -5602,7 +6514,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", - "indexmap 2.7.1", + "indexmap 2.9.0", ] [[package]] @@ -5622,7 +6534,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" dependencies = [ "phf_shared", - "rand", + "rand 0.8.5", ] [[package]] @@ -5635,7 +6547,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -5675,15 +6587,15 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" dependencies = [ - "der 0.7.9", + "der 0.7.10", "spki 0.7.3", ] [[package]] name = "pkg-config" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "plotters" @@ -5708,30 +6620,82 @@ checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" name = "plotters-svg" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" +checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" +dependencies = [ + "plotters-backend", +] + +[[package]] +name = "portable-atomic" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" + +[[package]] +name = "poseidon-primitives" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e4aaeda7a092e21165cc5f0cbc738e72a46f31c03c3cbd87b71ceae9d2d93bc" +dependencies = [ + "bitvec", + "ff 0.13.1", + "lazy_static", + "log", + "rand 0.8.5", + "rand_xorshift", + "thiserror 1.0.69", +] + +[[package]] +name = "postgres" +version = "0.19.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "363e6dfbdd780d3aa3597b6eb430db76bb315fa9bad7fae595bb8def808b8470" +dependencies = [ + "bytes", + "fallible-iterator", + "futures-util", + "log", + "tokio", + "tokio-postgres", +] + +[[package]] +name = "postgres-protocol" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76ff0abab4a9b844b93ef7b81f1efc0a366062aaef2cd702c76256b5dc075c54" dependencies = [ - "plotters-backend", + "base64 0.22.1", + "byteorder", + "bytes", + "fallible-iterator", + "hmac", + "md-5", + "memchr", + "rand 0.9.1", + "sha2 0.10.9", + "stringprep", ] [[package]] -name = "portable-atomic" -version = "1.10.0" +name = "postgres-types" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6" +checksum = "613283563cd90e1dfc3518d548caee47e0e725455ed619881f5cf21f36de4b48" +dependencies = [ + "bytes", + "fallible-iterator", + "postgres-protocol", +] [[package]] -name = "poseidon-primitives" -version = "0.2.0" +name = "potential_utf" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e4aaeda7a092e21165cc5f0cbc738e72a46f31c03c3cbd87b71ceae9d2d93bc" +checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" dependencies = [ - "bitvec", - "ff 0.13.0", - "lazy_static", - "log", - "rand", - "rand_xorshift", - "thiserror 1.0.69", + "zerovec", ] [[package]] @@ -5742,9 +6706,9 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" -version = "0.2.20" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ "zerocopy", ] @@ -5757,12 +6721,21 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" [[package]] name = "prettyplease" -version = "0.2.29" +version = "0.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6924ced06e1f7dfe3fa48d57b9f74f55d8915f5036121bef647ef4b204895fac" +checksum = "664ec5419c51e34154eec046ebcba56312d5a2fc3b09a06da188e1ad21afadf6" dependencies = [ "proc-macro2", - "syn 2.0.98", + "syn 2.0.101", +] + +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve 0.13.8", ] [[package]] @@ -5781,11 +6754,11 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "3.2.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" +checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" dependencies = [ - "toml_edit 0.22.24", + "toml_edit 0.22.26", ] [[package]] @@ -5807,14 +6780,14 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] name = "proc-macro2" -version = "1.0.93" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] @@ -5827,24 +6800,24 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", "version_check", "yansi 1.0.1", ] [[package]] name = "proptest" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4c2511913b88df1637da85cc8d96ec8e43a3f8bb8ccb71ee1ac240d6f3df58d" +checksum = "14cae93065090804185d3b75f0bf93b8eeda30c7a9b4a33d3bdb3988d6229e50" dependencies = [ - "bit-set", - "bit-vec", - "bitflags 2.8.0", + "bit-set 0.8.0", + "bit-vec 0.8.0", + "bitflags 2.9.1", "lazy_static", "num-traits", - "rand", - "rand_chacha", + "rand 0.8.5", + "rand_chacha 0.3.1", "rand_xorshift", "regex-syntax 0.8.5", "rusty-fork", @@ -5852,6 +6825,41 @@ dependencies = [ "unarray", ] +[[package]] +name = "pyo3" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5203598f366b11a02b13aa20cab591229ff0a89fd121a308a5df751d5fc9219" +dependencies = [ + "cfg-if", + "libc", + "memoffset", + "once_cell", + "portable-atomic", + "pyo3-build-config", + "pyo3-ffi", +] + +[[package]] +name = "pyo3-build-config" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99636d423fa2ca130fa5acde3059308006d46f98caac629418e53f7ebb1e9999" +dependencies = [ + "once_cell", + "target-lexicon 0.13.2", +] + +[[package]] +name = "pyo3-ffi" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78f9cf92ba9c409279bc3305b5409d90db2d2c22392d443a87df3a1adad59e33" +dependencies = [ + "libc", + "pyo3-build-config", +] + [[package]] name = "quanta" version = "0.12.5" @@ -5873,15 +6881,30 @@ version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" +[[package]] +name = "quickcheck" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "588f6378e4dd99458b60ec275b4477add41ce4fa9f64dcba6f15adccb19b50d6" +dependencies = [ + "rand 0.8.5", +] + [[package]] name = "quote" -version = "1.0.38" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" + [[package]] name = "radium" version = "0.7.0" @@ -5905,8 +6928,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha", - "rand_core", + "rand_chacha 0.3.1", + "rand_core 0.6.4", + "serde", +] + +[[package]] +name = "rand" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.3", "serde", ] @@ -5917,7 +6951,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.3", ] [[package]] @@ -5926,7 +6970,17 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.2.16", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.3", + "serde", ] [[package]] @@ -5935,16 +6989,16 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" dependencies = [ - "rand_core", + "rand_core 0.6.4", ] [[package]] name = "raw-cpuid" -version = "11.4.0" +version = "11.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "529468c1335c1c03919960dfefdb1b3648858c20d7ec2d0663e728e4a717efbc" +checksum = "c6df7ab838ed27997ba19a4664507e6f82b41fe6e20be42929332156e5e85146" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.1", ] [[package]] @@ -5969,11 +7023,11 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.11" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2f103c6d277498fbceb16e84d317e2a400f160f46904d5f5410848c829511a3" +checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.1", ] [[package]] @@ -5982,7 +7036,7 @@ version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.2.16", "libredox", "thiserror 1.0.69", ] @@ -6048,11 +7102,11 @@ dependencies = [ "encoding_rs", "futures-core", "futures-util", - "h2", + "h2 0.3.26", "http 0.2.12", "http-body 0.4.6", - "hyper", - "hyper-rustls", + "hyper 0.14.32", + "hyper-rustls 0.24.2", "ipnet", "js-sys", "log", @@ -6060,7 +7114,7 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls", + "rustls 0.21.12", "rustls-pemfile", "serde", "serde_json", @@ -6068,7 +7122,7 @@ dependencies = [ "sync_wrapper", "system-configuration", "tokio", - "tokio-rustls", + "tokio-rustls 0.24.1", "tower-service", "url", "wasm-bindgen", @@ -6080,46 +7134,162 @@ dependencies = [ [[package]] name = "revm" -version = "18.0.0" +version = "22.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15689a3c6a8d14b647b4666f2e236ef47b5a5133cdfd423f545947986fff7013" +checksum = "f5378e95ffe5c8377002dafeb6f7d370a55517cef7d6d6c16fc552253af3b123" +dependencies = [ + "revm-bytecode", + "revm-context", + "revm-context-interface", + "revm-database", + "revm-database-interface", + "revm-handler", + "revm-inspector", + "revm-interpreter", + "revm-precompile", + "revm-primitives 18.0.0", + "revm-state", +] + +[[package]] +name = "revm-bytecode" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e63e138d520c5c5bc25ecc82506e9e4e6e85a811809fc5251c594378dccabfc6" +dependencies = [ + "bitvec", + "phf", + "revm-primitives 18.0.0", + "serde", +] + +[[package]] +name = "revm-context" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9765628dfea4f3686aa8f2a72471c52801e6b38b601939ac16965f49bac66580" dependencies = [ - "auto_impl", "cfg-if", - "dyn-clone", + "derive-where", + "revm-bytecode", + "revm-context-interface", + "revm-database-interface", + "revm-primitives 18.0.0", + "revm-state", + "serde", +] + +[[package]] +name = "revm-context-interface" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82d74335aa1f14222cc4d3be1f62a029cc7dc03819cc8d080ff17b7e1d76375f" +dependencies = [ + "alloy-eip2930", + "alloy-eip7702", + "auto_impl", + "revm-database-interface", + "revm-primitives 18.0.0", + "revm-state", + "serde", +] + +[[package]] +name = "revm-database" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e5c80c5a2fd605f2119ee32a63fb3be941fb6a81ced8cdb3397abca28317224" +dependencies = [ + "alloy-eips", + "revm-bytecode", + "revm-database-interface", + "revm-primitives 18.0.0", + "revm-state", + "serde", +] + +[[package]] +name = "revm-database-interface" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0e4dfbc734b1ea67b5e8f8b3c7dc4283e2210d978cdaf6c7a45e97be5ea53b3" +dependencies = [ + "auto_impl", + "revm-primitives 18.0.0", + "revm-state", + "serde", +] + +[[package]] +name = "revm-handler" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8676379521c7bf179c31b685c5126ce7800eab5844122aef3231b97026d41a10" +dependencies = [ + "auto_impl", + "revm-bytecode", + "revm-context", + "revm-context-interface", + "revm-database-interface", "revm-interpreter", "revm-precompile", + "revm-primitives 18.0.0", + "revm-state", + "serde", +] + +[[package]] +name = "revm-inspector" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfed4ecf999a3f6ae776ae2d160478c5dca986a8c2d02168e04066b1e34c789e" +dependencies = [ + "auto_impl", + "revm-context", + "revm-database-interface", + "revm-handler", + "revm-interpreter", + "revm-primitives 18.0.0", + "revm-state", "serde", "serde_json", ] [[package]] name = "revm-interpreter" -version = "14.0.0" +version = "18.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74e3f11d0fed049a4a10f79820c59113a79b38aed4ebec786a79d5c667bfeb51" +checksum = "feb20260342003cfb791536e678ef5bbea1bfd1f8178b170e8885ff821985473" dependencies = [ - "revm-primitives 14.0.0", + "revm-bytecode", + "revm-context-interface", + "revm-primitives 18.0.0", "serde", ] [[package]] name = "revm-precompile" -version = "15.0.0" +version = "19.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e381060af24b750069a2b2d2c54bba273d84e8f5f9e8026fc9262298e26cc336" +checksum = "418e95eba68c9806c74f3e36cd5d2259170b61e90ac608b17ff8c435038ddace" dependencies = [ + "ark-bls12-381", + "ark-bn254 0.5.0", + "ark-ec 0.5.0", + "ark-ff 0.5.0", + "ark-serialize 0.5.0", "aurora-engine-modexp", "blst", "c-kzg", "cfg-if", "k256", + "libsecp256k1", "once_cell", - "revm-primitives 14.0.0", + "p256 0.13.2", + "revm-primitives 18.0.0", "ripemd", "secp256k1", - "sha2", - "substrate-bn", + "sha2 0.10.9", ] [[package]] @@ -6131,7 +7301,7 @@ dependencies = [ "alloy-primitives 0.4.2", "alloy-rlp", "auto_impl", - "bitflags 2.8.0", + "bitflags 2.9.1", "bitvec", "enumn", "hashbrown 0.14.5", @@ -6140,21 +7310,24 @@ dependencies = [ [[package]] name = "revm-primitives" -version = "14.0.0" +version = "18.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3702f132bb484f4f0d0ca4f6fbde3c82cfd745041abbedd6eda67730e1868ef0" +checksum = "1fc2283ff87358ec7501956c5dd8724a6c2be959c619c4861395ae5e0054575f" dependencies = [ - "alloy-eip2930", - "alloy-eip7702", - "alloy-primitives 0.8.25", - "auto_impl", - "bitflags 2.8.0", - "bitvec", - "c-kzg", - "cfg-if", - "dyn-clone", + "alloy-primitives 1.1.1", "enumn", - "hex", + "serde", +] + +[[package]] +name = "revm-state" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09dd121f6e66d75ab111fb51b4712f129511569bc3e41e6067ae760861418bd8" +dependencies = [ + "bitflags 2.9.1", + "revm-bytecode", + "revm-primitives 18.0.0", "serde", ] @@ -6181,13 +7354,13 @@ dependencies = [ [[package]] name = "ring" -version = "0.17.13" +version = "0.17.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ac5d832aa16abd7d1def883a8545280c20a60f523a370aa3a9617c2b8550ee" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", - "getrandom 0.2.15", + "getrandom 0.2.16", "libc", "untrusted", "windows-sys 0.52.0", @@ -6248,9 +7421,9 @@ dependencies = [ [[package]] name = "ruint" -version = "1.12.4" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5ef8fb1dd8de3870cb8400d51b4c2023854bbafd5431a3ac7e7317243e22d2f" +checksum = "78a46eb779843b2c4f21fac5773e25d6d5b7c8f0922876c91541790d2ca27eef" dependencies = [ "alloy-rlp", "ark-ff 0.3.0", @@ -6264,7 +7437,8 @@ dependencies = [ "parity-scale-codec", "primitive-types", "proptest", - "rand", + "rand 0.8.5", + "rand 0.9.1", "rlp", "ruint-macro", "serde", @@ -6317,7 +7491,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ - "semver 1.0.25", + "semver 1.0.26", ] [[package]] @@ -6326,10 +7500,23 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.1", + "errno", + "libc", + "linux-raw-sys 0.4.15", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustix" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" +dependencies = [ + "bitflags 2.9.1", "errno", "libc", - "linux-raw-sys", + "linux-raw-sys 0.9.4", "windows-sys 0.59.0", ] @@ -6341,10 +7528,24 @@ checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" dependencies = [ "log", "ring", - "rustls-webpki", + "rustls-webpki 0.101.7", "sct", ] +[[package]] +name = "rustls" +version = "0.23.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "730944ca083c1c233a75c09f199e973ca499344a2b7ba9e755c457e86fb4a321" +dependencies = [ + "aws-lc-rs", + "once_cell", + "rustls-pki-types", + "rustls-webpki 0.103.3", + "subtle", + "zeroize", +] + [[package]] name = "rustls-native-certs" version = "0.6.3" @@ -6354,7 +7555,19 @@ dependencies = [ "openssl-probe", "rustls-pemfile", "schannel", - "security-framework", + "security-framework 2.11.1", +] + +[[package]] +name = "rustls-native-certs" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcff2dd52b58a8d98a70243663a0d234c4e2b79235637849d15913394a247d3" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework 3.2.0", ] [[package]] @@ -6366,6 +7579,15 @@ dependencies = [ "base64 0.21.7", ] +[[package]] +name = "rustls-pki-types" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" +dependencies = [ + "zeroize", +] + [[package]] name = "rustls-webpki" version = "0.101.7" @@ -6376,11 +7598,23 @@ dependencies = [ "untrusted", ] +[[package]] +name = "rustls-webpki" +version = "0.103.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4a72fe2bcf7a6ac6fd7d0b9e5cb68aeb7d4c0a0271730218b3e92d43b4eb435" +dependencies = [ + "aws-lc-rs", + "ring", + "rustls-pki-types", + "untrusted", +] + [[package]] name = "rustversion" -version = "1.0.19" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" +checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" [[package]] name = "rusty-fork" @@ -6396,9 +7630,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.19" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "same-file" @@ -6430,7 +7664,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -6479,7 +7713,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" dependencies = [ "base16ct 0.2.0", - "der 0.7.9", + "der 0.7.10", "generic-array", "pkcs8 0.10.2", "subtle", @@ -6488,11 +7722,12 @@ dependencies = [ [[package]] name = "secp256k1" -version = "0.29.1" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113" +checksum = "b50c5943d326858130af85e049f2661ba3c78b26589b8ab98e65e80ae44a1252" dependencies = [ - "rand", + "bitcoin_hashes", + "rand 0.8.5", "secp256k1-sys", ] @@ -6511,8 +7746,21 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.8.0", - "core-foundation", + "bitflags 2.9.1", + "core-foundation 0.9.4", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271720403f46ca04f7ba6f55d438f8bd878d6b8ca0a1046e8228c4145bcbb316" +dependencies = [ + "bitflags 2.9.1", + "core-foundation 0.10.0", "core-foundation-sys", "libc", "security-framework-sys", @@ -6539,9 +7787,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.25" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03" +checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" dependencies = [ "serde", ] @@ -6557,9 +7805,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.218" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8dfc9d19bdbf6d17e22319da49161d5d0108e4188e8b680aef6299eed22df60" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" dependencies = [ "serde_derive", ] @@ -6584,22 +7832,22 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.218" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f09503e191f4e797cb8aac08e9a4a4695c5edf6a2e70e376d961ddd5c969f82b" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] name = "serde_json" -version = "1.0.139" +version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44f86c3acccc9c65b153fe1b85a3be07fe5515274ec9f0653b4a0875731c72a6" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" dependencies = [ - "indexmap 2.7.1", + "indexmap 2.9.0", "itoa", "memchr", "ryu", @@ -6647,7 +7895,7 @@ dependencies = [ "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.7.1", + "indexmap 2.9.0", "serde", "serde_derive", "serde_json", @@ -6664,7 +7912,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -6680,9 +7928,22 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.8" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + +[[package]] +name = "sha2" +version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", "cpufeatures", @@ -6726,9 +7987,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook-registry" -version = "1.4.2" +version = "1.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" dependencies = [ "libc", ] @@ -6740,7 +8001,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" dependencies = [ "digest 0.10.7", - "rand_core", + "rand_core 0.6.4", ] [[package]] @@ -6750,7 +8011,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ "digest 0.10.7", - "rand_core", + "rand_core 0.6.4", ] [[package]] @@ -6776,15 +8037,15 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.14.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" +checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" [[package]] name = "snark-verifier" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28e4c4ed1edca41687fe2d8a09ba30badb0a5cc7fa56dd1159d62aeab7c99ace" +checksum = "4d798d8ce8e29b8820ecc1028ac44cc4fc0f0296728af6fe6a0c4db05782c0a4" dependencies = [ "halo2-base", "halo2-ecc", @@ -6795,7 +8056,7 @@ dependencies = [ "num-integer", "num-traits", "pairing 0.23.0", - "rand", + "rand 0.8.5", "revm", "ruint", "serde", @@ -6804,9 +8065,9 @@ dependencies = [ [[package]] name = "snark-verifier-sdk" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "babff70ce6292fce03f692d68569f76b8f6710dbac7be7fe5f32c915909c9065" +checksum = "a338d065044702bf751e87cf353daac63e2fc4c53a3e323cbcd98c603ee6e66c" dependencies = [ "bincode", "ethereum-types", @@ -6818,8 +8079,8 @@ dependencies = [ "num-bigint 0.4.6", "num-integer", "num-traits", - "rand", - "rand_chacha", + "rand 0.8.5", + "rand_chacha 0.3.1", "serde", "serde_json", "snark-verifier", @@ -6827,9 +8088,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.5.8" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" +checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" dependencies = [ "libc", "windows-sys 0.52.0", @@ -6859,20 +8120,63 @@ checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" name = "spki" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" +checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" +dependencies = [ + "base64ct", + "der 0.6.1", +] + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der 0.7.10", +] + +[[package]] +name = "sqlformat" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bba3a93db0cc4f7bdece8bb09e77e2e785c20bfebf79eb8340ed80708048790" dependencies = [ - "base64ct", - "der 0.6.1", + "nom", + "unicode_categories", ] [[package]] -name = "spki" -version = "0.7.3" +name = "sqlx-core" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +checksum = "24ba59a9342a3d9bab6c56c118be528b27c9b60e490080e9711a04dccac83ef6" dependencies = [ - "base64ct", - "der 0.7.9", + "ahash", + "atoi", + "byteorder", + "bytes", + "crossbeam-queue", + "either", + "event-listener", + "futures-channel", + "futures-core", + "futures-intrusive", + "futures-io", + "futures-util", + "hashlink", + "hex", + "indexmap 2.9.0", + "log", + "memchr", + "once_cell", + "paste", + "percent-encoding", + "smallvec", + "sqlformat", + "thiserror 1.0.69", + "tracing", + "url", ] [[package]] @@ -6905,6 +8209,17 @@ dependencies = [ "precomputed-hash", ] +[[package]] +name = "stringprep" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1" +dependencies = [ + "unicode-bidi", + "unicode-normalization", + "unicode-properties", +] + [[package]] name = "strsim" version = "0.11.1" @@ -6930,20 +8245,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.98", -] - -[[package]] -name = "substrate-bn" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b5bbfa79abbae15dd642ea8176a21a635ff3c00059961d1ea27ad04e5b441c" -dependencies = [ - "byteorder", - "crunchy", - "lazy_static", - "rand", - "rustc-hex", + "syn 2.0.101", ] [[package]] @@ -6976,10 +8278,10 @@ dependencies = [ "hex", "once_cell", "reqwest", - "semver 1.0.25", + "semver 1.0.26", "serde", "serde_json", - "sha2", + "sha2 0.10.9", "thiserror 1.0.69", "url", "zip", @@ -6993,7 +8295,7 @@ checksum = "aa64b5e8eecd3a8af7cfc311e29db31a268a62d5953233d3e8243ec77a71c4e3" dependencies = [ "build_const", "hex", - "semver 1.0.25", + "semver 1.0.26", "serde_json", "svm-rs", ] @@ -7011,9 +8313,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.98" +version = "2.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" +checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" dependencies = [ "proc-macro2", "quote", @@ -7029,7 +8331,7 @@ dependencies = [ "paste", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -7040,13 +8342,13 @@ checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" [[package]] name = "synstructure" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -7056,7 +8358,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" dependencies = [ "bitflags 1.3.2", - "core-foundation", + "core-foundation 0.9.4", "system-configuration-sys", ] @@ -7082,17 +8384,22 @@ version = "0.12.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" +[[package]] +name = "target-lexicon" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e502f78cdbb8ba4718f566c418c52bc729126ffd16baee5baa718cf25dd5a69a" + [[package]] name = "tempfile" -version = "3.17.1" +version = "3.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22e5a0acb1f3f55f65cc4a866c361b2fb2a0ff6366785ae6fbb5f85df07ba230" +checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" dependencies = [ - "cfg-if", "fastrand", - "getrandom 0.3.1", + "getrandom 0.3.3", "once_cell", - "rustix", + "rustix 1.0.7", "windows-sys 0.59.0", ] @@ -7125,7 +8432,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -7136,7 +8443,7 @@ checksum = "5c89e72a01ed4c579669add59014b9a524d609c0c88c6a585ce37485879f6ffb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", "test-case-core", ] @@ -7148,7 +8455,7 @@ checksum = "e7f46083d221181166e5b6f6b1e5f1d499f3a76888826e6cb1d057554157cd0f" dependencies = [ "env_logger", "test-log-macros", - "tracing-subscriber", + "tracing-subscriber 0.3.19", ] [[package]] @@ -7159,7 +8466,7 @@ checksum = "888d0c3c6db53c0fdab160d2ed5e12ba745383d3e85813f2ea0f2b1475ab553f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -7173,11 +8480,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.11" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" dependencies = [ - "thiserror-impl 2.0.11", + "thiserror-impl 2.0.12", ] [[package]] @@ -7188,18 +8495,18 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] name = "thiserror-impl" -version = "2.0.11" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -7243,9 +8550,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.37" +version = "0.3.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21" +checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" dependencies = [ "deranged", "itoa", @@ -7260,15 +8567,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.2" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" +checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" [[package]] name = "time-macros" -version = "0.2.19" +version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de" +checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" dependencies = [ "num-conv", "time-core", @@ -7285,9 +8592,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.7.6" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" dependencies = [ "displaydoc", "zerovec", @@ -7303,11 +8610,26 @@ dependencies = [ "serde_json", ] +[[package]] +name = "tinyvec" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "tokio" -version = "1.43.1" +version = "1.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "492a604e2fd7f814268a378409e6c92b5525d747d10db9a229723f55a417958c" +checksum = "2513ca694ef9ede0fb23fe71a4ee4107cb102b9dc1930f6d0fd77aae068ae165" dependencies = [ "backtrace", "bytes", @@ -7328,7 +8650,33 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", +] + +[[package]] +name = "tokio-postgres" +version = "0.7.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c95d533c83082bb6490e0189acaa0bbeef9084e60471b696ca6988cd0541fb0" +dependencies = [ + "async-trait", + "byteorder", + "bytes", + "fallible-iterator", + "futures-channel", + "futures-util", + "log", + "parking_lot", + "percent-encoding", + "phf", + "pin-project-lite", + "postgres-protocol", + "postgres-types", + "rand 0.9.1", + "socket2", + "tokio", + "tokio-util", + "whoami", ] [[package]] @@ -7337,15 +8685,25 @@ version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ - "rustls", + "rustls 0.21.12", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" +dependencies = [ + "rustls 0.23.27", "tokio", ] [[package]] name = "tokio-util" -version = "0.7.13" +version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" +checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" dependencies = [ "bytes", "futures-core", @@ -7360,7 +8718,7 @@ version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" dependencies = [ - "indexmap 2.7.1", + "indexmap 2.9.0", "serde", "serde_spanned", "toml_datetime", @@ -7369,21 +8727,21 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.20" +version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148" +checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.22.24", + "toml_edit 0.22.26", ] [[package]] name = "toml_datetime" -version = "0.6.8" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" dependencies = [ "serde", ] @@ -7394,7 +8752,7 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.7.1", + "indexmap 2.9.0", "serde", "serde_spanned", "toml_datetime", @@ -7403,17 +8761,40 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.24" +version = "0.22.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" +checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" dependencies = [ - "indexmap 2.7.1", + "indexmap 2.9.0", "serde", "serde_spanned", "toml_datetime", - "winnow 0.7.3", + "toml_write", + "winnow 0.7.10", +] + +[[package]] +name = "toml_write" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfb942dfe1d8e29a7ee7fcbde5bd2b9a25fb89aa70caea2eba3bee836ff41076" + +[[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +dependencies = [ + "tower-layer", + "tower-service", ] +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + [[package]] name = "tower-service" version = "0.3.3" @@ -7426,6 +8807,7 @@ version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ + "log", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -7439,7 +8821,7 @@ checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -7462,7 +8844,7 @@ dependencies = [ "smallvec", "thiserror 1.0.69", "tracing", - "tracing-subscriber", + "tracing-subscriber 0.3.19", ] [[package]] @@ -7476,6 +8858,15 @@ dependencies = [ "tracing-core", ] +[[package]] +name = "tracing-subscriber" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e0d2eaa99c3c2e41547cfa109e910a68ea03823cccad4a0525dcbc9b01e8c71" +dependencies = [ + "tracing-core", +] + [[package]] name = "tracing-subscriber" version = "0.3.19" @@ -7549,11 +8940,32 @@ dependencies = [ "version_check", ] +[[package]] +name = "unicode-bidi" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" + [[package]] name = "unicode-ident" -version = "1.0.17" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "unicode-normalization" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-properties" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00e2473a93778eb0bad35909dff6a10d28e63f792f16ed15e404fca9d5eeedbe" +checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" [[package]] name = "unicode-width" @@ -7567,6 +8979,12 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" +[[package]] +name = "unicode_categories" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" + [[package]] name = "unroll" version = "0.1.5" @@ -7600,12 +9018,6 @@ version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" -[[package]] -name = "utf16_iter" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" - [[package]] name = "utf8_iter" version = "1.0.4" @@ -7620,9 +9032,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.13.2" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c1f41ffb7cf259f1ecc2876861a17e7142e63ead296f671f81f6ae85903e0d6" +checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" [[package]] name = "valuable" @@ -7697,13 +9109,19 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasi" -version = "0.13.3+wasi-0.2.2" +version = "0.14.2+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" dependencies = [ "wit-bindgen-rt", ] +[[package]] +name = "wasite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" + [[package]] name = "wasm-bindgen" version = "0.2.100" @@ -7726,7 +9144,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", "wasm-bindgen-shared", ] @@ -7761,7 +9179,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -7791,6 +9209,29 @@ version = "0.25.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix 0.38.44", +] + +[[package]] +name = "whoami" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6994d13118ab492c3c80c1f81928718159254c53c472bf9ce36f8dae4add02a7" +dependencies = [ + "redox_syscall", + "wasite", + "web-sys", +] + [[package]] name = "winapi" version = "0.3.9" @@ -7824,11 +9265,61 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-core" -version = "0.52.0" +version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" dependencies = [ - "windows-targets 0.52.6", + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "windows-interface" +version = "0.59.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "windows-link" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" + +[[package]] +name = "windows-result" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +dependencies = [ + "windows-link", ] [[package]] @@ -7882,13 +9373,29 @@ dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm", + "windows_i686_gnullvm 0.52.6", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows-targets" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" +dependencies = [ + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" @@ -7901,6 +9408,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + [[package]] name = "windows_aarch64_msvc" version = "0.48.5" @@ -7913,6 +9426,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + [[package]] name = "windows_i686_gnu" version = "0.48.5" @@ -7925,12 +9444,24 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + [[package]] name = "windows_i686_msvc" version = "0.48.5" @@ -7943,6 +9474,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" @@ -7955,6 +9492,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" @@ -7967,6 +9510,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + [[package]] name = "windows_x86_64_msvc" version = "0.48.5" @@ -7979,6 +9528,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + [[package]] name = "winnow" version = "0.5.40" @@ -7990,9 +9545,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.3" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7f4ea97f6f78012141bcdb6a216b2609f0979ada50b20ca5b52dde2eac2bb1" +checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec" dependencies = [ "memchr", ] @@ -8009,24 +9564,18 @@ dependencies = [ [[package]] name = "wit-bindgen-rt" -version = "0.33.0" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.1", ] -[[package]] -name = "write16" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" - [[package]] name = "writeable" -version = "0.5.5" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" +checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" [[package]] name = "wyz" @@ -8057,9 +9606,9 @@ checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" [[package]] name = "yoke" -version = "0.7.5" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" dependencies = [ "serde", "stable_deref_trait", @@ -8069,55 +9618,54 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.7.5" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", "synstructure", ] [[package]] name = "zerocopy" -version = "0.7.35" +version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" dependencies = [ - "byteorder", "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.35" +version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] name = "zerofrom" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", "synstructure", ] @@ -8138,14 +9686,25 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", +] + +[[package]] +name = "zerotrie" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", ] [[package]] name = "zerovec" -version = "0.10.4" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" dependencies = [ "yoke", "zerofrom", @@ -8154,13 +9713,13 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.10.3" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.98", + "syn 2.0.101", ] [[package]] @@ -8202,9 +9761,9 @@ dependencies = [ "jubjub", "lazy_static", "pasta_curves 0.5.1", - "rand", + "rand 0.8.5", "serde", - "sha2", + "sha2 0.10.9", "sha3", "subtle", ] diff --git a/Cargo.toml b/Cargo.toml index 4ac36c31d9..9d687e252b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,9 +61,7 @@ members = [ "extensions/ecc/sw-macros", "extensions/ecc/tests", "extensions/pairing/circuit", - "extensions/pairing/transpiler", "extensions/pairing/guest", - "extensions/pairing/tests", "guest-libs/verify_stark/guest", ] exclude = ["crates/sdk/example"] From 390a553717f75e464975cb64ba0a3ddbd0b22244 Mon Sep 17 00:00:00 2001 From: Avaneesh Kulkarni Date: Wed, 30 Apr 2025 19:11:41 -0400 Subject: [PATCH 21/41] Remove bigint integration tests from Cargo.toml --- Cargo.lock | 8 ++++++++ Cargo.toml | 1 - 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index f35f40abd8..5fbe435f3e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4722,6 +4722,7 @@ dependencies = [ "derive-new 0.6.0", "derive_more 1.0.0", "eyre", + "eyre", "halo2curves-axiom", "itertools 0.14.0", "num-bigint 0.4.6", @@ -4765,6 +4766,8 @@ dependencies = [ "openvm-algebra-moduli-macros", "openvm-custom-insn", "openvm-rv32im-guest", + "openvm-custom-insn", + "openvm-rv32im-guest", "serde-big-array", "strum_macros", ] @@ -5090,6 +5093,7 @@ version = "1.1.2" dependencies = [ "eyre", "halo2curves-axiom", + "halo2curves-axiom", "hex-literal", "num-bigint 0.4.6", "openvm-algebra-circuit", @@ -5263,6 +5267,8 @@ version = "1.1.2" dependencies = [ "openvm-platform", ] + "tiny-keccak", +] [[package]] name = "openvm-keccak256-transpiler" @@ -5825,6 +5831,8 @@ version = "1.1.2" dependencies = [ "openvm-platform", ] + "sha2", +] [[package]] name = "openvm-sha256-transpiler" diff --git a/Cargo.toml b/Cargo.toml index 9d687e252b..b27a39dab1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,6 @@ members = [ "extensions/bigint/circuit", "extensions/bigint/transpiler", "extensions/bigint/guest", - "extensions/bigint/tests", "extensions/keccak256/circuit", "extensions/keccak256/transpiler", "extensions/keccak256/guest", From ff70ae874779cd99fd0cf0396b14ffa3c3cf8aa5 Mon Sep 17 00:00:00 2001 From: Avaneesh Kulkarni Date: Wed, 30 Apr 2025 19:12:49 -0400 Subject: [PATCH 22/41] Fix rebase --- extensions/algebra/moduli-macros/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/algebra/moduli-macros/src/lib.rs b/extensions/algebra/moduli-macros/src/lib.rs index cc0c065fca..75b73e0899 100644 --- a/extensions/algebra/moduli-macros/src/lib.rs +++ b/extensions/algebra/moduli-macros/src/lib.rs @@ -1083,7 +1083,7 @@ pub fn moduli_init(input: TokenStream) -> TokenStream { externs.push(quote::quote_spanned! { span.into() => #[no_mangle] - extern "C" fn #setup_function() { + extern "C" fn #setup_extern_func() { #[cfg(target_os = "zkvm")] { let mut ptr = 0; From 72ccf70bcd3980f8441fc696ca9546acb41efad9 Mon Sep 17 00:00:00 2001 From: Avaneesh Kulkarni Date: Wed, 30 Apr 2025 19:23:03 -0400 Subject: [PATCH 23/41] Revert "Remove pairing guest library and tests (moved to new repo)" This reverts commit 27e82aa2595003df3afb9c9fd0de57fb995da696. --- Cargo.lock | 26 + .../pairing/guest/src/bls12_381/fp12.rs | 214 +++++ extensions/pairing/guest/src/bls12_381/fp2.rs | 76 ++ extensions/pairing/guest/src/bls12_381/mod.rs | 682 ++++++++++++++ .../pairing/guest/src/bls12_381/pairing.rs | 347 +++++++ .../pairing/guest/src/bls12_381/tests.rs | 317 +++++++ .../pairing/guest/src/bls12_381/utils.rs | 49 + extensions/pairing/guest/src/bn254/fp12.rs | 215 +++++ extensions/pairing/guest/src/bn254/fp2.rs | 76 ++ extensions/pairing/guest/src/bn254/mod.rs | 744 +++++++++++++++ extensions/pairing/guest/src/bn254/pairing.rs | 379 ++++++++ extensions/pairing/guest/src/bn254/tests.rs | 325 +++++++ extensions/pairing/guest/src/bn254/utils.rs | 49 + extensions/pairing/guest/src/lib.rs | 16 + extensions/pairing/tests/Cargo.toml | 34 + extensions/pairing/tests/programs/Cargo.toml | 51 + .../pairing/tests/programs/examples/bls_ec.rs | 11 + .../programs/examples/bls_final_exp_hint.rs | 24 + .../programs/examples/bn_final_exp_hint.rs | 24 + .../tests/programs/examples/fp12_mul.rs | 82 ++ .../tests/programs/examples/pairing_check.rs | 90 ++ .../examples/pairing_check_fallback.rs | 241 +++++ .../tests/programs/examples/pairing_line.rs | 147 +++ .../programs/examples/pairing_miller_loop.rs | 97 ++ .../programs/examples/pairing_miller_step.rs | 166 ++++ .../programs/openvm_init_bls_ec_bls12_381.rs | 3 + ...penvm_init_bls_final_exp_hint_bls12_381.rs | 4 + .../openvm_init_bn_final_exp_hint_bn254.rs | 4 + .../openvm_init_fp12_mul_bls12_381.rs | 4 + .../programs/openvm_init_fp12_mul_bn254.rs | 4 + .../openvm_init_pairing_check_bls12_381.rs | 4 + .../openvm_init_pairing_check_bn254.rs | 4 + ...m_init_pairing_check_fallback_bls12_381.rs | 4 + ...penvm_init_pairing_check_fallback_bn254.rs | 4 + .../openvm_init_pairing_line_bls12_381.rs | 4 + .../openvm_init_pairing_line_bn254.rs | 4 + ...envm_init_pairing_miller_loop_bls12_381.rs | 4 + .../openvm_init_pairing_miller_loop_bn254.rs | 4 + ...envm_init_pairing_miller_step_bls12_381.rs | 4 + .../openvm_init_pairing_miller_step_bn254.rs | 4 + extensions/pairing/tests/src/lib.rs | 884 ++++++++++++++++++ 41 files changed, 5425 insertions(+) create mode 100644 extensions/pairing/guest/src/bls12_381/fp12.rs create mode 100644 extensions/pairing/guest/src/bls12_381/fp2.rs create mode 100644 extensions/pairing/guest/src/bls12_381/mod.rs create mode 100644 extensions/pairing/guest/src/bls12_381/pairing.rs create mode 100644 extensions/pairing/guest/src/bls12_381/tests.rs create mode 100644 extensions/pairing/guest/src/bls12_381/utils.rs create mode 100644 extensions/pairing/guest/src/bn254/fp12.rs create mode 100644 extensions/pairing/guest/src/bn254/fp2.rs create mode 100644 extensions/pairing/guest/src/bn254/mod.rs create mode 100644 extensions/pairing/guest/src/bn254/pairing.rs create mode 100644 extensions/pairing/guest/src/bn254/tests.rs create mode 100644 extensions/pairing/guest/src/bn254/utils.rs create mode 100644 extensions/pairing/tests/Cargo.toml create mode 100644 extensions/pairing/tests/programs/Cargo.toml create mode 100644 extensions/pairing/tests/programs/examples/bls_ec.rs create mode 100644 extensions/pairing/tests/programs/examples/bls_final_exp_hint.rs create mode 100644 extensions/pairing/tests/programs/examples/bn_final_exp_hint.rs create mode 100644 extensions/pairing/tests/programs/examples/fp12_mul.rs create mode 100644 extensions/pairing/tests/programs/examples/pairing_check.rs create mode 100644 extensions/pairing/tests/programs/examples/pairing_check_fallback.rs create mode 100644 extensions/pairing/tests/programs/examples/pairing_line.rs create mode 100644 extensions/pairing/tests/programs/examples/pairing_miller_loop.rs create mode 100644 extensions/pairing/tests/programs/examples/pairing_miller_step.rs create mode 100644 extensions/pairing/tests/programs/openvm_init_bls_ec_bls12_381.rs create mode 100644 extensions/pairing/tests/programs/openvm_init_bls_final_exp_hint_bls12_381.rs create mode 100644 extensions/pairing/tests/programs/openvm_init_bn_final_exp_hint_bn254.rs create mode 100644 extensions/pairing/tests/programs/openvm_init_fp12_mul_bls12_381.rs create mode 100644 extensions/pairing/tests/programs/openvm_init_fp12_mul_bn254.rs create mode 100644 extensions/pairing/tests/programs/openvm_init_pairing_check_bls12_381.rs create mode 100644 extensions/pairing/tests/programs/openvm_init_pairing_check_bn254.rs create mode 100644 extensions/pairing/tests/programs/openvm_init_pairing_check_fallback_bls12_381.rs create mode 100644 extensions/pairing/tests/programs/openvm_init_pairing_check_fallback_bn254.rs create mode 100644 extensions/pairing/tests/programs/openvm_init_pairing_line_bls12_381.rs create mode 100644 extensions/pairing/tests/programs/openvm_init_pairing_line_bn254.rs create mode 100644 extensions/pairing/tests/programs/openvm_init_pairing_miller_loop_bls12_381.rs create mode 100644 extensions/pairing/tests/programs/openvm_init_pairing_miller_loop_bn254.rs create mode 100644 extensions/pairing/tests/programs/openvm_init_pairing_miller_step_bls12_381.rs create mode 100644 extensions/pairing/tests/programs/openvm_init_pairing_miller_step_bn254.rs create mode 100644 extensions/pairing/tests/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 5fbe435f3e..a3a6167df0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5531,6 +5531,32 @@ dependencies = [ "subtle", ] +[[package]] +name = "openvm-pairing-integration-tests" +version = "1.0.1-rc.0" +dependencies = [ + "eyre", + "halo2curves-axiom", + "num-bigint 0.4.6", + "num-traits", + "openvm", + "openvm-algebra-circuit", + "openvm-algebra-transpiler", + "openvm-circuit", + "openvm-ecc-circuit", + "openvm-ecc-guest", + "openvm-ecc-transpiler", + "openvm-instructions", + "openvm-pairing-circuit", + "openvm-pairing-guest", + "openvm-pairing-transpiler", + "openvm-rv32im-transpiler", + "openvm-stark-sdk", + "openvm-toolchain-tests", + "openvm-transpiler", + "rand", +] + [[package]] name = "openvm-pairing-transpiler" version = "1.1.2" diff --git a/extensions/pairing/guest/src/bls12_381/fp12.rs b/extensions/pairing/guest/src/bls12_381/fp12.rs new file mode 100644 index 0000000000..413f9377af --- /dev/null +++ b/extensions/pairing/guest/src/bls12_381/fp12.rs @@ -0,0 +1,214 @@ +use alloc::vec::Vec; +use core::ops::{Mul, MulAssign, Neg}; + +use openvm_algebra_guest::{ + field::{ComplexConjugate, FieldExtension}, + DivAssignUnsafe, DivUnsafe, Field, +}; + +use super::{Bls12_381, Fp, Fp2}; +use crate::pairing::{fp12_invert_assign, PairingIntrinsics, SexticExtField}; + +pub type Fp12 = SexticExtField; + +impl Fp12 { + pub fn invert(&self) -> Self { + let mut s = self.clone(); + fp12_invert_assign::(&mut s.c, &Bls12_381::XI); + s + } +} + +impl Field for Fp12 { + type SelfRef<'a> = &'a Self; + const ZERO: Self = Self::new([Fp2::ZERO; 6]); + const ONE: Self = Self::new([ + Fp2::ONE, + Fp2::ZERO, + Fp2::ZERO, + Fp2::ZERO, + Fp2::ZERO, + Fp2::ZERO, + ]); + + fn double_assign(&mut self) { + *self += self.clone(); + } + + fn square_assign(&mut self) { + *self *= self.clone(); + } +} + +impl FieldExtension for Fp12 { + const D: usize = 6; + type Coeffs = [Fp2; 6]; + + fn from_coeffs(coeffs: Self::Coeffs) -> Self { + Self::new(coeffs) + } + + fn from_bytes(bytes: &[u8]) -> Self { + assert_eq!(bytes.len(), 576); + Self::from_coeffs([ + Fp2::from_bytes(&bytes[0..96]), + Fp2::from_bytes(&bytes[96..192]), + Fp2::from_bytes(&bytes[192..288]), + Fp2::from_bytes(&bytes[288..384]), + Fp2::from_bytes(&bytes[384..480]), + Fp2::from_bytes(&bytes[480..576]), + ]) + } + + fn to_coeffs(self) -> Self::Coeffs { + self.c + } + + fn to_bytes(&self) -> Vec { + let mut bytes = Vec::with_capacity(576); + for coeff in self.clone().to_coeffs() { + bytes.extend_from_slice(&coeff.to_bytes()); + } + bytes + } + + fn embed(c0: Fp2) -> Self { + Self::new([c0, Fp2::ZERO, Fp2::ZERO, Fp2::ZERO, Fp2::ZERO, Fp2::ZERO]) + } + + /// We assume that the frobenius map power is < 12 + fn frobenius_map(&self, power: usize) -> Self { + if power & 1 != 0 { + let c0 = self.c[0].clone().conjugate(); + let c1 = self.c[1].clone().conjugate() * &Bls12_381::FROBENIUS_COEFFS[power][0]; + let c2 = self.c[2].clone().conjugate() * &Bls12_381::FROBENIUS_COEFFS[power][1]; + let c3 = self.c[3].clone().conjugate() * &Bls12_381::FROBENIUS_COEFFS[power][2]; + let c4 = self.c[4].clone().conjugate() * &Bls12_381::FROBENIUS_COEFFS[power][3]; + let c5 = self.c[5].clone().conjugate() * &Bls12_381::FROBENIUS_COEFFS[power][4]; + Self::new([c0, c1, c2, c3, c4, c5]) + } else { + let c0 = self.c[0].clone(); + let c1 = &self.c[1] * &Bls12_381::FROBENIUS_COEFFS[power][0]; + let c2 = &self.c[2] * &Bls12_381::FROBENIUS_COEFFS[power][1]; + let c3 = &self.c[3] * &Bls12_381::FROBENIUS_COEFFS[power][2]; + let c4 = &self.c[4] * &Bls12_381::FROBENIUS_COEFFS[power][3]; + let c5 = &self.c[5] * &Bls12_381::FROBENIUS_COEFFS[power][4]; + Self::new([c0, c1, c2, c3, c4, c5]) + } + } + + fn mul_base(&self, rhs: &Fp2) -> Self { + Self::new([ + &self.c[0] * rhs, + &self.c[1] * rhs, + &self.c[2] * rhs, + &self.c[3] * rhs, + &self.c[4] * rhs, + &self.c[5] * rhs, + ]) + } +} + +// This is ambiguous. It is conjugation for Fp12 over Fp6. +impl ComplexConjugate for Fp12 { + fn conjugate(self) -> Self { + let [c0, c1, c2, c3, c4, c5] = self.c; + Self::new([c0, -c1, c2, -c3, c4, -c5]) + } + + fn conjugate_assign(&mut self) { + self.c[1].neg_assign(); + self.c[3].neg_assign(); + self.c[5].neg_assign(); + } +} + +impl<'a> MulAssign<&'a Fp12> for Fp12 { + #[inline(always)] + fn mul_assign(&mut self, other: &'a Fp12) { + *self = crate::pairing::sextic_tower_mul(self, other, &Bls12_381::XI); + } +} + +impl<'a> Mul<&'a Fp12> for &'a Fp12 { + type Output = Fp12; + #[inline(always)] + fn mul(self, other: &'a Fp12) -> Self::Output { + crate::pairing::sextic_tower_mul(self, other, &Bls12_381::XI) + } +} + +impl MulAssign for Fp12 { + #[inline(always)] + fn mul_assign(&mut self, other: Self) { + self.mul_assign(&other); + } +} + +impl Mul for Fp12 { + type Output = Self; + #[inline(always)] + fn mul(mut self, other: Self) -> Self::Output { + self *= other; + self + } +} + +impl<'a> Mul<&'a Fp12> for Fp12 { + type Output = Self; + #[inline(always)] + fn mul(mut self, other: &'a Fp12) -> Self::Output { + self *= other; + self + } +} + +impl<'a> DivAssignUnsafe<&'a Fp12> for Fp12 { + #[inline(always)] + fn div_assign_unsafe(&mut self, other: &'a Fp12) { + *self *= other.invert(); + } +} + +impl<'a> DivUnsafe<&'a Fp12> for &'a Fp12 { + type Output = Fp12; + #[inline(always)] + fn div_unsafe(self, other: &'a Fp12) -> Self::Output { + let mut res = self.clone(); + res.div_assign_unsafe(other); + res + } +} + +impl DivAssignUnsafe for Fp12 { + #[inline(always)] + fn div_assign_unsafe(&mut self, other: Self) { + *self *= other.invert(); + } +} + +impl DivUnsafe for Fp12 { + type Output = Self; + #[inline(always)] + fn div_unsafe(mut self, other: Self) -> Self::Output { + self.div_assign_unsafe(other); + self + } +} + +impl<'a> DivUnsafe<&'a Fp12> for Fp12 { + type Output = Self; + #[inline(always)] + fn div_unsafe(mut self, other: &'a Fp12) -> Self::Output { + self.div_assign_unsafe(other); + self + } +} + +impl Neg for Fp12 { + type Output = Fp12; + #[inline(always)] + fn neg(self) -> Self::Output { + Self::ZERO - &self + } +} diff --git a/extensions/pairing/guest/src/bls12_381/fp2.rs b/extensions/pairing/guest/src/bls12_381/fp2.rs new file mode 100644 index 0000000000..20de223962 --- /dev/null +++ b/extensions/pairing/guest/src/bls12_381/fp2.rs @@ -0,0 +1,76 @@ +use alloc::vec::Vec; +use core::ops::Neg; + +use openvm_algebra_complex_macros::{complex_declare, complex_impl_field}; +use openvm_algebra_guest::{field::FieldExtension, Field, IntMod}; + +use super::Fp; + +#[cfg(not(target_os = "zkvm"))] +// Used in Fp2Extension config +pub const BLS12_381_COMPLEX_STRUCT_NAME: &str = "Bls12_381Fp2"; + +// The struct name needs to be globally unique for linking purposes. +// The mod_type is a path used only in the struct definition. +complex_declare! { + Bls12_381Fp2 { mod_type = Fp } +} + +complex_impl_field! { + Bls12_381Fp2, +} + +pub type Fp2 = Bls12_381Fp2; + +impl FieldExtension for Fp2 { + const D: usize = 2; + type Coeffs = [Fp; 2]; + + fn from_coeffs([c0, c1]: Self::Coeffs) -> Self { + Self { c0, c1 } + } + + fn from_bytes(bytes: &[u8]) -> Self { + assert_eq!(bytes.len(), 96); + Self::from_coeffs([ + Fp::from_const_bytes(bytes[0..48].try_into().unwrap()), + Fp::from_const_bytes(bytes[48..96].try_into().unwrap()), + ]) + } + + fn to_coeffs(self) -> Self::Coeffs { + [self.c0, self.c1] + } + + fn to_bytes(&self) -> Vec { + let mut bytes = Vec::with_capacity(96); + bytes.extend_from_slice(self.c0.as_le_bytes()); + bytes.extend_from_slice(self.c1.as_le_bytes()); + bytes + } + + fn embed(base_elem: Fp) -> Self { + Self { + c0: base_elem, + c1: ::ZERO, + } + } + + fn frobenius_map(&self, power: usize) -> Self { + if power % 2 == 0 { + self.clone() + } else { + Self { + c0: self.c0.clone(), + c1: (&self.c1).neg(), + } + } + } + + fn mul_base(&self, rhs: &Fp) -> Self { + Self { + c0: &self.c0 * rhs, + c1: &self.c1 * rhs, + } + } +} diff --git a/extensions/pairing/guest/src/bls12_381/mod.rs b/extensions/pairing/guest/src/bls12_381/mod.rs new file mode 100644 index 0000000000..f1940ebe21 --- /dev/null +++ b/extensions/pairing/guest/src/bls12_381/mod.rs @@ -0,0 +1,682 @@ +use core::ops::Neg; + +use openvm_algebra_guest::{Field, IntMod}; +use openvm_algebra_moduli_macros::moduli_declare; +use openvm_ecc_guest::{weierstrass::IntrinsicCurve, CyclicGroup, Group}; + +mod fp12; +mod fp2; +mod pairing; +#[cfg(all(feature = "halo2curves", not(target_os = "zkvm")))] +pub(crate) mod utils; + +pub use fp12::*; +pub use fp2::*; +use hex_literal::hex; +#[cfg(not(target_os = "zkvm"))] +use lazy_static::lazy_static; +#[cfg(not(target_os = "zkvm"))] +use num_bigint::BigUint; +use openvm_ecc_sw_macros::sw_declare; + +use crate::pairing::PairingIntrinsics; + +#[cfg(all(test, feature = "halo2curves", not(target_os = "zkvm")))] +mod tests; + +#[cfg(not(target_os = "zkvm"))] +lazy_static! { + pub static ref BLS12_381_MODULUS: BigUint = BigUint::from_bytes_be(&hex!( + "1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab" + )); + pub static ref BLS12_381_ORDER: BigUint = BigUint::from_bytes_be(&hex!( + "73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001" + )); +} + +pub const BLS12_381_XI_ISIZE: [isize; 2] = [1, 1]; +pub const BLS12_381_NUM_LIMBS: usize = 48; +pub const BLS12_381_LIMB_BITS: usize = 8; +pub const BLS12_381_BLOCK_SIZE: usize = 16; + +pub const BLS12_381_SEED_ABS: u64 = 0xd201000000010000; +// Encodes the Bls12_381 seed, x. +// x = sum_i BLS12_381_PSEUDO_BINARY_ENCODING[i] * 2^i +// where BLS12_381_PSEUDO_BINARY_ENCODING[i] is in {-1, 0, 1} +// Validated against BLS12_381_SEED_ABS by a test in tests.rs +pub const BLS12_381_PSEUDO_BINARY_ENCODING: [i8; 64] = [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, +]; + +moduli_declare! { + Bls12_381Fp { modulus = "0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab" }, + Bls12_381Scalar { modulus = "0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001" }, +} + +const CURVE_B: Bls12_381Fp = Bls12_381Fp::from_const_u8(4); + +sw_declare! { + Bls12_381G1Affine { mod_type = Bls12_381Fp, b = CURVE_B }, +} + +#[cfg(not(target_os = "zkvm"))] +// Used in WeierstrassExtension config +pub const BLS12_381_ECC_STRUCT_NAME: &str = "Bls12_381G1Affine"; + +pub type Fp = Bls12_381Fp; +pub type Scalar = Bls12_381Scalar; +/// Affine point representation of `Fp` points of BLS12-381. +/// **Note**: an instance of this type may be constructed that lies +/// on the curve but not necessarily in the prime order subgroup +/// because the group has cofactors. +pub type G1Affine = Bls12_381G1Affine; +pub use g2::G2Affine; + +impl Field for Fp { + type SelfRef<'a> = &'a Self; + const ZERO: Self = ::ZERO; + const ONE: Self = ::ONE; + + fn double_assign(&mut self) { + IntMod::double_assign(self); + } + + fn square_assign(&mut self) { + IntMod::square_assign(self); + } +} + +impl Field for Scalar { + type SelfRef<'a> = &'a Self; + const ZERO: Self = ::ZERO; + const ONE: Self = ::ONE; + + fn double_assign(&mut self) { + IntMod::double_assign(self); + } + + fn square_assign(&mut self) { + IntMod::square_assign(self); + } +} + +// https://hackmd.io/@benjaminion/bls12-381#Cofactor +// BLS12-381: The from_xy function will allow constructing elements that lie on the curve +// but aren't actually in the cyclic subgroup of prime order that is usually called G1. +impl CyclicGroup for G1Affine { + // https://github.com/zcash/librustzcash/blob/6e0364cd42a2b3d2b958a54771ef51a8db79dd29/pairing/src/bls12_381/README.md#generators + const GENERATOR: Self = G1Affine { + x: Bls12_381Fp::from_const_bytes(hex!( + "BBC622DB0AF03AFBEF1A7AF93FE8556C58AC1B173F3A4EA105B974974F8C68C30FACA94F8C63952694D79731A7D3F117" + )), + y: Bls12_381Fp::from_const_bytes(hex!( + "E1E7C5462923AA0CE48A88A244C73CD0EDB3042CCB18DB00F60AD0D595E0F5FCE48A1D74ED309EA0F1A0AAE381F4B308" + )), + }; + const NEG_GENERATOR: Self = G1Affine { + x: Bls12_381Fp::from_const_bytes(hex!( + "BBC622DB0AF03AFBEF1A7AF93FE8556C58AC1B173F3A4EA105B974974F8C68C30FACA94F8C63952694D79731A7D3F117" + )), + y: Bls12_381Fp::from_const_bytes(hex!( + "CAC239B9D6DC54AD1B75CB0EBA386F4E3642ACCAD5B95566C907B51DEF6A8167F2212ECFC8767DAAA845D555681D4D11" + )), + }; +} + +pub struct Bls12_381; + +impl IntrinsicCurve for Bls12_381 { + type Scalar = Scalar; + type Point = G1Affine; + + fn msm(coeffs: &[Self::Scalar], bases: &[Self::Point]) -> Self::Point { + openvm_ecc_guest::msm(coeffs, bases) + } +} + +// Define a G2Affine struct that implements curve operations using `Fp2` intrinsics +// but not special E(Fp2) intrinsics. +mod g2 { + use openvm_algebra_guest::Field; + use openvm_ecc_guest::{ + impl_sw_affine, impl_sw_group_ops, weierstrass::WeierstrassPoint, AffinePoint, Group, + }; + + use super::{Fp, Fp2}; + + const THREE: Fp2 = Fp2::new(Fp::from_const_u8(3), Fp::ZERO); + const B: Fp2 = Fp2::new(Fp::from_const_u8(4), Fp::from_const_u8(4)); + impl_sw_affine!(G2Affine, Fp2, THREE, B); + impl_sw_group_ops!(G2Affine, Fp2); +} + +impl PairingIntrinsics for Bls12_381 { + type Fp = Fp; + type Fp2 = Fp2; + type Fp12 = Fp12; + + const PAIRING_IDX: usize = 1; + // The sextic extension `Fp12` is `Fp2[X] / (X^6 - \xi)`, where `\xi` is a non-residue. + const XI: Fp2 = Fp2::new(Fp::from_const_u8(1), Fp::from_const_u8(1)); + const FP2_TWO: Fp2 = Fp2::new(Fp::from_const_u8(2), Fp::from_const_u8(0)); + const FP2_THREE: Fp2 = Fp2::new(Fp::from_const_u8(3), Fp::from_const_u8(0)); + + // Multiplication constants for the Frobenius map for coefficients in Fp2 c1..=c5 for powers + // 0..12 FROBENIUS_COEFFS\[i\]\[j\] = \xi^{(j + 1) * (p^i - 1)/6} when p = 1 (mod 6) + // These are validated against `halo2curves::bls12_381::FROBENIUS_COEFF_FQ12_C1` in tests.rs + const FROBENIUS_COEFFS: [[Self::Fp2; 5]; 12] = [ + [ + Fp2 { + c0: Bls12_381Fp(hex!( + "010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + )), + c1: Bls12_381Fp(hex!( + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + ")), + }, + Fp2 { + c0: Bls12_381Fp(hex!( + "010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + )), + c1: Bls12_381Fp(hex!( + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + ")), + }, + Fp2 { + c0: Bls12_381Fp(hex!( + "010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + )), + c1: Bls12_381Fp(hex!( + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + ")), + }, + Fp2 { + c0: Bls12_381Fp(hex!( + "010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + )), + c1: Bls12_381Fp(hex!( + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + ")), + }, + Fp2 { + c0: Bls12_381Fp(hex!( + "010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + )), + c1: Bls12_381Fp(hex!( + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + ")), + }, + ], + [ + Fp2 { + c0: Bls12_381Fp(hex!( + "b85f2392ed75078d3d81e7633da57ef6c4b9ba84d743247b4f5fbd3cfd03d60f1f0d2c20b4be31c26706bb02bfd30419" + )), + c1: Bls12_381Fp(hex!( + "f34adc6d128af72cc27e6c4dc15a2d285f3cf671c98e0cec6fb3c7b68747a154b89f1f2302e9e98832e0c4362b3efc00" + )) + }, + Fp2 { + c0: Bls12_381Fp(hex!( + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + )), + c1: Bls12_381Fp(hex!( + "acaa00000000fd8bfdff494feb2794409b5fb80f65297d89d49a75897d850daa85ded463864002ec99e67f39ea11011a" + )) + }, + Fp2 { + c0: Bls12_381Fp(hex!( + "09cce3edfb8410c8f405ec722f9967eec5419200176ef7775e43d3c2ab5d3948fe7fd16b6de331680b40ff37040eaf06" + )), + c1: Bls12_381Fp(hex!( + "09cce3edfb8410c8f405ec722f9967eec5419200176ef7775e43d3c2ab5d3948fe7fd16b6de331680b40ff37040eaf06" + )) + }, + Fp2 { + c0: Bls12_381Fp(hex!( + "adaa00000000fd8bfdff494feb2794409b5fb80f65297d89d49a75897d850daa85ded463864002ec99e67f39ea11011a" + )), + c1: Bls12_381Fp(hex!( + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + )) + }, + Fp2 { + c0: Bls12_381Fp(hex!( + "16810780e9fa189b32877f256e3e3ac666059c8e4ddfea8bee8f0b0c241698f345e0b1486bfa47dfd85f3a01d9cfb205" + )), + c1: Bls12_381Fp(hex!( + "9529f87f1605e61ecd78d48b90c17158bdf0146853f345dbd08279e76035df7091cc99fa4aadd36bc186453811424e14" + )) + }, + ], + [ + Fp2 { + c0: Bls12_381Fp(hex!( + "fffffeffffff012e02000a6213d817de8896f8e63ba9b3ddea770f6a07c669ba51ce76df2f67195f0000000000000000" + )), + c1: Bls12_381Fp(hex!( + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + )) + }, + Fp2 { + c0: Bls12_381Fp(hex!( + "fefffeffffff012e02000a6213d817de8896f8e63ba9b3ddea770f6a07c669ba51ce76df2f67195f0000000000000000" + )), + c1: Bls12_381Fp(hex!( + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + )) + }, + Fp2 { + c0: Bls12_381Fp(hex!( + "aaaafffffffffeb9ffff53b1feffab1e24f6b0f6a0d23067bf1285f3844b7764d7ac4b43b6a71b4b9ae67f39ea11011a" + )), + c1: Bls12_381Fp(hex!( + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + )) + }, + Fp2 { + c0: Bls12_381Fp(hex!( + "acaa00000000fd8bfdff494feb2794409b5fb80f65297d89d49a75897d850daa85ded463864002ec99e67f39ea11011a" + )), + c1: Bls12_381Fp(hex!( + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + )) + }, + Fp2 { + c0: Bls12_381Fp(hex!( + "adaa00000000fd8bfdff494feb2794409b5fb80f65297d89d49a75897d850daa85ded463864002ec99e67f39ea11011a" + )), + c1: Bls12_381Fp(hex!( + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + )) + }, + ], + [ + Fp2 { + c0: Bls12_381Fp(hex!( + "a2de1b12047beef10afa673ecf6644305eb41ef6896439ef60cfb130d9ed3d1cd92c7ad748c4e9e28ea68001e6035213" + )), + c1: Bls12_381Fp(hex!( + "09cce3edfb8410c8f405ec722f9967eec5419200176ef7775e43d3c2ab5d3948fe7fd16b6de331680b40ff37040eaf06" + )) + }, + Fp2 { + c0: Bls12_381Fp(hex!( + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + )), + c1: Bls12_381Fp(hex!( + "010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + )) + }, + Fp2 { + c0: Bls12_381Fp(hex!( + "a2de1b12047beef10afa673ecf6644305eb41ef6896439ef60cfb130d9ed3d1cd92c7ad748c4e9e28ea68001e6035213" + )), + c1: Bls12_381Fp(hex!( + "a2de1b12047beef10afa673ecf6644305eb41ef6896439ef60cfb130d9ed3d1cd92c7ad748c4e9e28ea68001e6035213" + )) + }, + Fp2 { + c0: Bls12_381Fp(hex!( + "aaaafffffffffeb9ffff53b1feffab1e24f6b0f6a0d23067bf1285f3844b7764d7ac4b43b6a71b4b9ae67f39ea11011a" + )), + c1: Bls12_381Fp(hex!( + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + )) + }, + Fp2 { + c0: Bls12_381Fp(hex!( + "09cce3edfb8410c8f405ec722f9967eec5419200176ef7775e43d3c2ab5d3948fe7fd16b6de331680b40ff37040eaf06" + )), + c1: Bls12_381Fp(hex!( + "a2de1b12047beef10afa673ecf6644305eb41ef6896439ef60cfb130d9ed3d1cd92c7ad748c4e9e28ea68001e6035213" + )) + }, + ], + [ + Fp2 { + c0: Bls12_381Fp(hex!( + "fefffeffffff012e02000a6213d817de8896f8e63ba9b3ddea770f6a07c669ba51ce76df2f67195f0000000000000000" + )), + c1: Bls12_381Fp(hex!( + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + )) + }, + Fp2 { + c0: Bls12_381Fp(hex!( + "acaa00000000fd8bfdff494feb2794409b5fb80f65297d89d49a75897d850daa85ded463864002ec99e67f39ea11011a" + )), + c1: Bls12_381Fp(hex!( + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + )) + }, + Fp2 { + c0: Bls12_381Fp(hex!( + "010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + )), + c1: Bls12_381Fp(hex!( + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + )) + }, + Fp2 { + c0: Bls12_381Fp(hex!( + "fefffeffffff012e02000a6213d817de8896f8e63ba9b3ddea770f6a07c669ba51ce76df2f67195f0000000000000000" + )), + c1: Bls12_381Fp(hex!( + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + )) + }, + Fp2 { + c0: Bls12_381Fp(hex!( + "acaa00000000fd8bfdff494feb2794409b5fb80f65297d89d49a75897d850daa85ded463864002ec99e67f39ea11011a" + )), + c1: Bls12_381Fp(hex!( + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + )) + }, + ], + [ + Fp2 { + c0: Bls12_381Fp(hex!( + "9529f87f1605e61ecd78d48b90c17158bdf0146853f345dbd08279e76035df7091cc99fa4aadd36bc186453811424e14" + )), + c1: Bls12_381Fp(hex!( + "16810780e9fa189b32877f256e3e3ac666059c8e4ddfea8bee8f0b0c241698f345e0b1486bfa47dfd85f3a01d9cfb205" + )) + }, + Fp2 { + c0: Bls12_381Fp(hex!( + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + )), + c1: Bls12_381Fp(hex!( + "fefffeffffff012e02000a6213d817de8896f8e63ba9b3ddea770f6a07c669ba51ce76df2f67195f0000000000000000" + )) + }, + Fp2 { + c0: Bls12_381Fp(hex!( + "09cce3edfb8410c8f405ec722f9967eec5419200176ef7775e43d3c2ab5d3948fe7fd16b6de331680b40ff37040eaf06" + )), + c1: Bls12_381Fp(hex!( + "09cce3edfb8410c8f405ec722f9967eec5419200176ef7775e43d3c2ab5d3948fe7fd16b6de331680b40ff37040eaf06" + )) + }, + Fp2 { + c0: Bls12_381Fp(hex!( + "fffffeffffff012e02000a6213d817de8896f8e63ba9b3ddea770f6a07c669ba51ce76df2f67195f0000000000000000" + )), + c1: Bls12_381Fp(hex!( + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + )) + }, + Fp2 { + c0: Bls12_381Fp(hex!( + "f34adc6d128af72cc27e6c4dc15a2d285f3cf671c98e0cec6fb3c7b68747a154b89f1f2302e9e98832e0c4362b3efc00" + )), + c1: Bls12_381Fp(hex!( + "b85f2392ed75078d3d81e7633da57ef6c4b9ba84d743247b4f5fbd3cfd03d60f1f0d2c20b4be31c26706bb02bfd30419" + )) + }, + ], + [ + Fp2 { + c0: Bls12_381Fp(hex!( + "aaaafffffffffeb9ffff53b1feffab1e24f6b0f6a0d23067bf1285f3844b7764d7ac4b43b6a71b4b9ae67f39ea11011a" + )), + c1: Bls12_381Fp(hex!( + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + )) + }, + Fp2 { + c0: Bls12_381Fp(hex!( + "010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + )), + c1: Bls12_381Fp(hex!( + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + )) + }, + Fp2 { + c0: Bls12_381Fp(hex!( + "aaaafffffffffeb9ffff53b1feffab1e24f6b0f6a0d23067bf1285f3844b7764d7ac4b43b6a71b4b9ae67f39ea11011a" + )), + c1: Bls12_381Fp(hex!( + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + )) + }, + Fp2 { + c0: Bls12_381Fp(hex!( + "010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + )), + c1: Bls12_381Fp(hex!( + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + )) + }, + Fp2 { + c0: Bls12_381Fp(hex!( + "aaaafffffffffeb9ffff53b1feffab1e24f6b0f6a0d23067bf1285f3844b7764d7ac4b43b6a71b4b9ae67f39ea11011a" + )), + c1: Bls12_381Fp(hex!( + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + )) + }, + ], + [ + Fp2 { + c0: Bls12_381Fp(hex!( + "f34adc6d128af72cc27e6c4dc15a2d285f3cf671c98e0cec6fb3c7b68747a154b89f1f2302e9e98832e0c4362b3efc00" + )), + c1: Bls12_381Fp(hex!( + "b85f2392ed75078d3d81e7633da57ef6c4b9ba84d743247b4f5fbd3cfd03d60f1f0d2c20b4be31c26706bb02bfd30419" + )) + }, + Fp2 { + c0: Bls12_381Fp(hex!( + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + )), + c1: Bls12_381Fp(hex!( + "acaa00000000fd8bfdff494feb2794409b5fb80f65297d89d49a75897d850daa85ded463864002ec99e67f39ea11011a" + )) + }, + Fp2 { + c0: Bls12_381Fp(hex!( + "a2de1b12047beef10afa673ecf6644305eb41ef6896439ef60cfb130d9ed3d1cd92c7ad748c4e9e28ea68001e6035213" + )), + c1: Bls12_381Fp(hex!( + "a2de1b12047beef10afa673ecf6644305eb41ef6896439ef60cfb130d9ed3d1cd92c7ad748c4e9e28ea68001e6035213" + )) + }, + Fp2 { + c0: Bls12_381Fp(hex!( + "adaa00000000fd8bfdff494feb2794409b5fb80f65297d89d49a75897d850daa85ded463864002ec99e67f39ea11011a" + )), + c1: Bls12_381Fp(hex!( + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + )) + }, + Fp2 { + c0: Bls12_381Fp(hex!( + "9529f87f1605e61ecd78d48b90c17158bdf0146853f345dbd08279e76035df7091cc99fa4aadd36bc186453811424e14" + )), + c1: Bls12_381Fp(hex!( + "16810780e9fa189b32877f256e3e3ac666059c8e4ddfea8bee8f0b0c241698f345e0b1486bfa47dfd85f3a01d9cfb205" + )) + }, + ], + [ + Fp2 { + c0: Bls12_381Fp(hex!( + "acaa00000000fd8bfdff494feb2794409b5fb80f65297d89d49a75897d850daa85ded463864002ec99e67f39ea11011a" + )), + c1: Bls12_381Fp(hex!( + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + )) + }, + Fp2 { + c0: Bls12_381Fp(hex!( + "fefffeffffff012e02000a6213d817de8896f8e63ba9b3ddea770f6a07c669ba51ce76df2f67195f0000000000000000" + )), + c1: Bls12_381Fp(hex!( + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + )) + }, + Fp2 { + c0: Bls12_381Fp(hex!( + "010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + )), + c1: Bls12_381Fp(hex!( + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + )) + }, + Fp2 { + c0: Bls12_381Fp(hex!( + "acaa00000000fd8bfdff494feb2794409b5fb80f65297d89d49a75897d850daa85ded463864002ec99e67f39ea11011a" + )), + c1: Bls12_381Fp(hex!( + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + )) + }, + Fp2 { + c0: Bls12_381Fp(hex!( + "fefffeffffff012e02000a6213d817de8896f8e63ba9b3ddea770f6a07c669ba51ce76df2f67195f0000000000000000" + )), + c1: Bls12_381Fp(hex!( + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + )) + }, + ], + [ + Fp2 { + c0: Bls12_381Fp(hex!( + "09cce3edfb8410c8f405ec722f9967eec5419200176ef7775e43d3c2ab5d3948fe7fd16b6de331680b40ff37040eaf06" + )), + c1: Bls12_381Fp(hex!( + "a2de1b12047beef10afa673ecf6644305eb41ef6896439ef60cfb130d9ed3d1cd92c7ad748c4e9e28ea68001e6035213" + )) + }, + Fp2 { + c0: Bls12_381Fp(hex!( + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + )), + c1: Bls12_381Fp(hex!( + "010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + )) + }, + Fp2 { + c0: Bls12_381Fp(hex!( + "09cce3edfb8410c8f405ec722f9967eec5419200176ef7775e43d3c2ab5d3948fe7fd16b6de331680b40ff37040eaf06" + )), + c1: Bls12_381Fp(hex!( + "09cce3edfb8410c8f405ec722f9967eec5419200176ef7775e43d3c2ab5d3948fe7fd16b6de331680b40ff37040eaf06" + )) + }, + Fp2 { + c0: Bls12_381Fp(hex!( + "aaaafffffffffeb9ffff53b1feffab1e24f6b0f6a0d23067bf1285f3844b7764d7ac4b43b6a71b4b9ae67f39ea11011a" + )), + c1: Bls12_381Fp(hex!( + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + )) + }, + Fp2 { + c0: Bls12_381Fp(hex!( + "a2de1b12047beef10afa673ecf6644305eb41ef6896439ef60cfb130d9ed3d1cd92c7ad748c4e9e28ea68001e6035213" + )), + c1: Bls12_381Fp(hex!( + "09cce3edfb8410c8f405ec722f9967eec5419200176ef7775e43d3c2ab5d3948fe7fd16b6de331680b40ff37040eaf06" + )) + }, + ], + [ + Fp2 { + c0: Bls12_381Fp(hex!( + "adaa00000000fd8bfdff494feb2794409b5fb80f65297d89d49a75897d850daa85ded463864002ec99e67f39ea11011a" + )), + c1: Bls12_381Fp(hex!( + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + )) + }, + Fp2 { + c0: Bls12_381Fp(hex!( + "acaa00000000fd8bfdff494feb2794409b5fb80f65297d89d49a75897d850daa85ded463864002ec99e67f39ea11011a" + )), + c1: Bls12_381Fp(hex!( + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + )) + }, + Fp2 { + c0: Bls12_381Fp(hex!( + "aaaafffffffffeb9ffff53b1feffab1e24f6b0f6a0d23067bf1285f3844b7764d7ac4b43b6a71b4b9ae67f39ea11011a" + )), + c1: Bls12_381Fp(hex!( + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + )) + }, + Fp2 { + c0: Bls12_381Fp(hex!( + "fefffeffffff012e02000a6213d817de8896f8e63ba9b3ddea770f6a07c669ba51ce76df2f67195f0000000000000000" + )), + c1: Bls12_381Fp(hex!( + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + )) + }, + Fp2 { + c0: Bls12_381Fp(hex!( + "fffffeffffff012e02000a6213d817de8896f8e63ba9b3ddea770f6a07c669ba51ce76df2f67195f0000000000000000" + )), + c1: Bls12_381Fp(hex!( + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + )) + }, + ], + [ + Fp2 { + c0: Bls12_381Fp(hex!( + "16810780e9fa189b32877f256e3e3ac666059c8e4ddfea8bee8f0b0c241698f345e0b1486bfa47dfd85f3a01d9cfb205" + )), + c1: Bls12_381Fp(hex!( + "9529f87f1605e61ecd78d48b90c17158bdf0146853f345dbd08279e76035df7091cc99fa4aadd36bc186453811424e14" + )) + }, + Fp2 { + c0: Bls12_381Fp(hex!( + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + )), + c1: Bls12_381Fp(hex!( + "fefffeffffff012e02000a6213d817de8896f8e63ba9b3ddea770f6a07c669ba51ce76df2f67195f0000000000000000" + )) + }, + Fp2 { + c0: Bls12_381Fp(hex!( + "a2de1b12047beef10afa673ecf6644305eb41ef6896439ef60cfb130d9ed3d1cd92c7ad748c4e9e28ea68001e6035213" + )), + c1: Bls12_381Fp(hex!( + "a2de1b12047beef10afa673ecf6644305eb41ef6896439ef60cfb130d9ed3d1cd92c7ad748c4e9e28ea68001e6035213" + )) + }, + Fp2 { + c0: Bls12_381Fp(hex!( + "fffffeffffff012e02000a6213d817de8896f8e63ba9b3ddea770f6a07c669ba51ce76df2f67195f0000000000000000" + )), + c1: Bls12_381Fp(hex!( + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + )) + }, + Fp2 { + c0: Bls12_381Fp(hex!( + "b85f2392ed75078d3d81e7633da57ef6c4b9ba84d743247b4f5fbd3cfd03d60f1f0d2c20b4be31c26706bb02bfd30419" + )), + c1: Bls12_381Fp(hex!( + "f34adc6d128af72cc27e6c4dc15a2d285f3cf671c98e0cec6fb3c7b68747a154b89f1f2302e9e98832e0c4362b3efc00" + )) + }, + ], + ]; +} + +impl Bls12_381 { + // FINAL_EXPONENT = (p^12 - 1) / r in big-endian + // Validated by a test in test.rs + pub const FINAL_EXPONENT: [u8; 540] = hex!( + "02ee1db5dcc825b7e1bda9c0496a1c0a89ee0193d4977b3f7d4507d07363baa13f8d14a917848517badc3a43d1073776ab353f2c30698e8cc7deada9c0aadff5e9cfee9a074e43b9a660835cc872ee83ff3a0f0f1c0ad0d6106feaf4e347aa68ad49466fa927e7bb9375331807a0dce2630d9aa4b113f414386b0e8819328148978e2b0dd39099b86e1ab656d2670d93e4d7acdd350da5359bc73ab61a0c5bf24c374693c49f570bcd2b01f3077ffb10bf24dde41064837f27611212596bc293c8d4c01f25118790f4684d0b9c40a68eb74bb22a40ee7169cdc1041296532fef459f12438dfc8e2886ef965e61a474c5c85b0129127a1b5ad0463434724538411d1676a53b5a62eb34c05739334f46c02c3f0bd0c55d3109cd15948d0a1fad20044ce6ad4c6bec3ec03ef19592004cedd556952c6d8823b19dadd7c2498345c6e5308f1c511291097db60b1749bf9b71a9f9e0100418a3ef0bc627751bbd81367066bca6a4c1b6dcfc5cceb73fc56947a403577dfa9e13c24ea820b09c1d9f7c31759c3635de3f7a3639991708e88adce88177456c49637fd7961be1a4c7e79fb02faa732e2f3ec2bea83d196283313492caa9d4aff1c910e9622d2a73f62537f2701aaef6539314043f7bbce5b78c7869aeb2181a67e49eeed2161daf3f881bd88592d767f67c4717489119226c2f011d4cab803e9d71650a6f80698e2f8491d12191a04406fbc8fbd5f48925f98630e68bfb24c0bcb9b55df57510" + ); +} diff --git a/extensions/pairing/guest/src/bls12_381/pairing.rs b/extensions/pairing/guest/src/bls12_381/pairing.rs new file mode 100644 index 0000000000..9cd7ade4a5 --- /dev/null +++ b/extensions/pairing/guest/src/bls12_381/pairing.rs @@ -0,0 +1,347 @@ +use alloc::vec::Vec; + +use itertools::izip; +use openvm_algebra_guest::{ + field::{ComplexConjugate, FieldExtension}, + DivUnsafe, Field, +}; +use openvm_ecc_guest::AffinePoint; +#[cfg(target_os = "zkvm")] +use { + crate::{PairingBaseFunct7, OPCODE, PAIRING_FUNCT3}, + core::mem::MaybeUninit, + openvm_platform::custom_insn_r, + openvm_rv32im_guest, + openvm_rv32im_guest::hint_buffer_u32, +}; + +use super::{Bls12_381, Fp, Fp12, Fp2, BLS12_381_PSEUDO_BINARY_ENCODING, BLS12_381_SEED_ABS}; +use crate::pairing::{ + exp_check_fallback, Evaluatable, EvaluatedLine, FromLineMType, LineMulMType, MillerStep, + MultiMillerLoop, PairingCheck, PairingCheckError, PairingIntrinsics, UnevaluatedLine, +}; +#[cfg(all(feature = "halo2curves", not(target_os = "zkvm")))] +use crate::{ + bls12_381::utils::{ + convert_bls12381_fp2_to_halo2_fq2, convert_bls12381_fp_to_halo2_fq, + convert_bls12381_halo2_fq12_to_fp12, + }, + halo2curves_shims::bls12_381::Bls12_381 as Halo2CurvesBls12_381, + pairing::FinalExp, +}; + +impl Evaluatable for UnevaluatedLine { + fn evaluate(&self, xy_frac: &(Fp, Fp)) -> EvaluatedLine { + let (x_over_y, y_inv) = xy_frac; + // Represents the line L(x,y) = 1 + b (x/y) w^-1 + c (1/y) w^-3 + EvaluatedLine { + b: self.b.mul_base(x_over_y), + c: self.c.mul_base(y_inv), + } + } +} + +impl FromLineMType for Fp12 { + // Since multiplying by w^3 doesn't change the miller loop result, we transform the line + // into L_new(x,y) = w^3 L(x,y) = w^3 + b (x/y) w^2 + c (1/y) + fn from_evaluated_line_m_type(line: EvaluatedLine) -> Fp12 { + Fp12::from_coeffs([line.c, Fp2::ZERO, line.b, Fp2::ONE, Fp2::ZERO, Fp2::ZERO]) + } +} + +// TODO[jpw]: make this into a macro depending on P::PAIRING_IDX when we have more curves +impl LineMulMType for Bls12_381 { + /// Multiplies two lines in 023-form to get an element in 02345-form + fn mul_023_by_023(l0: &EvaluatedLine, l1: &EvaluatedLine) -> [Fp2; 5] { + // l0 = c0 + b0 w^2 + w^3 + let b0 = &l0.b; + let c0 = &l0.c; + // l1 = c1 + b1 w^2 + w^3 + let b1 = &l1.b; + let c1 = &l1.c; + + // where w⁶ = xi + // l0 * l1 = c0c1 + (c0b1 + c1b0)w² + (c0 + c1)w³ + (b0b1)w⁴ + (b0 +b1)w⁵ + w⁶ + // = (c0c1 + xi) + (c0b1 + c1b0)w² + (c0 + c1)w³ + (b0b1)w⁴ + (b0 + b1)w⁵ + let x0 = c0 * c1 + Bls12_381::XI; + let x2 = c0 * b1 + c1 * b0; + let x3 = c0 + c1; + let x4 = b0 * b1; + let x5 = b0 + b1; + + [x0, x2, x3, x4, x5] + } + + /// Multiplies a line in 02345-form with a Fp12 element to get an Fp12 element + fn mul_by_023(f: &Fp12, l: &EvaluatedLine) -> Fp12 { + // this is only used if the number of lines is odd, which doesn't happen for our + // applications right now, so we can use this suboptimal implementation + Fp12::from_evaluated_line_m_type(l.clone()) * f + } + + /// Multiplies a line in 02345-form with a Fp12 element to get an Fp12 element + fn mul_by_02345(f: &Fp12, x: &[Fp2; 5]) -> Fp12 { + // we update the order of the coefficients to match the Fp12 coefficient ordering: + // Fp12 { + // c0: Fp6 { + // c0: x0, + // c1: x2, + // c2: x4, + // }, + // c1: Fp6 { + // c0: x1, + // c1: x3, + // c2: x5, + // }, + // } + let o0 = &x[0]; // coeff x0 + let o1 = &x[1]; // coeff x2 + let o2 = &x[3]; // coeff x4 + let o4 = &x[2]; // coeff x3 + let o5 = &x[4]; // coeff x5 + + let xi = &Bls12_381::XI; + + let self_coeffs = &f.c; + let s0 = &self_coeffs[0]; + let s1 = &self_coeffs[2]; + let s2 = &self_coeffs[4]; + let s3 = &self_coeffs[1]; + let s4 = &self_coeffs[3]; + let s5 = &self_coeffs[5]; + + // NOTE[yj]: Hand-calculated multiplication for Fp12 * 02345 ∈ Fp2; this is likely not the + // most efficient implementation c00 = cs0co0 + xi(cs1co2 + cs2co1 + cs3co5 + + // cs4co4) c01 = cs0co1 + cs1co0 + xi(cs2co2 + cs4co5 + cs5co4) + // c02 = cs0co2 + cs1co1 + cs2co0 + cs3co4 + xi(cs5co5) + // c10 = cs3co0 + xi(cs1co5 + cs2co4 + cs4co2 + cs5co1) + // c11 = cs0co4 + cs3co1 + cs4co0 + xi(cs2co5 + cs5co2) + // c12 = cs0co5 + cs1co4 + cs3co2 + cs4co1 + cs5co0 + // where cs*: self.c* + let c00 = s0 * o0 + xi * &(s1 * o2 + s2 * o1 + s3 * o5 + s4 * o4); + let c01 = s0 * o1 + s1 * o0 + xi * &(s2 * o2 + s4 * o5 + s5 * o4); + let c02 = s0 * o2 + s1 * o1 + s2 * o0 + s3 * o4 + xi * &(s5 * o5); + let c10 = s3 * o0 + xi * &(s1 * o5 + s2 * o4 + s4 * o2 + s5 * o1); + let c11 = s0 * o4 + s3 * o1 + s4 * o0 + xi * &(s2 * o5 + s5 * o2); + let c12 = s0 * o5 + s1 * o4 + s3 * o2 + s4 * o1 + s5 * o0; + + Fp12::from_coeffs([c00, c10, c01, c11, c02, c12]) + } +} + +#[allow(non_snake_case)] +impl MultiMillerLoop for Bls12_381 { + type Fp = Fp; + type Fp12 = Fp12; + + const SEED_ABS: u64 = BLS12_381_SEED_ABS; + const PSEUDO_BINARY_ENCODING: &[i8] = &BLS12_381_PSEUDO_BINARY_ENCODING; + + fn evaluate_lines_vec(f: Self::Fp12, lines: Vec>) -> Self::Fp12 { + let mut f = f; + let mut lines = lines; + if lines.len() % 2 == 1 { + f = Self::mul_by_023(&f, &lines.pop().unwrap()); + } + for chunk in lines.chunks(2) { + if let [line0, line1] = chunk { + let prod = Self::mul_023_by_023(line0, line1); + f = Self::mul_by_02345(&f, &prod); + } else { + panic!("lines.len() % 2 should be 0 at this point"); + } + } + f + } + + /// The expected output of this function when running the Miller loop with embedded exponent is + /// c^3 * l_{3Q} + fn pre_loop( + Q_acc: Vec>, + Q: &[AffinePoint], + c: Option, + xy_fracs: &[(Self::Fp, Self::Fp)], + ) -> (Self::Fp12, Vec>) { + let mut f = if let Some(mut c) = c { + // for the miller loop with embedded exponent, f will be set to c at the beginning of + // the function, and we will multiply by c again due to the last two values + // of the pseudo-binary encoding (BLS12_381_PSEUDO_BINARY_ENCODING) being 1. + // Therefore, the final value of f at the end of this block is c^3. + let mut c3 = c.clone(); + c.square_assign(); + c3 *= &c; + c3 + } else { + Self::Fp12::ONE + }; + + let mut Q_acc = Q_acc; + + // Special case the first iteration of the Miller loop with pseudo_binary_encoding = 1: + // this means that the first step is a double and add, but we need to separate the two steps + // since the optimized `miller_double_and_add_step` will fail because Q_acc is equal + // to Q_signed on the first iteration + let (Q_out_double, lines_2S) = Q_acc + .into_iter() + .map(|Q| Self::miller_double_step(&Q)) + .unzip::<_, _, Vec<_>, Vec<_>>(); + Q_acc = Q_out_double; + + let mut initial_lines = Vec::>::new(); + + let lines_iter = izip!(lines_2S.iter(), xy_fracs.iter()); + for (line_2S, xy_frac) in lines_iter { + let line = line_2S.evaluate(xy_frac); + initial_lines.push(line); + } + + let (Q_out_add, lines_S_plus_Q) = Q_acc + .iter() + .zip(Q.iter()) + .map(|(Q_acc, Q)| Self::miller_add_step(Q_acc, Q)) + .unzip::<_, _, Vec<_>, Vec<_>>(); + Q_acc = Q_out_add; + + let lines_iter = izip!(lines_S_plus_Q.iter(), xy_fracs.iter()); + for (lines_S_plus_Q, xy_frac) in lines_iter { + let line = lines_S_plus_Q.evaluate(xy_frac); + initial_lines.push(line); + } + + f = Self::evaluate_lines_vec(f, initial_lines); + + (f, Q_acc) + } + + /// After running the main body of the Miller loop, we conjugate f due to the curve seed x being + /// negative. + fn post_loop( + f: &Self::Fp12, + Q_acc: Vec>, + _Q: &[AffinePoint], + _c: Option, + _xy_fracs: &[(Self::Fp, Self::Fp)], + ) -> (Self::Fp12, Vec>) { + // Conjugate for negative component of the seed + // By Lemma 1 from https://www.iacr.org/archive/eurocrypt2011/66320047/66320047.pdf f_{x,Q} = conjugate( f_{|x|,Q} ) + let mut f = f.clone(); + f.conjugate_assign(); + (f, Q_acc) + } +} + +#[allow(non_snake_case)] +impl PairingCheck for Bls12_381 { + type Fp = Fp; + type Fp2 = Fp2; + type Fp12 = Fp12; + + #[allow(unused_variables)] + fn pairing_check_hint( + P: &[AffinePoint], + Q: &[AffinePoint], + ) -> (Self::Fp12, Self::Fp12) { + #[cfg(not(target_os = "zkvm"))] + { + #[cfg(not(feature = "halo2curves"))] + panic!("`halo2curves` feature must be enabled to use pairing check hint on host"); + + #[cfg(feature = "halo2curves")] + { + let p_halo2 = P + .iter() + .map(|p| { + AffinePoint::new( + convert_bls12381_fp_to_halo2_fq(p.x.clone()), + convert_bls12381_fp_to_halo2_fq(p.y.clone()), + ) + }) + .collect::>(); + let q_halo2 = Q + .iter() + .map(|q| { + AffinePoint::new( + convert_bls12381_fp2_to_halo2_fq2(q.x.clone()), + convert_bls12381_fp2_to_halo2_fq2(q.y.clone()), + ) + }) + .collect::>(); + let fq12 = Halo2CurvesBls12_381::multi_miller_loop(&p_halo2, &q_halo2); + let (c_fq12, s_fq12) = Halo2CurvesBls12_381::final_exp_hint(&fq12); + let c = convert_bls12381_halo2_fq12_to_fp12(c_fq12); + let s = convert_bls12381_halo2_fq12_to_fp12(s_fq12); + (c, s) + } + } + #[cfg(target_os = "zkvm")] + { + let hint = MaybeUninit::<(Fp12, Fp12)>::uninit(); + // We do not rely on the slice P's memory layout since rust does not guarantee it across + // compiler versions. + let p_fat_ptr = (P.as_ptr() as u32, P.len() as u32); + let q_fat_ptr = (Q.as_ptr() as u32, Q.len() as u32); + unsafe { + custom_insn_r!( + opcode = OPCODE, + funct3 = PAIRING_FUNCT3, + funct7 = ((Bls12_381::PAIRING_IDX as u8) * PairingBaseFunct7::PAIRING_MAX_KINDS + PairingBaseFunct7::HintFinalExp as u8), + rd = Const "x0", + rs1 = In &p_fat_ptr, + rs2 = In &q_fat_ptr + ); + let ptr = hint.as_ptr() as *const u8; + hint_buffer_u32!(ptr, (48 * 12 * 2) / 4); + hint.assume_init() + } + } + } + + fn pairing_check( + P: &[AffinePoint], + Q: &[AffinePoint], + ) -> Result<(), PairingCheckError> { + Self::try_honest_pairing_check(P, Q).unwrap_or_else(|| { + let f = Self::multi_miller_loop(P, Q); + exp_check_fallback(&f, &Self::FINAL_EXPONENT) + }) + } +} + +#[allow(non_snake_case)] +impl Bls12_381 { + // The paper only describes the implementation for Bn254, so we use the gnark implementation for + // Bls12_381. Adapted from the gnark implementation: + // https://github.com/Consensys/gnark/blob/af754dd1c47a92be375930ae1abfbd134c5310d8/std/algebra/emulated/fields_bls12381/e12_pairing.go#L394C1-L395C1 + fn try_honest_pairing_check( + P: &[AffinePoint<::Fp>], + Q: &[AffinePoint<::Fp2>], + ) -> Option> { + let (c, s) = Self::pairing_check_hint(P, Q); + + // The gnark implementation checks that f * s = c^{q - x} where x is the curve seed. + // We check an equivalent condition: f * c^x * s = c^q. + // This is because we can compute f * c^x by embedding the c^x computation in the miller + // loop. + + // We compute c^q before c is consumed by conjugate() below + let c_q = FieldExtension::frobenius_map(&c, 1); + + // Since the Bls12_381 curve has a negative seed, the miller loop for Bls12_381 is computed + // as f_{Miller,x,Q}(P) = conjugate( f_{Miller,-x,Q}(P) * c^{-x} ). + // We will pass in the conjugate inverse of c into the miller loop so that we compute + // fc = conjugate( f_{Miller,-x,Q}(P) * c'^{-x} ) (where c' is the conjugate inverse of c) + // = f_{Miller,x,Q}(P) * c^x + let c_conj = c.conjugate(); + if c_conj == Fp12::ZERO { + return None; + } + let c_conj_inv = Fp12::ONE.div_unsafe(&c_conj); + let fc = Self::multi_miller_loop_embedded_exp(P, Q, Some(c_conj_inv)); + + if fc * s == c_q { + Some(Ok(())) + } else { + None + } + } +} diff --git a/extensions/pairing/guest/src/bls12_381/tests.rs b/extensions/pairing/guest/src/bls12_381/tests.rs new file mode 100644 index 0000000000..9ca38d8586 --- /dev/null +++ b/extensions/pairing/guest/src/bls12_381/tests.rs @@ -0,0 +1,317 @@ +use group::ff::Field; +use halo2curves_axiom::bls12_381::{ + Fq, Fq12, Fq2, Fq6, G1Affine, G2Affine, G2Prepared, MillerLoopResult, FROBENIUS_COEFF_FQ12_C1, +}; +use num_bigint::BigUint; +use num_traits::One; +use openvm_algebra_guest::{field::FieldExtension, IntMod}; +use openvm_ecc_guest::{weierstrass::WeierstrassPoint, AffinePoint}; +use rand::{rngs::StdRng, SeedableRng}; + +use super::{Fp, Fp12, Fp2, BLS12_381_MODULUS, BLS12_381_ORDER}; +use crate::{ + bls12_381::{ + utils::{ + convert_bls12381_fp12_to_halo2_fq12, convert_bls12381_halo2_fq12_to_fp12, + convert_bls12381_halo2_fq2_to_fp2, convert_bls12381_halo2_fq_to_fp, + convert_g2_affine_halo2_to_openvm, + }, + Bls12_381, G2Affine as OpenVmG2Affine, BLS12_381_PSEUDO_BINARY_ENCODING, + BLS12_381_SEED_ABS, + }, + pairing::{ + fp2_invert_assign, fp6_invert_assign, fp6_square_assign, FinalExp, MultiMillerLoop, + PairingCheck, PairingIntrinsics, + }, +}; + +#[test] +fn test_bls12381_frobenius_coeffs() { + #[allow(clippy::needless_range_loop)] + for i in 0..12 { + for j in 0..5 { + assert_eq!( + Bls12_381::FROBENIUS_COEFFS[i][j], + convert_bls12381_halo2_fq2_to_fp2(FROBENIUS_COEFF_FQ12_C1[i].pow([j as u64 + 1])), + "FROBENIUS_COEFFS[{}][{}] failed", + i, + j + ) + } + } +} + +#[test] +fn test_bls12381_frobenius() { + let mut rng = StdRng::seed_from_u64(15); + for pow in 0..12 { + let fq = Fq12::random(&mut rng); + let mut fq_frob = fq; + for _ in 0..pow { + fq_frob = fq_frob.frobenius_map(); + } + + let fp = convert_bls12381_halo2_fq12_to_fp12(fq); + let fp_frob = fp.frobenius_map(pow); + + assert_eq!(fp_frob, convert_bls12381_halo2_fq12_to_fp12(fq_frob)); + } +} + +#[test] +fn test_fp12_invert() { + let mut rng = StdRng::seed_from_u64(15); + let fq = Fq12::random(&mut rng); + let fq_inv = fq.invert().unwrap(); + + let fp = convert_bls12381_halo2_fq12_to_fp12(fq); + let fp_inv = fp.invert(); + assert_eq!(fp_inv, convert_bls12381_halo2_fq12_to_fp12(fq_inv)); +} + +#[test] +fn test_fp6_invert() { + let mut rng = StdRng::seed_from_u64(20); + let fq6 = Fq6 { + c0: Fq2::random(&mut rng), + c1: Fq2::random(&mut rng), + c2: Fq2::random(&mut rng), + }; + let fq6_inv = fq6.invert().unwrap(); + + let fp6c0 = convert_bls12381_halo2_fq2_to_fp2(fq6.c0); + let fp6c1 = convert_bls12381_halo2_fq2_to_fp2(fq6.c1); + let fp6c2 = convert_bls12381_halo2_fq2_to_fp2(fq6.c2); + let mut fp6 = [fp6c0, fp6c1, fp6c2]; + fp6_invert_assign::(&mut fp6, &Bls12_381::XI); + + let fq6_invc0 = convert_bls12381_halo2_fq2_to_fp2(fq6_inv.c0); + let fq6_invc1 = convert_bls12381_halo2_fq2_to_fp2(fq6_inv.c1); + let fq6_invc2 = convert_bls12381_halo2_fq2_to_fp2(fq6_inv.c2); + let fq6_inv = [fq6_invc0, fq6_invc1, fq6_invc2]; + assert_eq!(fp6, fq6_inv); +} + +#[test] +fn test_fp2_invert() { + let mut rng = StdRng::seed_from_u64(25); + let fq2 = Fq2::random(&mut rng); + let fq2_inv = fq2.invert().unwrap(); + + let mut fp2 = convert_bls12381_halo2_fq2_to_fp2(fq2).to_coeffs(); + fp2_invert_assign::(&mut fp2); + assert_eq!(fp2, convert_bls12381_halo2_fq2_to_fp2(fq2_inv).to_coeffs()); +} + +#[test] +fn test_fp6_square() { + let mut rng = StdRng::seed_from_u64(45); + let fq6 = Fq6 { + c0: Fq2::random(&mut rng), + c1: Fq2::random(&mut rng), + c2: Fq2::random(&mut rng), + }; + let fq6_sq = fq6.square(); + + let fp6c0 = convert_bls12381_halo2_fq2_to_fp2(fq6.c0); + let fp6c1 = convert_bls12381_halo2_fq2_to_fp2(fq6.c1); + let fp6c2 = convert_bls12381_halo2_fq2_to_fp2(fq6.c2); + let mut fp6 = [fp6c0, fp6c1, fp6c2]; + fp6_square_assign::(&mut fp6, &Bls12_381::XI); + + let fq6_sqc0 = convert_bls12381_halo2_fq2_to_fp2(fq6_sq.c0); + let fq6_sqc1 = convert_bls12381_halo2_fq2_to_fp2(fq6_sq.c1); + let fq6_sqc2 = convert_bls12381_halo2_fq2_to_fp2(fq6_sq.c2); + let fq6_sq = [fq6_sqc0, fq6_sqc1, fq6_sqc2]; + assert_eq!(fp6, fq6_sq); +} + +#[test] +fn test_fp2_square() { + let mut rng = StdRng::seed_from_u64(55); + let fq2 = Fq2::random(&mut rng); + let fq2_sq = fq2.square(); + + let fp2 = convert_bls12381_halo2_fq2_to_fp2(fq2); + let fp2_sq = &fp2 * &fp2; + assert_eq!(fp2_sq, convert_bls12381_halo2_fq2_to_fp2(fq2_sq)); +} + +#[test] +fn test_fp_add() { + let mut rng = StdRng::seed_from_u64(65); + let fq = Fq::random(&mut rng); + let fq_res = fq + Fq::one(); + + let fp = convert_bls12381_halo2_fq_to_fp(fq); + let fp_res = fp + Fp::ONE; + assert_eq!(fp_res, convert_bls12381_halo2_fq_to_fp(fq_res)); +} + +#[test] +fn test_fp_one() { + let fp_one = Fp::ONE; + let fq_one = Fq::ONE; + assert_eq!(fp_one, convert_bls12381_halo2_fq_to_fp(fq_one)); +} + +// Gt(Fq12) is not public +fn assert_miller_results_eq(a: MillerLoopResult, b: Fp12) { + let b = convert_bls12381_fp12_to_halo2_fq12(b); + crate::halo2curves_shims::bls12_381::tests::assert_miller_results_eq(a, b); +} + +#[test] +fn test_bls12381_miller_loop() { + let mut rng = StdRng::seed_from_u64(65); + let h2c_p = G1Affine::random(&mut rng); + let h2c_q = G2Affine::random(&mut rng); + + let p = AffinePoint { + x: convert_bls12381_halo2_fq_to_fp(h2c_p.x), + y: convert_bls12381_halo2_fq_to_fp(h2c_p.y), + }; + let q = AffinePoint { + x: convert_bls12381_halo2_fq2_to_fp2(h2c_q.x), + y: convert_bls12381_halo2_fq2_to_fp2(h2c_q.y), + }; + + // Compare against halo2curves implementation + let h2c_q_prepared = G2Prepared::from(h2c_q); + let compare_miller = + halo2curves_axiom::bls12_381::multi_miller_loop(&[(&h2c_p, &h2c_q_prepared)]); + let f = Bls12_381::multi_miller_loop(&[p], &[q]); + assert_miller_results_eq(compare_miller, f); +} + +#[test] +fn test_bls12381_miller_loop_identity() { + let mut rng = StdRng::seed_from_u64(33); + let h2c_p = G1Affine::identity(); + let h2c_q = G2Affine::random(&mut rng); + + let p = AffinePoint { + x: convert_bls12381_halo2_fq_to_fp(Fq::ZERO), + y: convert_bls12381_halo2_fq_to_fp(Fq::ZERO), + }; + let q = AffinePoint { + x: convert_bls12381_halo2_fq2_to_fp2(h2c_q.x), + y: convert_bls12381_halo2_fq2_to_fp2(h2c_q.y), + }; + + let f = Bls12_381::multi_miller_loop(&[p], &[q]); + // halo2curves implementation + let h2c_q_prepared = G2Prepared::from(h2c_q); + let compare_miller = + halo2curves_axiom::bls12_381::multi_miller_loop(&[(&h2c_p, &h2c_q_prepared)]); + assert_miller_results_eq(compare_miller, f); +} + +#[test] +fn test_bls12381_miller_loop_identity_2() { + let mut rng = StdRng::seed_from_u64(33); + let h2c_p = G1Affine::random(&mut rng); + let h2c_q = G2Affine::identity(); + let p = AffinePoint { + x: convert_bls12381_halo2_fq_to_fp(h2c_p.x), + y: convert_bls12381_halo2_fq_to_fp(h2c_p.y), + }; + let q = AffinePoint { + x: convert_bls12381_halo2_fq2_to_fp2(Fq2::ZERO), + y: convert_bls12381_halo2_fq2_to_fp2(Fq2::ZERO), + }; + + let f = Bls12_381::multi_miller_loop(&[p], &[q]); + // halo2curves implementation + let h2c_q_prepared = G2Prepared::from(h2c_q); + let compare_miller = + halo2curves_axiom::bls12_381::multi_miller_loop(&[(&h2c_p, &h2c_q_prepared)]); + assert_miller_results_eq(compare_miller, f); +} + +// test on host is enough since we are testing the curve formulas and not anything +// about intrinsic functions +#[test] +fn test_bls12381_g2_affine() { + let mut rng = StdRng::seed_from_u64(34); + for _ in 0..10 { + let p = G2Affine::random(&mut rng); + let q = G2Affine::random(&mut rng); + let expected_add = G2Affine::from(p + q); + let expected_sub = G2Affine::from(p - q); + let expected_neg = -p; + let expected_double = G2Affine::from(p + p); + let [p, q] = [p, q].map(|p| { + let x = convert_bls12381_halo2_fq2_to_fp2(p.x); + let y = convert_bls12381_halo2_fq2_to_fp2(p.y); + // check on curve + OpenVmG2Affine::from_xy(x, y).unwrap() + }); + let r_add = &p + &q; + let r_sub = &p - &q; + let r_neg = -&p; + let r_double = &p + &p; + + for (expected, actual) in [ + (expected_add, r_add), + (expected_sub, r_sub), + (expected_neg, r_neg), + (expected_double, r_double), + ] { + assert_eq!(convert_g2_affine_halo2_to_openvm(expected), actual); + } + } +} + +#[test] +fn test_bls12381_pairing_check_hint_host() { + let mut rng = StdRng::seed_from_u64(83); + let h2c_p = G1Affine::random(&mut rng); + let h2c_q = G2Affine::random(&mut rng); + + let p = AffinePoint { + x: convert_bls12381_halo2_fq_to_fp(h2c_p.x), + y: convert_bls12381_halo2_fq_to_fp(h2c_p.y), + }; + let q = AffinePoint { + x: convert_bls12381_halo2_fq2_to_fp2(h2c_q.x), + y: convert_bls12381_halo2_fq2_to_fp2(h2c_q.y), + }; + + let (c, s) = Bls12_381::pairing_check_hint(&[p], &[q]); + + let p_cmp = AffinePoint { + x: h2c_p.x, + y: h2c_p.y, + }; + let q_cmp = AffinePoint { + x: h2c_q.x, + y: h2c_q.y, + }; + + let f_cmp = + crate::halo2curves_shims::bls12_381::Bls12_381::multi_miller_loop(&[p_cmp], &[q_cmp]); + let (c_cmp, s_cmp) = crate::halo2curves_shims::bls12_381::Bls12_381::final_exp_hint(&f_cmp); + let c_cmp = convert_bls12381_halo2_fq12_to_fp12(c_cmp); + let s_cmp = convert_bls12381_halo2_fq12_to_fp12(s_cmp); + + assert_eq!(c, c_cmp); + assert_eq!(s, s_cmp); +} + +#[test] +fn test_bls12381_final_exponent() { + let final_exp = (BLS12_381_MODULUS.pow(12) - BigUint::one()) / BLS12_381_ORDER.clone(); + assert_eq!(Bls12_381::FINAL_EXPONENT.to_vec(), final_exp.to_bytes_be()); +} + +#[test] +fn test_bls12381_pseudo_binary_encoding() { + let mut x: i128 = 0; + let mut power_of_2 = 1; + for b in BLS12_381_PSEUDO_BINARY_ENCODING.iter() { + x += (*b as i128) * power_of_2; + power_of_2 *= 2; + } + assert_eq!(x.unsigned_abs(), BLS12_381_SEED_ABS as u128); +} diff --git a/extensions/pairing/guest/src/bls12_381/utils.rs b/extensions/pairing/guest/src/bls12_381/utils.rs new file mode 100644 index 0000000000..51c749c596 --- /dev/null +++ b/extensions/pairing/guest/src/bls12_381/utils.rs @@ -0,0 +1,49 @@ +use halo2curves_axiom::bls12_381::{Fq, Fq12, Fq2, G2Affine}; +use openvm_algebra_guest::{field::FieldExtension, IntMod}; +use openvm_ecc_guest::weierstrass::WeierstrassPoint; + +use super::{Fp, Fp12, Fp2}; +use crate::bls12_381::G2Affine as OpenVmG2Affine; + +pub(crate) fn convert_bls12381_halo2_fq_to_fp(x: Fq) -> Fp { + let bytes = x.to_bytes(); + Fp::from_le_bytes(&bytes) +} + +pub(crate) fn convert_bls12381_halo2_fq2_to_fp2(x: Fq2) -> Fp2 { + Fp2::new( + convert_bls12381_halo2_fq_to_fp(x.c0), + convert_bls12381_halo2_fq_to_fp(x.c1), + ) +} + +pub(crate) fn convert_bls12381_halo2_fq12_to_fp12(x: Fq12) -> Fp12 { + Fp12 { + c: x.to_coeffs().map(convert_bls12381_halo2_fq2_to_fp2), + } +} + +pub(crate) fn convert_bls12381_fp_to_halo2_fq(x: Fp) -> Fq { + Fq::from_bytes(&x.0).unwrap() +} + +pub(crate) fn convert_bls12381_fp2_to_halo2_fq2(x: Fp2) -> Fq2 { + Fq2 { + c0: convert_bls12381_fp_to_halo2_fq(x.c0.clone()), + c1: convert_bls12381_fp_to_halo2_fq(x.c1.clone()), + } +} + +#[allow(unused)] +pub(crate) fn convert_bls12381_fp12_to_halo2_fq12(x: Fp12) -> Fq12 { + let c = x.to_coeffs(); + Fq12::from_coeffs(c.map(convert_bls12381_fp2_to_halo2_fq2)) +} + +#[allow(unused)] +pub(crate) fn convert_g2_affine_halo2_to_openvm(p: G2Affine) -> OpenVmG2Affine { + OpenVmG2Affine::from_xy_unchecked( + convert_bls12381_halo2_fq2_to_fp2(p.x), + convert_bls12381_halo2_fq2_to_fp2(p.y), + ) +} diff --git a/extensions/pairing/guest/src/bn254/fp12.rs b/extensions/pairing/guest/src/bn254/fp12.rs new file mode 100644 index 0000000000..d8b9b07415 --- /dev/null +++ b/extensions/pairing/guest/src/bn254/fp12.rs @@ -0,0 +1,215 @@ +use alloc::vec::Vec; +use core::ops::{Mul, MulAssign, Neg}; + +use openvm_algebra_guest::{ + field::{ComplexConjugate, FieldExtension}, + DivAssignUnsafe, DivUnsafe, Field, +}; + +use super::{Bn254, Fp, Fp2}; +use crate::pairing::{fp12_invert_assign, PairingIntrinsics, SexticExtField}; + +pub type Fp12 = SexticExtField; + +impl Fp12 { + pub fn invert(&self) -> Self { + let mut s = self.clone(); + fp12_invert_assign::(&mut s.c, &Bn254::XI); + s + } +} + +impl Field for Fp12 { + type SelfRef<'a> = &'a Self; + const ZERO: Self = Self::new([Fp2::ZERO; 6]); + const ONE: Self = Self::new([ + Fp2::ONE, + Fp2::ZERO, + Fp2::ZERO, + Fp2::ZERO, + Fp2::ZERO, + Fp2::ZERO, + ]); + + fn double_assign(&mut self) { + *self += self.clone(); + } + + fn square_assign(&mut self) { + *self *= self.clone(); + } +} + +impl FieldExtension for Fp12 { + const D: usize = 6; + type Coeffs = [Fp2; 6]; + + fn from_coeffs(coeffs: Self::Coeffs) -> Self { + Self::new(coeffs) + } + + fn from_bytes(bytes: &[u8]) -> Self { + assert_eq!(bytes.len(), 384); + Self::from_coeffs([ + Fp2::from_bytes(&bytes[0..64]), + Fp2::from_bytes(&bytes[64..128]), + Fp2::from_bytes(&bytes[128..192]), + Fp2::from_bytes(&bytes[192..256]), + Fp2::from_bytes(&bytes[256..320]), + Fp2::from_bytes(&bytes[320..384]), + ]) + } + + fn to_coeffs(self) -> Self::Coeffs { + self.c + } + + fn to_bytes(&self) -> Vec { + let mut bytes = Vec::with_capacity(384); + for coeff in self.clone().to_coeffs() { + bytes.extend_from_slice(&coeff.to_bytes()); + } + bytes + } + + fn embed(c0: Fp2) -> Self { + Self::new([c0, Fp2::ZERO, Fp2::ZERO, Fp2::ZERO, Fp2::ZERO, Fp2::ZERO]) + } + + /// We assume that the frobenius map power is < 12 + fn frobenius_map(&self, power: usize) -> Self { + if power & 1 != 0 { + let c0 = self.c[0].clone().conjugate(); + let c1 = self.c[1].clone().conjugate() * &Bn254::FROBENIUS_COEFFS[power][0]; + let c2 = self.c[2].clone().conjugate() * &Bn254::FROBENIUS_COEFFS[power][1]; + let c3 = self.c[3].clone().conjugate() * &Bn254::FROBENIUS_COEFFS[power][2]; + let c4 = self.c[4].clone().conjugate() * &Bn254::FROBENIUS_COEFFS[power][3]; + let c5 = self.c[5].clone().conjugate() * &Bn254::FROBENIUS_COEFFS[power][4]; + Self::new([c0, c1, c2, c3, c4, c5]) + } else { + let c0 = self.c[0].clone(); + let c1 = &self.c[1] * &Bn254::FROBENIUS_COEFFS[power][0]; + let c2 = &self.c[2] * &Bn254::FROBENIUS_COEFFS[power][1]; + let c3 = &self.c[3] * &Bn254::FROBENIUS_COEFFS[power][2]; + let c4 = &self.c[4] * &Bn254::FROBENIUS_COEFFS[power][3]; + let c5 = &self.c[5] * &Bn254::FROBENIUS_COEFFS[power][4]; + Self::new([c0, c1, c2, c3, c4, c5]) + } + } + + fn mul_base(&self, rhs: &Fp2) -> Self { + Self::new([ + &self.c[0] * rhs, + &self.c[1] * rhs, + &self.c[2] * rhs, + &self.c[3] * rhs, + &self.c[4] * rhs, + &self.c[5] * rhs, + ]) + } +} + +// This is ambiguous. It is conjugation for Fp12 over Fp6. +impl ComplexConjugate for Fp12 { + #[inline(always)] + fn conjugate(self) -> Self { + let [c0, c1, c2, c3, c4, c5] = self.c; + Self::new([c0, -c1, c2, -c3, c4, -c5]) + } + + fn conjugate_assign(&mut self) { + self.c[1].neg_assign(); + self.c[3].neg_assign(); + self.c[5].neg_assign(); + } +} + +impl<'a> MulAssign<&'a Fp12> for Fp12 { + #[inline(always)] + fn mul_assign(&mut self, other: &'a Fp12) { + *self = crate::pairing::sextic_tower_mul(self, other, &Bn254::XI); + } +} + +impl<'a> Mul<&'a Fp12> for &'a Fp12 { + type Output = Fp12; + #[inline(always)] + fn mul(self, other: &'a Fp12) -> Self::Output { + crate::pairing::sextic_tower_mul(self, other, &Bn254::XI) + } +} + +impl MulAssign for Fp12 { + #[inline(always)] + fn mul_assign(&mut self, other: Self) { + self.mul_assign(&other); + } +} + +impl Mul for Fp12 { + type Output = Self; + #[inline(always)] + fn mul(mut self, other: Self) -> Self::Output { + self *= other; + self + } +} + +impl<'a> Mul<&'a Fp12> for Fp12 { + type Output = Self; + #[inline(always)] + fn mul(mut self, other: &'a Fp12) -> Fp12 { + self *= other; + self + } +} + +impl<'a> DivAssignUnsafe<&'a Fp12> for Fp12 { + #[inline(always)] + fn div_assign_unsafe(&mut self, other: &'a Fp12) { + *self *= other.invert(); + } +} + +impl<'a> DivUnsafe<&'a Fp12> for &'a Fp12 { + type Output = Fp12; + #[inline(always)] + fn div_unsafe(self, other: &'a Fp12) -> Self::Output { + let mut res = self.clone(); + res.div_assign_unsafe(other); + res + } +} + +impl DivAssignUnsafe for Fp12 { + #[inline(always)] + fn div_assign_unsafe(&mut self, other: Self) { + *self *= other.invert(); + } +} + +impl DivUnsafe for Fp12 { + type Output = Self; + #[inline(always)] + fn div_unsafe(mut self, other: Self) -> Self::Output { + self.div_assign_unsafe(other); + self + } +} + +impl<'a> DivUnsafe<&'a Fp12> for Fp12 { + type Output = Self; + #[inline(always)] + fn div_unsafe(mut self, other: &'a Fp12) -> Self::Output { + self.div_assign_unsafe(other); + self + } +} + +impl Neg for Fp12 { + type Output = Fp12; + #[inline(always)] + fn neg(self) -> Self::Output { + Self::ZERO - &self + } +} diff --git a/extensions/pairing/guest/src/bn254/fp2.rs b/extensions/pairing/guest/src/bn254/fp2.rs new file mode 100644 index 0000000000..b086b7d6aa --- /dev/null +++ b/extensions/pairing/guest/src/bn254/fp2.rs @@ -0,0 +1,76 @@ +use alloc::vec::Vec; +use core::ops::Neg; + +use openvm_algebra_complex_macros::{complex_declare, complex_impl_field}; +use openvm_algebra_guest::{field::FieldExtension, Field, IntMod}; + +use super::Fp; + +#[cfg(not(target_os = "zkvm"))] +// Used in Fp2Extension config +pub const BN254_COMPLEX_STRUCT_NAME: &str = "Bn254Fp2"; + +// The struct name needs to be globally unique for linking purposes. +// The mod_type is a path used only in the struct definition. +complex_declare! { + Bn254Fp2 { mod_type = Fp } +} + +complex_impl_field! { + Bn254Fp2, +} + +pub type Fp2 = Bn254Fp2; + +impl FieldExtension for Fp2 { + const D: usize = 2; + type Coeffs = [Fp; 2]; + + fn from_coeffs([c0, c1]: Self::Coeffs) -> Self { + Self { c0, c1 } + } + + fn from_bytes(bytes: &[u8]) -> Self { + assert_eq!(bytes.len(), 64); + Self::from_coeffs([ + Fp::from_const_bytes(bytes[0..32].try_into().unwrap()), + Fp::from_const_bytes(bytes[32..64].try_into().unwrap()), + ]) + } + + fn to_coeffs(self) -> Self::Coeffs { + [self.c0, self.c1] + } + + fn to_bytes(&self) -> Vec { + let mut bytes = Vec::with_capacity(64); + bytes.extend_from_slice(self.c0.as_le_bytes()); + bytes.extend_from_slice(self.c1.as_le_bytes()); + bytes + } + + fn embed(c0: Fp) -> Self { + Self { + c0, + c1: ::ZERO, + } + } + + fn frobenius_map(&self, power: usize) -> Self { + if power % 2 == 0 { + self.clone() + } else { + Self { + c0: self.c0.clone(), + c1: (&self.c1).neg(), + } + } + } + + fn mul_base(&self, rhs: &Fp) -> Self { + Self { + c0: &self.c0 * rhs, + c1: &self.c1 * rhs, + } + } +} diff --git a/extensions/pairing/guest/src/bn254/mod.rs b/extensions/pairing/guest/src/bn254/mod.rs new file mode 100644 index 0000000000..676a0adabd --- /dev/null +++ b/extensions/pairing/guest/src/bn254/mod.rs @@ -0,0 +1,744 @@ +use core::ops::{Add, Neg}; + +use hex_literal::hex; +#[cfg(not(target_os = "zkvm"))] +use lazy_static::lazy_static; +#[cfg(not(target_os = "zkvm"))] +use num_bigint::BigUint; +use openvm_algebra_guest::{Field, IntMod}; +use openvm_algebra_moduli_macros::moduli_declare; +use openvm_ecc_guest::{ + weierstrass::{CachedMulTable, IntrinsicCurve}, + CyclicGroup, Group, +}; +use openvm_ecc_sw_macros::sw_declare; + +use crate::pairing::PairingIntrinsics; + +mod fp12; +mod fp2; +pub mod pairing; +#[cfg(all(feature = "halo2curves", not(target_os = "zkvm")))] +pub(crate) mod utils; + +pub use fp12::*; +pub use fp2::*; + +#[cfg(all(test, feature = "halo2curves", not(target_os = "zkvm")))] +pub mod tests; + +#[cfg(not(target_os = "zkvm"))] +lazy_static! { + pub static ref BN254_MODULUS: BigUint = BigUint::from_bytes_be(&hex!( + "30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47" + )); + pub static ref BN254_ORDER: BigUint = BigUint::from_bytes_be(&hex!( + "30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001" + )); +} + +pub const BN254_XI_ISIZE: [isize; 2] = [9, 1]; +pub const BN254_NUM_LIMBS: usize = 32; +pub const BN254_LIMB_BITS: usize = 8; +pub const BN254_BLOCK_SIZE: usize = 32; + +pub const BN254_SEED: u64 = 0x44e992b44a6909f1; +// Encodes 6x+2 where x is the BN254 seed. +// 6*x+2 = sum_i BN254_PSEUDO_BINARY_ENCODING[i] * 2^i +// where BN254_PSEUDO_BINARY_ENCODING[i] is in {-1, 0, 1} +// Validated against BN254_SEED_ABS by a test in tests.rs +pub const BN254_PSEUDO_BINARY_ENCODING: [i8; 66] = [ + 0, 0, 0, 1, 0, 1, 0, -1, 0, 0, -1, 0, 0, 0, 1, 0, 0, -1, 0, -1, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0, + -1, 0, 0, 1, 0, -1, 0, 0, 1, 0, 0, 0, 0, 0, -1, 0, 0, -1, 0, 1, 0, -1, 0, 0, 0, -1, 0, -1, 0, + 0, 0, 1, 0, -1, 0, 1, +]; + +moduli_declare! { + Bn254Fp { modulus = "21888242871839275222246405745257275088696311157297823662689037894645226208583" }, + Bn254Scalar { modulus = "21888242871839275222246405745257275088548364400416034343698204186575808495617" }, +} + +const CURVE_B: Bn254Fp = Bn254Fp::from_const_bytes(hex!( + "0300000000000000000000000000000000000000000000000000000000000000" +)); + +sw_declare! { + Bn254G1Affine { mod_type = Bn254Fp, b = CURVE_B }, +} + +#[cfg(not(target_os = "zkvm"))] +// Used in WeierstrassExtension config +pub const BN254_ECC_STRUCT_NAME: &str = "Bn254G1Affine"; + +pub type Fp = Bn254Fp; +pub type Scalar = Bn254Scalar; +pub type G1Affine = Bn254G1Affine; +pub use g2::G2Affine; + +impl Field for Fp { + type SelfRef<'a> = &'a Self; + const ZERO: Self = ::ZERO; + const ONE: Self = ::ONE; + + fn double_assign(&mut self) { + IntMod::double_assign(self); + } + + fn square_assign(&mut self) { + IntMod::square_assign(self); + } +} + +impl Field for Scalar { + type SelfRef<'a> = &'a Self; + const ZERO: Self = ::ZERO; + const ONE: Self = ::ONE; + + fn double_assign(&mut self) { + IntMod::double_assign(self); + } + + fn square_assign(&mut self) { + IntMod::square_assign(self); + } +} + +impl CyclicGroup for G1Affine { + // https://eips.ethereum.org/EIPS/eip-197 + const GENERATOR: Self = G1Affine { + x: Bn254Fp::from_const_u8(1), + y: Bn254Fp::from_const_u8(2), + }; + const NEG_GENERATOR: Self = G1Affine { + x: Bn254Fp::from_const_u8(1), + y: Bn254Fp::from_const_bytes(hex!( + "45FD7CD8168C203C8DCA7168916A81975D588181B64550B829A031E1724E6430" + )), + }; +} + +// Define a G2Affine struct that implements curve operations using `Fp2` intrinsics +// but not special E(Fp2) intrinsics. +mod g2 { + use hex_literal::hex; + use openvm_algebra_guest::Field; + use openvm_ecc_guest::{ + impl_sw_affine, impl_sw_group_ops, weierstrass::WeierstrassPoint, AffinePoint, Group, + }; + + use super::{Fp, Fp2}; + + const THREE: Fp2 = Fp2::new(Fp::from_const_u8(3), Fp::ZERO); + // 3 / (9 + u) + // validated by a test below + const B: Fp2 = Fp2::new( + Fp::from_const_bytes(hex!( + "e538a124dce66732a3efdb59e5c5b4b5c36ae01b9918be81aeaab8ce409d142b" + )), + Fp::from_const_bytes(hex!( + "d215c38506bda2e452182de584a04fa7f4fdd8eeadaf2ccdd4fef03ab0139700" + )), + ); + impl_sw_affine!(G2Affine, Fp2, THREE, B); + impl_sw_group_ops!(G2Affine, Fp2); + + #[test] + fn test_g2_curve_equation_b() { + use openvm_algebra_guest::DivUnsafe; + let b = Fp2::new(Fp::from_const_u8(3), Fp::ZERO) + .div_unsafe(Fp2::new(Fp::from_const_u8(9), Fp::ONE)); + assert_eq!(b, B); + } +} + +pub struct Bn254; + +impl Bn254 { + // Same as the values from halo2curves_shims + // Validated by a test in tests.rs + pub const FROBENIUS_COEFF_FQ6_C1: [Fp2; 3] = [ + Fp2 { + c0: Bn254Fp(hex!( + "0100000000000000000000000000000000000000000000000000000000000000" + )), + c1: Bn254Fp(hex!( + "0000000000000000000000000000000000000000000000000000000000000000" + )), + }, + Fp2 { + c0: Bn254Fp(hex!( + "3d556f175795e3990c33c3c210c38cb743b159f53cec0b4cf711794f9847b32f" + )), + c1: Bn254Fp(hex!( + "a2cb0f641cd56516ce9d7c0b1d2aae3294075ad78bcca44b20aeeb6150e5c916" + )), + }, + Fp2 { + c0: Bn254Fp(hex!( + "48fd7c60e544bde43d6e96bb9f068fc2b0ccace0e7d96d5e29a031e1724e6430" + )), + c1: Bn254Fp(hex!( + "0000000000000000000000000000000000000000000000000000000000000000" + )), + }, + ]; + + // Same as the values from halo2curves_shims + // Validated by a test in tests.rs + pub const XI_TO_Q_MINUS_1_OVER_2: Fp2 = Fp2 { + c0: Bn254Fp(hex!( + "5a13a071460154dc9859c9a9ede0aadbb9f9e2b698c65edcdcf59a4805f33c06" + )), + c1: Bn254Fp(hex!( + "e3b02326637fd382d25ba28fc97d80212b6f79eca7b504079a0441acbc3cc007" + )), + }; + + // FINAL_EXPONENT = (p^12 - 1) / r in big-endian + // Validated by a test in test.rs + pub const FINAL_EXPONENT: [u8; 349] = hex!( + "2f4b6dc97020fddadf107d20bc842d43bf6369b1ff6a1c71015f3f7be2e1e30a73bb94fec0daf15466b2383a5d3ec3d15ad524d8f70c54efee1bd8c3b21377e563a09a1b705887e72eceaddea3790364a61f676baaf977870e88d5c6c8fef0781361e443ae77f5b63a2a2264487f2940a8b1ddb3d15062cd0fb2015dfc6668449aed3cc48a82d0d602d268c7daab6a41294c0cc4ebe5664568dfc50e1648a45a4a1e3a5195846a3ed011a337a02088ec80e0ebae8755cfe107acf3aafb40494e406f804216bb10cf430b0f37856b42db8dc5514724ee93dfb10826f0dd4a0364b9580291d2cd65664814fde37ca80bb4ea44eacc5e641bbadf423f9a2cbf813b8d145da90029baee7ddadda71c7f3811c4105262945bba1668c3be69a3c230974d83561841d766f9c9d570bb7fbe04c7e8a6c3c760c0de81def35692da361102b6b9b2b918837fa97896e84abb40a4efb7e54523a486964b64ca86f120" + ); +} + +impl IntrinsicCurve for Bn254 { + type Scalar = Scalar; + type Point = G1Affine; + + fn msm(coeffs: &[Self::Scalar], bases: &[Self::Point]) -> Self::Point + where + for<'a> &'a Self::Point: Add<&'a Self::Point, Output = Self::Point>, + { + // heuristic + if coeffs.len() < 25 { + // BN254(Fp) is of prime order by Weil conjecture: + // + let table = CachedMulTable::::new_with_prime_order(bases, 4); + table.windowed_mul(coeffs) + } else { + openvm_ecc_guest::msm(coeffs, bases) + } + } +} + +impl PairingIntrinsics for Bn254 { + type Fp = Fp; + type Fp2 = Fp2; + type Fp12 = Fp12; + + const PAIRING_IDX: usize = 0; + // The sextic extension `Fp12` is `Fp2[X] / (X^6 - \xi)`, where `\xi` is a non-residue. + const XI: Fp2 = Fp2::new(Fp::from_const_u8(9), Fp::from_const_u8(1)); + const FP2_TWO: Fp2 = Fp2::new(Fp::from_const_u8(2), Fp::from_const_u8(0)); + const FP2_THREE: Fp2 = Fp2::new(Fp::from_const_u8(3), Fp::from_const_u8(0)); + // Multiplication constants for the Frobenius map for coefficients in Fp2 c1..=c5 for powers + // 0..12 FROBENIUS_COEFFS\[i\]\[j\] = \xi^{(j + 1) * (p^i - 1)/6} when p = 1 (mod 6) + // These are validated against `halo2curves::bn256::FROBENIUS_COEFF_FQ12_C1` in tests.rs + // (Note that bn256 here is another name for bn254) + const FROBENIUS_COEFFS: [[Self::Fp2; 5]; 12] = [ + [ + Fp2 { + c0: Bn254Fp(hex!( + "0100000000000000000000000000000000000000000000000000000000000000" + )), + c1: Bn254Fp(hex!( + "0000000000000000000000000000000000000000000000000000000000000000" + )), + }, + Fp2 { + c0: Bn254Fp(hex!( + "0100000000000000000000000000000000000000000000000000000000000000" + )), + c1: Bn254Fp(hex!( + "0000000000000000000000000000000000000000000000000000000000000000" + )), + }, + Fp2 { + c0: Bn254Fp(hex!( + "0100000000000000000000000000000000000000000000000000000000000000" + )), + c1: Bn254Fp(hex!( + "0000000000000000000000000000000000000000000000000000000000000000" + )), + }, + Fp2 { + c0: Bn254Fp(hex!( + "0100000000000000000000000000000000000000000000000000000000000000" + )), + c1: Bn254Fp(hex!( + "0000000000000000000000000000000000000000000000000000000000000000" + )), + }, + Fp2 { + c0: Bn254Fp(hex!( + "0100000000000000000000000000000000000000000000000000000000000000" + )), + c1: Bn254Fp(hex!( + "0000000000000000000000000000000000000000000000000000000000000000" + )), + }, + ], + [ + Fp2 { + c0: Bn254Fp(hex!( + "70e4c9dcda350bd676212f29081e525c608be676dd9fb9e8dfa765281cb78412" + )), + c1: Bn254Fp(hex!( + "ac62f3805ff05ccae5c7ee8e779279748e0b1512fe7c32a6e6e7fab4f3966924" + )), + }, + Fp2 { + c0: Bn254Fp(hex!( + "3d556f175795e3990c33c3c210c38cb743b159f53cec0b4cf711794f9847b32f" + )), + c1: Bn254Fp(hex!( + "a2cb0f641cd56516ce9d7c0b1d2aae3294075ad78bcca44b20aeeb6150e5c916" + )), + }, + Fp2 { + c0: Bn254Fp(hex!( + "5a13a071460154dc9859c9a9ede0aadbb9f9e2b698c65edcdcf59a4805f33c06" + )), + c1: Bn254Fp(hex!( + "e3b02326637fd382d25ba28fc97d80212b6f79eca7b504079a0441acbc3cc007" + )), + }, + Fp2 { + c0: Bn254Fp(hex!( + "62a71e92551f8a8472ec94bef76533d3841e185ab7c0f38001a8ee645e4fb505" + )), + c1: Bn254Fp(hex!( + "26812bcd11473bc163c7de1bead28536921c0b3bb0803a9fee8afde7db5e142c" + )), + }, + Fp2 { + c0: Bn254Fp(hex!( + "2f69b7ea10c8a22ed31baa559b455c42f43f35a461363ae94986794fe7c18301" + )), + c1: Bn254Fp(hex!( + "4b2c0c6eeeb8c624c02a8e6799cb80b07d9f72c746b27fa27506fd76caf2ac12" + )), + }, + ], + [ + Fp2 { + c0: Bn254Fp(hex!( + "49fd7c60e544bde43d6e96bb9f068fc2b0ccace0e7d96d5e29a031e1724e6430" + )), + c1: Bn254Fp(hex!( + "0000000000000000000000000000000000000000000000000000000000000000" + )), + }, + Fp2 { + c0: Bn254Fp(hex!( + "48fd7c60e544bde43d6e96bb9f068fc2b0ccace0e7d96d5e29a031e1724e6430" + )), + c1: Bn254Fp(hex!( + "0000000000000000000000000000000000000000000000000000000000000000" + )), + }, + Fp2 { + c0: Bn254Fp(hex!( + "46fd7cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e6430" + )), + c1: Bn254Fp(hex!( + "0000000000000000000000000000000000000000000000000000000000000000" + )), + }, + Fp2 { + c0: Bn254Fp(hex!( + "feffff77314763574f5cdbacf163f2d4ac8bd4a0ce6be2590000000000000000" + )), + c1: Bn254Fp(hex!( + "0000000000000000000000000000000000000000000000000000000000000000" + )), + }, + Fp2 { + c0: Bn254Fp(hex!( + "ffffff77314763574f5cdbacf163f2d4ac8bd4a0ce6be2590000000000000000" + )), + c1: Bn254Fp(hex!( + "0000000000000000000000000000000000000000000000000000000000000000" + )), + }, + ], + [ + Fp2 { + c0: Bn254Fp(hex!( + "7fa6d41e397d6fe84ad255be8db34c8990aaacd08c60e9efbbe482cccf81dc19" + )), + c1: Bn254Fp(hex!( + "01c1c0f42baa9476ec39d497e3a5037f9d137635e3eecb06737de70bb6f8ab00" + )), + }, + Fp2 { + c0: Bn254Fp(hex!( + "6dfbdc7be86e747bd342695d3dfd5f80ac259f95771cffba0aef55b778e05608" + )), + c1: Bn254Fp(hex!( + "de86a5aa2bab0c383126ff98bf31df0f4f0926ec6d0ef3a96f76d1b341def104" + )), + }, + Fp2 { + c0: Bn254Fp(hex!( + "ede9dc66d08acc5ff470a8bea389d6bba35e9eca1d7ff1db4caa96986d5b272a" + )), + c1: Bn254Fp(hex!( + "644c59b2b30c4db9ba6ecfd8c7ec007632e907950e904bb18f9bf034b611a428" + )), + }, + Fp2 { + c0: Bn254Fp(hex!( + "66f0cb3cbc921a0ecb6bb075450933e64e44b2b5f7e0be19ab8dc011668cc50b" + )), + c1: Bn254Fp(hex!( + "9f230c739dede35fe5967f73089e4aa4041dd20ceff6b0fe120a91e199e9d523" + )), + }, + Fp2 { + c0: Bn254Fp(hex!( + "431b26767084deeba5847c969880d62e693f4d3bfa99167105092c954490c413" + )), + c1: Bn254Fp(hex!( + "992428841304251f21800220eada2d3e3d63482a28b2b19f0bddb1596a36db16" + )), + }, + ], + [ + Fp2 { + c0: Bn254Fp(hex!( + "48fd7c60e544bde43d6e96bb9f068fc2b0ccace0e7d96d5e29a031e1724e6430" + )), + c1: Bn254Fp(hex!( + "0000000000000000000000000000000000000000000000000000000000000000" + )), + }, + Fp2 { + c0: Bn254Fp(hex!( + "feffff77314763574f5cdbacf163f2d4ac8bd4a0ce6be2590000000000000000" + )), + c1: Bn254Fp(hex!( + "0000000000000000000000000000000000000000000000000000000000000000" + )), + }, + Fp2 { + c0: Bn254Fp(hex!( + "0100000000000000000000000000000000000000000000000000000000000000" + )), + c1: Bn254Fp(hex!( + "0000000000000000000000000000000000000000000000000000000000000000" + )), + }, + Fp2 { + c0: Bn254Fp(hex!( + "48fd7c60e544bde43d6e96bb9f068fc2b0ccace0e7d96d5e29a031e1724e6430" + )), + c1: Bn254Fp(hex!( + "0000000000000000000000000000000000000000000000000000000000000000" + )), + }, + Fp2 { + c0: Bn254Fp(hex!( + "feffff77314763574f5cdbacf163f2d4ac8bd4a0ce6be2590000000000000000" + )), + c1: Bn254Fp(hex!( + "0000000000000000000000000000000000000000000000000000000000000000" + )), + }, + ], + [ + Fp2 { + c0: Bn254Fp(hex!( + "0fc20a425e476412d4b026958595fa2c301fc659afc02f07dc3c1da4b3ca5707" + )), + c1: Bn254Fp(hex!( + "9c5b4a4ce34558e8933c5771fd7d0ba26c60e2a49bb7e918b6351e3835b0a60c" + )), + }, + Fp2 { + c0: Bn254Fp(hex!( + "e4a9ad1dee13e9623a1fb7b0d41416f7cad90978b8829569513f94bbd474be28" + )), + c1: Bn254Fp(hex!( + "c7aac7c9ce0baeed8d06f6c3b40ef4547a4701bebc6ab8c2997b74cbe08aa814" + )), + }, + Fp2 { + c0: Bn254Fp(hex!( + "5a13a071460154dc9859c9a9ede0aadbb9f9e2b698c65edcdcf59a4805f33c06" + )), + c1: Bn254Fp(hex!( + "e3b02326637fd382d25ba28fc97d80212b6f79eca7b504079a0441acbc3cc007" + )), + }, + Fp2 { + c0: Bn254Fp(hex!( + "7f65920905da7ba94f722c3454fb1ade89f5b67107a49d1d7d6a826aae72e91e" + )), + c1: Bn254Fp(hex!( + "c955c2707ee32157d136854130643254247725bbcd13b5d251abd4f86f54de10" + )), + }, + Fp2 { + c0: Bn254Fp(hex!( + "14b26e8b5fbc3bbdd268d240fd3a7aec74ff17979863dc87bb82b2455dce4012" + )), + c1: Bn254Fp(hex!( + "4ef81b16254b5efa605574b8500fad8dbfc3d562e1ff31fd95d6b4e29f432e04" + )), + }, + ], + [ + Fp2 { + c0: Bn254Fp(hex!( + "46fd7cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e6430" + )), + c1: Bn254Fp(hex!( + "0000000000000000000000000000000000000000000000000000000000000000" + )), + }, + Fp2 { + c0: Bn254Fp(hex!( + "0100000000000000000000000000000000000000000000000000000000000000" + )), + c1: Bn254Fp(hex!( + "0000000000000000000000000000000000000000000000000000000000000000" + )), + }, + Fp2 { + c0: Bn254Fp(hex!( + "46fd7cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e6430" + )), + c1: Bn254Fp(hex!( + "0000000000000000000000000000000000000000000000000000000000000000" + )), + }, + Fp2 { + c0: Bn254Fp(hex!( + "0100000000000000000000000000000000000000000000000000000000000000" + )), + c1: Bn254Fp(hex!( + "0000000000000000000000000000000000000000000000000000000000000000" + )), + }, + Fp2 { + c0: Bn254Fp(hex!( + "46fd7cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e6430" + )), + c1: Bn254Fp(hex!( + "0000000000000000000000000000000000000000000000000000000000000000" + )), + }, + ], + [ + Fp2 { + c0: Bn254Fp(hex!( + "d718b3fb3b56156616a9423f894c2f3bfdcc9a0ad9a596cf49f8cbb85697df1d" + )), + c1: Bn254Fp(hex!( + "9b9a8957b79bc371a70283d919d80723cf4c6c6fb8c81d1243b8362c7fb7fa0b" + )), + }, + Fp2 { + c0: Bn254Fp(hex!( + "3d556f175795e3990c33c3c210c38cb743b159f53cec0b4cf711794f9847b32f" + )), + c1: Bn254Fp(hex!( + "a2cb0f641cd56516ce9d7c0b1d2aae3294075ad78bcca44b20aeeb6150e5c916" + )), + }, + Fp2 { + c0: Bn254Fp(hex!( + "ede9dc66d08acc5ff470a8bea389d6bba35e9eca1d7ff1db4caa96986d5b272a" + )), + c1: Bn254Fp(hex!( + "644c59b2b30c4db9ba6ecfd8c7ec007632e907950e904bb18f9bf034b611a428" + )), + }, + Fp2 { + c0: Bn254Fp(hex!( + "62a71e92551f8a8472ec94bef76533d3841e185ab7c0f38001a8ee645e4fb505" + )), + c1: Bn254Fp(hex!( + "26812bcd11473bc163c7de1bead28536921c0b3bb0803a9fee8afde7db5e142c" + )), + }, + Fp2 { + c0: Bn254Fp(hex!( + "1894c5ed05c47d0dbaaec712f624255569184cdd540f16cfdf19b8918b8ce02e" + )), + c1: Bn254Fp(hex!( + "fcd0706a28d35917cd9fe300f89e00e7dfb80eba6f93d015b499346aa85bb71d" + )), + }, + ], + [ + Fp2 { + c0: Bn254Fp(hex!( + "feffff77314763574f5cdbacf163f2d4ac8bd4a0ce6be2590000000000000000" + )), + c1: Bn254Fp(hex!( + "0000000000000000000000000000000000000000000000000000000000000000" + )), + }, + Fp2 { + c0: Bn254Fp(hex!( + "48fd7c60e544bde43d6e96bb9f068fc2b0ccace0e7d96d5e29a031e1724e6430" + )), + c1: Bn254Fp(hex!( + "0000000000000000000000000000000000000000000000000000000000000000" + )), + }, + Fp2 { + c0: Bn254Fp(hex!( + "0100000000000000000000000000000000000000000000000000000000000000" + )), + c1: Bn254Fp(hex!( + "0000000000000000000000000000000000000000000000000000000000000000" + )), + }, + Fp2 { + c0: Bn254Fp(hex!( + "feffff77314763574f5cdbacf163f2d4ac8bd4a0ce6be2590000000000000000" + )), + c1: Bn254Fp(hex!( + "0000000000000000000000000000000000000000000000000000000000000000" + )), + }, + Fp2 { + c0: Bn254Fp(hex!( + "48fd7c60e544bde43d6e96bb9f068fc2b0ccace0e7d96d5e29a031e1724e6430" + )), + c1: Bn254Fp(hex!( + "0000000000000000000000000000000000000000000000000000000000000000" + )), + }, + ], + [ + Fp2 { + c0: Bn254Fp(hex!( + "c856a8b9dd0eb15342f81baa03b7340ecdadd4b029e566c86dbbae14a3cc8716" + )), + c1: Bn254Fp(hex!( + "463cbce3eae18bc5a0909dd0adc47d18c0440b4cd35684b1b6224ad5bc55b82f" + )), + }, + Fp2 { + c0: Bn254Fp(hex!( + "6dfbdc7be86e747bd342695d3dfd5f80ac259f95771cffba0aef55b778e05608" + )), + c1: Bn254Fp(hex!( + "de86a5aa2bab0c383126ff98bf31df0f4f0926ec6d0ef3a96f76d1b341def104" + )), + }, + Fp2 { + c0: Bn254Fp(hex!( + "5a13a071460154dc9859c9a9ede0aadbb9f9e2b698c65edcdcf59a4805f33c06" + )), + c1: Bn254Fp(hex!( + "e3b02326637fd382d25ba28fc97d80212b6f79eca7b504079a0441acbc3cc007" + )), + }, + Fp2 { + c0: Bn254Fp(hex!( + "66f0cb3cbc921a0ecb6bb075450933e64e44b2b5f7e0be19ab8dc011668cc50b" + )), + c1: Bn254Fp(hex!( + "9f230c739dede35fe5967f73089e4aa4041dd20ceff6b0fe120a91e199e9d523" + )), + }, + Fp2 { + c0: Bn254Fp(hex!( + "04e25662a6074250e745f5d1f8e9aa68f4183446bcab39472497054c2ebe9f1c" + )), + c1: Bn254Fp(hex!( + "aed854540388fb1c6c4a6f48a78f535920f538578e939e181ec37f8708188919" + )), + }, + ], + [ + Fp2 { + c0: Bn254Fp(hex!( + "ffffff77314763574f5cdbacf163f2d4ac8bd4a0ce6be2590000000000000000" + )), + c1: Bn254Fp(hex!( + "0000000000000000000000000000000000000000000000000000000000000000" + )), + }, + Fp2 { + c0: Bn254Fp(hex!( + "feffff77314763574f5cdbacf163f2d4ac8bd4a0ce6be2590000000000000000" + )), + c1: Bn254Fp(hex!( + "0000000000000000000000000000000000000000000000000000000000000000" + )), + }, + Fp2 { + c0: Bn254Fp(hex!( + "46fd7cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e6430" + )), + c1: Bn254Fp(hex!( + "0000000000000000000000000000000000000000000000000000000000000000" + )), + }, + Fp2 { + c0: Bn254Fp(hex!( + "48fd7c60e544bde43d6e96bb9f068fc2b0ccace0e7d96d5e29a031e1724e6430" + )), + c1: Bn254Fp(hex!( + "0000000000000000000000000000000000000000000000000000000000000000" + )), + }, + Fp2 { + c0: Bn254Fp(hex!( + "49fd7c60e544bde43d6e96bb9f068fc2b0ccace0e7d96d5e29a031e1724e6430" + )), + c1: Bn254Fp(hex!( + "0000000000000000000000000000000000000000000000000000000000000000" + )), + }, + ], + [ + Fp2 { + c0: Bn254Fp(hex!( + "383b7296b844bc29b9194bd30bd5866a2d39bb27078520b14d63143dbf830c29" + )), + c1: Bn254Fp(hex!( + "aba1328c3346c853f98d1af793ec75f5f0f79edc1a8e669f736a13a93d9ebd23" + )), + }, + Fp2 { + c0: Bn254Fp(hex!( + "e4a9ad1dee13e9623a1fb7b0d41416f7cad90978b8829569513f94bbd474be28" + )), + c1: Bn254Fp(hex!( + "c7aac7c9ce0baeed8d06f6c3b40ef4547a4701bebc6ab8c2997b74cbe08aa814" + )), + }, + Fp2 { + c0: Bn254Fp(hex!( + "ede9dc66d08acc5ff470a8bea389d6bba35e9eca1d7ff1db4caa96986d5b272a" + )), + c1: Bn254Fp(hex!( + "644c59b2b30c4db9ba6ecfd8c7ec007632e907950e904bb18f9bf034b611a428" + )), + }, + Fp2 { + c0: Bn254Fp(hex!( + "7f65920905da7ba94f722c3454fb1ade89f5b67107a49d1d7d6a826aae72e91e" + )), + c1: Bn254Fp(hex!( + "c955c2707ee32157d136854130643254247725bbcd13b5d251abd4f86f54de10" + )), + }, + Fp2 { + c0: Bn254Fp(hex!( + "334b0e4db7cfe47eba619f27942f07abe85869ea1de273306e1d7f9b1580231e" + )), + c1: Bn254Fp(hex!( + "f90461c2f140c2412c75fdaf405bd4099e94ab1ed5451ebb93c97cfed20a362c" + )), + }, + ], + ]; +} diff --git a/extensions/pairing/guest/src/bn254/pairing.rs b/extensions/pairing/guest/src/bn254/pairing.rs new file mode 100644 index 0000000000..25a0d6b7fe --- /dev/null +++ b/extensions/pairing/guest/src/bn254/pairing.rs @@ -0,0 +1,379 @@ +use alloc::vec::Vec; + +use itertools::izip; +use openvm_algebra_guest::{field::FieldExtension, DivUnsafe, Field}; +use openvm_ecc_guest::AffinePoint; +#[cfg(target_os = "zkvm")] +use { + crate::{PairingBaseFunct7, OPCODE, PAIRING_FUNCT3}, + core::mem::MaybeUninit, + openvm_platform::custom_insn_r, + openvm_rv32im_guest::hint_buffer_u32, +}; + +use super::{Bn254, Fp, Fp12, Fp2, BN254_PSEUDO_BINARY_ENCODING, BN254_SEED}; +use crate::pairing::{ + exp_check_fallback, Evaluatable, EvaluatedLine, FromLineDType, LineMulDType, MillerStep, + MultiMillerLoop, PairingCheck, PairingCheckError, PairingIntrinsics, UnevaluatedLine, +}; +#[cfg(all(feature = "halo2curves", not(target_os = "zkvm")))] +use crate::{ + bn254::utils::{ + convert_bn254_fp2_to_halo2_fq2, convert_bn254_fp_to_halo2_fq, + convert_bn254_halo2_fq12_to_fp12, + }, + halo2curves_shims::bn254::Bn254 as Halo2CurvesBn254, + pairing::FinalExp, +}; + +impl Evaluatable for UnevaluatedLine { + fn evaluate(&self, xy_frac: &(Fp, Fp)) -> EvaluatedLine { + let (x_over_y, y_inv) = xy_frac; + // Represents the line L(x,y) = 1 + b (x/y) w^1 + c (1/y) w^3 + EvaluatedLine { + b: self.b.mul_base(x_over_y), + c: self.c.mul_base(y_inv), + } + } +} + +impl FromLineDType for Fp12 { + fn from_evaluated_line_d_type(line: EvaluatedLine) -> Fp12 { + FieldExtension::::from_coeffs([ + Fp2::ONE, + line.b, + Fp2::ZERO, + line.c, + Fp2::ZERO, + Fp2::ZERO, + ]) + } +} + +// TODO[jpw]: make this into a macro depending on P::PAIRING_IDX when we have more curves +impl LineMulDType for Bn254 { + /// Multiplies two lines in 013-form to get an element in 01234-form + fn mul_013_by_013(l0: &EvaluatedLine, l1: &EvaluatedLine) -> [Fp2; 5] { + let b0 = &l0.b; + let c0 = &l0.c; + let b1 = &l1.b; + let c1 = &l1.c; + + // where w⁶ = xi + // l0 * l1 = 1 + (b0 + b1)w + (b0b1)w² + (c0 + c1)w³ + (b0c1 + b1c0)w⁴ + (c0c1)w⁶ + // = (1 + c0c1 * xi) + (b0 + b1)w + (b0b1)w² + (c0 + c1)w³ + (b0c1 + b1c0)w⁴ + let x0 = Fp2::ONE + c0 * c1 * &Bn254::XI; + let x1 = b0 + b1; + let x2 = b0 * b1; + let x3 = c0 + c1; + let x4 = b0 * c1 + b1 * c0; + + [x0, x1, x2, x3, x4] + } + + /// Multiplies a line in 013-form with a Fp12 element to get an Fp12 element + fn mul_by_013(f: &Fp12, l: &EvaluatedLine) -> Fp12 { + Fp12::from_evaluated_line_d_type(l.clone()) * f + } + + /// Multiplies a line in 01234-form with a Fp12 element to get an Fp12 element + fn mul_by_01234(f: &Fp12, x: &[Fp2; 5]) -> Fp12 { + // we update the order of the coefficients to match the Fp12 coefficient ordering: + // Fp12 { + // c0: Fp6 { + // c0: x0, + // c1: x2, + // c2: x4, + // }, + // c1: Fp6 { + // c0: x1, + // c1: x3, + // c2: x5, + // }, + // } + let o0 = &x[0]; + let o1 = &x[2]; + let o2 = &x[4]; + let o3 = &x[1]; + let o4 = &x[3]; + + let xi = &Bn254::XI; + + let self_coeffs = &f.c; + let s0 = &self_coeffs[0]; + let s1 = &self_coeffs[2]; + let s2 = &self_coeffs[4]; + let s3 = &self_coeffs[1]; + let s4 = &self_coeffs[3]; + let s5 = &self_coeffs[5]; + + // NOTE[yj]: Hand-calculated multiplication for Fp12 * 01234 ∈ Fp2; this is likely not the + // most efficient implementation c00 = cs0co0 + xi(cs1co2 + cs2co1 + cs4co4 + + // cs5co3) c01 = cs0co1 + cs1co0 + cs3co3 + xi(cs2co2 + cs5co4) + // c02 = cs0co2 + cs1co1 + cs2co0 + cs3co4 + cs4co3 + // c10 = cs0co3 + cs3co0 + xi(cs2co4 + cs4co2 + cs5co1) + // c11 = cs0co4 + cs1co3 + cs3co1 + cs4co0 + xi(cs5co2) + // c12 = cs1co4 + cs2co3 + cs3co2 + cs4co1 + cs5co0 + let c00 = s0 * o0 + xi * &(s1 * o2 + s2 * o1 + s4 * o4 + s5 * o3); + let c01 = s0 * o1 + s1 * o0 + s3 * o3 + xi * &(s2 * o2 + s5 * o4); + let c02 = s0 * o2 + s1 * o1 + s2 * o0 + s3 * o4 + s4 * o3; + let c10 = s0 * o3 + s3 * o0 + xi * &(s2 * o4 + s4 * o2 + s5 * o1); + let c11 = s0 * o4 + s1 * o3 + s3 * o1 + s4 * o0 + xi * &(s5 * o2); + let c12 = s1 * o4 + s2 * o3 + s3 * o2 + s4 * o1 + s5 * o0; + + Fp12::from_coeffs([c00, c10, c01, c11, c02, c12]) + } +} + +#[allow(non_snake_case)] +impl MultiMillerLoop for Bn254 { + type Fp = Fp; + type Fp12 = Fp12; + + const SEED_ABS: u64 = BN254_SEED; + const PSEUDO_BINARY_ENCODING: &[i8] = &BN254_PSEUDO_BINARY_ENCODING; + + fn evaluate_lines_vec(f: Self::Fp12, lines: Vec>) -> Self::Fp12 { + let mut f = f; + let mut lines = lines; + if lines.len() % 2 == 1 { + f = Self::mul_by_013(&f, &lines.pop().unwrap()); + } + for chunk in lines.chunks(2) { + if let [line0, line1] = chunk { + let prod = Self::mul_013_by_013(line0, line1); + f = Self::mul_by_01234(&f, &prod); + } else { + panic!("lines.len() % 2 should be 0 at this point"); + } + } + f + } + + /// The expected output of this function when running the Miller loop with embedded exponent is + /// c^2 * l_{2Q} + fn pre_loop( + Q_acc: Vec>, + _Q: &[AffinePoint], + c: Option, + xy_fracs: &[(Self::Fp, Self::Fp)], + ) -> (Self::Fp12, Vec>) { + let mut f = if let Some(mut c) = c { + // for the miller loop with embedded exponent, f will be set to c at the beginning of + // the function, and we will square c due to the last two values of the + // pseudo-binary encoding (BN254_PSEUDO_BINARY_ENCODING) being 0 and 1. + // Therefore, the final value of f at the end of this block is c^2. + c.square_assign(); + c + } else { + Self::Fp12::ONE + }; + + let mut Q_acc = Q_acc; + let mut initial_lines = Vec::>::new(); + + // We don't need to special case the first iteration for Bn254, but since we are using the + // same Miller loop implementation for both Bn254 and Bls12_381, we need to do the + // first iteration separately here. + let (Q_out_double, lines_2S) = Q_acc + .into_iter() + .map(|Q| Self::miller_double_step(&Q)) + .unzip::<_, _, Vec<_>, Vec<_>>(); + Q_acc = Q_out_double; + + let lines_iter = izip!(lines_2S.iter(), xy_fracs.iter()); + for (line_2S, xy_frac) in lines_iter { + let line = line_2S.evaluate(xy_frac); + initial_lines.push(line); + } + + f = Self::evaluate_lines_vec(f, initial_lines); + + (f, Q_acc) + } + + /// Compute f_{Miller,Q}(P) from f_{6x+2,Q}(P) + fn post_loop( + f: &Self::Fp12, + Q_acc: Vec>, // at this point, Q_acc = (6x+2)Q + Q: &[AffinePoint], + _c: Option, + xy_fracs: &[(Self::Fp, Self::Fp)], + ) -> (Self::Fp12, Vec>) { + let mut Q_acc = Q_acc; + let mut lines = Vec::>::new(); + + let x_to_q_minus_1_over_3 = &Self::FROBENIUS_COEFF_FQ6_C1[1]; + let x_to_q_sq_minus_1_over_3 = &Self::FROBENIUS_COEFF_FQ6_C1[2]; + + // For each q, compute q1 such that `frob_p(twist(q)) = twist(q1)` + let q1_vec = Q + .iter() + .map(|Q| { + let x = Q.x.frobenius_map(1); + let x = x * x_to_q_minus_1_over_3; + let y = Q.y.frobenius_map(1); + let y = y * &Self::XI_TO_Q_MINUS_1_OVER_2; + AffinePoint { x, y } + }) + .collect::>(); + + // compute l_{(6x+2)\Psi(Q), \phi_p(\Psi(Q))} where \phi_p is the Frobenius map + let (Q_out_add, lines_S_plus_Q) = Q_acc + .iter() + .zip(q1_vec.iter()) + .map(|(Q_acc, q1)| Self::miller_add_step(Q_acc, q1)) + .unzip::<_, _, Vec<_>, Vec<_>>(); + Q_acc = Q_out_add; + + let lines_iter = izip!(lines_S_plus_Q.iter(), xy_fracs.iter()); + for (lines_S_plus_Q, xy_frac) in lines_iter { + let line = lines_S_plus_Q.evaluate(xy_frac); + lines.push(line); + } + + // For each q, compute q2 such that `-frob_p^2(twist(q)) = twist(q2)` + let q2_vec = Q + .iter() + .map(|Q| { + // There is a frobenius mapping π²(Q) that we skip here since it is equivalent to + // the identity mapping + let x = &Q.x * x_to_q_sq_minus_1_over_3; + AffinePoint { x, y: Q.y.clone() } + }) + .collect::>(); + + // compute l_{(6x+2)\Psi(Q) + \phi_p(\Psi(Q)), -(\phi_p)^2(\Psi(Q))} where \phi_p is the + // Frobenius map + let (Q_out_add, lines_S_plus_Q) = Q_acc + .iter() + .zip(q2_vec.iter()) + .map(|(Q_acc, q2)| Self::miller_add_step(Q_acc, q2)) + .unzip::<_, _, Vec<_>, Vec<_>>(); + Q_acc = Q_out_add; + + let lines_iter = izip!(lines_S_plus_Q.iter(), xy_fracs.iter()); + for (lines_S_plus_Q, xy_frac) in lines_iter { + let line = lines_S_plus_Q.evaluate(xy_frac); + lines.push(line); + } + + let mut f = f.clone(); + f = Self::evaluate_lines_vec(f, lines); + + (f, Q_acc) + } +} + +#[allow(non_snake_case)] +impl PairingCheck for Bn254 { + type Fp = Fp; + type Fp2 = Fp2; + type Fp12 = Fp12; + + #[allow(unused_variables)] + fn pairing_check_hint( + P: &[AffinePoint], + Q: &[AffinePoint], + ) -> (Self::Fp12, Self::Fp12) { + #[cfg(not(target_os = "zkvm"))] + { + #[cfg(not(feature = "halo2curves"))] + panic!("`halo2curves` feature must be enabled to use pairing check hint on host"); + + #[cfg(feature = "halo2curves")] + { + let p_halo2 = P + .iter() + .map(|p| { + AffinePoint::new( + convert_bn254_fp_to_halo2_fq(p.x.clone()), + convert_bn254_fp_to_halo2_fq(p.y.clone()), + ) + }) + .collect::>(); + let q_halo2 = Q + .iter() + .map(|q| { + AffinePoint::new( + convert_bn254_fp2_to_halo2_fq2(q.x.clone()), + convert_bn254_fp2_to_halo2_fq2(q.y.clone()), + ) + }) + .collect::>(); + let fq12 = Halo2CurvesBn254::multi_miller_loop(&p_halo2, &q_halo2); + let (c_fq12, s_fq12) = Halo2CurvesBn254::final_exp_hint(&fq12); + let c = convert_bn254_halo2_fq12_to_fp12(c_fq12); + let s = convert_bn254_halo2_fq12_to_fp12(s_fq12); + (c, s) + } + } + #[cfg(target_os = "zkvm")] + { + let hint = MaybeUninit::<(Fp12, Fp12)>::uninit(); + // We do not rely on the slice P's memory layout since rust does not guarantee it across + // compiler versions. + let p_fat_ptr = (P.as_ptr() as u32, P.len() as u32); + let q_fat_ptr = (Q.as_ptr() as u32, Q.len() as u32); + unsafe { + custom_insn_r!( + opcode = OPCODE, + funct3 = PAIRING_FUNCT3, + funct7 = ((Bn254::PAIRING_IDX as u8) * PairingBaseFunct7::PAIRING_MAX_KINDS + PairingBaseFunct7::HintFinalExp as u8), + rd = Const "x0", + rs1 = In &p_fat_ptr, + rs2 = In &q_fat_ptr + ); + let ptr = hint.as_ptr() as *const u8; + hint_buffer_u32!(ptr, (32 * 12 * 2) / 4); + hint.assume_init() + } + } + } + + fn pairing_check( + P: &[AffinePoint], + Q: &[AffinePoint], + ) -> Result<(), PairingCheckError> { + Self::try_honest_pairing_check(P, Q).unwrap_or_else(|| { + let f = Self::multi_miller_loop(P, Q); + exp_check_fallback(&f, &Self::FINAL_EXPONENT) + }) + } +} + +#[allow(non_snake_case)] +impl Bn254 { + fn try_honest_pairing_check( + P: &[AffinePoint<::Fp>], + Q: &[AffinePoint<::Fp2>], + ) -> Option> { + let (c, u) = Self::pairing_check_hint(P, Q); + if c == Fp12::ZERO { + return None; + } + let c_inv = Fp12::ONE.div_unsafe(&c); + + // We follow Theorem 3 of https://eprint.iacr.org/2024/640.pdf to check that the pairing equals 1 + // By the theorem, it suffices to provide c and u such that f * u == c^λ. + // Since λ = 6x + 2 + q^3 - q^2 + q, we will check the equivalent condition: + // f * c^-{6x + 2} * u * c^-{q^3 - q^2 + q} == 1 + // This is because we can compute f * c^-{6x+2} by embedding the c^-{6x+2} computation in + // the miller loop. + + // c_mul = c^-{q^3 - q^2 + q} + let c_q3_inv = FieldExtension::frobenius_map(&c_inv, 3); + let c_q2 = FieldExtension::frobenius_map(&c, 2); + let c_q_inv = FieldExtension::frobenius_map(&c_inv, 1); + let c_mul = c_q3_inv * c_q2 * c_q_inv; + + // Pass c inverse into the miller loop so that we compute fc == f * c^-{6x + 2} + let fc = Self::multi_miller_loop_embedded_exp(P, Q, Some(c_inv)); + + if fc * c_mul * u == Fp12::ONE { + Some(Ok(())) + } else { + None + } + } +} diff --git a/extensions/pairing/guest/src/bn254/tests.rs b/extensions/pairing/guest/src/bn254/tests.rs new file mode 100644 index 0000000000..331bb64692 --- /dev/null +++ b/extensions/pairing/guest/src/bn254/tests.rs @@ -0,0 +1,325 @@ +use group::{ff::Field, prime::PrimeCurveAffine}; +use halo2curves_axiom::bn256::{ + Fq, Fq12, Fq2, Fq6, G1Affine, G2Affine, G2Prepared, Gt, FROBENIUS_COEFF_FQ12_C1, + FROBENIUS_COEFF_FQ6_C1, XI_TO_Q_MINUS_1_OVER_2, +}; +use num_bigint::BigUint; +use num_traits::One; +use openvm_algebra_guest::{field::FieldExtension, IntMod}; +use openvm_ecc_guest::{weierstrass::WeierstrassPoint, AffinePoint}; +use rand::{rngs::StdRng, SeedableRng}; + +use super::{Fp, Fp12, Fp2}; +use crate::{ + bn254::{ + utils::{ + convert_bn254_fp12_to_halo2_fq12, convert_bn254_halo2_fq12_to_fp12, + convert_bn254_halo2_fq2_to_fp2, convert_bn254_halo2_fq_to_fp, + convert_g2_affine_halo2_to_openvm, + }, + Bn254, G2Affine as OpenVmG2Affine, BN254_MODULUS, BN254_ORDER, + BN254_PSEUDO_BINARY_ENCODING, BN254_SEED, + }, + pairing::{ + fp2_invert_assign, fp6_invert_assign, fp6_square_assign, FinalExp, MultiMillerLoop, + PairingCheck, PairingIntrinsics, + }, +}; + +#[test] +fn test_bn254_frobenius_coeffs() { + #[allow(clippy::needless_range_loop)] + for i in 0..12 { + for j in 0..5 { + assert_eq!( + Bn254::FROBENIUS_COEFFS[i][j], + convert_bn254_halo2_fq2_to_fp2(FROBENIUS_COEFF_FQ12_C1[i].pow([j as u64 + 1])), + "FROBENIUS_COEFFS[{}][{}] failed", + i, + j + ) + } + } +} + +#[test] +fn test_bn254_frobenius() { + let mut rng = StdRng::seed_from_u64(15); + for pow in 0..12 { + let fq = Fq12::random(&mut rng); + let fq_frob = fq.frobenius_map(pow); + + let fp = convert_bn254_halo2_fq12_to_fp12(fq); + let fp_frob = fp.frobenius_map(pow); + + assert_eq!(fp_frob, convert_bn254_halo2_fq12_to_fp12(fq_frob)); + } +} + +#[test] +fn test_fp12_invert() { + let mut rng = StdRng::seed_from_u64(15); + let fq = Fq12::random(&mut rng); + let fq_inv = fq.invert().unwrap(); + + let fp = convert_bn254_halo2_fq12_to_fp12(fq); + let fp_inv = fp.invert(); + assert_eq!(fp_inv, convert_bn254_halo2_fq12_to_fp12(fq_inv)); +} + +#[test] +fn test_fp6_invert() { + let mut rng = StdRng::seed_from_u64(20); + let fq6 = Fq6::random(&mut rng); + let fq6_inv = fq6.invert().unwrap(); + + let fp6c0 = convert_bn254_halo2_fq2_to_fp2(fq6.c0); + let fp6c1 = convert_bn254_halo2_fq2_to_fp2(fq6.c1); + let fp6c2 = convert_bn254_halo2_fq2_to_fp2(fq6.c2); + let mut fp6 = [fp6c0, fp6c1, fp6c2]; + fp6_invert_assign::(&mut fp6, &Bn254::XI); + + let fq6_invc0 = convert_bn254_halo2_fq2_to_fp2(fq6_inv.c0); + let fq6_invc1 = convert_bn254_halo2_fq2_to_fp2(fq6_inv.c1); + let fq6_invc2 = convert_bn254_halo2_fq2_to_fp2(fq6_inv.c2); + let fq6_inv = [fq6_invc0, fq6_invc1, fq6_invc2]; + assert_eq!(fp6, fq6_inv); +} + +#[test] +fn test_fp2_invert() { + let mut rng = StdRng::seed_from_u64(25); + let fq2 = Fq2::random(&mut rng); + let fq2_inv = fq2.invert().unwrap(); + + let mut fp2 = convert_bn254_halo2_fq2_to_fp2(fq2).to_coeffs(); + fp2_invert_assign::(&mut fp2); + assert_eq!(fp2, convert_bn254_halo2_fq2_to_fp2(fq2_inv).to_coeffs()); +} + +#[test] +fn test_fp6_square() { + let mut rng = StdRng::seed_from_u64(45); + let fq6 = Fq6::random(&mut rng); + let fq6_sq = fq6.square(); + + let fp6c0 = convert_bn254_halo2_fq2_to_fp2(fq6.c0); + let fp6c1 = convert_bn254_halo2_fq2_to_fp2(fq6.c1); + let fp6c2 = convert_bn254_halo2_fq2_to_fp2(fq6.c2); + let mut fp6 = [fp6c0, fp6c1, fp6c2]; + fp6_square_assign::(&mut fp6, &Bn254::XI); + + let fq6_sqc0 = convert_bn254_halo2_fq2_to_fp2(fq6_sq.c0); + let fq6_sqc1 = convert_bn254_halo2_fq2_to_fp2(fq6_sq.c1); + let fq6_sqc2 = convert_bn254_halo2_fq2_to_fp2(fq6_sq.c2); + let fq6_sq = [fq6_sqc0, fq6_sqc1, fq6_sqc2]; + assert_eq!(fp6, fq6_sq); +} + +#[test] +fn test_fp2_square() { + let mut rng = StdRng::seed_from_u64(55); + let fq2 = Fq2::random(&mut rng); + let fq2_sq = fq2.square(); + + let fp2 = convert_bn254_halo2_fq2_to_fp2(fq2); + let fp2_sq = &fp2 * &fp2; + assert_eq!(fp2_sq, convert_bn254_halo2_fq2_to_fp2(fq2_sq)); +} + +#[test] +fn test_fp_add() { + let mut rng = StdRng::seed_from_u64(65); + let fq = Fq::random(&mut rng); + let fq_res = fq + Fq::one(); + + let fp = convert_bn254_halo2_fq_to_fp(fq); + let fp_res = fp + Fp::ONE; + assert_eq!(fp_res, convert_bn254_halo2_fq_to_fp(fq_res)); +} + +#[test] +fn test_fp_one() { + let fp_one = Fp::ONE; + let fq_one = Fq::ONE; + assert_eq!(fp_one, convert_bn254_halo2_fq_to_fp(fq_one)); +} + +// Gt(Fq12) is not public +fn assert_miller_results_eq(a: Gt, b: Fp12) { + let b = convert_bn254_fp12_to_halo2_fq12(b); + crate::halo2curves_shims::bn254::tests::assert_miller_results_eq(a, b); +} + +#[test] +fn test_bn254_miller_loop() { + let mut rng = StdRng::seed_from_u64(53); + let h2c_p = G1Affine::random(&mut rng); + let h2c_q = G2Affine::random(&mut rng); + + let p = AffinePoint { + x: convert_bn254_halo2_fq_to_fp(h2c_p.x), + y: convert_bn254_halo2_fq_to_fp(h2c_p.y), + }; + let q = AffinePoint { + x: convert_bn254_halo2_fq2_to_fp2(h2c_q.x), + y: convert_bn254_halo2_fq2_to_fp2(h2c_q.y), + }; + + // Compare against halo2curves implementation + let h2c_q_prepared = G2Prepared::from(h2c_q); + let compare_miller = halo2curves_axiom::bn256::multi_miller_loop(&[(&h2c_p, &h2c_q_prepared)]); + let f = Bn254::multi_miller_loop(&[p], &[q]); + assert_miller_results_eq(compare_miller, f); +} + +#[test] +fn test_bn254_miller_loop_identity() { + let mut rng = StdRng::seed_from_u64(33); + let h2c_p = G1Affine::identity(); + let h2c_q = G2Affine::random(&mut rng); + + let p = AffinePoint { + x: convert_bn254_halo2_fq_to_fp(Fq::ZERO), + y: convert_bn254_halo2_fq_to_fp(Fq::ZERO), + }; + let q = AffinePoint { + x: convert_bn254_halo2_fq2_to_fp2(h2c_q.x), + y: convert_bn254_halo2_fq2_to_fp2(h2c_q.y), + }; + + let f = Bn254::multi_miller_loop(&[p], &[q]); + // halo2curves implementation + let h2c_q_prepared = G2Prepared::from(h2c_q); + let compare_miller = halo2curves_axiom::bn256::multi_miller_loop(&[(&h2c_p, &h2c_q_prepared)]); + assert_miller_results_eq(compare_miller, f); +} + +#[test] +fn test_bn254_miller_loop_identity_2() { + let mut rng = StdRng::seed_from_u64(33); + let h2c_p = G1Affine::random(&mut rng); + let h2c_q = G2Affine::identity(); + let p = AffinePoint { + x: convert_bn254_halo2_fq_to_fp(h2c_p.x), + y: convert_bn254_halo2_fq_to_fp(h2c_p.y), + }; + let q = AffinePoint { + x: convert_bn254_halo2_fq2_to_fp2(Fq2::ZERO), + y: convert_bn254_halo2_fq2_to_fp2(Fq2::ZERO), + }; + + let f = Bn254::multi_miller_loop(&[p], &[q]); + // halo2curves implementation + let h2c_q_prepared = G2Prepared::from(h2c_q); + let compare_miller = halo2curves_axiom::bn256::multi_miller_loop(&[(&h2c_p, &h2c_q_prepared)]); + assert_miller_results_eq(compare_miller, f); +} + +// test on host is enough since we are testing the curve formulas and not anything +// about intrinsic functions +#[test] +fn test_bn254_g2_affine() { + let mut rng = StdRng::seed_from_u64(34); + for _ in 0..10 { + let p = G2Affine::random(&mut rng); + let q = G2Affine::random(&mut rng); + let expected_add = G2Affine::from(p + q); + let expected_sub = G2Affine::from(p - q); + let expected_neg = -p; + let expected_double = G2Affine::from(p + p); + let [p, q] = [p, q].map(|p| { + let x = convert_bn254_halo2_fq2_to_fp2(p.x); + let y = convert_bn254_halo2_fq2_to_fp2(p.y); + // check on curve + OpenVmG2Affine::from_xy(x, y).unwrap() + }); + let r_add = &p + &q; + let r_sub = &p - &q; + let r_neg = -&p; + let r_double = &p + &p; + + for (expected, actual) in [ + (expected_add, r_add), + (expected_sub, r_sub), + (expected_neg, r_neg), + (expected_double, r_double), + ] { + assert_eq!(convert_g2_affine_halo2_to_openvm(expected), actual); + } + } +} + +#[test] +fn test_bn254_pairing_check_hint_host() { + let mut rng = StdRng::seed_from_u64(83); + let h2c_p = G1Affine::random(&mut rng); + let h2c_q = G2Affine::random(&mut rng); + + let p = AffinePoint { + x: convert_bn254_halo2_fq_to_fp(h2c_p.x), + y: convert_bn254_halo2_fq_to_fp(h2c_p.y), + }; + let q = AffinePoint { + x: convert_bn254_halo2_fq2_to_fp2(h2c_q.x), + y: convert_bn254_halo2_fq2_to_fp2(h2c_q.y), + }; + + let (c, u) = Bn254::pairing_check_hint(&[p], &[q]); + + let p_cmp = AffinePoint { + x: h2c_p.x, + y: h2c_p.y, + }; + let q_cmp = AffinePoint { + x: h2c_q.x, + y: h2c_q.y, + }; + + let f_cmp = crate::halo2curves_shims::bn254::Bn254::multi_miller_loop(&[p_cmp], &[q_cmp]); + let (c_cmp, u_cmp) = crate::halo2curves_shims::bn254::Bn254::final_exp_hint(&f_cmp); + let c_cmp = convert_bn254_halo2_fq12_to_fp12(c_cmp); + let u_cmp = convert_bn254_halo2_fq12_to_fp12(u_cmp); + + assert_eq!(c, c_cmp); + assert_eq!(u, u_cmp); +} + +#[test] +fn test_bn254_final_exponent() { + let final_exp = (BN254_MODULUS.pow(12) - BigUint::one()) / BN254_ORDER.clone(); + assert_eq!(Bn254::FINAL_EXPONENT.to_vec(), final_exp.to_bytes_be()); +} + +#[test] +fn test_bn254_frobenius_coeffs_fq6() { + #[allow(clippy::needless_range_loop)] + for i in 0..3 { + assert_eq!( + Bn254::FROBENIUS_COEFF_FQ6_C1[i], + convert_bn254_halo2_fq2_to_fp2(FROBENIUS_COEFF_FQ6_C1[i]), + "FROBENIUS_COEFFS_FQ6_C1[{}] failed", + i, + ) + } +} + +#[test] +fn test_bn254_pseudo_binary_encoding() { + let mut x: i128 = 0; + let mut power_of_2 = 1; + for b in BN254_PSEUDO_BINARY_ENCODING.iter() { + x += (*b as i128) * power_of_2; + power_of_2 *= 2; + } + assert_eq!(x.unsigned_abs(), 6 * (BN254_SEED as u128) + 2); +} + +#[test] +fn test_bn254_xi_to_q_minus_1_over_2() { + assert_eq!( + Bn254::XI_TO_Q_MINUS_1_OVER_2, + convert_bn254_halo2_fq2_to_fp2(XI_TO_Q_MINUS_1_OVER_2), + "XI_TO_Q_MINUS_1_OVER_2 failed", + ) +} diff --git a/extensions/pairing/guest/src/bn254/utils.rs b/extensions/pairing/guest/src/bn254/utils.rs new file mode 100644 index 0000000000..9102e980db --- /dev/null +++ b/extensions/pairing/guest/src/bn254/utils.rs @@ -0,0 +1,49 @@ +use halo2curves_axiom::bn256::{Fq, Fq12, Fq2, G2Affine}; +use openvm_algebra_guest::{field::FieldExtension, IntMod}; +use openvm_ecc_guest::weierstrass::WeierstrassPoint; + +use super::{Fp, Fp12, Fp2}; +use crate::bn254::G2Affine as OpenVmG2Affine; + +pub(crate) fn convert_bn254_halo2_fq_to_fp(x: Fq) -> Fp { + let bytes = x.to_bytes(); + Fp::from_le_bytes(&bytes) +} + +pub(crate) fn convert_bn254_halo2_fq2_to_fp2(x: Fq2) -> Fp2 { + Fp2::new( + convert_bn254_halo2_fq_to_fp(x.c0), + convert_bn254_halo2_fq_to_fp(x.c1), + ) +} + +pub(crate) fn convert_bn254_halo2_fq12_to_fp12(x: Fq12) -> Fp12 { + Fp12 { + c: x.to_coeffs().map(convert_bn254_halo2_fq2_to_fp2), + } +} + +pub(crate) fn convert_bn254_fp_to_halo2_fq(x: Fp) -> Fq { + Fq::from_bytes(&x.0).unwrap() +} + +pub(crate) fn convert_bn254_fp2_to_halo2_fq2(x: Fp2) -> Fq2 { + Fq2 { + c0: convert_bn254_fp_to_halo2_fq(x.c0.clone()), + c1: convert_bn254_fp_to_halo2_fq(x.c1.clone()), + } +} + +#[allow(unused)] +pub(crate) fn convert_bn254_fp12_to_halo2_fq12(x: Fp12) -> Fq12 { + let c = x.to_coeffs(); + Fq12::from_coeffs(c.map(convert_bn254_fp2_to_halo2_fq2)) +} + +#[allow(unused)] +pub(crate) fn convert_g2_affine_halo2_to_openvm(p: G2Affine) -> OpenVmG2Affine { + OpenVmG2Affine::from_xy_unchecked( + convert_bn254_halo2_fq2_to_fp2(p.x), + convert_bn254_halo2_fq2_to_fp2(p.y), + ) +} diff --git a/extensions/pairing/guest/src/lib.rs b/extensions/pairing/guest/src/lib.rs index 9cf0042076..d0fdcf5eab 100644 --- a/extensions/pairing/guest/src/lib.rs +++ b/extensions/pairing/guest/src/lib.rs @@ -16,8 +16,24 @@ impl PairingBaseFunct7 { pub const PAIRING_MAX_KINDS: u8 = 16; } +extern crate alloc; +extern crate self as openvm_ecc; + +#[cfg(feature = "halo2curves")] +pub use halo2curves_axiom as halo2curves; +pub use openvm_algebra_guest as algebra; + /// Implementation of this library's traits on halo2curves types. /// Used for testing and also VM runtime execution. /// These should **only** be importable on a host machine. #[cfg(all(feature = "halo2curves", not(target_os = "zkvm")))] pub mod halo2curves_shims; +/// Traits for optimal Ate pairing check using intrinsic functions. +pub mod pairing; + +/// Types for BLS12-381 curve with intrinsic functions. +#[cfg(feature = "bls12_381")] +pub mod bls12_381; +/// Types for BN254 curve with intrinsic functions. +#[cfg(feature = "bn254")] +pub mod bn254; diff --git a/extensions/pairing/tests/Cargo.toml b/extensions/pairing/tests/Cargo.toml new file mode 100644 index 0000000000..6a337bfab0 --- /dev/null +++ b/extensions/pairing/tests/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "openvm-pairing-integration-tests" +description = "Integration tests for the OpenVM pairing extension" +version.workspace = true +authors.workspace = true +edition.workspace = true +homepage.workspace = true +repository.workspace = true + +[dependencies] +openvm-instructions = { workspace = true } +openvm-stark-sdk.workspace = true +openvm-circuit = { workspace = true, features = ["test-utils"] } +openvm-transpiler.workspace = true +openvm-algebra-circuit.workspace = true +openvm-algebra-transpiler.workspace = true +openvm-pairing-circuit.workspace = true +openvm-pairing-transpiler.workspace = true +openvm-pairing-guest.workspace = true +openvm-ecc-circuit.workspace = true +openvm-ecc-guest.workspace = true +openvm-ecc-transpiler.workspace = true +openvm-rv32im-transpiler.workspace = true +openvm = { workspace = true } +openvm-toolchain-tests = { workspace = true } +eyre.workspace = true +rand.workspace = true +num-bigint.workspace = true +num-traits.workspace = true +halo2curves-axiom = { workspace = true } + +[features] +default = ["parallel"] +parallel = ["openvm-circuit/parallel"] diff --git a/extensions/pairing/tests/programs/Cargo.toml b/extensions/pairing/tests/programs/Cargo.toml new file mode 100644 index 0000000000..6e5cd66984 --- /dev/null +++ b/extensions/pairing/tests/programs/Cargo.toml @@ -0,0 +1,51 @@ +[workspace] +[package] +name = "openvm-pairing-test-programs" +version = "0.0.0" +edition = "2021" + +[dependencies] +openvm = { path = "../../../../crates/toolchain/openvm" } +openvm-platform = { path = "../../../../crates/toolchain/platform" } + +openvm-algebra-guest = { path = "../../../../extensions/algebra/guest", default-features = false } +openvm-algebra-moduli-macros = { path = "../../../../extensions/algebra/moduli-macros", default-features = false } +openvm-algebra-complex-macros = { path = "../../../../extensions/algebra/complex-macros", default-features = false } +openvm-ecc-guest = { path = "../../../../extensions/ecc/guest", default-features = false } +openvm-ecc-sw-macros = { path = "../../../../extensions/ecc/sw-macros", default-features = false } +openvm-pairing-guest = { path = "../../../../extensions/pairing/guest", default-features = false } + +serde = { version = "1.0", default-features = false, features = [ + "alloc", + "derive", +] } +hex = { version = "0.4.3", default-features = false, features = ["alloc"] } +hex-literal = { version = "0.4.1", default-features = false } +k256 = { version = "0.13.3", default-features = false, features = [ + "ecdsa-core", + "ecdsa", +], optional = true } + +[features] +default = [] +std = ["serde/std", "openvm/std"] + +bn254 = ["openvm-pairing-guest/bn254"] +bls12_381 = ["openvm-pairing-guest/bls12_381"] + +[profile.release] +panic = "abort" +lto = "thin" # turn on lto = fat to decrease binary size, but this optimizes out some missing extern links so we shouldn't use it for testing +# strip = "symbols" + +[[example]] +name = "bn_final_exp_hint" +required-features = ["bn254"] + +[[example]] +name = "bls_final_exp_hint" +required-features = ["bls12_381"] + +[[example]] +name = "bls_ec" +required-features = ["bls12_381"] diff --git a/extensions/pairing/tests/programs/examples/bls_ec.rs b/extensions/pairing/tests/programs/examples/bls_ec.rs new file mode 100644 index 0000000000..93f72115cf --- /dev/null +++ b/extensions/pairing/tests/programs/examples/bls_ec.rs @@ -0,0 +1,11 @@ +#![cfg_attr(not(feature = "std"), no_main)] +#![cfg_attr(not(feature = "std"), no_std)] + +#[allow(unused_imports)] +use openvm_pairing_guest::bls12_381::Bls12_381G1Affine; + +openvm::init!("openvm_init_bls_ec_bls12_381.rs"); + +openvm::entry!(main); + +pub fn main() {} diff --git a/extensions/pairing/tests/programs/examples/bls_final_exp_hint.rs b/extensions/pairing/tests/programs/examples/bls_final_exp_hint.rs new file mode 100644 index 0000000000..33660e0951 --- /dev/null +++ b/extensions/pairing/tests/programs/examples/bls_final_exp_hint.rs @@ -0,0 +1,24 @@ +#![cfg_attr(not(feature = "std"), no_main)] +#![cfg_attr(not(feature = "std"), no_std)] + +extern crate alloc; + +use alloc::vec::Vec; + +use openvm::io::read; +use openvm_ecc_guest::AffinePoint; +use openvm_pairing_guest::{ + bls12_381::{Bls12_381, Fp, Fp12, Fp2}, + pairing::PairingCheck, +}; + +openvm::entry!(main); + +openvm::init!("openvm_init_bls_final_exp_hint_bls12_381.rs"); + +pub fn main() { + #[allow(clippy::type_complexity)] + let (p, q, expected): (Vec>, Vec>, (Fp12, Fp12)) = read(); + let actual = Bls12_381::pairing_check_hint(&p, &q); + assert_eq!(actual, expected); +} diff --git a/extensions/pairing/tests/programs/examples/bn_final_exp_hint.rs b/extensions/pairing/tests/programs/examples/bn_final_exp_hint.rs new file mode 100644 index 0000000000..347afa72bb --- /dev/null +++ b/extensions/pairing/tests/programs/examples/bn_final_exp_hint.rs @@ -0,0 +1,24 @@ +#![cfg_attr(not(feature = "std"), no_main)] +#![cfg_attr(not(feature = "std"), no_std)] + +extern crate alloc; + +use alloc::vec::Vec; + +use openvm::io::read; +use openvm_ecc_guest::AffinePoint; +use openvm_pairing_guest::{ + bn254::{Bn254, Fp, Fp12, Fp2}, + pairing::PairingCheck, +}; + +openvm::entry!(main); + +openvm::init!("openvm_init_bn_final_exp_hint_bn254.rs"); + +pub fn main() { + #[allow(clippy::type_complexity)] + let (p, q, expected): (Vec>, Vec>, (Fp12, Fp12)) = read(); + let actual = Bn254::pairing_check_hint(&p, &q); + assert_eq!(actual, expected); +} diff --git a/extensions/pairing/tests/programs/examples/fp12_mul.rs b/extensions/pairing/tests/programs/examples/fp12_mul.rs new file mode 100644 index 0000000000..aeec2fb63d --- /dev/null +++ b/extensions/pairing/tests/programs/examples/fp12_mul.rs @@ -0,0 +1,82 @@ +#![cfg_attr(not(feature = "std"), no_main)] +#![cfg_attr(not(feature = "std"), no_std)] +#![allow(unused_imports)] + +use openvm::io::read_vec; +use openvm_algebra_guest::{field::FieldExtension, IntMod}; + +openvm::entry!(main); + +#[cfg(feature = "bn254")] +mod bn254 { + use openvm_pairing_guest::bn254::{Fp, Fp12}; + + use super::*; + + openvm::init!("openvm_init_fp12_mul_bn254.rs"); + + pub fn test_fp12_mul(io: &[u8]) { + assert_eq!(io.len(), 32 * 36); + + let f0 = &io[0..32 * 12]; + let f1 = &io[32 * 12..32 * 24]; + let r_cmp = &io[32 * 24..32 * 36]; + + let f0_cast = Fp12::from_bytes(f0); + let f1_cast = Fp12::from_bytes(f1); + + let r = f0_cast * f1_cast; + let mut r_bytes = [0u8; 32 * 12]; + r.to_coeffs() + .iter() + .flat_map(|fp2| fp2.clone().to_coeffs()) + .enumerate() + .for_each(|(i, fp)| r_bytes[i * 32..(i + 1) * 32].copy_from_slice(fp.as_le_bytes())); + + assert_eq!(r_bytes, r_cmp); + } +} + +#[cfg(feature = "bls12_381")] +mod bls12_381 { + use openvm_pairing_guest::bls12_381::{Fp, Fp12}; + + use super::*; + + openvm::init!("openvm_init_fp12_mul_bls12_381.rs"); + + pub fn test_fp12_mul(io: &[u8]) { + assert_eq!(io.len(), 48 * 36); + + let f0 = &io[0..48 * 12]; + let f1 = &io[48 * 12..48 * 24]; + let r_cmp = &io[48 * 24..48 * 36]; + + let f0_cast = Fp12::from_bytes(f0); + let f1_cast = Fp12::from_bytes(f1); + + let r = f0_cast * f1_cast; + let mut r_bytes = [0u8; 48 * 12]; + r.to_coeffs() + .iter() + .flat_map(|fp2| fp2.clone().to_coeffs()) + .enumerate() + .for_each(|(i, fp)| r_bytes[i * 48..(i + 1) * 48].copy_from_slice(fp.as_le_bytes())); + + assert_eq!(r_bytes, r_cmp); + } +} + +pub fn main() { + #[allow(unused_variables)] + let io = read_vec(); + + #[cfg(feature = "bn254")] + { + bn254::test_fp12_mul(&io) + } + #[cfg(feature = "bls12_381")] + { + bls12_381::test_fp12_mul(&io) + } +} diff --git a/extensions/pairing/tests/programs/examples/pairing_check.rs b/extensions/pairing/tests/programs/examples/pairing_check.rs new file mode 100644 index 0000000000..c01caded79 --- /dev/null +++ b/extensions/pairing/tests/programs/examples/pairing_check.rs @@ -0,0 +1,90 @@ +#![cfg_attr(not(feature = "std"), no_main)] +#![cfg_attr(not(feature = "std"), no_std)] +#![allow(unused_imports)] + +extern crate alloc; + +use openvm::io::read_vec; +use openvm_ecc_guest::AffinePoint; +use openvm_pairing_guest::pairing::PairingCheck; + +openvm::entry!(main); + +#[cfg(feature = "bn254")] +mod bn254 { + use alloc::format; + + use openvm_algebra_guest::{field::FieldExtension, IntMod}; + use openvm_pairing_guest::bn254::{Bn254, Fp, Fp2}; + + use super::*; + + openvm::init!("openvm_init_pairing_check_bn254.rs"); + + pub fn test_pairing_check(io: &[u8]) { + let s0 = &io[0..32 * 2]; + let s1 = &io[32 * 2..32 * 4]; + let q0 = &io[32 * 4..32 * 8]; + let q1 = &io[32 * 8..32 * 12]; + + let s0_cast = + AffinePoint::new(Fp::from_le_bytes(&s0[..32]), Fp::from_le_bytes(&s0[32..64])); + let s1_cast = + AffinePoint::new(Fp::from_le_bytes(&s1[..32]), Fp::from_le_bytes(&s1[32..64])); + let q0_cast = AffinePoint::new(Fp2::from_bytes(&q0[..64]), Fp2::from_bytes(&q0[64..128])); + let q1_cast = AffinePoint::new(Fp2::from_bytes(&q1[..64]), Fp2::from_bytes(&q1[64..128])); + + let f = Bn254::pairing_check( + &[s0_cast.clone(), s1_cast.clone()], + &[q0_cast.clone(), q1_cast.clone()], + ); + assert_eq!(f, Ok(())); + } +} + +#[cfg(feature = "bls12_381")] +mod bls12_381 { + + use alloc::format; + + use openvm_algebra_guest::{field::FieldExtension, IntMod}; + use openvm_pairing_guest::bls12_381::{Bls12_381, Fp, Fp2}; + + use super::*; + + openvm::init!("openvm_init_pairing_check_bls12_381.rs"); + + pub fn test_pairing_check(io: &[u8]) { + let s0 = &io[0..48 * 2]; + let s1 = &io[48 * 2..48 * 4]; + let q0 = &io[48 * 4..48 * 8]; + let q1 = &io[48 * 8..48 * 12]; + + let s0_cast = + AffinePoint::new(Fp::from_le_bytes(&s0[..48]), Fp::from_le_bytes(&s0[48..96])); + let s1_cast = + AffinePoint::new(Fp::from_le_bytes(&s1[..48]), Fp::from_le_bytes(&s1[48..96])); + let q0_cast = AffinePoint::new(Fp2::from_bytes(&q0[..96]), Fp2::from_bytes(&q0[96..192])); + let q1_cast = AffinePoint::new(Fp2::from_bytes(&q1[..96]), Fp2::from_bytes(&q1[96..192])); + + let f = Bls12_381::pairing_check( + &[s0_cast.clone(), s1_cast.clone()], + &[q0_cast.clone(), q1_cast.clone()], + ); + assert_eq!(f, Ok(())); + } +} + +pub fn main() { + #[allow(unused_variables)] + let io = read_vec(); + + #[cfg(feature = "bn254")] + { + bn254::test_pairing_check(&io); + } + #[cfg(feature = "bls12_381")] + { + bls12_381::test_pairing_check(&io); + } +} diff --git a/extensions/pairing/tests/programs/examples/pairing_check_fallback.rs b/extensions/pairing/tests/programs/examples/pairing_check_fallback.rs new file mode 100644 index 0000000000..da3bcbb16f --- /dev/null +++ b/extensions/pairing/tests/programs/examples/pairing_check_fallback.rs @@ -0,0 +1,241 @@ +#![cfg_attr(not(feature = "std"), no_main)] +#![cfg_attr(not(feature = "std"), no_std)] +#![allow(unused_imports)] + +extern crate alloc; + +use openvm::io::read_vec; +use openvm_algebra_guest::{ + field::{ComplexConjugate, FieldExtension}, + DivUnsafe, Field, IntMod, +}; +use openvm_ecc_guest::AffinePoint; +use openvm_pairing_guest::pairing::{ + exp_check_fallback, MultiMillerLoop, PairingCheck, PairingCheckError, +}; + +openvm::entry!(main); + +#[cfg(feature = "bn254")] +mod bn254 { + use alloc::format; + + use openvm_pairing_guest::bn254::{Bn254, Fp, Fp12, Fp2}; + + use super::*; + + openvm::init!("openvm_init_pairing_check_fallback_bn254.rs"); + + // Wrapper so that we can override `pairing_check_hint` + struct Bn254Wrapper(Bn254); + + #[allow(non_snake_case)] + impl PairingCheck for Bn254Wrapper { + type Fp = Fp; + type Fp2 = Fp2; + type Fp12 = Fp12; + + fn pairing_check_hint( + _P: &[AffinePoint], + _Q: &[AffinePoint], + ) -> (Self::Fp12, Self::Fp12) { + // return dummy values + (Fp12::ZERO, Fp12::ZERO) + } + + // copied from Bn254::pairing_check + fn pairing_check( + P: &[AffinePoint], + Q: &[AffinePoint], + ) -> Result<(), PairingCheckError> { + Self::try_honest_pairing_check(P, Q).unwrap_or_else(|| { + let f = Bn254::multi_miller_loop(P, Q); + exp_check_fallback(&f, &Bn254::FINAL_EXPONENT) + }) + } + } + + #[allow(non_snake_case)] + impl Bn254Wrapper { + // copied from Bn254::try_honest_pairing_check + fn try_honest_pairing_check( + P: &[AffinePoint<::Fp>], + Q: &[AffinePoint<::Fp2>], + ) -> Option> { + let (c, s) = Self::pairing_check_hint(P, Q); + + // f * s = c^{q - x} + // f * s = c^q * c^-x + // f * c^x * c^-q * s = 1, + // where fc = f * c'^x (embedded Miller loop with c conjugate inverse), + // and the curve seed x = -0xd201000000010000 + // the miller loop computation includes a conjugation at the end because the value of + // the seed is negative, so we need to conjugate the miller loop input c + // as c'. We then substitute y = -x to get c^-y and finally compute c'^-y + // as input to the miller loop: f * c'^-y * c^-q * s = 1 + let c_q = FieldExtension::frobenius_map(&c, 1); + let c_conj = c.conjugate(); + if c_conj == Fp12::ZERO { + return None; + } + let c_conj_inv = Fp12::ONE.div_unsafe(&c_conj); + + // fc = f_{Miller,x,Q}(P) * c^{x} + // where + // fc = conjugate( f_{Miller,-x,Q}(P) * c'^{-x} ), with c' denoting the conjugate of c + let fc = Bn254::multi_miller_loop_embedded_exp(P, Q, Some(c_conj_inv)); + + if fc * s == c_q { + Some(Ok(())) + } else { + None + } + } + } + + pub fn test_pairing_check(io: &[u8]) { + let s0 = &io[0..32 * 2]; + let s1 = &io[32 * 2..32 * 4]; + let q0 = &io[32 * 4..32 * 8]; + let q1 = &io[32 * 8..32 * 12]; + + let s0_cast = + AffinePoint::new(Fp::from_le_bytes(&s0[..32]), Fp::from_le_bytes(&s0[32..64])); + let s1_cast = + AffinePoint::new(Fp::from_le_bytes(&s1[..32]), Fp::from_le_bytes(&s1[32..64])); + let q0_cast = AffinePoint::new(Fp2::from_bytes(&q0[..64]), Fp2::from_bytes(&q0[64..128])); + let q1_cast = AffinePoint::new(Fp2::from_bytes(&q1[..64]), Fp2::from_bytes(&q1[64..128])); + + let f = Bn254Wrapper::pairing_check( + &[s0_cast.clone(), s1_cast.clone()], + &[q0_cast.clone(), q1_cast.clone()], + ); + assert_eq!(f, Ok(())); + + let f = Bn254Wrapper::pairing_check( + &[-s0_cast.clone(), s1_cast.clone()], + &[q0_cast.clone(), q1_cast.clone()], + ); + assert_eq!(f, Err(PairingCheckError)); + } +} + +#[cfg(feature = "bls12_381")] +mod bls12_381 { + + use alloc::format; + + use openvm_pairing_guest::bls12_381::{Bls12_381, Fp, Fp12, Fp2}; + + use super::*; + + openvm::init!("openvm_init_pairing_check_fallback_bls12_381.rs"); + + // Wrapper so that we can override `pairing_check_hint` + struct Bls12_381Wrapper(Bls12_381); + + #[allow(non_snake_case)] + impl PairingCheck for Bls12_381Wrapper { + type Fp = Fp; + type Fp2 = Fp2; + type Fp12 = Fp12; + + #[allow(unused_variables)] + fn pairing_check_hint( + _P: &[AffinePoint], + _Q: &[AffinePoint], + ) -> (Self::Fp12, Self::Fp12) { + // return dummy values + (Fp12::ZERO, Fp12::ZERO) + } + + // copied from Bls12_381::pairing_check + fn pairing_check( + P: &[AffinePoint], + Q: &[AffinePoint], + ) -> Result<(), PairingCheckError> { + Self::try_honest_pairing_check(P, Q).unwrap_or_else(|| { + let f = Bls12_381::multi_miller_loop(P, Q); + exp_check_fallback(&f, &Bls12_381::FINAL_EXPONENT) + }) + } + } + + #[allow(non_snake_case)] + impl Bls12_381Wrapper { + // copied from Bls12_381::try_honest_pairing_check + fn try_honest_pairing_check( + P: &[AffinePoint<::Fp>], + Q: &[AffinePoint<::Fp2>], + ) -> Option> { + let (c, s) = Self::pairing_check_hint(P, Q); + + // f * s = c^{q - x} + // f * s = c^q * c^-x + // f * c^x * c^-q * s = 1, + // where fc = f * c'^x (embedded Miller loop with c conjugate inverse), + // and the curve seed x = -0xd201000000010000 + // the miller loop computation includes a conjugation at the end because the value of + // the seed is negative, so we need to conjugate the miller loop input c + // as c'. We then substitute y = -x to get c^-y and finally compute c'^-y + // as input to the miller loop: f * c'^-y * c^-q * s = 1 + let c_q = FieldExtension::frobenius_map(&c, 1); + let c_conj = c.conjugate(); + if c_conj == Fp12::ZERO { + return None; + } + let c_conj_inv = Fp12::ONE.div_unsafe(&c_conj); + + // fc = f_{Miller,x,Q}(P) * c^{x} + // where + // fc = conjugate( f_{Miller,-x,Q}(P) * c'^{-x} ), with c' denoting the conjugate of c + let fc = Bls12_381::multi_miller_loop_embedded_exp(P, Q, Some(c_conj_inv)); + + if fc * s == c_q { + Some(Ok(())) + } else { + None + } + } + } + + pub fn test_pairing_check(io: &[u8]) { + let s0 = &io[0..48 * 2]; + let s1 = &io[48 * 2..48 * 4]; + let q0 = &io[48 * 4..48 * 8]; + let q1 = &io[48 * 8..48 * 12]; + + let s0_cast = + AffinePoint::new(Fp::from_le_bytes(&s0[..48]), Fp::from_le_bytes(&s0[48..96])); + let s1_cast = + AffinePoint::new(Fp::from_le_bytes(&s1[..48]), Fp::from_le_bytes(&s1[48..96])); + let q0_cast = AffinePoint::new(Fp2::from_bytes(&q0[..96]), Fp2::from_bytes(&q0[96..192])); + let q1_cast = AffinePoint::new(Fp2::from_bytes(&q1[..96]), Fp2::from_bytes(&q1[96..192])); + + let f = Bls12_381Wrapper::pairing_check( + &[s0_cast.clone(), s1_cast.clone()], + &[q0_cast.clone(), q1_cast.clone()], + ); + assert_eq!(f, Ok(())); + + let f = Bls12_381Wrapper::pairing_check( + &[-s0_cast.clone(), s1_cast.clone()], + &[q0_cast.clone(), q1_cast.clone()], + ); + assert_eq!(f, Err(PairingCheckError)); + } +} + +pub fn main() { + #[allow(unused_variables)] + let io = read_vec(); + + #[cfg(feature = "bn254")] + { + bn254::test_pairing_check(&io); + } + #[cfg(feature = "bls12_381")] + { + bls12_381::test_pairing_check(&io); + } +} diff --git a/extensions/pairing/tests/programs/examples/pairing_line.rs b/extensions/pairing/tests/programs/examples/pairing_line.rs new file mode 100644 index 0000000000..b36c200391 --- /dev/null +++ b/extensions/pairing/tests/programs/examples/pairing_line.rs @@ -0,0 +1,147 @@ +#![allow(unused_imports)] +#![cfg_attr(not(feature = "std"), no_main)] +#![cfg_attr(not(feature = "std"), no_std)] + +use openvm::io::read_vec; +use openvm_algebra_guest::{field::FieldExtension, IntMod}; +use openvm_pairing_guest::pairing::{EvaluatedLine, LineMulDType, LineMulMType}; + +openvm::entry!(main); + +#[cfg(feature = "bn254")] +mod bn254 { + use openvm_pairing_guest::bn254::{Bn254, Fp, Fp12, Fp2}; + + use super::*; + + openvm::init!("openvm_init_pairing_line_bn254.rs"); + + pub fn test_mul_013_by_013(io: &[u8]) { + assert_eq!(io.len(), 32 * 18); + let l0 = &io[..32 * 4]; + let l1 = &io[32 * 4..32 * 8]; + let expected = &io[32 * 8..32 * 18]; + + let l0_cast = EvaluatedLine { + b: Fp2::from_bytes(&l0[..64]), + c: Fp2::from_bytes(&l0[64..128]), + }; + let l1_cast = EvaluatedLine { + b: Fp2::from_bytes(&l1[..64]), + c: Fp2::from_bytes(&l1[64..128]), + }; + + let r = Bn254::mul_013_by_013(&l0_cast, &l1_cast); + let mut r_bytes = [0u8; 32 * 10]; + let mut i = 0; + for x in r { + r_bytes[i..i + 32].copy_from_slice(x.c0.as_le_bytes()); + r_bytes[i + 32..i + 64].copy_from_slice(x.c1.as_le_bytes()); + i += 64; + } + assert_eq!(r_bytes, expected); + } + + pub fn test_mul_by_01234(io: &[u8]) { + assert_eq!(io.len(), 32 * 34); + let f = &io[..32 * 12]; + let x = &io[32 * 12..32 * 22]; + let expected = &io[32 * 22..32 * 34]; + + let f_cast = Fp12::from_bytes(f); + let x_cast = [ + Fp2::from_bytes(&x[..64]), + Fp2::from_bytes(&x[64..128]), + Fp2::from_bytes(&x[128..192]), + Fp2::from_bytes(&x[192..256]), + Fp2::from_bytes(&x[256..320]), + ]; + + let r = Bn254::mul_by_01234(&f_cast, &x_cast); + let mut r_bytes = [0u8; 32 * 12]; + let mut i = 0; + for x in r.to_coeffs() { + r_bytes[i..i + 32].copy_from_slice(x.c0.as_le_bytes()); + r_bytes[i + 32..i + 64].copy_from_slice(x.c1.as_le_bytes()); + i += 64; + } + assert_eq!(r_bytes, expected); + } +} + +#[cfg(feature = "bls12_381")] +mod bls12_381 { + use openvm_pairing_guest::bls12_381::{Bls12_381, Fp, Fp12, Fp2}; + + use super::*; + + openvm::init!("openvm_init_pairing_line_bls12_381.rs"); + + pub fn test_mul_023_by_023(io: &[u8]) { + assert_eq!(io.len(), 48 * 18); + let l0 = &io[..48 * 4]; + let l1 = &io[48 * 4..48 * 8]; + let expected = &io[48 * 8..48 * 18]; + + let l0_cast = EvaluatedLine { + b: Fp2::from_bytes(&l0[..48 * 2]), + c: Fp2::from_bytes(&l0[48 * 2..48 * 4]), + }; + let l1_cast = EvaluatedLine { + b: Fp2::from_bytes(&l1[..48 * 2]), + c: Fp2::from_bytes(&l1[48 * 2..48 * 4]), + }; + + let r = Bls12_381::mul_023_by_023(&l0_cast, &l1_cast); + let mut r_bytes = [0u8; 48 * 10]; + let mut i = 0; + for x in r { + r_bytes[i..i + 48].copy_from_slice(x.c0.as_le_bytes()); + r_bytes[i + 48..i + 96].copy_from_slice(x.c1.as_le_bytes()); + i += 96; + } + assert_eq!(r_bytes, expected); + } + + pub fn test_mul_by_02345(io: &[u8]) { + assert_eq!(io.len(), 48 * 34); + let f = &io[..48 * 12]; + let x = &io[48 * 12..48 * 22]; + let expected = &io[48 * 22..48 * 34]; + + let f_cast = Fp12::from_bytes(f); + let x_cast = [ + Fp2::from_bytes(&x[..48 * 2]), + Fp2::from_bytes(&x[48 * 2..48 * 4]), + Fp2::from_bytes(&x[48 * 4..48 * 6]), + Fp2::from_bytes(&x[48 * 6..48 * 8]), + Fp2::from_bytes(&x[48 * 8..48 * 10]), + ]; + + let r = Bls12_381::mul_by_02345(&f_cast, &x_cast); + let mut r_bytes = [0u8; 48 * 12]; + let mut i = 0; + for x in r.to_coeffs() { + r_bytes[i..i + 48].copy_from_slice(x.c0.as_le_bytes()); + r_bytes[i + 48..i + 96].copy_from_slice(x.c1.as_le_bytes()); + i += 96; + } + assert_eq!(r_bytes, expected); + } +} + +pub fn main() { + #[allow(unused_variables)] + let io = read_vec(); + + #[cfg(feature = "bn254")] + { + bn254::test_mul_013_by_013(&io[..32 * 18]); + bn254::test_mul_by_01234(&io[32 * 18..32 * 52]); + } + #[cfg(feature = "bls12_381")] + { + bls12_381::test_mul_023_by_023(&io[..48 * 18]); + bls12_381::test_mul_by_02345(&io[48 * 18..48 * 52]); + } +} diff --git a/extensions/pairing/tests/programs/examples/pairing_miller_loop.rs b/extensions/pairing/tests/programs/examples/pairing_miller_loop.rs new file mode 100644 index 0000000000..a9d8f09dbd --- /dev/null +++ b/extensions/pairing/tests/programs/examples/pairing_miller_loop.rs @@ -0,0 +1,97 @@ +#![allow(unused_imports)] +#![cfg_attr(not(feature = "std"), no_main)] +#![cfg_attr(not(feature = "std"), no_std)] + +extern crate alloc; + +use openvm::io::read_vec; +use openvm_algebra_guest::{field::FieldExtension, IntMod}; +use openvm_ecc_guest::AffinePoint; +use openvm_pairing_guest::pairing::MultiMillerLoop; + +openvm::entry!(main); + +#[cfg(feature = "bn254")] +mod bn254 { + use openvm_pairing_guest::bn254::{Bn254, Fp, Fp2}; + + use super::*; + + openvm::init!("openvm_init_pairing_miller_loop_bn254.rs"); + + pub fn test_miller_loop(io: &[u8]) { + let s0 = &io[0..32 * 2]; + let s1 = &io[32 * 2..32 * 4]; + let q0 = &io[32 * 4..32 * 8]; + let q1 = &io[32 * 8..32 * 12]; + let f_cmp = &io[32 * 12..32 * 24]; + + let s0_cast = + AffinePoint::new(Fp::from_le_bytes(&s0[..32]), Fp::from_le_bytes(&s0[32..64])); + let s1_cast = + AffinePoint::new(Fp::from_le_bytes(&s1[..32]), Fp::from_le_bytes(&s1[32..64])); + let q0_cast = AffinePoint::new(Fp2::from_bytes(&q0[..64]), Fp2::from_bytes(&q0[64..128])); + let q1_cast = AffinePoint::new(Fp2::from_bytes(&q1[..64]), Fp2::from_bytes(&q1[64..128])); + + let f = Bn254::multi_miller_loop(&[s0_cast, s1_cast], &[q0_cast, q1_cast]); + let mut f_bytes = [0u8; 32 * 12]; + f.to_coeffs() + .iter() + .flat_map(|fp2| fp2.clone().to_coeffs()) + .enumerate() + .for_each(|(i, fp)| f_bytes[i * 32..(i + 1) * 32].copy_from_slice(fp.as_le_bytes())); + + assert_eq!(f_bytes, f_cmp); + } +} + +#[cfg(feature = "bls12_381")] +mod bls12_381 { + use openvm_pairing_guest::bls12_381::{Bls12_381, Fp, Fp2}; + + use super::*; + + openvm::init!("openvm_init_pairing_miller_loop_bls12_381.rs"); + + pub fn test_miller_loop(io: &[u8]) { + let s0 = &io[0..48 * 2]; + let s1 = &io[48 * 2..48 * 4]; + let q0 = &io[48 * 4..48 * 8]; + let q1 = &io[48 * 8..48 * 12]; + let f_cmp = &io[48 * 12..48 * 24]; + + let s0_cast = + AffinePoint::new(Fp::from_le_bytes(&s0[..48]), Fp::from_le_bytes(&s0[48..96])); + let s1_cast = + AffinePoint::new(Fp::from_le_bytes(&s1[..48]), Fp::from_le_bytes(&s1[48..96])); + let q0_cast = AffinePoint::new(Fp2::from_bytes(&q0[..96]), Fp2::from_bytes(&q0[96..192])); + let q1_cast = AffinePoint::new(Fp2::from_bytes(&q1[..96]), Fp2::from_bytes(&q1[96..192])); + + let f = Bls12_381::multi_miller_loop( + &[s0_cast.clone(), s1_cast.clone()], + &[q0_cast.clone(), q1_cast.clone()], + ); + let mut f_bytes = [0u8; 48 * 12]; + f.to_coeffs() + .iter() + .flat_map(|fp2| fp2.clone().to_coeffs()) + .enumerate() + .for_each(|(i, fp)| f_bytes[i * 48..(i + 1) * 48].copy_from_slice(fp.as_le_bytes())); + + assert_eq!(f_bytes, f_cmp); + } +} + +pub fn main() { + #[allow(unused_variables)] + let io = read_vec(); + + #[cfg(feature = "bn254")] + { + bn254::test_miller_loop(&io); + } + #[cfg(feature = "bls12_381")] + { + bls12_381::test_miller_loop(&io); + } +} diff --git a/extensions/pairing/tests/programs/examples/pairing_miller_step.rs b/extensions/pairing/tests/programs/examples/pairing_miller_step.rs new file mode 100644 index 0000000000..a2d4662cf4 --- /dev/null +++ b/extensions/pairing/tests/programs/examples/pairing_miller_step.rs @@ -0,0 +1,166 @@ +#![allow(unused_imports)] +#![cfg_attr(not(feature = "std"), no_main)] +#![cfg_attr(not(feature = "std"), no_std)] + +use openvm::io::read_vec; +use openvm_algebra_guest::IntMod; +use openvm_ecc_guest::AffinePoint; +use openvm_pairing_guest::pairing::MillerStep; + +openvm::entry!(main); + +#[cfg(feature = "bn254")] +mod bn254 { + use openvm_algebra_guest::field::FieldExtension; + use openvm_pairing_guest::bn254::{Bn254, Fp, Fp2}; + + use super::*; + + openvm::init!("openvm_init_pairing_miller_step_bn254.rs"); + + pub fn test_miller_step(io: &[u8]) { + assert_eq!(io.len(), 32 * 12); + let s = &io[..32 * 4]; + let pt = &io[32 * 4..32 * 8]; + let l = &io[32 * 8..32 * 12]; + + let s_cast = AffinePoint::new(Fp2::from_bytes(&s[..64]), Fp2::from_bytes(&s[64..128])); + + let (pt_cmp, l_cmp) = Bn254::miller_double_step(&s_cast); + let mut pt_bytes = [0u8; 32 * 4]; + let mut l_bytes = [0u8; 32 * 4]; + + // TODO: if we ever need to change this, we should switch to using `StdIn::write` to + // serialize for us and use `read()` instead of `read_vec()` + pt_bytes[0..32].copy_from_slice(pt_cmp.x.c0.as_le_bytes()); + pt_bytes[32..2 * 32].copy_from_slice(pt_cmp.x.c1.as_le_bytes()); + pt_bytes[2 * 32..3 * 32].copy_from_slice(pt_cmp.y.c0.as_le_bytes()); + pt_bytes[3 * 32..4 * 32].copy_from_slice(pt_cmp.y.c1.as_le_bytes()); + l_bytes[0..32].copy_from_slice(l_cmp.b.c0.as_le_bytes()); + l_bytes[32..2 * 32].copy_from_slice(l_cmp.b.c1.as_le_bytes()); + l_bytes[2 * 32..3 * 32].copy_from_slice(l_cmp.c.c0.as_le_bytes()); + l_bytes[3 * 32..4 * 32].copy_from_slice(l_cmp.c.c1.as_le_bytes()); + + assert_eq!(pt_bytes, pt); + assert_eq!(l_bytes, l); + } + + pub fn test_miller_double_and_add_step(io: &[u8]) { + assert_eq!(io.len(), 32 * 20); + let s = &io[0..32 * 4]; + let q = &io[32 * 4..32 * 8]; + let pt = &io[32 * 8..32 * 12]; + let l0 = &io[32 * 12..32 * 16]; + let l1 = &io[32 * 16..32 * 20]; + + let s_cast = AffinePoint::new(Fp2::from_bytes(&s[..64]), Fp2::from_bytes(&s[64..128])); + let q_cast = AffinePoint::new(Fp2::from_bytes(&q[..64]), Fp2::from_bytes(&q[64..128])); + let (pt_cmp, l0_cmp, l1_cmp) = Bn254::miller_double_and_add_step(&s_cast, &q_cast); + let mut pt_bytes = [0u8; 32 * 4]; + let mut l0_bytes = [0u8; 32 * 4]; + let mut l1_bytes = [0u8; 32 * 4]; + + // TODO: if we ever need to change this, we should switch to using `StdIn::write` to + // serialize for us and use `read()` instead of `read_vec()` + pt_bytes[0..32].copy_from_slice(pt_cmp.x.c0.as_le_bytes()); + pt_bytes[32..2 * 32].copy_from_slice(pt_cmp.x.c1.as_le_bytes()); + pt_bytes[2 * 32..3 * 32].copy_from_slice(pt_cmp.y.c0.as_le_bytes()); + pt_bytes[3 * 32..4 * 32].copy_from_slice(pt_cmp.y.c1.as_le_bytes()); + l0_bytes[0..32].copy_from_slice(l0_cmp.b.c0.as_le_bytes()); + l0_bytes[32..2 * 32].copy_from_slice(l0_cmp.b.c1.as_le_bytes()); + l0_bytes[2 * 32..3 * 32].copy_from_slice(l0_cmp.c.c0.as_le_bytes()); + l0_bytes[3 * 32..4 * 32].copy_from_slice(l0_cmp.c.c1.as_le_bytes()); + l1_bytes[0..32].copy_from_slice(l1_cmp.b.c0.as_le_bytes()); + l1_bytes[32..2 * 32].copy_from_slice(l1_cmp.b.c1.as_le_bytes()); + l1_bytes[2 * 32..3 * 32].copy_from_slice(l1_cmp.c.c0.as_le_bytes()); + l1_bytes[3 * 32..4 * 32].copy_from_slice(l1_cmp.c.c1.as_le_bytes()); + + assert_eq!(pt_bytes, pt); + assert_eq!(l0_bytes, l0); + assert_eq!(l1_bytes, l1); + } +} + +#[cfg(feature = "bls12_381")] +mod bls12_381 { + use openvm_algebra_guest::field::FieldExtension; + use openvm_pairing_guest::bls12_381::{Bls12_381, Fp, Fp2}; + + use super::*; + + openvm::init!("openvm_init_pairing_miller_step_bls12_381.rs"); + + pub fn test_miller_step(io: &[u8]) { + assert_eq!(io.len(), 48 * 12); + let s = &io[..48 * 4]; + let pt = &io[48 * 4..48 * 8]; + let l = &io[48 * 8..48 * 12]; + + let s_cast = AffinePoint::new(Fp2::from_bytes(&s[..96]), Fp2::from_bytes(&s[96..192])); + + let (pt_cmp, l_cmp) = Bls12_381::miller_double_step(&s_cast); + let mut pt_bytes = [0u8; 48 * 4]; + let mut l_bytes = [0u8; 48 * 4]; + + pt_bytes[0..48].copy_from_slice(pt_cmp.x.c0.as_le_bytes()); + pt_bytes[48..2 * 48].copy_from_slice(pt_cmp.x.c1.as_le_bytes()); + pt_bytes[2 * 48..3 * 48].copy_from_slice(pt_cmp.y.c0.as_le_bytes()); + pt_bytes[3 * 48..4 * 48].copy_from_slice(pt_cmp.y.c1.as_le_bytes()); + l_bytes[0..48].copy_from_slice(l_cmp.b.c0.as_le_bytes()); + l_bytes[48..2 * 48].copy_from_slice(l_cmp.b.c1.as_le_bytes()); + l_bytes[2 * 48..3 * 48].copy_from_slice(l_cmp.c.c0.as_le_bytes()); + l_bytes[3 * 48..4 * 48].copy_from_slice(l_cmp.c.c1.as_le_bytes()); + + assert_eq!(pt_bytes, pt); + assert_eq!(l_bytes, l); + } + + pub fn test_miller_double_and_add_step(io: &[u8]) { + assert_eq!(io.len(), 48 * 20); + let s = &io[0..48 * 4]; + let q = &io[48 * 4..48 * 8]; + let pt = &io[48 * 8..48 * 12]; + let l0 = &io[48 * 12..48 * 16]; + let l1 = &io[48 * 16..48 * 20]; + + let s_cast = AffinePoint::new(Fp2::from_bytes(&s[..96]), Fp2::from_bytes(&s[96..192])); + let q_cast = AffinePoint::new(Fp2::from_bytes(&q[..96]), Fp2::from_bytes(&q[96..192])); + let (pt_cmp, l0_cmp, l1_cmp) = Bls12_381::miller_double_and_add_step(&s_cast, &q_cast); + let mut pt_bytes = [0u8; 48 * 4]; + let mut l0_bytes = [0u8; 48 * 4]; + let mut l1_bytes = [0u8; 48 * 4]; + + pt_bytes[0..48].copy_from_slice(pt_cmp.x.c0.as_le_bytes()); + pt_bytes[48..2 * 48].copy_from_slice(pt_cmp.x.c1.as_le_bytes()); + pt_bytes[2 * 48..3 * 48].copy_from_slice(pt_cmp.y.c0.as_le_bytes()); + pt_bytes[3 * 48..4 * 48].copy_from_slice(pt_cmp.y.c1.as_le_bytes()); + l0_bytes[0..48].copy_from_slice(l0_cmp.b.c0.as_le_bytes()); + l0_bytes[48..2 * 48].copy_from_slice(l0_cmp.b.c1.as_le_bytes()); + l0_bytes[2 * 48..3 * 48].copy_from_slice(l0_cmp.c.c0.as_le_bytes()); + l0_bytes[3 * 48..4 * 48].copy_from_slice(l0_cmp.c.c1.as_le_bytes()); + l1_bytes[0..48].copy_from_slice(l1_cmp.b.c0.as_le_bytes()); + l1_bytes[48..2 * 48].copy_from_slice(l1_cmp.b.c1.as_le_bytes()); + l1_bytes[2 * 48..3 * 48].copy_from_slice(l1_cmp.c.c0.as_le_bytes()); + l1_bytes[3 * 48..4 * 48].copy_from_slice(l1_cmp.c.c1.as_le_bytes()); + + assert_eq!(pt_bytes, pt); + assert_eq!(l0_bytes, l0); + assert_eq!(l1_bytes, l1); + } +} + +pub fn main() { + #[allow(unused_variables)] + let io = read_vec(); + + #[cfg(feature = "bn254")] + { + bn254::test_miller_step(&io[..32 * 12]); + bn254::test_miller_double_and_add_step(&io[32 * 12..]); + } + #[cfg(feature = "bls12_381")] + { + bls12_381::test_miller_step(&io[..48 * 12]); + bls12_381::test_miller_double_and_add_step(&io[48 * 12..]); + } +} diff --git a/extensions/pairing/tests/programs/openvm_init_bls_ec_bls12_381.rs b/extensions/pairing/tests/programs/openvm_init_bls_ec_bls12_381.rs new file mode 100644 index 0000000000..95a4e46fd3 --- /dev/null +++ b/extensions/pairing/tests/programs/openvm_init_bls_ec_bls12_381.rs @@ -0,0 +1,3 @@ +// This file is automatically generated by cargo openvm. Do not rename or edit. +openvm_algebra_guest::moduli_macros::moduli_init! { "4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787", "52435875175126190479447740508185965837690552500527637822603658699938581184513" } +openvm_ecc_guest::sw_macros::sw_init! { Bls12_381G1Affine } diff --git a/extensions/pairing/tests/programs/openvm_init_bls_final_exp_hint_bls12_381.rs b/extensions/pairing/tests/programs/openvm_init_bls_final_exp_hint_bls12_381.rs new file mode 100644 index 0000000000..00181b03ef --- /dev/null +++ b/extensions/pairing/tests/programs/openvm_init_bls_final_exp_hint_bls12_381.rs @@ -0,0 +1,4 @@ +// This file is automatically generated by cargo openvm. Do not rename or edit. +openvm_algebra_guest::moduli_macros::moduli_init! { "4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787" } +openvm_algebra_guest::complex_macros::complex_init! { Bls12_381Fp2 { mod_idx = 0 } } +openvm_ecc_guest::sw_macros::sw_init! { } diff --git a/extensions/pairing/tests/programs/openvm_init_bn_final_exp_hint_bn254.rs b/extensions/pairing/tests/programs/openvm_init_bn_final_exp_hint_bn254.rs new file mode 100644 index 0000000000..c130859ad8 --- /dev/null +++ b/extensions/pairing/tests/programs/openvm_init_bn_final_exp_hint_bn254.rs @@ -0,0 +1,4 @@ +// This file is automatically generated by cargo openvm. Do not rename or edit. +openvm_algebra_guest::moduli_macros::moduli_init! { "21888242871839275222246405745257275088696311157297823662689037894645226208583" } +openvm_algebra_guest::complex_macros::complex_init! { Bn254Fp2 { mod_idx = 0 } } +openvm_ecc_guest::sw_macros::sw_init! { } diff --git a/extensions/pairing/tests/programs/openvm_init_fp12_mul_bls12_381.rs b/extensions/pairing/tests/programs/openvm_init_fp12_mul_bls12_381.rs new file mode 100644 index 0000000000..c130859ad8 --- /dev/null +++ b/extensions/pairing/tests/programs/openvm_init_fp12_mul_bls12_381.rs @@ -0,0 +1,4 @@ +// This file is automatically generated by cargo openvm. Do not rename or edit. +openvm_algebra_guest::moduli_macros::moduli_init! { "21888242871839275222246405745257275088696311157297823662689037894645226208583" } +openvm_algebra_guest::complex_macros::complex_init! { Bn254Fp2 { mod_idx = 0 } } +openvm_ecc_guest::sw_macros::sw_init! { } diff --git a/extensions/pairing/tests/programs/openvm_init_fp12_mul_bn254.rs b/extensions/pairing/tests/programs/openvm_init_fp12_mul_bn254.rs new file mode 100644 index 0000000000..c130859ad8 --- /dev/null +++ b/extensions/pairing/tests/programs/openvm_init_fp12_mul_bn254.rs @@ -0,0 +1,4 @@ +// This file is automatically generated by cargo openvm. Do not rename or edit. +openvm_algebra_guest::moduli_macros::moduli_init! { "21888242871839275222246405745257275088696311157297823662689037894645226208583" } +openvm_algebra_guest::complex_macros::complex_init! { Bn254Fp2 { mod_idx = 0 } } +openvm_ecc_guest::sw_macros::sw_init! { } diff --git a/extensions/pairing/tests/programs/openvm_init_pairing_check_bls12_381.rs b/extensions/pairing/tests/programs/openvm_init_pairing_check_bls12_381.rs new file mode 100644 index 0000000000..00181b03ef --- /dev/null +++ b/extensions/pairing/tests/programs/openvm_init_pairing_check_bls12_381.rs @@ -0,0 +1,4 @@ +// This file is automatically generated by cargo openvm. Do not rename or edit. +openvm_algebra_guest::moduli_macros::moduli_init! { "4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787" } +openvm_algebra_guest::complex_macros::complex_init! { Bls12_381Fp2 { mod_idx = 0 } } +openvm_ecc_guest::sw_macros::sw_init! { } diff --git a/extensions/pairing/tests/programs/openvm_init_pairing_check_bn254.rs b/extensions/pairing/tests/programs/openvm_init_pairing_check_bn254.rs new file mode 100644 index 0000000000..c130859ad8 --- /dev/null +++ b/extensions/pairing/tests/programs/openvm_init_pairing_check_bn254.rs @@ -0,0 +1,4 @@ +// This file is automatically generated by cargo openvm. Do not rename or edit. +openvm_algebra_guest::moduli_macros::moduli_init! { "21888242871839275222246405745257275088696311157297823662689037894645226208583" } +openvm_algebra_guest::complex_macros::complex_init! { Bn254Fp2 { mod_idx = 0 } } +openvm_ecc_guest::sw_macros::sw_init! { } diff --git a/extensions/pairing/tests/programs/openvm_init_pairing_check_fallback_bls12_381.rs b/extensions/pairing/tests/programs/openvm_init_pairing_check_fallback_bls12_381.rs new file mode 100644 index 0000000000..00181b03ef --- /dev/null +++ b/extensions/pairing/tests/programs/openvm_init_pairing_check_fallback_bls12_381.rs @@ -0,0 +1,4 @@ +// This file is automatically generated by cargo openvm. Do not rename or edit. +openvm_algebra_guest::moduli_macros::moduli_init! { "4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787" } +openvm_algebra_guest::complex_macros::complex_init! { Bls12_381Fp2 { mod_idx = 0 } } +openvm_ecc_guest::sw_macros::sw_init! { } diff --git a/extensions/pairing/tests/programs/openvm_init_pairing_check_fallback_bn254.rs b/extensions/pairing/tests/programs/openvm_init_pairing_check_fallback_bn254.rs new file mode 100644 index 0000000000..c130859ad8 --- /dev/null +++ b/extensions/pairing/tests/programs/openvm_init_pairing_check_fallback_bn254.rs @@ -0,0 +1,4 @@ +// This file is automatically generated by cargo openvm. Do not rename or edit. +openvm_algebra_guest::moduli_macros::moduli_init! { "21888242871839275222246405745257275088696311157297823662689037894645226208583" } +openvm_algebra_guest::complex_macros::complex_init! { Bn254Fp2 { mod_idx = 0 } } +openvm_ecc_guest::sw_macros::sw_init! { } diff --git a/extensions/pairing/tests/programs/openvm_init_pairing_line_bls12_381.rs b/extensions/pairing/tests/programs/openvm_init_pairing_line_bls12_381.rs new file mode 100644 index 0000000000..00181b03ef --- /dev/null +++ b/extensions/pairing/tests/programs/openvm_init_pairing_line_bls12_381.rs @@ -0,0 +1,4 @@ +// This file is automatically generated by cargo openvm. Do not rename or edit. +openvm_algebra_guest::moduli_macros::moduli_init! { "4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787" } +openvm_algebra_guest::complex_macros::complex_init! { Bls12_381Fp2 { mod_idx = 0 } } +openvm_ecc_guest::sw_macros::sw_init! { } diff --git a/extensions/pairing/tests/programs/openvm_init_pairing_line_bn254.rs b/extensions/pairing/tests/programs/openvm_init_pairing_line_bn254.rs new file mode 100644 index 0000000000..c130859ad8 --- /dev/null +++ b/extensions/pairing/tests/programs/openvm_init_pairing_line_bn254.rs @@ -0,0 +1,4 @@ +// This file is automatically generated by cargo openvm. Do not rename or edit. +openvm_algebra_guest::moduli_macros::moduli_init! { "21888242871839275222246405745257275088696311157297823662689037894645226208583" } +openvm_algebra_guest::complex_macros::complex_init! { Bn254Fp2 { mod_idx = 0 } } +openvm_ecc_guest::sw_macros::sw_init! { } diff --git a/extensions/pairing/tests/programs/openvm_init_pairing_miller_loop_bls12_381.rs b/extensions/pairing/tests/programs/openvm_init_pairing_miller_loop_bls12_381.rs new file mode 100644 index 0000000000..00181b03ef --- /dev/null +++ b/extensions/pairing/tests/programs/openvm_init_pairing_miller_loop_bls12_381.rs @@ -0,0 +1,4 @@ +// This file is automatically generated by cargo openvm. Do not rename or edit. +openvm_algebra_guest::moduli_macros::moduli_init! { "4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787" } +openvm_algebra_guest::complex_macros::complex_init! { Bls12_381Fp2 { mod_idx = 0 } } +openvm_ecc_guest::sw_macros::sw_init! { } diff --git a/extensions/pairing/tests/programs/openvm_init_pairing_miller_loop_bn254.rs b/extensions/pairing/tests/programs/openvm_init_pairing_miller_loop_bn254.rs new file mode 100644 index 0000000000..c130859ad8 --- /dev/null +++ b/extensions/pairing/tests/programs/openvm_init_pairing_miller_loop_bn254.rs @@ -0,0 +1,4 @@ +// This file is automatically generated by cargo openvm. Do not rename or edit. +openvm_algebra_guest::moduli_macros::moduli_init! { "21888242871839275222246405745257275088696311157297823662689037894645226208583" } +openvm_algebra_guest::complex_macros::complex_init! { Bn254Fp2 { mod_idx = 0 } } +openvm_ecc_guest::sw_macros::sw_init! { } diff --git a/extensions/pairing/tests/programs/openvm_init_pairing_miller_step_bls12_381.rs b/extensions/pairing/tests/programs/openvm_init_pairing_miller_step_bls12_381.rs new file mode 100644 index 0000000000..00181b03ef --- /dev/null +++ b/extensions/pairing/tests/programs/openvm_init_pairing_miller_step_bls12_381.rs @@ -0,0 +1,4 @@ +// This file is automatically generated by cargo openvm. Do not rename or edit. +openvm_algebra_guest::moduli_macros::moduli_init! { "4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787" } +openvm_algebra_guest::complex_macros::complex_init! { Bls12_381Fp2 { mod_idx = 0 } } +openvm_ecc_guest::sw_macros::sw_init! { } diff --git a/extensions/pairing/tests/programs/openvm_init_pairing_miller_step_bn254.rs b/extensions/pairing/tests/programs/openvm_init_pairing_miller_step_bn254.rs new file mode 100644 index 0000000000..c130859ad8 --- /dev/null +++ b/extensions/pairing/tests/programs/openvm_init_pairing_miller_step_bn254.rs @@ -0,0 +1,4 @@ +// This file is automatically generated by cargo openvm. Do not rename or edit. +openvm_algebra_guest::moduli_macros::moduli_init! { "21888242871839275222246405745257275088696311157297823662689037894645226208583" } +openvm_algebra_guest::complex_macros::complex_init! { Bn254Fp2 { mod_idx = 0 } } +openvm_ecc_guest::sw_macros::sw_init! { } diff --git a/extensions/pairing/tests/src/lib.rs b/extensions/pairing/tests/src/lib.rs new file mode 100644 index 0000000000..7f12f01241 --- /dev/null +++ b/extensions/pairing/tests/src/lib.rs @@ -0,0 +1,884 @@ +#![allow(non_snake_case)] + +#[cfg(test)] +mod bn254 { + use std::iter; + + use eyre::Result; + use halo2curves_axiom::{ + bn256::{Fq12, Fq2, Fr, G1Affine, G2Affine}, + ff::Field, + }; + use openvm_algebra_circuit::{Fp2Extension, ModularExtension}; + use openvm_algebra_transpiler::{Fp2TranspilerExtension, ModularTranspilerExtension}; + use openvm_circuit::{ + arch::SystemConfig, + utils::{air_test_impl, air_test_with_min_segments}, + }; + use openvm_ecc_circuit::WeierstrassExtension; + use openvm_ecc_guest::{ + algebra::{field::FieldExtension, IntMod}, + AffinePoint, + }; + use openvm_instructions::exe::VmExe; + use openvm_pairing_circuit::{PairingCurve, PairingExtension, Rv32PairingConfig}; + use openvm_pairing_guest::{ + bn254::{BN254_COMPLEX_STRUCT_NAME, BN254_MODULUS}, + halo2curves_shims::bn254::Bn254, + pairing::{EvaluatedLine, FinalExp, LineMulDType, MillerStep, MultiMillerLoop}, + }; + use openvm_pairing_transpiler::PairingTranspilerExtension; + use openvm_rv32im_transpiler::{ + Rv32ITranspilerExtension, Rv32IoTranspilerExtension, Rv32MTranspilerExtension, + }; + use openvm_stark_sdk::{openvm_stark_backend::p3_field::FieldAlgebra, p3_baby_bear::BabyBear}; + use openvm_toolchain_tests::{build_example_program_at_path_with_features, get_programs_dir}; + use openvm_transpiler::{transpiler::Transpiler, FromElf}; + use rand::SeedableRng; + + type F = BabyBear; + + pub fn get_testing_config() -> Rv32PairingConfig { + let primes = [BN254_MODULUS.clone()]; + let complex_struct_names = [BN254_COMPLEX_STRUCT_NAME.to_string()]; + let primes_with_names = complex_struct_names + .into_iter() + .zip(primes.clone()) + .collect::>(); + Rv32PairingConfig { + system: SystemConfig::default().with_continuations(), + base: Default::default(), + mul: Default::default(), + io: Default::default(), + modular: ModularExtension::new(primes.to_vec()), + fp2: Fp2Extension::new(primes_with_names), + weierstrass: WeierstrassExtension::new(vec![]), + pairing: PairingExtension::new(vec![PairingCurve::Bn254]), + } + } + + #[test] + fn test_bn254_fp12_mul() -> Result<()> { + let config = get_testing_config(); + let elf = build_example_program_at_path_with_features( + get_programs_dir!(), + "fp12_mul", + ["bn254"], + &config, + )?; + let openvm_exe = VmExe::from_elf( + elf, + Transpiler::::default() + .with_extension(Rv32ITranspilerExtension) + .with_extension(Rv32MTranspilerExtension) + .with_extension(Rv32IoTranspilerExtension) + .with_extension(PairingTranspilerExtension) + .with_extension(ModularTranspilerExtension) + .with_extension(Fp2TranspilerExtension), + )?; + + let mut rng = rand::rngs::StdRng::seed_from_u64(2); + let f0 = Fq12::random(&mut rng); + let f1 = Fq12::random(&mut rng); + let r = f0 * f1; + + let io = [f0, f1, r] + .into_iter() + .flat_map(|fp12| fp12.to_coeffs()) + .flat_map(|fp2| fp2.to_bytes()) + .map(FieldAlgebra::from_canonical_u8) + .collect::>(); + + air_test_with_min_segments(config, openvm_exe, vec![io], 1); + Ok(()) + } + + #[test] + fn test_bn254_line_functions() -> Result<()> { + let config = get_testing_config(); + let elf = build_example_program_at_path_with_features( + get_programs_dir!(), + "pairing_line", + ["bn254"], + &config, + )?; + let openvm_exe = VmExe::from_elf( + elf, + Transpiler::::default() + .with_extension(Rv32ITranspilerExtension) + .with_extension(Rv32MTranspilerExtension) + .with_extension(Rv32IoTranspilerExtension) + .with_extension(PairingTranspilerExtension) + .with_extension(ModularTranspilerExtension) + .with_extension(Fp2TranspilerExtension), + )?; + + let mut rng = rand::rngs::StdRng::seed_from_u64(2); + let a = G2Affine::random(&mut rng); + let b = G2Affine::random(&mut rng); + let c = G2Affine::random(&mut rng); + + let f = Fq12::random(&mut rng); + let l0 = EvaluatedLine:: { b: a.x, c: a.y }; + let l1 = EvaluatedLine:: { b: b.x, c: b.y }; + + // Test mul_013_by_013 + let r0 = Bn254::mul_013_by_013(&l0, &l1); + let io0 = [l0, l1] + .into_iter() + .flat_map(|fp2| fp2.into_iter()) + .chain(r0) + .flat_map(|fp2| fp2.to_coeffs()) + .flat_map(|fp| fp.to_bytes()) + .map(FieldAlgebra::from_canonical_u8) + .collect::>(); + + // Test mul_by_01234 + let x = [c.x, c.y, b.x, b.y, a.x]; + let r1 = Bn254::mul_by_01234(&f, &x); + let io1 = iter::empty() + .chain(f.to_coeffs()) + .chain(x) + .chain(r1.to_coeffs()) + .flat_map(|fp2| fp2.to_coeffs()) + .flat_map(|fp| fp.to_bytes()) + .map(FieldAlgebra::from_canonical_u8) + .collect::>(); + + let io_all = io0.into_iter().chain(io1).collect::>(); + + air_test_with_min_segments(config, openvm_exe, vec![io_all], 1); + Ok(()) + } + + #[test] + fn test_bn254_miller_step() -> Result<()> { + let config = get_testing_config(); + let elf = build_example_program_at_path_with_features( + get_programs_dir!(), + "pairing_miller_step", + ["bn254"], + &config, + )?; + let openvm_exe = VmExe::from_elf( + elf, + Transpiler::::default() + .with_extension(Rv32ITranspilerExtension) + .with_extension(Rv32MTranspilerExtension) + .with_extension(Rv32IoTranspilerExtension) + .with_extension(PairingTranspilerExtension) + .with_extension(ModularTranspilerExtension) + .with_extension(Fp2TranspilerExtension), + )?; + + let mut rng = rand::rngs::StdRng::seed_from_u64(20); + let S = G2Affine::random(&mut rng); + let Q = G2Affine::random(&mut rng); + + let s = AffinePoint::new(S.x, S.y); + let q = AffinePoint::new(Q.x, Q.y); + + // Test miller_double_step + let (pt, l) = Bn254::miller_double_step(&s); + let io0 = [s.x, s.y, pt.x, pt.y, l.b, l.c] + .into_iter() + .flat_map(|fp| fp.to_bytes()) + .map(FieldAlgebra::from_canonical_u8) + .collect::>(); + + // Test miller_double_and_add_step + let (pt, l0, l1) = Bn254::miller_double_and_add_step(&s, &q); + let io1 = [s.x, s.y, q.x, q.y, pt.x, pt.y, l0.b, l0.c, l1.b, l1.c] + .into_iter() + .flat_map(|fp| fp.to_bytes()) + .map(FieldAlgebra::from_canonical_u8) + .collect::>(); + + let io_all = io0.into_iter().chain(io1).collect::>(); + + air_test_with_min_segments(config, openvm_exe, vec![io_all], 1); + Ok(()) + } + + #[test] + fn test_bn254_miller_loop() -> Result<()> { + let config = get_testing_config(); + let elf = build_example_program_at_path_with_features( + get_programs_dir!(), + "pairing_miller_loop", + ["bn254"], + &config, + )?; + let openvm_exe = VmExe::from_elf( + elf, + Transpiler::::default() + .with_extension(Rv32ITranspilerExtension) + .with_extension(Rv32MTranspilerExtension) + .with_extension(Rv32IoTranspilerExtension) + .with_extension(PairingTranspilerExtension) + .with_extension(ModularTranspilerExtension) + .with_extension(Fp2TranspilerExtension), + )?; + + let S = G1Affine::generator(); + let Q = G2Affine::generator(); + + let mut S_mul = [S * Fr::from(1), S * Fr::from(2)]; + S_mul[1].y = -S_mul[1].y; + let Q_mul = [Q * Fr::from(2), Q * Fr::from(1)]; + + let s = S_mul.map(|s| AffinePoint::new(s.x, s.y)); + let q = Q_mul.map(|p| AffinePoint::new(p.x, p.y)); + + // Test miller_loop + let f = Bn254::multi_miller_loop(&s, &q); + let io0 = s + .into_iter() + .flat_map(|pt| [pt.x, pt.y].into_iter().flat_map(|fp| fp.to_bytes())) + .map(FieldAlgebra::from_canonical_u8) + .collect::>(); + + let io1 = q + .into_iter() + .flat_map(|pt| [pt.x, pt.y].into_iter()) + .chain(f.to_coeffs()) + .flat_map(|fp2| fp2.to_coeffs()) + .flat_map(|fp| fp.to_bytes()) + .map(FieldAlgebra::from_canonical_u8) + .collect::>(); + + let io_all = io0.into_iter().chain(io1).collect::>(); + + air_test_with_min_segments(config, openvm_exe, vec![io_all], 1); + Ok(()) + } + + #[test] + fn test_bn254_pairing_check() -> Result<()> { + let config = get_testing_config(); + let elf = build_example_program_at_path_with_features( + get_programs_dir!(), + "pairing_check", + ["bn254"], + &config, + )?; + let openvm_exe = VmExe::from_elf( + elf, + Transpiler::::default() + .with_extension(Rv32ITranspilerExtension) + .with_extension(Rv32MTranspilerExtension) + .with_extension(Rv32IoTranspilerExtension) + .with_extension(PairingTranspilerExtension) + .with_extension(ModularTranspilerExtension) + .with_extension(Fp2TranspilerExtension), + )?; + + let S = G1Affine::generator(); + let Q = G2Affine::generator(); + + let mut S_mul = [ + G1Affine::from(S * Fr::from(1)), + G1Affine::from(S * Fr::from(2)), + ]; + S_mul[1].y = -S_mul[1].y; + let Q_mul = [ + G2Affine::from(Q * Fr::from(2)), + G2Affine::from(Q * Fr::from(1)), + ]; + + let s = S_mul.map(|s| AffinePoint::new(s.x, s.y)); + let q = Q_mul.map(|p| AffinePoint::new(p.x, p.y)); + + // Gather inputs + let io0 = s + .into_iter() + .flat_map(|pt| [pt.x, pt.y].into_iter().flat_map(|fp| fp.to_bytes())) + .map(FieldAlgebra::from_canonical_u8) + .collect::>(); + + let io1 = q + .into_iter() + .flat_map(|pt| [pt.x, pt.y].into_iter()) + .flat_map(|fp2| fp2.to_coeffs()) + .flat_map(|fp| fp.to_bytes()) + .map(FieldAlgebra::from_canonical_u8) + .collect::>(); + + let io_all = io0.into_iter().chain(io1).collect::>(); + + air_test_with_min_segments(config, openvm_exe, vec![io_all], 1); + Ok(()) + } + + #[test] + fn test_bn254_pairing_check_fallback() -> Result<()> { + let config = get_testing_config(); + let elf = build_example_program_at_path_with_features( + get_programs_dir!(), + "pairing_check_fallback", + ["bn254"], + &config, + )?; + let openvm_exe = VmExe::from_elf( + elf, + Transpiler::::default() + .with_extension(Rv32ITranspilerExtension) + .with_extension(Rv32MTranspilerExtension) + .with_extension(Rv32IoTranspilerExtension) + .with_extension(PairingTranspilerExtension) + .with_extension(ModularTranspilerExtension) + .with_extension(Fp2TranspilerExtension), + )?; + + let S = G1Affine::generator(); + let Q = G2Affine::generator(); + + let mut S_mul = [ + G1Affine::from(S * Fr::from(1)), + G1Affine::from(S * Fr::from(2)), + ]; + S_mul[1].y = -S_mul[1].y; + let Q_mul = [ + G2Affine::from(Q * Fr::from(2)), + G2Affine::from(Q * Fr::from(1)), + ]; + + let s = S_mul.map(|s| AffinePoint::new(s.x, s.y)); + let q = Q_mul.map(|p| AffinePoint::new(p.x, p.y)); + + // Gather inputs + let io0 = s + .into_iter() + .flat_map(|pt| [pt.x, pt.y].into_iter().flat_map(|fp| fp.to_bytes())) + .map(FieldAlgebra::from_canonical_u8) + .collect::>(); + + let io1 = q + .into_iter() + .flat_map(|pt| [pt.x, pt.y].into_iter()) + .flat_map(|fp2| fp2.to_coeffs()) + .flat_map(|fp| fp.to_bytes()) + .map(FieldAlgebra::from_canonical_u8) + .collect::>(); + + let io_all = io0.into_iter().chain(io1).collect::>(); + // Don't run debugger because it's slow + air_test_impl(get_testing_config(), openvm_exe, vec![io_all], 1, false); + Ok(()) + } + + #[test] + fn test_bn254_final_exp_hint() -> Result<()> { + let config = get_testing_config(); + let elf = build_example_program_at_path_with_features( + get_programs_dir!(), + "bn_final_exp_hint", + ["bn254"], + &config, + )?; + let openvm_exe = VmExe::from_elf( + elf, + Transpiler::::default() + .with_extension(Rv32ITranspilerExtension) + .with_extension(Rv32MTranspilerExtension) + .with_extension(Rv32IoTranspilerExtension) + .with_extension(PairingTranspilerExtension) + .with_extension(ModularTranspilerExtension) + .with_extension(Fp2TranspilerExtension), + )?; + + let P = G1Affine::generator(); + let Q = G2Affine::generator(); + let ps = vec![AffinePoint::new(P.x, P.y), AffinePoint::new(P.x, -P.y)]; + let qs = vec![AffinePoint::new(Q.x, Q.y), AffinePoint::new(Q.x, Q.y)]; + let f = Bn254::multi_miller_loop(&ps, &qs); + let (c, s) = Bn254::final_exp_hint(&f); + let ps = ps + .into_iter() + .map(|pt| { + let [x, y] = [pt.x, pt.y] + .map(|x| openvm_pairing_guest::bn254::Fp::from_le_bytes(&x.to_bytes())); + AffinePoint::new(x, y) + }) + .collect::>(); + let qs = qs + .into_iter() + .map(|pt| { + let [x, y] = [pt.x, pt.y] + .map(|x| openvm_pairing_guest::bn254::Fp2::from_bytes(&x.to_bytes())); + AffinePoint::new(x, y) + }) + .collect::>(); + let [c, s] = [c, s].map(|x| openvm_pairing_guest::bn254::Fp12::from_bytes(&x.to_bytes())); + let io = (ps, qs, (c, s)); + let io = openvm::serde::to_vec(&io).unwrap(); + let io = io + .into_iter() + .flat_map(|w| w.to_le_bytes()) + .map(F::from_canonical_u8) + .collect(); + air_test_with_min_segments(config, openvm_exe, vec![io], 1); + Ok(()) + } +} + +#[cfg(test)] +mod bls12_381 { + use eyre::Result; + use halo2curves_axiom::{ + bls12_381::{Fq12, Fq2, Fr, G1Affine, G2Affine}, + ff::Field, + }; + use num_bigint::BigUint; + use num_traits::{self, FromPrimitive}; + use openvm_algebra_circuit::{Fp2Extension, ModularExtension}; + use openvm_algebra_transpiler::{Fp2TranspilerExtension, ModularTranspilerExtension}; + use openvm_circuit::{ + arch::{instructions::exe::VmExe, SystemConfig}, + utils::{air_test, air_test_impl, air_test_with_min_segments}, + }; + use openvm_ecc_circuit::{CurveConfig, Rv32WeierstrassConfig, WeierstrassExtension}; + use openvm_ecc_guest::{ + algebra::{field::FieldExtension, IntMod}, + AffinePoint, + }; + use openvm_ecc_transpiler::EccTranspilerExtension; + use openvm_pairing_circuit::{PairingCurve, PairingExtension, Rv32PairingConfig}; + use openvm_pairing_guest::{ + bls12_381::{ + BLS12_381_COMPLEX_STRUCT_NAME, BLS12_381_ECC_STRUCT_NAME, BLS12_381_MODULUS, + BLS12_381_ORDER, + }, + halo2curves_shims::bls12_381::Bls12_381, + pairing::{EvaluatedLine, FinalExp, LineMulMType, MillerStep, MultiMillerLoop}, + }; + use openvm_pairing_transpiler::PairingTranspilerExtension; + use openvm_rv32im_transpiler::{ + Rv32ITranspilerExtension, Rv32IoTranspilerExtension, Rv32MTranspilerExtension, + }; + use openvm_stark_sdk::{openvm_stark_backend::p3_field::FieldAlgebra, p3_baby_bear::BabyBear}; + use openvm_toolchain_tests::{build_example_program_at_path_with_features, get_programs_dir}; + use openvm_transpiler::{transpiler::Transpiler, FromElf}; + use rand::SeedableRng; + + type F = BabyBear; + + pub fn get_testing_config() -> Rv32PairingConfig { + let primes = [BLS12_381_MODULUS.clone()]; + let complex_struct_names = [BLS12_381_COMPLEX_STRUCT_NAME.to_string()]; + let primes_with_names = complex_struct_names + .into_iter() + .zip(primes.clone()) + .collect::>(); + Rv32PairingConfig { + system: SystemConfig::default().with_continuations(), + base: Default::default(), + mul: Default::default(), + io: Default::default(), + modular: ModularExtension::new(primes.to_vec()), + fp2: Fp2Extension::new(primes_with_names), + weierstrass: WeierstrassExtension::new(vec![]), + pairing: PairingExtension::new(vec![PairingCurve::Bls12_381]), + } + } + + #[test] + fn test_bls_ec() -> Result<()> { + let curve = CurveConfig { + struct_name: BLS12_381_ECC_STRUCT_NAME.to_string(), + modulus: BLS12_381_MODULUS.clone(), + scalar: BLS12_381_ORDER.clone(), + a: BigUint::ZERO, + b: BigUint::from_u8(4).unwrap(), + }; + let config = Rv32WeierstrassConfig::new(vec![curve]); + let elf = build_example_program_at_path_with_features( + get_programs_dir!(), + "bls_ec", + ["bls12_381"], + &config, + )?; + let openvm_exe = VmExe::from_elf( + elf, + Transpiler::::default() + .with_extension(Rv32ITranspilerExtension) + .with_extension(Rv32MTranspilerExtension) + .with_extension(Rv32IoTranspilerExtension) + .with_extension(EccTranspilerExtension) + .with_extension(ModularTranspilerExtension), + )?; + air_test(config, openvm_exe); + Ok(()) + } + + #[test] + fn test_bls12_381_fp12_mul() -> Result<()> { + let config = get_testing_config(); + let elf = build_example_program_at_path_with_features( + get_programs_dir!(), + "fp12_mul", + ["bls12_381"], + &config, + )?; + let openvm_exe = VmExe::from_elf( + elf, + Transpiler::::default() + .with_extension(Rv32ITranspilerExtension) + .with_extension(Rv32MTranspilerExtension) + .with_extension(Rv32IoTranspilerExtension) + .with_extension(PairingTranspilerExtension) + .with_extension(ModularTranspilerExtension) + .with_extension(Fp2TranspilerExtension), + )?; + + let mut rng = rand::rngs::StdRng::seed_from_u64(50); + let f0 = Fq12::random(&mut rng); + let f1 = Fq12::random(&mut rng); + let r = f0 * f1; + + let io = [f0, f1, r] + .into_iter() + .flat_map(|fp12| fp12.to_coeffs()) + .flat_map(|fp2| fp2.to_bytes()) + .map(FieldAlgebra::from_canonical_u8) + .collect::>(); + + air_test_with_min_segments(config, openvm_exe, vec![io], 1); + Ok(()) + } + + #[test] + fn test_bls12_381_line_functions() -> Result<()> { + let config = get_testing_config(); + let elf = build_example_program_at_path_with_features( + get_programs_dir!(), + "pairing_line", + ["bls12_381"], + &config, + )?; + let openvm_exe = VmExe::from_elf( + elf, + Transpiler::::default() + .with_extension(Rv32ITranspilerExtension) + .with_extension(Rv32MTranspilerExtension) + .with_extension(Rv32IoTranspilerExtension) + .with_extension(PairingTranspilerExtension) + .with_extension(ModularTranspilerExtension) + .with_extension(Fp2TranspilerExtension), + )?; + + let mut rng = rand::rngs::StdRng::seed_from_u64(5); + let a = G2Affine::random(&mut rng); + let b = G2Affine::random(&mut rng); + let c = G2Affine::random(&mut rng); + + let f = Fq12::random(&mut rng); + let l0 = EvaluatedLine:: { b: a.x, c: a.y }; + let l1 = EvaluatedLine:: { b: b.x, c: b.y }; + + // Test mul_023_by_023 + let r0 = Bls12_381::mul_023_by_023(&l0, &l1); + let io0 = [l0, l1] + .into_iter() + .flat_map(|fp2| fp2.into_iter()) + .chain(r0) + .flat_map(|fp2| fp2.to_coeffs()) + .flat_map(|fp| fp.to_bytes()) + .map(FieldAlgebra::from_canonical_u8) + .collect::>(); + + // Test mul_by_02345 + let x = [c.x, c.y, b.x, b.y, a.x]; + let r1 = Bls12_381::mul_by_02345(&f, &x); + let io1 = f + .to_coeffs() + .into_iter() + .chain(x) + .chain(r1.to_coeffs()) + .flat_map(|fp2| fp2.to_coeffs()) + .flat_map(|fp| fp.to_bytes()) + .map(FieldAlgebra::from_canonical_u8) + .collect::>(); + + let io_all = io0.into_iter().chain(io1).collect::>(); + + air_test_with_min_segments(config, openvm_exe, vec![io_all], 1); + Ok(()) + } + + #[test] + fn test_bls12_381_miller_step() -> Result<()> { + let config = get_testing_config(); + let elf = build_example_program_at_path_with_features( + get_programs_dir!(), + "pairing_miller_step", + ["bls12_381"], + &config, + )?; + let openvm_exe = VmExe::from_elf( + elf, + Transpiler::::default() + .with_extension(Rv32ITranspilerExtension) + .with_extension(Rv32MTranspilerExtension) + .with_extension(Rv32IoTranspilerExtension) + .with_extension(PairingTranspilerExtension) + .with_extension(ModularTranspilerExtension) + .with_extension(Fp2TranspilerExtension), + )?; + + let mut rng = rand::rngs::StdRng::seed_from_u64(88); + let S = G2Affine::random(&mut rng); + let Q = G2Affine::random(&mut rng); + + let s = AffinePoint::new(S.x, S.y); + let q = AffinePoint::new(Q.x, Q.y); + + // Test miller_double_step + let (pt, l) = Bls12_381::miller_double_step(&s); + let io0 = [s.x, s.y, pt.x, pt.y, l.b, l.c] + .into_iter() + .flat_map(|fp| fp.to_bytes()) + .map(FieldAlgebra::from_canonical_u8) + .collect::>(); + + // Test miller_double_and_add_step + let (pt, l0, l1) = Bls12_381::miller_double_and_add_step(&s, &q); + let io1 = [s.x, s.y, q.x, q.y, pt.x, pt.y, l0.b, l0.c, l1.b, l1.c] + .into_iter() + .flat_map(|fp| fp.to_bytes()) + .map(FieldAlgebra::from_canonical_u8) + .collect::>(); + + let io_all = io0.into_iter().chain(io1).collect::>(); + + air_test_with_min_segments(config, openvm_exe, vec![io_all], 1); + Ok(()) + } + + #[test] + fn test_bls12_381_miller_loop() -> Result<()> { + let config = get_testing_config(); + let elf = build_example_program_at_path_with_features( + get_programs_dir!(), + "pairing_miller_loop", + ["bls12_381"], + &config, + )?; + let openvm_exe = VmExe::from_elf( + elf, + Transpiler::::default() + .with_extension(Rv32ITranspilerExtension) + .with_extension(Rv32MTranspilerExtension) + .with_extension(Rv32IoTranspilerExtension) + .with_extension(PairingTranspilerExtension) + .with_extension(ModularTranspilerExtension) + .with_extension(Fp2TranspilerExtension), + )?; + + let S = G1Affine::generator(); + let Q = G2Affine::generator(); + + let mut S_mul = [ + G1Affine::from(S * Fr::from(1)), + G1Affine::from(S * Fr::from(2)), + ]; + S_mul[1].y = -S_mul[1].y; + let Q_mul = [ + G2Affine::from(Q * Fr::from(2)), + G2Affine::from(Q * Fr::from(1)), + ]; + + let s = S_mul.map(|s| AffinePoint::new(s.x, s.y)); + let q = Q_mul.map(|p| AffinePoint::new(p.x, p.y)); + + // Test miller_loop + let f = Bls12_381::multi_miller_loop(&s, &q); + let io0 = s + .into_iter() + .flat_map(|pt| [pt.x, pt.y].into_iter().flat_map(|fp| fp.to_bytes())) + .map(FieldAlgebra::from_canonical_u8) + .collect::>(); + + let io1 = q + .into_iter() + .flat_map(|pt| [pt.x, pt.y].into_iter()) + .chain(f.to_coeffs()) + .flat_map(|fp2| fp2.to_coeffs()) + .flat_map(|fp| fp.to_bytes()) + .map(FieldAlgebra::from_canonical_u8) + .collect::>(); + + let io_all = io0.into_iter().chain(io1).collect::>(); + + air_test_with_min_segments(config, openvm_exe, vec![io_all], 1); + Ok(()) + } + + #[test] + fn test_bls12_381_pairing_check() -> Result<()> { + let config = get_testing_config(); + let elf = build_example_program_at_path_with_features( + get_programs_dir!(), + "pairing_check", + ["bls12_381"], + &config, + )?; + let openvm_exe = VmExe::from_elf( + elf, + Transpiler::::default() + .with_extension(Rv32ITranspilerExtension) + .with_extension(Rv32MTranspilerExtension) + .with_extension(Rv32IoTranspilerExtension) + .with_extension(PairingTranspilerExtension) + .with_extension(ModularTranspilerExtension) + .with_extension(Fp2TranspilerExtension), + )?; + + let S = G1Affine::generator(); + let Q = G2Affine::generator(); + + let mut S_mul = [ + G1Affine::from(S * Fr::from(1)), + G1Affine::from(S * Fr::from(2)), + ]; + S_mul[1].y = -S_mul[1].y; + let Q_mul = [ + G2Affine::from(Q * Fr::from(2)), + G2Affine::from(Q * Fr::from(1)), + ]; + let s = S_mul.map(|s| AffinePoint::new(s.x, s.y)); + let q = Q_mul.map(|p| AffinePoint::new(p.x, p.y)); + + // Gather inputs + let io0 = s + .into_iter() + .flat_map(|pt| [pt.x, pt.y].into_iter().flat_map(|fp| fp.to_bytes())) + .map(FieldAlgebra::from_canonical_u8) + .collect::>(); + + let io1 = q + .into_iter() + .flat_map(|pt| [pt.x, pt.y].into_iter()) + .flat_map(|fp2| fp2.to_coeffs()) + .flat_map(|fp| fp.to_bytes()) + .map(FieldAlgebra::from_canonical_u8) + .collect::>(); + + let io_all = io0.into_iter().chain(io1).collect::>(); + + air_test_with_min_segments(config, openvm_exe, vec![io_all], 1); + Ok(()) + } + + #[ignore] + #[test] + fn test_bls12_381_pairing_check_fallback() -> Result<()> { + let config = get_testing_config(); + let elf = build_example_program_at_path_with_features( + get_programs_dir!(), + "pairing_check_fallback", + ["bls12_381"], + &config, + )?; + let openvm_exe = VmExe::from_elf( + elf, + Transpiler::::default() + .with_extension(Rv32ITranspilerExtension) + .with_extension(Rv32MTranspilerExtension) + .with_extension(Rv32IoTranspilerExtension) + .with_extension(PairingTranspilerExtension) + .with_extension(ModularTranspilerExtension) + .with_extension(Fp2TranspilerExtension), + )?; + + let S = G1Affine::generator(); + let Q = G2Affine::generator(); + + let mut S_mul = [ + G1Affine::from(S * Fr::from(1)), + G1Affine::from(S * Fr::from(2)), + ]; + S_mul[1].y = -S_mul[1].y; + let Q_mul = [ + G2Affine::from(Q * Fr::from(2)), + G2Affine::from(Q * Fr::from(1)), + ]; + let s = S_mul.map(|s| AffinePoint::new(s.x, s.y)); + let q = Q_mul.map(|p| AffinePoint::new(p.x, p.y)); + + // Gather inputs + let io0 = s + .into_iter() + .flat_map(|pt| [pt.x, pt.y].into_iter().flat_map(|fp| fp.to_bytes())) + .map(FieldAlgebra::from_canonical_u8) + .collect::>(); + + let io1 = q + .into_iter() + .flat_map(|pt| [pt.x, pt.y].into_iter()) + .flat_map(|fp2| fp2.to_coeffs()) + .flat_map(|fp| fp.to_bytes()) + .map(FieldAlgebra::from_canonical_u8) + .collect::>(); + + let io_all = io0.into_iter().chain(io1).collect::>(); + // Don't run debugger because it's slow + air_test_impl(get_testing_config(), openvm_exe, vec![io_all], 1, false); + Ok(()) + } + + #[test] + fn test_bls12_381_final_exp_hint() -> Result<()> { + let config = get_testing_config(); + let elf = build_example_program_at_path_with_features( + get_programs_dir!(), + "bls_final_exp_hint", + ["bls12_381"], + &config, + )?; + let openvm_exe = VmExe::from_elf( + elf, + Transpiler::::default() + .with_extension(Rv32ITranspilerExtension) + .with_extension(Rv32MTranspilerExtension) + .with_extension(Rv32IoTranspilerExtension) + .with_extension(PairingTranspilerExtension) + .with_extension(ModularTranspilerExtension) + .with_extension(Fp2TranspilerExtension), + )?; + + let P = G1Affine::generator(); + let Q = G2Affine::generator(); + let ps = vec![AffinePoint::new(P.x, P.y), AffinePoint::new(P.x, -P.y)]; + let qs = vec![AffinePoint::new(Q.x, Q.y), AffinePoint::new(Q.x, Q.y)]; + let f = Bls12_381::multi_miller_loop(&ps, &qs); + let (c, s) = Bls12_381::final_exp_hint(&f); + let ps = ps + .into_iter() + .map(|pt| { + let [x, y] = [pt.x, pt.y] + .map(|x| openvm_pairing_guest::bls12_381::Fp::from_le_bytes(&x.to_bytes())); + AffinePoint::new(x, y) + }) + .collect::>(); + let qs = qs + .into_iter() + .map(|pt| { + let [x, y] = [pt.x, pt.y] + .map(|x| openvm_pairing_guest::bls12_381::Fp2::from_bytes(&x.to_bytes())); + AffinePoint::new(x, y) + }) + .collect::>(); + let [c, s] = + [c, s].map(|x| openvm_pairing_guest::bls12_381::Fp12::from_bytes(&x.to_bytes())); + let io = (ps, qs, (c, s)); + let io = openvm::serde::to_vec(&io).unwrap(); + let io = io + .into_iter() + .flat_map(|w| w.to_le_bytes()) + .map(F::from_canonical_u8) + .collect(); + air_test_with_min_segments(config, openvm_exe, vec![io], 1); + Ok(()) + } +} From b12190f1ac0cf708627b2e5fb83ef424ae42de9e Mon Sep 17 00:00:00 2001 From: Avaneesh Kulkarni Date: Wed, 30 Apr 2025 21:17:04 -0400 Subject: [PATCH 24/41] Delete parts of the pairing guest library --- Cargo.lock | 26 - .../tests/programs/openvm_init_sqrt.rs} | 3 +- .../tests/programs/openvm_init_decompress.rs | 3 - .../openvm_init_decompress_invalid_hint.rs | 3 - .../ecc/tests/programs/openvm_init_ec.rs | 3 - .../programs/openvm_init_ec_nonzero_a.rs | 3 - .../programs/openvm_init_ec_two_curves.rs | 3 - .../pairing/guest/src/bls12_381/fp12.rs | 214 ----- extensions/pairing/guest/src/bls12_381/fp2.rs | 76 -- extensions/pairing/guest/src/bls12_381/mod.rs | 645 ------------- .../pairing/guest/src/bls12_381/pairing.rs | 347 ------- .../pairing/guest/src/bls12_381/tests.rs | 307 +----- .../pairing/guest/src/bls12_381/utils.rs | 49 - extensions/pairing/guest/src/bn254/fp12.rs | 215 ----- extensions/pairing/guest/src/bn254/fp2.rs | 76 -- extensions/pairing/guest/src/bn254/mod.rs | 706 -------------- extensions/pairing/guest/src/bn254/pairing.rs | 379 -------- extensions/pairing/guest/src/bn254/tests.rs | 315 +------ extensions/pairing/guest/src/bn254/utils.rs | 49 - .../bls12_381/miller_loop.rs | 27 +- .../halo2curves_shims/bls12_381/tests/mod.rs | 15 +- .../halo2curves_shims/bn254/miller_loop.rs | 28 +- .../src/halo2curves_shims/bn254/tests/mod.rs | 13 +- extensions/pairing/guest/src/lib.rs | 1 + extensions/pairing/guest/src/pairing/mod.rs | 5 - .../guest/src/pairing/operations/fp12.rs | 34 - .../guest/src/pairing/operations/fp2.rs | 16 - .../guest/src/pairing/operations/fp6.rs | 187 ---- .../guest/src/pairing/operations/mod.rs | 9 - .../guest/src/pairing/sextic_ext_field.rs | 158 ---- extensions/pairing/tests/Cargo.toml | 34 - extensions/pairing/tests/programs/Cargo.toml | 51 - .../pairing/tests/programs/examples/bls_ec.rs | 11 - .../programs/examples/bls_final_exp_hint.rs | 24 - .../programs/examples/bn_final_exp_hint.rs | 24 - .../tests/programs/examples/fp12_mul.rs | 82 -- .../tests/programs/examples/pairing_check.rs | 90 -- .../examples/pairing_check_fallback.rs | 241 ----- .../tests/programs/examples/pairing_line.rs | 147 --- .../programs/examples/pairing_miller_loop.rs | 97 -- .../programs/examples/pairing_miller_step.rs | 166 ---- .../programs/openvm_init_bls_ec_bls12_381.rs | 3 - ...penvm_init_bls_final_exp_hint_bls12_381.rs | 4 - .../openvm_init_bn_final_exp_hint_bn254.rs | 4 - .../openvm_init_fp12_mul_bls12_381.rs | 4 - .../programs/openvm_init_fp12_mul_bn254.rs | 4 - .../openvm_init_pairing_check_bls12_381.rs | 4 - .../openvm_init_pairing_check_bn254.rs | 4 - ...m_init_pairing_check_fallback_bls12_381.rs | 4 - ...penvm_init_pairing_check_fallback_bn254.rs | 4 - .../openvm_init_pairing_line_bls12_381.rs | 4 - .../openvm_init_pairing_line_bn254.rs | 4 - ...envm_init_pairing_miller_loop_bls12_381.rs | 4 - .../openvm_init_pairing_miller_loop_bn254.rs | 4 - ...envm_init_pairing_miller_step_bls12_381.rs | 4 - .../openvm_init_pairing_miller_step_bn254.rs | 4 - extensions/pairing/tests/src/lib.rs | 884 ------------------ 57 files changed, 73 insertions(+), 5752 deletions(-) rename extensions/{ecc/tests/programs/openvm_init_ecdsa.rs => algebra/tests/programs/openvm_init_sqrt.rs} (58%) delete mode 100644 extensions/ecc/tests/programs/openvm_init_decompress.rs delete mode 100644 extensions/ecc/tests/programs/openvm_init_decompress_invalid_hint.rs delete mode 100644 extensions/ecc/tests/programs/openvm_init_ec.rs delete mode 100644 extensions/ecc/tests/programs/openvm_init_ec_nonzero_a.rs delete mode 100644 extensions/ecc/tests/programs/openvm_init_ec_two_curves.rs delete mode 100644 extensions/pairing/guest/src/bls12_381/fp12.rs delete mode 100644 extensions/pairing/guest/src/bls12_381/fp2.rs delete mode 100644 extensions/pairing/guest/src/bls12_381/pairing.rs delete mode 100644 extensions/pairing/guest/src/bls12_381/utils.rs delete mode 100644 extensions/pairing/guest/src/bn254/fp12.rs delete mode 100644 extensions/pairing/guest/src/bn254/fp2.rs delete mode 100644 extensions/pairing/guest/src/bn254/pairing.rs delete mode 100644 extensions/pairing/guest/src/bn254/utils.rs delete mode 100644 extensions/pairing/guest/src/pairing/operations/fp12.rs delete mode 100644 extensions/pairing/guest/src/pairing/operations/fp2.rs delete mode 100644 extensions/pairing/guest/src/pairing/operations/fp6.rs delete mode 100644 extensions/pairing/guest/src/pairing/operations/mod.rs delete mode 100644 extensions/pairing/guest/src/pairing/sextic_ext_field.rs delete mode 100644 extensions/pairing/tests/Cargo.toml delete mode 100644 extensions/pairing/tests/programs/Cargo.toml delete mode 100644 extensions/pairing/tests/programs/examples/bls_ec.rs delete mode 100644 extensions/pairing/tests/programs/examples/bls_final_exp_hint.rs delete mode 100644 extensions/pairing/tests/programs/examples/bn_final_exp_hint.rs delete mode 100644 extensions/pairing/tests/programs/examples/fp12_mul.rs delete mode 100644 extensions/pairing/tests/programs/examples/pairing_check.rs delete mode 100644 extensions/pairing/tests/programs/examples/pairing_check_fallback.rs delete mode 100644 extensions/pairing/tests/programs/examples/pairing_line.rs delete mode 100644 extensions/pairing/tests/programs/examples/pairing_miller_loop.rs delete mode 100644 extensions/pairing/tests/programs/examples/pairing_miller_step.rs delete mode 100644 extensions/pairing/tests/programs/openvm_init_bls_ec_bls12_381.rs delete mode 100644 extensions/pairing/tests/programs/openvm_init_bls_final_exp_hint_bls12_381.rs delete mode 100644 extensions/pairing/tests/programs/openvm_init_bn_final_exp_hint_bn254.rs delete mode 100644 extensions/pairing/tests/programs/openvm_init_fp12_mul_bls12_381.rs delete mode 100644 extensions/pairing/tests/programs/openvm_init_fp12_mul_bn254.rs delete mode 100644 extensions/pairing/tests/programs/openvm_init_pairing_check_bls12_381.rs delete mode 100644 extensions/pairing/tests/programs/openvm_init_pairing_check_bn254.rs delete mode 100644 extensions/pairing/tests/programs/openvm_init_pairing_check_fallback_bls12_381.rs delete mode 100644 extensions/pairing/tests/programs/openvm_init_pairing_check_fallback_bn254.rs delete mode 100644 extensions/pairing/tests/programs/openvm_init_pairing_line_bls12_381.rs delete mode 100644 extensions/pairing/tests/programs/openvm_init_pairing_line_bn254.rs delete mode 100644 extensions/pairing/tests/programs/openvm_init_pairing_miller_loop_bls12_381.rs delete mode 100644 extensions/pairing/tests/programs/openvm_init_pairing_miller_loop_bn254.rs delete mode 100644 extensions/pairing/tests/programs/openvm_init_pairing_miller_step_bls12_381.rs delete mode 100644 extensions/pairing/tests/programs/openvm_init_pairing_miller_step_bn254.rs delete mode 100644 extensions/pairing/tests/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index a3a6167df0..5fbe435f3e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5531,32 +5531,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "openvm-pairing-integration-tests" -version = "1.0.1-rc.0" -dependencies = [ - "eyre", - "halo2curves-axiom", - "num-bigint 0.4.6", - "num-traits", - "openvm", - "openvm-algebra-circuit", - "openvm-algebra-transpiler", - "openvm-circuit", - "openvm-ecc-circuit", - "openvm-ecc-guest", - "openvm-ecc-transpiler", - "openvm-instructions", - "openvm-pairing-circuit", - "openvm-pairing-guest", - "openvm-pairing-transpiler", - "openvm-rv32im-transpiler", - "openvm-stark-sdk", - "openvm-toolchain-tests", - "openvm-transpiler", - "rand", -] - [[package]] name = "openvm-pairing-transpiler" version = "1.1.2" diff --git a/extensions/ecc/tests/programs/openvm_init_ecdsa.rs b/extensions/algebra/tests/programs/openvm_init_sqrt.rs similarity index 58% rename from extensions/ecc/tests/programs/openvm_init_ecdsa.rs rename to extensions/algebra/tests/programs/openvm_init_sqrt.rs index bec9f527e9..31acaf4433 100644 --- a/extensions/ecc/tests/programs/openvm_init_ecdsa.rs +++ b/extensions/algebra/tests/programs/openvm_init_sqrt.rs @@ -1,3 +1,2 @@ // This file is automatically generated by cargo openvm. Do not rename or edit. -openvm_algebra_guest::moduli_macros::moduli_init! { "115792089237316195423570985008687907853269984665640564039457584007908834671663", "115792089237316195423570985008687907852837564279074904382605163141518161494337" } -openvm_ecc_guest::sw_macros::sw_init! { Secp256k1Point } +openvm_algebra_guest::moduli_macros::moduli_init! { "115792089237316195423570985008687907853269984665640564039457584007908834671663" } diff --git a/extensions/ecc/tests/programs/openvm_init_decompress.rs b/extensions/ecc/tests/programs/openvm_init_decompress.rs deleted file mode 100644 index b6137ae9ee..0000000000 --- a/extensions/ecc/tests/programs/openvm_init_decompress.rs +++ /dev/null @@ -1,3 +0,0 @@ -// This file is automatically generated by cargo openvm. Do not rename or edit. -openvm_algebra_guest::moduli_macros::moduli_init! { "115792089237316195423570985008687907853269984665640564039457584007908834671663", "115792089237316195423570985008687907852837564279074904382605163141518161494337", "115792089237316195423570985008687907853269984665640564039457584007913129639501", "1000000007", "26959946667150639794667015087019630673557916260026308143510066298881", "26959946667150639794667015087019625940457807714424391721682722368061" } -openvm_ecc_guest::sw_macros::sw_init! { Secp256k1Point, CurvePoint5mod8, CurvePoint1mod4 } diff --git a/extensions/ecc/tests/programs/openvm_init_decompress_invalid_hint.rs b/extensions/ecc/tests/programs/openvm_init_decompress_invalid_hint.rs deleted file mode 100644 index b6137ae9ee..0000000000 --- a/extensions/ecc/tests/programs/openvm_init_decompress_invalid_hint.rs +++ /dev/null @@ -1,3 +0,0 @@ -// This file is automatically generated by cargo openvm. Do not rename or edit. -openvm_algebra_guest::moduli_macros::moduli_init! { "115792089237316195423570985008687907853269984665640564039457584007908834671663", "115792089237316195423570985008687907852837564279074904382605163141518161494337", "115792089237316195423570985008687907853269984665640564039457584007913129639501", "1000000007", "26959946667150639794667015087019630673557916260026308143510066298881", "26959946667150639794667015087019625940457807714424391721682722368061" } -openvm_ecc_guest::sw_macros::sw_init! { Secp256k1Point, CurvePoint5mod8, CurvePoint1mod4 } diff --git a/extensions/ecc/tests/programs/openvm_init_ec.rs b/extensions/ecc/tests/programs/openvm_init_ec.rs deleted file mode 100644 index bec9f527e9..0000000000 --- a/extensions/ecc/tests/programs/openvm_init_ec.rs +++ /dev/null @@ -1,3 +0,0 @@ -// This file is automatically generated by cargo openvm. Do not rename or edit. -openvm_algebra_guest::moduli_macros::moduli_init! { "115792089237316195423570985008687907853269984665640564039457584007908834671663", "115792089237316195423570985008687907852837564279074904382605163141518161494337" } -openvm_ecc_guest::sw_macros::sw_init! { Secp256k1Point } diff --git a/extensions/ecc/tests/programs/openvm_init_ec_nonzero_a.rs b/extensions/ecc/tests/programs/openvm_init_ec_nonzero_a.rs deleted file mode 100644 index 02f8b5c05d..0000000000 --- a/extensions/ecc/tests/programs/openvm_init_ec_nonzero_a.rs +++ /dev/null @@ -1,3 +0,0 @@ -// This file is automatically generated by cargo openvm. Do not rename or edit. -openvm_algebra_guest::moduli_macros::moduli_init! { "115792089210356248762697446949407573530086143415290314195533631308867097853951", "115792089210356248762697446949407573529996955224135760342422259061068512044369" } -openvm_ecc_guest::sw_macros::sw_init! { P256Point } diff --git a/extensions/ecc/tests/programs/openvm_init_ec_two_curves.rs b/extensions/ecc/tests/programs/openvm_init_ec_two_curves.rs deleted file mode 100644 index 8689190544..0000000000 --- a/extensions/ecc/tests/programs/openvm_init_ec_two_curves.rs +++ /dev/null @@ -1,3 +0,0 @@ -// This file is automatically generated by cargo openvm. Do not rename or edit. -openvm_algebra_guest::moduli_macros::moduli_init! { "115792089237316195423570985008687907853269984665640564039457584007908834671663", "115792089237316195423570985008687907852837564279074904382605163141518161494337", "115792089210356248762697446949407573530086143415290314195533631308867097853951", "115792089210356248762697446949407573529996955224135760342422259061068512044369" } -openvm_ecc_guest::sw_macros::sw_init! { Secp256k1Point, P256Point } diff --git a/extensions/pairing/guest/src/bls12_381/fp12.rs b/extensions/pairing/guest/src/bls12_381/fp12.rs deleted file mode 100644 index 413f9377af..0000000000 --- a/extensions/pairing/guest/src/bls12_381/fp12.rs +++ /dev/null @@ -1,214 +0,0 @@ -use alloc::vec::Vec; -use core::ops::{Mul, MulAssign, Neg}; - -use openvm_algebra_guest::{ - field::{ComplexConjugate, FieldExtension}, - DivAssignUnsafe, DivUnsafe, Field, -}; - -use super::{Bls12_381, Fp, Fp2}; -use crate::pairing::{fp12_invert_assign, PairingIntrinsics, SexticExtField}; - -pub type Fp12 = SexticExtField; - -impl Fp12 { - pub fn invert(&self) -> Self { - let mut s = self.clone(); - fp12_invert_assign::(&mut s.c, &Bls12_381::XI); - s - } -} - -impl Field for Fp12 { - type SelfRef<'a> = &'a Self; - const ZERO: Self = Self::new([Fp2::ZERO; 6]); - const ONE: Self = Self::new([ - Fp2::ONE, - Fp2::ZERO, - Fp2::ZERO, - Fp2::ZERO, - Fp2::ZERO, - Fp2::ZERO, - ]); - - fn double_assign(&mut self) { - *self += self.clone(); - } - - fn square_assign(&mut self) { - *self *= self.clone(); - } -} - -impl FieldExtension for Fp12 { - const D: usize = 6; - type Coeffs = [Fp2; 6]; - - fn from_coeffs(coeffs: Self::Coeffs) -> Self { - Self::new(coeffs) - } - - fn from_bytes(bytes: &[u8]) -> Self { - assert_eq!(bytes.len(), 576); - Self::from_coeffs([ - Fp2::from_bytes(&bytes[0..96]), - Fp2::from_bytes(&bytes[96..192]), - Fp2::from_bytes(&bytes[192..288]), - Fp2::from_bytes(&bytes[288..384]), - Fp2::from_bytes(&bytes[384..480]), - Fp2::from_bytes(&bytes[480..576]), - ]) - } - - fn to_coeffs(self) -> Self::Coeffs { - self.c - } - - fn to_bytes(&self) -> Vec { - let mut bytes = Vec::with_capacity(576); - for coeff in self.clone().to_coeffs() { - bytes.extend_from_slice(&coeff.to_bytes()); - } - bytes - } - - fn embed(c0: Fp2) -> Self { - Self::new([c0, Fp2::ZERO, Fp2::ZERO, Fp2::ZERO, Fp2::ZERO, Fp2::ZERO]) - } - - /// We assume that the frobenius map power is < 12 - fn frobenius_map(&self, power: usize) -> Self { - if power & 1 != 0 { - let c0 = self.c[0].clone().conjugate(); - let c1 = self.c[1].clone().conjugate() * &Bls12_381::FROBENIUS_COEFFS[power][0]; - let c2 = self.c[2].clone().conjugate() * &Bls12_381::FROBENIUS_COEFFS[power][1]; - let c3 = self.c[3].clone().conjugate() * &Bls12_381::FROBENIUS_COEFFS[power][2]; - let c4 = self.c[4].clone().conjugate() * &Bls12_381::FROBENIUS_COEFFS[power][3]; - let c5 = self.c[5].clone().conjugate() * &Bls12_381::FROBENIUS_COEFFS[power][4]; - Self::new([c0, c1, c2, c3, c4, c5]) - } else { - let c0 = self.c[0].clone(); - let c1 = &self.c[1] * &Bls12_381::FROBENIUS_COEFFS[power][0]; - let c2 = &self.c[2] * &Bls12_381::FROBENIUS_COEFFS[power][1]; - let c3 = &self.c[3] * &Bls12_381::FROBENIUS_COEFFS[power][2]; - let c4 = &self.c[4] * &Bls12_381::FROBENIUS_COEFFS[power][3]; - let c5 = &self.c[5] * &Bls12_381::FROBENIUS_COEFFS[power][4]; - Self::new([c0, c1, c2, c3, c4, c5]) - } - } - - fn mul_base(&self, rhs: &Fp2) -> Self { - Self::new([ - &self.c[0] * rhs, - &self.c[1] * rhs, - &self.c[2] * rhs, - &self.c[3] * rhs, - &self.c[4] * rhs, - &self.c[5] * rhs, - ]) - } -} - -// This is ambiguous. It is conjugation for Fp12 over Fp6. -impl ComplexConjugate for Fp12 { - fn conjugate(self) -> Self { - let [c0, c1, c2, c3, c4, c5] = self.c; - Self::new([c0, -c1, c2, -c3, c4, -c5]) - } - - fn conjugate_assign(&mut self) { - self.c[1].neg_assign(); - self.c[3].neg_assign(); - self.c[5].neg_assign(); - } -} - -impl<'a> MulAssign<&'a Fp12> for Fp12 { - #[inline(always)] - fn mul_assign(&mut self, other: &'a Fp12) { - *self = crate::pairing::sextic_tower_mul(self, other, &Bls12_381::XI); - } -} - -impl<'a> Mul<&'a Fp12> for &'a Fp12 { - type Output = Fp12; - #[inline(always)] - fn mul(self, other: &'a Fp12) -> Self::Output { - crate::pairing::sextic_tower_mul(self, other, &Bls12_381::XI) - } -} - -impl MulAssign for Fp12 { - #[inline(always)] - fn mul_assign(&mut self, other: Self) { - self.mul_assign(&other); - } -} - -impl Mul for Fp12 { - type Output = Self; - #[inline(always)] - fn mul(mut self, other: Self) -> Self::Output { - self *= other; - self - } -} - -impl<'a> Mul<&'a Fp12> for Fp12 { - type Output = Self; - #[inline(always)] - fn mul(mut self, other: &'a Fp12) -> Self::Output { - self *= other; - self - } -} - -impl<'a> DivAssignUnsafe<&'a Fp12> for Fp12 { - #[inline(always)] - fn div_assign_unsafe(&mut self, other: &'a Fp12) { - *self *= other.invert(); - } -} - -impl<'a> DivUnsafe<&'a Fp12> for &'a Fp12 { - type Output = Fp12; - #[inline(always)] - fn div_unsafe(self, other: &'a Fp12) -> Self::Output { - let mut res = self.clone(); - res.div_assign_unsafe(other); - res - } -} - -impl DivAssignUnsafe for Fp12 { - #[inline(always)] - fn div_assign_unsafe(&mut self, other: Self) { - *self *= other.invert(); - } -} - -impl DivUnsafe for Fp12 { - type Output = Self; - #[inline(always)] - fn div_unsafe(mut self, other: Self) -> Self::Output { - self.div_assign_unsafe(other); - self - } -} - -impl<'a> DivUnsafe<&'a Fp12> for Fp12 { - type Output = Self; - #[inline(always)] - fn div_unsafe(mut self, other: &'a Fp12) -> Self::Output { - self.div_assign_unsafe(other); - self - } -} - -impl Neg for Fp12 { - type Output = Fp12; - #[inline(always)] - fn neg(self) -> Self::Output { - Self::ZERO - &self - } -} diff --git a/extensions/pairing/guest/src/bls12_381/fp2.rs b/extensions/pairing/guest/src/bls12_381/fp2.rs deleted file mode 100644 index 20de223962..0000000000 --- a/extensions/pairing/guest/src/bls12_381/fp2.rs +++ /dev/null @@ -1,76 +0,0 @@ -use alloc::vec::Vec; -use core::ops::Neg; - -use openvm_algebra_complex_macros::{complex_declare, complex_impl_field}; -use openvm_algebra_guest::{field::FieldExtension, Field, IntMod}; - -use super::Fp; - -#[cfg(not(target_os = "zkvm"))] -// Used in Fp2Extension config -pub const BLS12_381_COMPLEX_STRUCT_NAME: &str = "Bls12_381Fp2"; - -// The struct name needs to be globally unique for linking purposes. -// The mod_type is a path used only in the struct definition. -complex_declare! { - Bls12_381Fp2 { mod_type = Fp } -} - -complex_impl_field! { - Bls12_381Fp2, -} - -pub type Fp2 = Bls12_381Fp2; - -impl FieldExtension for Fp2 { - const D: usize = 2; - type Coeffs = [Fp; 2]; - - fn from_coeffs([c0, c1]: Self::Coeffs) -> Self { - Self { c0, c1 } - } - - fn from_bytes(bytes: &[u8]) -> Self { - assert_eq!(bytes.len(), 96); - Self::from_coeffs([ - Fp::from_const_bytes(bytes[0..48].try_into().unwrap()), - Fp::from_const_bytes(bytes[48..96].try_into().unwrap()), - ]) - } - - fn to_coeffs(self) -> Self::Coeffs { - [self.c0, self.c1] - } - - fn to_bytes(&self) -> Vec { - let mut bytes = Vec::with_capacity(96); - bytes.extend_from_slice(self.c0.as_le_bytes()); - bytes.extend_from_slice(self.c1.as_le_bytes()); - bytes - } - - fn embed(base_elem: Fp) -> Self { - Self { - c0: base_elem, - c1: ::ZERO, - } - } - - fn frobenius_map(&self, power: usize) -> Self { - if power % 2 == 0 { - self.clone() - } else { - Self { - c0: self.c0.clone(), - c1: (&self.c1).neg(), - } - } - } - - fn mul_base(&self, rhs: &Fp) -> Self { - Self { - c0: &self.c0 * rhs, - c1: &self.c1 * rhs, - } - } -} diff --git a/extensions/pairing/guest/src/bls12_381/mod.rs b/extensions/pairing/guest/src/bls12_381/mod.rs index f1940ebe21..898370a532 100644 --- a/extensions/pairing/guest/src/bls12_381/mod.rs +++ b/extensions/pairing/guest/src/bls12_381/mod.rs @@ -1,25 +1,8 @@ -use core::ops::Neg; - -use openvm_algebra_guest::{Field, IntMod}; -use openvm_algebra_moduli_macros::moduli_declare; -use openvm_ecc_guest::{weierstrass::IntrinsicCurve, CyclicGroup, Group}; - -mod fp12; -mod fp2; -mod pairing; -#[cfg(all(feature = "halo2curves", not(target_os = "zkvm")))] -pub(crate) mod utils; - -pub use fp12::*; -pub use fp2::*; use hex_literal::hex; #[cfg(not(target_os = "zkvm"))] use lazy_static::lazy_static; #[cfg(not(target_os = "zkvm"))] use num_bigint::BigUint; -use openvm_ecc_sw_macros::sw_declare; - -use crate::pairing::PairingIntrinsics; #[cfg(all(test, feature = "halo2curves", not(target_os = "zkvm")))] mod tests; @@ -49,634 +32,6 @@ pub const BLS12_381_PSEUDO_BINARY_ENCODING: [i8; 64] = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, ]; -moduli_declare! { - Bls12_381Fp { modulus = "0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab" }, - Bls12_381Scalar { modulus = "0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001" }, -} - -const CURVE_B: Bls12_381Fp = Bls12_381Fp::from_const_u8(4); - -sw_declare! { - Bls12_381G1Affine { mod_type = Bls12_381Fp, b = CURVE_B }, -} - #[cfg(not(target_os = "zkvm"))] // Used in WeierstrassExtension config pub const BLS12_381_ECC_STRUCT_NAME: &str = "Bls12_381G1Affine"; - -pub type Fp = Bls12_381Fp; -pub type Scalar = Bls12_381Scalar; -/// Affine point representation of `Fp` points of BLS12-381. -/// **Note**: an instance of this type may be constructed that lies -/// on the curve but not necessarily in the prime order subgroup -/// because the group has cofactors. -pub type G1Affine = Bls12_381G1Affine; -pub use g2::G2Affine; - -impl Field for Fp { - type SelfRef<'a> = &'a Self; - const ZERO: Self = ::ZERO; - const ONE: Self = ::ONE; - - fn double_assign(&mut self) { - IntMod::double_assign(self); - } - - fn square_assign(&mut self) { - IntMod::square_assign(self); - } -} - -impl Field for Scalar { - type SelfRef<'a> = &'a Self; - const ZERO: Self = ::ZERO; - const ONE: Self = ::ONE; - - fn double_assign(&mut self) { - IntMod::double_assign(self); - } - - fn square_assign(&mut self) { - IntMod::square_assign(self); - } -} - -// https://hackmd.io/@benjaminion/bls12-381#Cofactor -// BLS12-381: The from_xy function will allow constructing elements that lie on the curve -// but aren't actually in the cyclic subgroup of prime order that is usually called G1. -impl CyclicGroup for G1Affine { - // https://github.com/zcash/librustzcash/blob/6e0364cd42a2b3d2b958a54771ef51a8db79dd29/pairing/src/bls12_381/README.md#generators - const GENERATOR: Self = G1Affine { - x: Bls12_381Fp::from_const_bytes(hex!( - "BBC622DB0AF03AFBEF1A7AF93FE8556C58AC1B173F3A4EA105B974974F8C68C30FACA94F8C63952694D79731A7D3F117" - )), - y: Bls12_381Fp::from_const_bytes(hex!( - "E1E7C5462923AA0CE48A88A244C73CD0EDB3042CCB18DB00F60AD0D595E0F5FCE48A1D74ED309EA0F1A0AAE381F4B308" - )), - }; - const NEG_GENERATOR: Self = G1Affine { - x: Bls12_381Fp::from_const_bytes(hex!( - "BBC622DB0AF03AFBEF1A7AF93FE8556C58AC1B173F3A4EA105B974974F8C68C30FACA94F8C63952694D79731A7D3F117" - )), - y: Bls12_381Fp::from_const_bytes(hex!( - "CAC239B9D6DC54AD1B75CB0EBA386F4E3642ACCAD5B95566C907B51DEF6A8167F2212ECFC8767DAAA845D555681D4D11" - )), - }; -} - -pub struct Bls12_381; - -impl IntrinsicCurve for Bls12_381 { - type Scalar = Scalar; - type Point = G1Affine; - - fn msm(coeffs: &[Self::Scalar], bases: &[Self::Point]) -> Self::Point { - openvm_ecc_guest::msm(coeffs, bases) - } -} - -// Define a G2Affine struct that implements curve operations using `Fp2` intrinsics -// but not special E(Fp2) intrinsics. -mod g2 { - use openvm_algebra_guest::Field; - use openvm_ecc_guest::{ - impl_sw_affine, impl_sw_group_ops, weierstrass::WeierstrassPoint, AffinePoint, Group, - }; - - use super::{Fp, Fp2}; - - const THREE: Fp2 = Fp2::new(Fp::from_const_u8(3), Fp::ZERO); - const B: Fp2 = Fp2::new(Fp::from_const_u8(4), Fp::from_const_u8(4)); - impl_sw_affine!(G2Affine, Fp2, THREE, B); - impl_sw_group_ops!(G2Affine, Fp2); -} - -impl PairingIntrinsics for Bls12_381 { - type Fp = Fp; - type Fp2 = Fp2; - type Fp12 = Fp12; - - const PAIRING_IDX: usize = 1; - // The sextic extension `Fp12` is `Fp2[X] / (X^6 - \xi)`, where `\xi` is a non-residue. - const XI: Fp2 = Fp2::new(Fp::from_const_u8(1), Fp::from_const_u8(1)); - const FP2_TWO: Fp2 = Fp2::new(Fp::from_const_u8(2), Fp::from_const_u8(0)); - const FP2_THREE: Fp2 = Fp2::new(Fp::from_const_u8(3), Fp::from_const_u8(0)); - - // Multiplication constants for the Frobenius map for coefficients in Fp2 c1..=c5 for powers - // 0..12 FROBENIUS_COEFFS\[i\]\[j\] = \xi^{(j + 1) * (p^i - 1)/6} when p = 1 (mod 6) - // These are validated against `halo2curves::bls12_381::FROBENIUS_COEFF_FQ12_C1` in tests.rs - const FROBENIUS_COEFFS: [[Self::Fp2; 5]; 12] = [ - [ - Fp2 { - c0: Bls12_381Fp(hex!( - "010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 - ")), - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 - ")), - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 - ")), - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 - ")), - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 - ")), - }, - ], - [ - Fp2 { - c0: Bls12_381Fp(hex!( - "b85f2392ed75078d3d81e7633da57ef6c4b9ba84d743247b4f5fbd3cfd03d60f1f0d2c20b4be31c26706bb02bfd30419" - )), - c1: Bls12_381Fp(hex!( - "f34adc6d128af72cc27e6c4dc15a2d285f3cf671c98e0cec6fb3c7b68747a154b89f1f2302e9e98832e0c4362b3efc00" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )), - c1: Bls12_381Fp(hex!( - "acaa00000000fd8bfdff494feb2794409b5fb80f65297d89d49a75897d850daa85ded463864002ec99e67f39ea11011a" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "09cce3edfb8410c8f405ec722f9967eec5419200176ef7775e43d3c2ab5d3948fe7fd16b6de331680b40ff37040eaf06" - )), - c1: Bls12_381Fp(hex!( - "09cce3edfb8410c8f405ec722f9967eec5419200176ef7775e43d3c2ab5d3948fe7fd16b6de331680b40ff37040eaf06" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "adaa00000000fd8bfdff494feb2794409b5fb80f65297d89d49a75897d850daa85ded463864002ec99e67f39ea11011a" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "16810780e9fa189b32877f256e3e3ac666059c8e4ddfea8bee8f0b0c241698f345e0b1486bfa47dfd85f3a01d9cfb205" - )), - c1: Bls12_381Fp(hex!( - "9529f87f1605e61ecd78d48b90c17158bdf0146853f345dbd08279e76035df7091cc99fa4aadd36bc186453811424e14" - )) - }, - ], - [ - Fp2 { - c0: Bls12_381Fp(hex!( - "fffffeffffff012e02000a6213d817de8896f8e63ba9b3ddea770f6a07c669ba51ce76df2f67195f0000000000000000" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "fefffeffffff012e02000a6213d817de8896f8e63ba9b3ddea770f6a07c669ba51ce76df2f67195f0000000000000000" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "aaaafffffffffeb9ffff53b1feffab1e24f6b0f6a0d23067bf1285f3844b7764d7ac4b43b6a71b4b9ae67f39ea11011a" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "acaa00000000fd8bfdff494feb2794409b5fb80f65297d89d49a75897d850daa85ded463864002ec99e67f39ea11011a" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "adaa00000000fd8bfdff494feb2794409b5fb80f65297d89d49a75897d850daa85ded463864002ec99e67f39ea11011a" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )) - }, - ], - [ - Fp2 { - c0: Bls12_381Fp(hex!( - "a2de1b12047beef10afa673ecf6644305eb41ef6896439ef60cfb130d9ed3d1cd92c7ad748c4e9e28ea68001e6035213" - )), - c1: Bls12_381Fp(hex!( - "09cce3edfb8410c8f405ec722f9967eec5419200176ef7775e43d3c2ab5d3948fe7fd16b6de331680b40ff37040eaf06" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )), - c1: Bls12_381Fp(hex!( - "010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "a2de1b12047beef10afa673ecf6644305eb41ef6896439ef60cfb130d9ed3d1cd92c7ad748c4e9e28ea68001e6035213" - )), - c1: Bls12_381Fp(hex!( - "a2de1b12047beef10afa673ecf6644305eb41ef6896439ef60cfb130d9ed3d1cd92c7ad748c4e9e28ea68001e6035213" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "aaaafffffffffeb9ffff53b1feffab1e24f6b0f6a0d23067bf1285f3844b7764d7ac4b43b6a71b4b9ae67f39ea11011a" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "09cce3edfb8410c8f405ec722f9967eec5419200176ef7775e43d3c2ab5d3948fe7fd16b6de331680b40ff37040eaf06" - )), - c1: Bls12_381Fp(hex!( - "a2de1b12047beef10afa673ecf6644305eb41ef6896439ef60cfb130d9ed3d1cd92c7ad748c4e9e28ea68001e6035213" - )) - }, - ], - [ - Fp2 { - c0: Bls12_381Fp(hex!( - "fefffeffffff012e02000a6213d817de8896f8e63ba9b3ddea770f6a07c669ba51ce76df2f67195f0000000000000000" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "acaa00000000fd8bfdff494feb2794409b5fb80f65297d89d49a75897d850daa85ded463864002ec99e67f39ea11011a" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "fefffeffffff012e02000a6213d817de8896f8e63ba9b3ddea770f6a07c669ba51ce76df2f67195f0000000000000000" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "acaa00000000fd8bfdff494feb2794409b5fb80f65297d89d49a75897d850daa85ded463864002ec99e67f39ea11011a" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )) - }, - ], - [ - Fp2 { - c0: Bls12_381Fp(hex!( - "9529f87f1605e61ecd78d48b90c17158bdf0146853f345dbd08279e76035df7091cc99fa4aadd36bc186453811424e14" - )), - c1: Bls12_381Fp(hex!( - "16810780e9fa189b32877f256e3e3ac666059c8e4ddfea8bee8f0b0c241698f345e0b1486bfa47dfd85f3a01d9cfb205" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )), - c1: Bls12_381Fp(hex!( - "fefffeffffff012e02000a6213d817de8896f8e63ba9b3ddea770f6a07c669ba51ce76df2f67195f0000000000000000" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "09cce3edfb8410c8f405ec722f9967eec5419200176ef7775e43d3c2ab5d3948fe7fd16b6de331680b40ff37040eaf06" - )), - c1: Bls12_381Fp(hex!( - "09cce3edfb8410c8f405ec722f9967eec5419200176ef7775e43d3c2ab5d3948fe7fd16b6de331680b40ff37040eaf06" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "fffffeffffff012e02000a6213d817de8896f8e63ba9b3ddea770f6a07c669ba51ce76df2f67195f0000000000000000" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "f34adc6d128af72cc27e6c4dc15a2d285f3cf671c98e0cec6fb3c7b68747a154b89f1f2302e9e98832e0c4362b3efc00" - )), - c1: Bls12_381Fp(hex!( - "b85f2392ed75078d3d81e7633da57ef6c4b9ba84d743247b4f5fbd3cfd03d60f1f0d2c20b4be31c26706bb02bfd30419" - )) - }, - ], - [ - Fp2 { - c0: Bls12_381Fp(hex!( - "aaaafffffffffeb9ffff53b1feffab1e24f6b0f6a0d23067bf1285f3844b7764d7ac4b43b6a71b4b9ae67f39ea11011a" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "aaaafffffffffeb9ffff53b1feffab1e24f6b0f6a0d23067bf1285f3844b7764d7ac4b43b6a71b4b9ae67f39ea11011a" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "aaaafffffffffeb9ffff53b1feffab1e24f6b0f6a0d23067bf1285f3844b7764d7ac4b43b6a71b4b9ae67f39ea11011a" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )) - }, - ], - [ - Fp2 { - c0: Bls12_381Fp(hex!( - "f34adc6d128af72cc27e6c4dc15a2d285f3cf671c98e0cec6fb3c7b68747a154b89f1f2302e9e98832e0c4362b3efc00" - )), - c1: Bls12_381Fp(hex!( - "b85f2392ed75078d3d81e7633da57ef6c4b9ba84d743247b4f5fbd3cfd03d60f1f0d2c20b4be31c26706bb02bfd30419" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )), - c1: Bls12_381Fp(hex!( - "acaa00000000fd8bfdff494feb2794409b5fb80f65297d89d49a75897d850daa85ded463864002ec99e67f39ea11011a" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "a2de1b12047beef10afa673ecf6644305eb41ef6896439ef60cfb130d9ed3d1cd92c7ad748c4e9e28ea68001e6035213" - )), - c1: Bls12_381Fp(hex!( - "a2de1b12047beef10afa673ecf6644305eb41ef6896439ef60cfb130d9ed3d1cd92c7ad748c4e9e28ea68001e6035213" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "adaa00000000fd8bfdff494feb2794409b5fb80f65297d89d49a75897d850daa85ded463864002ec99e67f39ea11011a" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "9529f87f1605e61ecd78d48b90c17158bdf0146853f345dbd08279e76035df7091cc99fa4aadd36bc186453811424e14" - )), - c1: Bls12_381Fp(hex!( - "16810780e9fa189b32877f256e3e3ac666059c8e4ddfea8bee8f0b0c241698f345e0b1486bfa47dfd85f3a01d9cfb205" - )) - }, - ], - [ - Fp2 { - c0: Bls12_381Fp(hex!( - "acaa00000000fd8bfdff494feb2794409b5fb80f65297d89d49a75897d850daa85ded463864002ec99e67f39ea11011a" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "fefffeffffff012e02000a6213d817de8896f8e63ba9b3ddea770f6a07c669ba51ce76df2f67195f0000000000000000" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "acaa00000000fd8bfdff494feb2794409b5fb80f65297d89d49a75897d850daa85ded463864002ec99e67f39ea11011a" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "fefffeffffff012e02000a6213d817de8896f8e63ba9b3ddea770f6a07c669ba51ce76df2f67195f0000000000000000" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )) - }, - ], - [ - Fp2 { - c0: Bls12_381Fp(hex!( - "09cce3edfb8410c8f405ec722f9967eec5419200176ef7775e43d3c2ab5d3948fe7fd16b6de331680b40ff37040eaf06" - )), - c1: Bls12_381Fp(hex!( - "a2de1b12047beef10afa673ecf6644305eb41ef6896439ef60cfb130d9ed3d1cd92c7ad748c4e9e28ea68001e6035213" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )), - c1: Bls12_381Fp(hex!( - "010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "09cce3edfb8410c8f405ec722f9967eec5419200176ef7775e43d3c2ab5d3948fe7fd16b6de331680b40ff37040eaf06" - )), - c1: Bls12_381Fp(hex!( - "09cce3edfb8410c8f405ec722f9967eec5419200176ef7775e43d3c2ab5d3948fe7fd16b6de331680b40ff37040eaf06" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "aaaafffffffffeb9ffff53b1feffab1e24f6b0f6a0d23067bf1285f3844b7764d7ac4b43b6a71b4b9ae67f39ea11011a" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "a2de1b12047beef10afa673ecf6644305eb41ef6896439ef60cfb130d9ed3d1cd92c7ad748c4e9e28ea68001e6035213" - )), - c1: Bls12_381Fp(hex!( - "09cce3edfb8410c8f405ec722f9967eec5419200176ef7775e43d3c2ab5d3948fe7fd16b6de331680b40ff37040eaf06" - )) - }, - ], - [ - Fp2 { - c0: Bls12_381Fp(hex!( - "adaa00000000fd8bfdff494feb2794409b5fb80f65297d89d49a75897d850daa85ded463864002ec99e67f39ea11011a" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "acaa00000000fd8bfdff494feb2794409b5fb80f65297d89d49a75897d850daa85ded463864002ec99e67f39ea11011a" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "aaaafffffffffeb9ffff53b1feffab1e24f6b0f6a0d23067bf1285f3844b7764d7ac4b43b6a71b4b9ae67f39ea11011a" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "fefffeffffff012e02000a6213d817de8896f8e63ba9b3ddea770f6a07c669ba51ce76df2f67195f0000000000000000" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "fffffeffffff012e02000a6213d817de8896f8e63ba9b3ddea770f6a07c669ba51ce76df2f67195f0000000000000000" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )) - }, - ], - [ - Fp2 { - c0: Bls12_381Fp(hex!( - "16810780e9fa189b32877f256e3e3ac666059c8e4ddfea8bee8f0b0c241698f345e0b1486bfa47dfd85f3a01d9cfb205" - )), - c1: Bls12_381Fp(hex!( - "9529f87f1605e61ecd78d48b90c17158bdf0146853f345dbd08279e76035df7091cc99fa4aadd36bc186453811424e14" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )), - c1: Bls12_381Fp(hex!( - "fefffeffffff012e02000a6213d817de8896f8e63ba9b3ddea770f6a07c669ba51ce76df2f67195f0000000000000000" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "a2de1b12047beef10afa673ecf6644305eb41ef6896439ef60cfb130d9ed3d1cd92c7ad748c4e9e28ea68001e6035213" - )), - c1: Bls12_381Fp(hex!( - "a2de1b12047beef10afa673ecf6644305eb41ef6896439ef60cfb130d9ed3d1cd92c7ad748c4e9e28ea68001e6035213" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "fffffeffffff012e02000a6213d817de8896f8e63ba9b3ddea770f6a07c669ba51ce76df2f67195f0000000000000000" - )), - c1: Bls12_381Fp(hex!( - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - )) - }, - Fp2 { - c0: Bls12_381Fp(hex!( - "b85f2392ed75078d3d81e7633da57ef6c4b9ba84d743247b4f5fbd3cfd03d60f1f0d2c20b4be31c26706bb02bfd30419" - )), - c1: Bls12_381Fp(hex!( - "f34adc6d128af72cc27e6c4dc15a2d285f3cf671c98e0cec6fb3c7b68747a154b89f1f2302e9e98832e0c4362b3efc00" - )) - }, - ], - ]; -} - -impl Bls12_381 { - // FINAL_EXPONENT = (p^12 - 1) / r in big-endian - // Validated by a test in test.rs - pub const FINAL_EXPONENT: [u8; 540] = hex!( - "02ee1db5dcc825b7e1bda9c0496a1c0a89ee0193d4977b3f7d4507d07363baa13f8d14a917848517badc3a43d1073776ab353f2c30698e8cc7deada9c0aadff5e9cfee9a074e43b9a660835cc872ee83ff3a0f0f1c0ad0d6106feaf4e347aa68ad49466fa927e7bb9375331807a0dce2630d9aa4b113f414386b0e8819328148978e2b0dd39099b86e1ab656d2670d93e4d7acdd350da5359bc73ab61a0c5bf24c374693c49f570bcd2b01f3077ffb10bf24dde41064837f27611212596bc293c8d4c01f25118790f4684d0b9c40a68eb74bb22a40ee7169cdc1041296532fef459f12438dfc8e2886ef965e61a474c5c85b0129127a1b5ad0463434724538411d1676a53b5a62eb34c05739334f46c02c3f0bd0c55d3109cd15948d0a1fad20044ce6ad4c6bec3ec03ef19592004cedd556952c6d8823b19dadd7c2498345c6e5308f1c511291097db60b1749bf9b71a9f9e0100418a3ef0bc627751bbd81367066bca6a4c1b6dcfc5cceb73fc56947a403577dfa9e13c24ea820b09c1d9f7c31759c3635de3f7a3639991708e88adce88177456c49637fd7961be1a4c7e79fb02faa732e2f3ec2bea83d196283313492caa9d4aff1c910e9622d2a73f62537f2701aaef6539314043f7bbce5b78c7869aeb2181a67e49eeed2161daf3f881bd88592d767f67c4717489119226c2f011d4cab803e9d71650a6f80698e2f8491d12191a04406fbc8fbd5f48925f98630e68bfb24c0bcb9b55df57510" - ); -} diff --git a/extensions/pairing/guest/src/bls12_381/pairing.rs b/extensions/pairing/guest/src/bls12_381/pairing.rs deleted file mode 100644 index 9cd7ade4a5..0000000000 --- a/extensions/pairing/guest/src/bls12_381/pairing.rs +++ /dev/null @@ -1,347 +0,0 @@ -use alloc::vec::Vec; - -use itertools::izip; -use openvm_algebra_guest::{ - field::{ComplexConjugate, FieldExtension}, - DivUnsafe, Field, -}; -use openvm_ecc_guest::AffinePoint; -#[cfg(target_os = "zkvm")] -use { - crate::{PairingBaseFunct7, OPCODE, PAIRING_FUNCT3}, - core::mem::MaybeUninit, - openvm_platform::custom_insn_r, - openvm_rv32im_guest, - openvm_rv32im_guest::hint_buffer_u32, -}; - -use super::{Bls12_381, Fp, Fp12, Fp2, BLS12_381_PSEUDO_BINARY_ENCODING, BLS12_381_SEED_ABS}; -use crate::pairing::{ - exp_check_fallback, Evaluatable, EvaluatedLine, FromLineMType, LineMulMType, MillerStep, - MultiMillerLoop, PairingCheck, PairingCheckError, PairingIntrinsics, UnevaluatedLine, -}; -#[cfg(all(feature = "halo2curves", not(target_os = "zkvm")))] -use crate::{ - bls12_381::utils::{ - convert_bls12381_fp2_to_halo2_fq2, convert_bls12381_fp_to_halo2_fq, - convert_bls12381_halo2_fq12_to_fp12, - }, - halo2curves_shims::bls12_381::Bls12_381 as Halo2CurvesBls12_381, - pairing::FinalExp, -}; - -impl Evaluatable for UnevaluatedLine { - fn evaluate(&self, xy_frac: &(Fp, Fp)) -> EvaluatedLine { - let (x_over_y, y_inv) = xy_frac; - // Represents the line L(x,y) = 1 + b (x/y) w^-1 + c (1/y) w^-3 - EvaluatedLine { - b: self.b.mul_base(x_over_y), - c: self.c.mul_base(y_inv), - } - } -} - -impl FromLineMType for Fp12 { - // Since multiplying by w^3 doesn't change the miller loop result, we transform the line - // into L_new(x,y) = w^3 L(x,y) = w^3 + b (x/y) w^2 + c (1/y) - fn from_evaluated_line_m_type(line: EvaluatedLine) -> Fp12 { - Fp12::from_coeffs([line.c, Fp2::ZERO, line.b, Fp2::ONE, Fp2::ZERO, Fp2::ZERO]) - } -} - -// TODO[jpw]: make this into a macro depending on P::PAIRING_IDX when we have more curves -impl LineMulMType for Bls12_381 { - /// Multiplies two lines in 023-form to get an element in 02345-form - fn mul_023_by_023(l0: &EvaluatedLine, l1: &EvaluatedLine) -> [Fp2; 5] { - // l0 = c0 + b0 w^2 + w^3 - let b0 = &l0.b; - let c0 = &l0.c; - // l1 = c1 + b1 w^2 + w^3 - let b1 = &l1.b; - let c1 = &l1.c; - - // where w⁶ = xi - // l0 * l1 = c0c1 + (c0b1 + c1b0)w² + (c0 + c1)w³ + (b0b1)w⁴ + (b0 +b1)w⁵ + w⁶ - // = (c0c1 + xi) + (c0b1 + c1b0)w² + (c0 + c1)w³ + (b0b1)w⁴ + (b0 + b1)w⁵ - let x0 = c0 * c1 + Bls12_381::XI; - let x2 = c0 * b1 + c1 * b0; - let x3 = c0 + c1; - let x4 = b0 * b1; - let x5 = b0 + b1; - - [x0, x2, x3, x4, x5] - } - - /// Multiplies a line in 02345-form with a Fp12 element to get an Fp12 element - fn mul_by_023(f: &Fp12, l: &EvaluatedLine) -> Fp12 { - // this is only used if the number of lines is odd, which doesn't happen for our - // applications right now, so we can use this suboptimal implementation - Fp12::from_evaluated_line_m_type(l.clone()) * f - } - - /// Multiplies a line in 02345-form with a Fp12 element to get an Fp12 element - fn mul_by_02345(f: &Fp12, x: &[Fp2; 5]) -> Fp12 { - // we update the order of the coefficients to match the Fp12 coefficient ordering: - // Fp12 { - // c0: Fp6 { - // c0: x0, - // c1: x2, - // c2: x4, - // }, - // c1: Fp6 { - // c0: x1, - // c1: x3, - // c2: x5, - // }, - // } - let o0 = &x[0]; // coeff x0 - let o1 = &x[1]; // coeff x2 - let o2 = &x[3]; // coeff x4 - let o4 = &x[2]; // coeff x3 - let o5 = &x[4]; // coeff x5 - - let xi = &Bls12_381::XI; - - let self_coeffs = &f.c; - let s0 = &self_coeffs[0]; - let s1 = &self_coeffs[2]; - let s2 = &self_coeffs[4]; - let s3 = &self_coeffs[1]; - let s4 = &self_coeffs[3]; - let s5 = &self_coeffs[5]; - - // NOTE[yj]: Hand-calculated multiplication for Fp12 * 02345 ∈ Fp2; this is likely not the - // most efficient implementation c00 = cs0co0 + xi(cs1co2 + cs2co1 + cs3co5 + - // cs4co4) c01 = cs0co1 + cs1co0 + xi(cs2co2 + cs4co5 + cs5co4) - // c02 = cs0co2 + cs1co1 + cs2co0 + cs3co4 + xi(cs5co5) - // c10 = cs3co0 + xi(cs1co5 + cs2co4 + cs4co2 + cs5co1) - // c11 = cs0co4 + cs3co1 + cs4co0 + xi(cs2co5 + cs5co2) - // c12 = cs0co5 + cs1co4 + cs3co2 + cs4co1 + cs5co0 - // where cs*: self.c* - let c00 = s0 * o0 + xi * &(s1 * o2 + s2 * o1 + s3 * o5 + s4 * o4); - let c01 = s0 * o1 + s1 * o0 + xi * &(s2 * o2 + s4 * o5 + s5 * o4); - let c02 = s0 * o2 + s1 * o1 + s2 * o0 + s3 * o4 + xi * &(s5 * o5); - let c10 = s3 * o0 + xi * &(s1 * o5 + s2 * o4 + s4 * o2 + s5 * o1); - let c11 = s0 * o4 + s3 * o1 + s4 * o0 + xi * &(s2 * o5 + s5 * o2); - let c12 = s0 * o5 + s1 * o4 + s3 * o2 + s4 * o1 + s5 * o0; - - Fp12::from_coeffs([c00, c10, c01, c11, c02, c12]) - } -} - -#[allow(non_snake_case)] -impl MultiMillerLoop for Bls12_381 { - type Fp = Fp; - type Fp12 = Fp12; - - const SEED_ABS: u64 = BLS12_381_SEED_ABS; - const PSEUDO_BINARY_ENCODING: &[i8] = &BLS12_381_PSEUDO_BINARY_ENCODING; - - fn evaluate_lines_vec(f: Self::Fp12, lines: Vec>) -> Self::Fp12 { - let mut f = f; - let mut lines = lines; - if lines.len() % 2 == 1 { - f = Self::mul_by_023(&f, &lines.pop().unwrap()); - } - for chunk in lines.chunks(2) { - if let [line0, line1] = chunk { - let prod = Self::mul_023_by_023(line0, line1); - f = Self::mul_by_02345(&f, &prod); - } else { - panic!("lines.len() % 2 should be 0 at this point"); - } - } - f - } - - /// The expected output of this function when running the Miller loop with embedded exponent is - /// c^3 * l_{3Q} - fn pre_loop( - Q_acc: Vec>, - Q: &[AffinePoint], - c: Option, - xy_fracs: &[(Self::Fp, Self::Fp)], - ) -> (Self::Fp12, Vec>) { - let mut f = if let Some(mut c) = c { - // for the miller loop with embedded exponent, f will be set to c at the beginning of - // the function, and we will multiply by c again due to the last two values - // of the pseudo-binary encoding (BLS12_381_PSEUDO_BINARY_ENCODING) being 1. - // Therefore, the final value of f at the end of this block is c^3. - let mut c3 = c.clone(); - c.square_assign(); - c3 *= &c; - c3 - } else { - Self::Fp12::ONE - }; - - let mut Q_acc = Q_acc; - - // Special case the first iteration of the Miller loop with pseudo_binary_encoding = 1: - // this means that the first step is a double and add, but we need to separate the two steps - // since the optimized `miller_double_and_add_step` will fail because Q_acc is equal - // to Q_signed on the first iteration - let (Q_out_double, lines_2S) = Q_acc - .into_iter() - .map(|Q| Self::miller_double_step(&Q)) - .unzip::<_, _, Vec<_>, Vec<_>>(); - Q_acc = Q_out_double; - - let mut initial_lines = Vec::>::new(); - - let lines_iter = izip!(lines_2S.iter(), xy_fracs.iter()); - for (line_2S, xy_frac) in lines_iter { - let line = line_2S.evaluate(xy_frac); - initial_lines.push(line); - } - - let (Q_out_add, lines_S_plus_Q) = Q_acc - .iter() - .zip(Q.iter()) - .map(|(Q_acc, Q)| Self::miller_add_step(Q_acc, Q)) - .unzip::<_, _, Vec<_>, Vec<_>>(); - Q_acc = Q_out_add; - - let lines_iter = izip!(lines_S_plus_Q.iter(), xy_fracs.iter()); - for (lines_S_plus_Q, xy_frac) in lines_iter { - let line = lines_S_plus_Q.evaluate(xy_frac); - initial_lines.push(line); - } - - f = Self::evaluate_lines_vec(f, initial_lines); - - (f, Q_acc) - } - - /// After running the main body of the Miller loop, we conjugate f due to the curve seed x being - /// negative. - fn post_loop( - f: &Self::Fp12, - Q_acc: Vec>, - _Q: &[AffinePoint], - _c: Option, - _xy_fracs: &[(Self::Fp, Self::Fp)], - ) -> (Self::Fp12, Vec>) { - // Conjugate for negative component of the seed - // By Lemma 1 from https://www.iacr.org/archive/eurocrypt2011/66320047/66320047.pdf f_{x,Q} = conjugate( f_{|x|,Q} ) - let mut f = f.clone(); - f.conjugate_assign(); - (f, Q_acc) - } -} - -#[allow(non_snake_case)] -impl PairingCheck for Bls12_381 { - type Fp = Fp; - type Fp2 = Fp2; - type Fp12 = Fp12; - - #[allow(unused_variables)] - fn pairing_check_hint( - P: &[AffinePoint], - Q: &[AffinePoint], - ) -> (Self::Fp12, Self::Fp12) { - #[cfg(not(target_os = "zkvm"))] - { - #[cfg(not(feature = "halo2curves"))] - panic!("`halo2curves` feature must be enabled to use pairing check hint on host"); - - #[cfg(feature = "halo2curves")] - { - let p_halo2 = P - .iter() - .map(|p| { - AffinePoint::new( - convert_bls12381_fp_to_halo2_fq(p.x.clone()), - convert_bls12381_fp_to_halo2_fq(p.y.clone()), - ) - }) - .collect::>(); - let q_halo2 = Q - .iter() - .map(|q| { - AffinePoint::new( - convert_bls12381_fp2_to_halo2_fq2(q.x.clone()), - convert_bls12381_fp2_to_halo2_fq2(q.y.clone()), - ) - }) - .collect::>(); - let fq12 = Halo2CurvesBls12_381::multi_miller_loop(&p_halo2, &q_halo2); - let (c_fq12, s_fq12) = Halo2CurvesBls12_381::final_exp_hint(&fq12); - let c = convert_bls12381_halo2_fq12_to_fp12(c_fq12); - let s = convert_bls12381_halo2_fq12_to_fp12(s_fq12); - (c, s) - } - } - #[cfg(target_os = "zkvm")] - { - let hint = MaybeUninit::<(Fp12, Fp12)>::uninit(); - // We do not rely on the slice P's memory layout since rust does not guarantee it across - // compiler versions. - let p_fat_ptr = (P.as_ptr() as u32, P.len() as u32); - let q_fat_ptr = (Q.as_ptr() as u32, Q.len() as u32); - unsafe { - custom_insn_r!( - opcode = OPCODE, - funct3 = PAIRING_FUNCT3, - funct7 = ((Bls12_381::PAIRING_IDX as u8) * PairingBaseFunct7::PAIRING_MAX_KINDS + PairingBaseFunct7::HintFinalExp as u8), - rd = Const "x0", - rs1 = In &p_fat_ptr, - rs2 = In &q_fat_ptr - ); - let ptr = hint.as_ptr() as *const u8; - hint_buffer_u32!(ptr, (48 * 12 * 2) / 4); - hint.assume_init() - } - } - } - - fn pairing_check( - P: &[AffinePoint], - Q: &[AffinePoint], - ) -> Result<(), PairingCheckError> { - Self::try_honest_pairing_check(P, Q).unwrap_or_else(|| { - let f = Self::multi_miller_loop(P, Q); - exp_check_fallback(&f, &Self::FINAL_EXPONENT) - }) - } -} - -#[allow(non_snake_case)] -impl Bls12_381 { - // The paper only describes the implementation for Bn254, so we use the gnark implementation for - // Bls12_381. Adapted from the gnark implementation: - // https://github.com/Consensys/gnark/blob/af754dd1c47a92be375930ae1abfbd134c5310d8/std/algebra/emulated/fields_bls12381/e12_pairing.go#L394C1-L395C1 - fn try_honest_pairing_check( - P: &[AffinePoint<::Fp>], - Q: &[AffinePoint<::Fp2>], - ) -> Option> { - let (c, s) = Self::pairing_check_hint(P, Q); - - // The gnark implementation checks that f * s = c^{q - x} where x is the curve seed. - // We check an equivalent condition: f * c^x * s = c^q. - // This is because we can compute f * c^x by embedding the c^x computation in the miller - // loop. - - // We compute c^q before c is consumed by conjugate() below - let c_q = FieldExtension::frobenius_map(&c, 1); - - // Since the Bls12_381 curve has a negative seed, the miller loop for Bls12_381 is computed - // as f_{Miller,x,Q}(P) = conjugate( f_{Miller,-x,Q}(P) * c^{-x} ). - // We will pass in the conjugate inverse of c into the miller loop so that we compute - // fc = conjugate( f_{Miller,-x,Q}(P) * c'^{-x} ) (where c' is the conjugate inverse of c) - // = f_{Miller,x,Q}(P) * c^x - let c_conj = c.conjugate(); - if c_conj == Fp12::ZERO { - return None; - } - let c_conj_inv = Fp12::ONE.div_unsafe(&c_conj); - let fc = Self::multi_miller_loop_embedded_exp(P, Q, Some(c_conj_inv)); - - if fc * s == c_q { - Some(Ok(())) - } else { - None - } - } -} diff --git a/extensions/pairing/guest/src/bls12_381/tests.rs b/extensions/pairing/guest/src/bls12_381/tests.rs index 9ca38d8586..04d1f23d0f 100644 --- a/extensions/pairing/guest/src/bls12_381/tests.rs +++ b/extensions/pairing/guest/src/bls12_381/tests.rs @@ -1,309 +1,4 @@ -use group::ff::Field; -use halo2curves_axiom::bls12_381::{ - Fq, Fq12, Fq2, Fq6, G1Affine, G2Affine, G2Prepared, MillerLoopResult, FROBENIUS_COEFF_FQ12_C1, -}; -use num_bigint::BigUint; -use num_traits::One; -use openvm_algebra_guest::{field::FieldExtension, IntMod}; -use openvm_ecc_guest::{weierstrass::WeierstrassPoint, AffinePoint}; -use rand::{rngs::StdRng, SeedableRng}; - -use super::{Fp, Fp12, Fp2, BLS12_381_MODULUS, BLS12_381_ORDER}; -use crate::{ - bls12_381::{ - utils::{ - convert_bls12381_fp12_to_halo2_fq12, convert_bls12381_halo2_fq12_to_fp12, - convert_bls12381_halo2_fq2_to_fp2, convert_bls12381_halo2_fq_to_fp, - convert_g2_affine_halo2_to_openvm, - }, - Bls12_381, G2Affine as OpenVmG2Affine, BLS12_381_PSEUDO_BINARY_ENCODING, - BLS12_381_SEED_ABS, - }, - pairing::{ - fp2_invert_assign, fp6_invert_assign, fp6_square_assign, FinalExp, MultiMillerLoop, - PairingCheck, PairingIntrinsics, - }, -}; - -#[test] -fn test_bls12381_frobenius_coeffs() { - #[allow(clippy::needless_range_loop)] - for i in 0..12 { - for j in 0..5 { - assert_eq!( - Bls12_381::FROBENIUS_COEFFS[i][j], - convert_bls12381_halo2_fq2_to_fp2(FROBENIUS_COEFF_FQ12_C1[i].pow([j as u64 + 1])), - "FROBENIUS_COEFFS[{}][{}] failed", - i, - j - ) - } - } -} - -#[test] -fn test_bls12381_frobenius() { - let mut rng = StdRng::seed_from_u64(15); - for pow in 0..12 { - let fq = Fq12::random(&mut rng); - let mut fq_frob = fq; - for _ in 0..pow { - fq_frob = fq_frob.frobenius_map(); - } - - let fp = convert_bls12381_halo2_fq12_to_fp12(fq); - let fp_frob = fp.frobenius_map(pow); - - assert_eq!(fp_frob, convert_bls12381_halo2_fq12_to_fp12(fq_frob)); - } -} - -#[test] -fn test_fp12_invert() { - let mut rng = StdRng::seed_from_u64(15); - let fq = Fq12::random(&mut rng); - let fq_inv = fq.invert().unwrap(); - - let fp = convert_bls12381_halo2_fq12_to_fp12(fq); - let fp_inv = fp.invert(); - assert_eq!(fp_inv, convert_bls12381_halo2_fq12_to_fp12(fq_inv)); -} - -#[test] -fn test_fp6_invert() { - let mut rng = StdRng::seed_from_u64(20); - let fq6 = Fq6 { - c0: Fq2::random(&mut rng), - c1: Fq2::random(&mut rng), - c2: Fq2::random(&mut rng), - }; - let fq6_inv = fq6.invert().unwrap(); - - let fp6c0 = convert_bls12381_halo2_fq2_to_fp2(fq6.c0); - let fp6c1 = convert_bls12381_halo2_fq2_to_fp2(fq6.c1); - let fp6c2 = convert_bls12381_halo2_fq2_to_fp2(fq6.c2); - let mut fp6 = [fp6c0, fp6c1, fp6c2]; - fp6_invert_assign::(&mut fp6, &Bls12_381::XI); - - let fq6_invc0 = convert_bls12381_halo2_fq2_to_fp2(fq6_inv.c0); - let fq6_invc1 = convert_bls12381_halo2_fq2_to_fp2(fq6_inv.c1); - let fq6_invc2 = convert_bls12381_halo2_fq2_to_fp2(fq6_inv.c2); - let fq6_inv = [fq6_invc0, fq6_invc1, fq6_invc2]; - assert_eq!(fp6, fq6_inv); -} - -#[test] -fn test_fp2_invert() { - let mut rng = StdRng::seed_from_u64(25); - let fq2 = Fq2::random(&mut rng); - let fq2_inv = fq2.invert().unwrap(); - - let mut fp2 = convert_bls12381_halo2_fq2_to_fp2(fq2).to_coeffs(); - fp2_invert_assign::(&mut fp2); - assert_eq!(fp2, convert_bls12381_halo2_fq2_to_fp2(fq2_inv).to_coeffs()); -} - -#[test] -fn test_fp6_square() { - let mut rng = StdRng::seed_from_u64(45); - let fq6 = Fq6 { - c0: Fq2::random(&mut rng), - c1: Fq2::random(&mut rng), - c2: Fq2::random(&mut rng), - }; - let fq6_sq = fq6.square(); - - let fp6c0 = convert_bls12381_halo2_fq2_to_fp2(fq6.c0); - let fp6c1 = convert_bls12381_halo2_fq2_to_fp2(fq6.c1); - let fp6c2 = convert_bls12381_halo2_fq2_to_fp2(fq6.c2); - let mut fp6 = [fp6c0, fp6c1, fp6c2]; - fp6_square_assign::(&mut fp6, &Bls12_381::XI); - - let fq6_sqc0 = convert_bls12381_halo2_fq2_to_fp2(fq6_sq.c0); - let fq6_sqc1 = convert_bls12381_halo2_fq2_to_fp2(fq6_sq.c1); - let fq6_sqc2 = convert_bls12381_halo2_fq2_to_fp2(fq6_sq.c2); - let fq6_sq = [fq6_sqc0, fq6_sqc1, fq6_sqc2]; - assert_eq!(fp6, fq6_sq); -} - -#[test] -fn test_fp2_square() { - let mut rng = StdRng::seed_from_u64(55); - let fq2 = Fq2::random(&mut rng); - let fq2_sq = fq2.square(); - - let fp2 = convert_bls12381_halo2_fq2_to_fp2(fq2); - let fp2_sq = &fp2 * &fp2; - assert_eq!(fp2_sq, convert_bls12381_halo2_fq2_to_fp2(fq2_sq)); -} - -#[test] -fn test_fp_add() { - let mut rng = StdRng::seed_from_u64(65); - let fq = Fq::random(&mut rng); - let fq_res = fq + Fq::one(); - - let fp = convert_bls12381_halo2_fq_to_fp(fq); - let fp_res = fp + Fp::ONE; - assert_eq!(fp_res, convert_bls12381_halo2_fq_to_fp(fq_res)); -} - -#[test] -fn test_fp_one() { - let fp_one = Fp::ONE; - let fq_one = Fq::ONE; - assert_eq!(fp_one, convert_bls12381_halo2_fq_to_fp(fq_one)); -} - -// Gt(Fq12) is not public -fn assert_miller_results_eq(a: MillerLoopResult, b: Fp12) { - let b = convert_bls12381_fp12_to_halo2_fq12(b); - crate::halo2curves_shims::bls12_381::tests::assert_miller_results_eq(a, b); -} - -#[test] -fn test_bls12381_miller_loop() { - let mut rng = StdRng::seed_from_u64(65); - let h2c_p = G1Affine::random(&mut rng); - let h2c_q = G2Affine::random(&mut rng); - - let p = AffinePoint { - x: convert_bls12381_halo2_fq_to_fp(h2c_p.x), - y: convert_bls12381_halo2_fq_to_fp(h2c_p.y), - }; - let q = AffinePoint { - x: convert_bls12381_halo2_fq2_to_fp2(h2c_q.x), - y: convert_bls12381_halo2_fq2_to_fp2(h2c_q.y), - }; - - // Compare against halo2curves implementation - let h2c_q_prepared = G2Prepared::from(h2c_q); - let compare_miller = - halo2curves_axiom::bls12_381::multi_miller_loop(&[(&h2c_p, &h2c_q_prepared)]); - let f = Bls12_381::multi_miller_loop(&[p], &[q]); - assert_miller_results_eq(compare_miller, f); -} - -#[test] -fn test_bls12381_miller_loop_identity() { - let mut rng = StdRng::seed_from_u64(33); - let h2c_p = G1Affine::identity(); - let h2c_q = G2Affine::random(&mut rng); - - let p = AffinePoint { - x: convert_bls12381_halo2_fq_to_fp(Fq::ZERO), - y: convert_bls12381_halo2_fq_to_fp(Fq::ZERO), - }; - let q = AffinePoint { - x: convert_bls12381_halo2_fq2_to_fp2(h2c_q.x), - y: convert_bls12381_halo2_fq2_to_fp2(h2c_q.y), - }; - - let f = Bls12_381::multi_miller_loop(&[p], &[q]); - // halo2curves implementation - let h2c_q_prepared = G2Prepared::from(h2c_q); - let compare_miller = - halo2curves_axiom::bls12_381::multi_miller_loop(&[(&h2c_p, &h2c_q_prepared)]); - assert_miller_results_eq(compare_miller, f); -} - -#[test] -fn test_bls12381_miller_loop_identity_2() { - let mut rng = StdRng::seed_from_u64(33); - let h2c_p = G1Affine::random(&mut rng); - let h2c_q = G2Affine::identity(); - let p = AffinePoint { - x: convert_bls12381_halo2_fq_to_fp(h2c_p.x), - y: convert_bls12381_halo2_fq_to_fp(h2c_p.y), - }; - let q = AffinePoint { - x: convert_bls12381_halo2_fq2_to_fp2(Fq2::ZERO), - y: convert_bls12381_halo2_fq2_to_fp2(Fq2::ZERO), - }; - - let f = Bls12_381::multi_miller_loop(&[p], &[q]); - // halo2curves implementation - let h2c_q_prepared = G2Prepared::from(h2c_q); - let compare_miller = - halo2curves_axiom::bls12_381::multi_miller_loop(&[(&h2c_p, &h2c_q_prepared)]); - assert_miller_results_eq(compare_miller, f); -} - -// test on host is enough since we are testing the curve formulas and not anything -// about intrinsic functions -#[test] -fn test_bls12381_g2_affine() { - let mut rng = StdRng::seed_from_u64(34); - for _ in 0..10 { - let p = G2Affine::random(&mut rng); - let q = G2Affine::random(&mut rng); - let expected_add = G2Affine::from(p + q); - let expected_sub = G2Affine::from(p - q); - let expected_neg = -p; - let expected_double = G2Affine::from(p + p); - let [p, q] = [p, q].map(|p| { - let x = convert_bls12381_halo2_fq2_to_fp2(p.x); - let y = convert_bls12381_halo2_fq2_to_fp2(p.y); - // check on curve - OpenVmG2Affine::from_xy(x, y).unwrap() - }); - let r_add = &p + &q; - let r_sub = &p - &q; - let r_neg = -&p; - let r_double = &p + &p; - - for (expected, actual) in [ - (expected_add, r_add), - (expected_sub, r_sub), - (expected_neg, r_neg), - (expected_double, r_double), - ] { - assert_eq!(convert_g2_affine_halo2_to_openvm(expected), actual); - } - } -} - -#[test] -fn test_bls12381_pairing_check_hint_host() { - let mut rng = StdRng::seed_from_u64(83); - let h2c_p = G1Affine::random(&mut rng); - let h2c_q = G2Affine::random(&mut rng); - - let p = AffinePoint { - x: convert_bls12381_halo2_fq_to_fp(h2c_p.x), - y: convert_bls12381_halo2_fq_to_fp(h2c_p.y), - }; - let q = AffinePoint { - x: convert_bls12381_halo2_fq2_to_fp2(h2c_q.x), - y: convert_bls12381_halo2_fq2_to_fp2(h2c_q.y), - }; - - let (c, s) = Bls12_381::pairing_check_hint(&[p], &[q]); - - let p_cmp = AffinePoint { - x: h2c_p.x, - y: h2c_p.y, - }; - let q_cmp = AffinePoint { - x: h2c_q.x, - y: h2c_q.y, - }; - - let f_cmp = - crate::halo2curves_shims::bls12_381::Bls12_381::multi_miller_loop(&[p_cmp], &[q_cmp]); - let (c_cmp, s_cmp) = crate::halo2curves_shims::bls12_381::Bls12_381::final_exp_hint(&f_cmp); - let c_cmp = convert_bls12381_halo2_fq12_to_fp12(c_cmp); - let s_cmp = convert_bls12381_halo2_fq12_to_fp12(s_cmp); - - assert_eq!(c, c_cmp); - assert_eq!(s, s_cmp); -} - -#[test] -fn test_bls12381_final_exponent() { - let final_exp = (BLS12_381_MODULUS.pow(12) - BigUint::one()) / BLS12_381_ORDER.clone(); - assert_eq!(Bls12_381::FINAL_EXPONENT.to_vec(), final_exp.to_bytes_be()); -} +use crate::bls12_381::{BLS12_381_PSEUDO_BINARY_ENCODING, BLS12_381_SEED_ABS}; #[test] fn test_bls12381_pseudo_binary_encoding() { diff --git a/extensions/pairing/guest/src/bls12_381/utils.rs b/extensions/pairing/guest/src/bls12_381/utils.rs deleted file mode 100644 index 51c749c596..0000000000 --- a/extensions/pairing/guest/src/bls12_381/utils.rs +++ /dev/null @@ -1,49 +0,0 @@ -use halo2curves_axiom::bls12_381::{Fq, Fq12, Fq2, G2Affine}; -use openvm_algebra_guest::{field::FieldExtension, IntMod}; -use openvm_ecc_guest::weierstrass::WeierstrassPoint; - -use super::{Fp, Fp12, Fp2}; -use crate::bls12_381::G2Affine as OpenVmG2Affine; - -pub(crate) fn convert_bls12381_halo2_fq_to_fp(x: Fq) -> Fp { - let bytes = x.to_bytes(); - Fp::from_le_bytes(&bytes) -} - -pub(crate) fn convert_bls12381_halo2_fq2_to_fp2(x: Fq2) -> Fp2 { - Fp2::new( - convert_bls12381_halo2_fq_to_fp(x.c0), - convert_bls12381_halo2_fq_to_fp(x.c1), - ) -} - -pub(crate) fn convert_bls12381_halo2_fq12_to_fp12(x: Fq12) -> Fp12 { - Fp12 { - c: x.to_coeffs().map(convert_bls12381_halo2_fq2_to_fp2), - } -} - -pub(crate) fn convert_bls12381_fp_to_halo2_fq(x: Fp) -> Fq { - Fq::from_bytes(&x.0).unwrap() -} - -pub(crate) fn convert_bls12381_fp2_to_halo2_fq2(x: Fp2) -> Fq2 { - Fq2 { - c0: convert_bls12381_fp_to_halo2_fq(x.c0.clone()), - c1: convert_bls12381_fp_to_halo2_fq(x.c1.clone()), - } -} - -#[allow(unused)] -pub(crate) fn convert_bls12381_fp12_to_halo2_fq12(x: Fp12) -> Fq12 { - let c = x.to_coeffs(); - Fq12::from_coeffs(c.map(convert_bls12381_fp2_to_halo2_fq2)) -} - -#[allow(unused)] -pub(crate) fn convert_g2_affine_halo2_to_openvm(p: G2Affine) -> OpenVmG2Affine { - OpenVmG2Affine::from_xy_unchecked( - convert_bls12381_halo2_fq2_to_fp2(p.x), - convert_bls12381_halo2_fq2_to_fp2(p.y), - ) -} diff --git a/extensions/pairing/guest/src/bn254/fp12.rs b/extensions/pairing/guest/src/bn254/fp12.rs deleted file mode 100644 index d8b9b07415..0000000000 --- a/extensions/pairing/guest/src/bn254/fp12.rs +++ /dev/null @@ -1,215 +0,0 @@ -use alloc::vec::Vec; -use core::ops::{Mul, MulAssign, Neg}; - -use openvm_algebra_guest::{ - field::{ComplexConjugate, FieldExtension}, - DivAssignUnsafe, DivUnsafe, Field, -}; - -use super::{Bn254, Fp, Fp2}; -use crate::pairing::{fp12_invert_assign, PairingIntrinsics, SexticExtField}; - -pub type Fp12 = SexticExtField; - -impl Fp12 { - pub fn invert(&self) -> Self { - let mut s = self.clone(); - fp12_invert_assign::(&mut s.c, &Bn254::XI); - s - } -} - -impl Field for Fp12 { - type SelfRef<'a> = &'a Self; - const ZERO: Self = Self::new([Fp2::ZERO; 6]); - const ONE: Self = Self::new([ - Fp2::ONE, - Fp2::ZERO, - Fp2::ZERO, - Fp2::ZERO, - Fp2::ZERO, - Fp2::ZERO, - ]); - - fn double_assign(&mut self) { - *self += self.clone(); - } - - fn square_assign(&mut self) { - *self *= self.clone(); - } -} - -impl FieldExtension for Fp12 { - const D: usize = 6; - type Coeffs = [Fp2; 6]; - - fn from_coeffs(coeffs: Self::Coeffs) -> Self { - Self::new(coeffs) - } - - fn from_bytes(bytes: &[u8]) -> Self { - assert_eq!(bytes.len(), 384); - Self::from_coeffs([ - Fp2::from_bytes(&bytes[0..64]), - Fp2::from_bytes(&bytes[64..128]), - Fp2::from_bytes(&bytes[128..192]), - Fp2::from_bytes(&bytes[192..256]), - Fp2::from_bytes(&bytes[256..320]), - Fp2::from_bytes(&bytes[320..384]), - ]) - } - - fn to_coeffs(self) -> Self::Coeffs { - self.c - } - - fn to_bytes(&self) -> Vec { - let mut bytes = Vec::with_capacity(384); - for coeff in self.clone().to_coeffs() { - bytes.extend_from_slice(&coeff.to_bytes()); - } - bytes - } - - fn embed(c0: Fp2) -> Self { - Self::new([c0, Fp2::ZERO, Fp2::ZERO, Fp2::ZERO, Fp2::ZERO, Fp2::ZERO]) - } - - /// We assume that the frobenius map power is < 12 - fn frobenius_map(&self, power: usize) -> Self { - if power & 1 != 0 { - let c0 = self.c[0].clone().conjugate(); - let c1 = self.c[1].clone().conjugate() * &Bn254::FROBENIUS_COEFFS[power][0]; - let c2 = self.c[2].clone().conjugate() * &Bn254::FROBENIUS_COEFFS[power][1]; - let c3 = self.c[3].clone().conjugate() * &Bn254::FROBENIUS_COEFFS[power][2]; - let c4 = self.c[4].clone().conjugate() * &Bn254::FROBENIUS_COEFFS[power][3]; - let c5 = self.c[5].clone().conjugate() * &Bn254::FROBENIUS_COEFFS[power][4]; - Self::new([c0, c1, c2, c3, c4, c5]) - } else { - let c0 = self.c[0].clone(); - let c1 = &self.c[1] * &Bn254::FROBENIUS_COEFFS[power][0]; - let c2 = &self.c[2] * &Bn254::FROBENIUS_COEFFS[power][1]; - let c3 = &self.c[3] * &Bn254::FROBENIUS_COEFFS[power][2]; - let c4 = &self.c[4] * &Bn254::FROBENIUS_COEFFS[power][3]; - let c5 = &self.c[5] * &Bn254::FROBENIUS_COEFFS[power][4]; - Self::new([c0, c1, c2, c3, c4, c5]) - } - } - - fn mul_base(&self, rhs: &Fp2) -> Self { - Self::new([ - &self.c[0] * rhs, - &self.c[1] * rhs, - &self.c[2] * rhs, - &self.c[3] * rhs, - &self.c[4] * rhs, - &self.c[5] * rhs, - ]) - } -} - -// This is ambiguous. It is conjugation for Fp12 over Fp6. -impl ComplexConjugate for Fp12 { - #[inline(always)] - fn conjugate(self) -> Self { - let [c0, c1, c2, c3, c4, c5] = self.c; - Self::new([c0, -c1, c2, -c3, c4, -c5]) - } - - fn conjugate_assign(&mut self) { - self.c[1].neg_assign(); - self.c[3].neg_assign(); - self.c[5].neg_assign(); - } -} - -impl<'a> MulAssign<&'a Fp12> for Fp12 { - #[inline(always)] - fn mul_assign(&mut self, other: &'a Fp12) { - *self = crate::pairing::sextic_tower_mul(self, other, &Bn254::XI); - } -} - -impl<'a> Mul<&'a Fp12> for &'a Fp12 { - type Output = Fp12; - #[inline(always)] - fn mul(self, other: &'a Fp12) -> Self::Output { - crate::pairing::sextic_tower_mul(self, other, &Bn254::XI) - } -} - -impl MulAssign for Fp12 { - #[inline(always)] - fn mul_assign(&mut self, other: Self) { - self.mul_assign(&other); - } -} - -impl Mul for Fp12 { - type Output = Self; - #[inline(always)] - fn mul(mut self, other: Self) -> Self::Output { - self *= other; - self - } -} - -impl<'a> Mul<&'a Fp12> for Fp12 { - type Output = Self; - #[inline(always)] - fn mul(mut self, other: &'a Fp12) -> Fp12 { - self *= other; - self - } -} - -impl<'a> DivAssignUnsafe<&'a Fp12> for Fp12 { - #[inline(always)] - fn div_assign_unsafe(&mut self, other: &'a Fp12) { - *self *= other.invert(); - } -} - -impl<'a> DivUnsafe<&'a Fp12> for &'a Fp12 { - type Output = Fp12; - #[inline(always)] - fn div_unsafe(self, other: &'a Fp12) -> Self::Output { - let mut res = self.clone(); - res.div_assign_unsafe(other); - res - } -} - -impl DivAssignUnsafe for Fp12 { - #[inline(always)] - fn div_assign_unsafe(&mut self, other: Self) { - *self *= other.invert(); - } -} - -impl DivUnsafe for Fp12 { - type Output = Self; - #[inline(always)] - fn div_unsafe(mut self, other: Self) -> Self::Output { - self.div_assign_unsafe(other); - self - } -} - -impl<'a> DivUnsafe<&'a Fp12> for Fp12 { - type Output = Self; - #[inline(always)] - fn div_unsafe(mut self, other: &'a Fp12) -> Self::Output { - self.div_assign_unsafe(other); - self - } -} - -impl Neg for Fp12 { - type Output = Fp12; - #[inline(always)] - fn neg(self) -> Self::Output { - Self::ZERO - &self - } -} diff --git a/extensions/pairing/guest/src/bn254/fp2.rs b/extensions/pairing/guest/src/bn254/fp2.rs deleted file mode 100644 index b086b7d6aa..0000000000 --- a/extensions/pairing/guest/src/bn254/fp2.rs +++ /dev/null @@ -1,76 +0,0 @@ -use alloc::vec::Vec; -use core::ops::Neg; - -use openvm_algebra_complex_macros::{complex_declare, complex_impl_field}; -use openvm_algebra_guest::{field::FieldExtension, Field, IntMod}; - -use super::Fp; - -#[cfg(not(target_os = "zkvm"))] -// Used in Fp2Extension config -pub const BN254_COMPLEX_STRUCT_NAME: &str = "Bn254Fp2"; - -// The struct name needs to be globally unique for linking purposes. -// The mod_type is a path used only in the struct definition. -complex_declare! { - Bn254Fp2 { mod_type = Fp } -} - -complex_impl_field! { - Bn254Fp2, -} - -pub type Fp2 = Bn254Fp2; - -impl FieldExtension for Fp2 { - const D: usize = 2; - type Coeffs = [Fp; 2]; - - fn from_coeffs([c0, c1]: Self::Coeffs) -> Self { - Self { c0, c1 } - } - - fn from_bytes(bytes: &[u8]) -> Self { - assert_eq!(bytes.len(), 64); - Self::from_coeffs([ - Fp::from_const_bytes(bytes[0..32].try_into().unwrap()), - Fp::from_const_bytes(bytes[32..64].try_into().unwrap()), - ]) - } - - fn to_coeffs(self) -> Self::Coeffs { - [self.c0, self.c1] - } - - fn to_bytes(&self) -> Vec { - let mut bytes = Vec::with_capacity(64); - bytes.extend_from_slice(self.c0.as_le_bytes()); - bytes.extend_from_slice(self.c1.as_le_bytes()); - bytes - } - - fn embed(c0: Fp) -> Self { - Self { - c0, - c1: ::ZERO, - } - } - - fn frobenius_map(&self, power: usize) -> Self { - if power % 2 == 0 { - self.clone() - } else { - Self { - c0: self.c0.clone(), - c1: (&self.c1).neg(), - } - } - } - - fn mul_base(&self, rhs: &Fp) -> Self { - Self { - c0: &self.c0 * rhs, - c1: &self.c1 * rhs, - } - } -} diff --git a/extensions/pairing/guest/src/bn254/mod.rs b/extensions/pairing/guest/src/bn254/mod.rs index 676a0adabd..5116e4e053 100644 --- a/extensions/pairing/guest/src/bn254/mod.rs +++ b/extensions/pairing/guest/src/bn254/mod.rs @@ -1,28 +1,8 @@ -use core::ops::{Add, Neg}; - use hex_literal::hex; #[cfg(not(target_os = "zkvm"))] use lazy_static::lazy_static; #[cfg(not(target_os = "zkvm"))] use num_bigint::BigUint; -use openvm_algebra_guest::{Field, IntMod}; -use openvm_algebra_moduli_macros::moduli_declare; -use openvm_ecc_guest::{ - weierstrass::{CachedMulTable, IntrinsicCurve}, - CyclicGroup, Group, -}; -use openvm_ecc_sw_macros::sw_declare; - -use crate::pairing::PairingIntrinsics; - -mod fp12; -mod fp2; -pub mod pairing; -#[cfg(all(feature = "halo2curves", not(target_os = "zkvm")))] -pub(crate) mod utils; - -pub use fp12::*; -pub use fp2::*; #[cfg(all(test, feature = "halo2curves", not(target_os = "zkvm")))] pub mod tests; @@ -53,692 +33,6 @@ pub const BN254_PSEUDO_BINARY_ENCODING: [i8; 66] = [ 0, 0, 1, 0, -1, 0, 1, ]; -moduli_declare! { - Bn254Fp { modulus = "21888242871839275222246405745257275088696311157297823662689037894645226208583" }, - Bn254Scalar { modulus = "21888242871839275222246405745257275088548364400416034343698204186575808495617" }, -} - -const CURVE_B: Bn254Fp = Bn254Fp::from_const_bytes(hex!( - "0300000000000000000000000000000000000000000000000000000000000000" -)); - -sw_declare! { - Bn254G1Affine { mod_type = Bn254Fp, b = CURVE_B }, -} - #[cfg(not(target_os = "zkvm"))] // Used in WeierstrassExtension config pub const BN254_ECC_STRUCT_NAME: &str = "Bn254G1Affine"; - -pub type Fp = Bn254Fp; -pub type Scalar = Bn254Scalar; -pub type G1Affine = Bn254G1Affine; -pub use g2::G2Affine; - -impl Field for Fp { - type SelfRef<'a> = &'a Self; - const ZERO: Self = ::ZERO; - const ONE: Self = ::ONE; - - fn double_assign(&mut self) { - IntMod::double_assign(self); - } - - fn square_assign(&mut self) { - IntMod::square_assign(self); - } -} - -impl Field for Scalar { - type SelfRef<'a> = &'a Self; - const ZERO: Self = ::ZERO; - const ONE: Self = ::ONE; - - fn double_assign(&mut self) { - IntMod::double_assign(self); - } - - fn square_assign(&mut self) { - IntMod::square_assign(self); - } -} - -impl CyclicGroup for G1Affine { - // https://eips.ethereum.org/EIPS/eip-197 - const GENERATOR: Self = G1Affine { - x: Bn254Fp::from_const_u8(1), - y: Bn254Fp::from_const_u8(2), - }; - const NEG_GENERATOR: Self = G1Affine { - x: Bn254Fp::from_const_u8(1), - y: Bn254Fp::from_const_bytes(hex!( - "45FD7CD8168C203C8DCA7168916A81975D588181B64550B829A031E1724E6430" - )), - }; -} - -// Define a G2Affine struct that implements curve operations using `Fp2` intrinsics -// but not special E(Fp2) intrinsics. -mod g2 { - use hex_literal::hex; - use openvm_algebra_guest::Field; - use openvm_ecc_guest::{ - impl_sw_affine, impl_sw_group_ops, weierstrass::WeierstrassPoint, AffinePoint, Group, - }; - - use super::{Fp, Fp2}; - - const THREE: Fp2 = Fp2::new(Fp::from_const_u8(3), Fp::ZERO); - // 3 / (9 + u) - // validated by a test below - const B: Fp2 = Fp2::new( - Fp::from_const_bytes(hex!( - "e538a124dce66732a3efdb59e5c5b4b5c36ae01b9918be81aeaab8ce409d142b" - )), - Fp::from_const_bytes(hex!( - "d215c38506bda2e452182de584a04fa7f4fdd8eeadaf2ccdd4fef03ab0139700" - )), - ); - impl_sw_affine!(G2Affine, Fp2, THREE, B); - impl_sw_group_ops!(G2Affine, Fp2); - - #[test] - fn test_g2_curve_equation_b() { - use openvm_algebra_guest::DivUnsafe; - let b = Fp2::new(Fp::from_const_u8(3), Fp::ZERO) - .div_unsafe(Fp2::new(Fp::from_const_u8(9), Fp::ONE)); - assert_eq!(b, B); - } -} - -pub struct Bn254; - -impl Bn254 { - // Same as the values from halo2curves_shims - // Validated by a test in tests.rs - pub const FROBENIUS_COEFF_FQ6_C1: [Fp2; 3] = [ - Fp2 { - c0: Bn254Fp(hex!( - "0100000000000000000000000000000000000000000000000000000000000000" - )), - c1: Bn254Fp(hex!( - "0000000000000000000000000000000000000000000000000000000000000000" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "3d556f175795e3990c33c3c210c38cb743b159f53cec0b4cf711794f9847b32f" - )), - c1: Bn254Fp(hex!( - "a2cb0f641cd56516ce9d7c0b1d2aae3294075ad78bcca44b20aeeb6150e5c916" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "48fd7c60e544bde43d6e96bb9f068fc2b0ccace0e7d96d5e29a031e1724e6430" - )), - c1: Bn254Fp(hex!( - "0000000000000000000000000000000000000000000000000000000000000000" - )), - }, - ]; - - // Same as the values from halo2curves_shims - // Validated by a test in tests.rs - pub const XI_TO_Q_MINUS_1_OVER_2: Fp2 = Fp2 { - c0: Bn254Fp(hex!( - "5a13a071460154dc9859c9a9ede0aadbb9f9e2b698c65edcdcf59a4805f33c06" - )), - c1: Bn254Fp(hex!( - "e3b02326637fd382d25ba28fc97d80212b6f79eca7b504079a0441acbc3cc007" - )), - }; - - // FINAL_EXPONENT = (p^12 - 1) / r in big-endian - // Validated by a test in test.rs - pub const FINAL_EXPONENT: [u8; 349] = hex!( - "2f4b6dc97020fddadf107d20bc842d43bf6369b1ff6a1c71015f3f7be2e1e30a73bb94fec0daf15466b2383a5d3ec3d15ad524d8f70c54efee1bd8c3b21377e563a09a1b705887e72eceaddea3790364a61f676baaf977870e88d5c6c8fef0781361e443ae77f5b63a2a2264487f2940a8b1ddb3d15062cd0fb2015dfc6668449aed3cc48a82d0d602d268c7daab6a41294c0cc4ebe5664568dfc50e1648a45a4a1e3a5195846a3ed011a337a02088ec80e0ebae8755cfe107acf3aafb40494e406f804216bb10cf430b0f37856b42db8dc5514724ee93dfb10826f0dd4a0364b9580291d2cd65664814fde37ca80bb4ea44eacc5e641bbadf423f9a2cbf813b8d145da90029baee7ddadda71c7f3811c4105262945bba1668c3be69a3c230974d83561841d766f9c9d570bb7fbe04c7e8a6c3c760c0de81def35692da361102b6b9b2b918837fa97896e84abb40a4efb7e54523a486964b64ca86f120" - ); -} - -impl IntrinsicCurve for Bn254 { - type Scalar = Scalar; - type Point = G1Affine; - - fn msm(coeffs: &[Self::Scalar], bases: &[Self::Point]) -> Self::Point - where - for<'a> &'a Self::Point: Add<&'a Self::Point, Output = Self::Point>, - { - // heuristic - if coeffs.len() < 25 { - // BN254(Fp) is of prime order by Weil conjecture: - // - let table = CachedMulTable::::new_with_prime_order(bases, 4); - table.windowed_mul(coeffs) - } else { - openvm_ecc_guest::msm(coeffs, bases) - } - } -} - -impl PairingIntrinsics for Bn254 { - type Fp = Fp; - type Fp2 = Fp2; - type Fp12 = Fp12; - - const PAIRING_IDX: usize = 0; - // The sextic extension `Fp12` is `Fp2[X] / (X^6 - \xi)`, where `\xi` is a non-residue. - const XI: Fp2 = Fp2::new(Fp::from_const_u8(9), Fp::from_const_u8(1)); - const FP2_TWO: Fp2 = Fp2::new(Fp::from_const_u8(2), Fp::from_const_u8(0)); - const FP2_THREE: Fp2 = Fp2::new(Fp::from_const_u8(3), Fp::from_const_u8(0)); - // Multiplication constants for the Frobenius map for coefficients in Fp2 c1..=c5 for powers - // 0..12 FROBENIUS_COEFFS\[i\]\[j\] = \xi^{(j + 1) * (p^i - 1)/6} when p = 1 (mod 6) - // These are validated against `halo2curves::bn256::FROBENIUS_COEFF_FQ12_C1` in tests.rs - // (Note that bn256 here is another name for bn254) - const FROBENIUS_COEFFS: [[Self::Fp2; 5]; 12] = [ - [ - Fp2 { - c0: Bn254Fp(hex!( - "0100000000000000000000000000000000000000000000000000000000000000" - )), - c1: Bn254Fp(hex!( - "0000000000000000000000000000000000000000000000000000000000000000" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "0100000000000000000000000000000000000000000000000000000000000000" - )), - c1: Bn254Fp(hex!( - "0000000000000000000000000000000000000000000000000000000000000000" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "0100000000000000000000000000000000000000000000000000000000000000" - )), - c1: Bn254Fp(hex!( - "0000000000000000000000000000000000000000000000000000000000000000" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "0100000000000000000000000000000000000000000000000000000000000000" - )), - c1: Bn254Fp(hex!( - "0000000000000000000000000000000000000000000000000000000000000000" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "0100000000000000000000000000000000000000000000000000000000000000" - )), - c1: Bn254Fp(hex!( - "0000000000000000000000000000000000000000000000000000000000000000" - )), - }, - ], - [ - Fp2 { - c0: Bn254Fp(hex!( - "70e4c9dcda350bd676212f29081e525c608be676dd9fb9e8dfa765281cb78412" - )), - c1: Bn254Fp(hex!( - "ac62f3805ff05ccae5c7ee8e779279748e0b1512fe7c32a6e6e7fab4f3966924" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "3d556f175795e3990c33c3c210c38cb743b159f53cec0b4cf711794f9847b32f" - )), - c1: Bn254Fp(hex!( - "a2cb0f641cd56516ce9d7c0b1d2aae3294075ad78bcca44b20aeeb6150e5c916" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "5a13a071460154dc9859c9a9ede0aadbb9f9e2b698c65edcdcf59a4805f33c06" - )), - c1: Bn254Fp(hex!( - "e3b02326637fd382d25ba28fc97d80212b6f79eca7b504079a0441acbc3cc007" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "62a71e92551f8a8472ec94bef76533d3841e185ab7c0f38001a8ee645e4fb505" - )), - c1: Bn254Fp(hex!( - "26812bcd11473bc163c7de1bead28536921c0b3bb0803a9fee8afde7db5e142c" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "2f69b7ea10c8a22ed31baa559b455c42f43f35a461363ae94986794fe7c18301" - )), - c1: Bn254Fp(hex!( - "4b2c0c6eeeb8c624c02a8e6799cb80b07d9f72c746b27fa27506fd76caf2ac12" - )), - }, - ], - [ - Fp2 { - c0: Bn254Fp(hex!( - "49fd7c60e544bde43d6e96bb9f068fc2b0ccace0e7d96d5e29a031e1724e6430" - )), - c1: Bn254Fp(hex!( - "0000000000000000000000000000000000000000000000000000000000000000" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "48fd7c60e544bde43d6e96bb9f068fc2b0ccace0e7d96d5e29a031e1724e6430" - )), - c1: Bn254Fp(hex!( - "0000000000000000000000000000000000000000000000000000000000000000" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "46fd7cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e6430" - )), - c1: Bn254Fp(hex!( - "0000000000000000000000000000000000000000000000000000000000000000" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "feffff77314763574f5cdbacf163f2d4ac8bd4a0ce6be2590000000000000000" - )), - c1: Bn254Fp(hex!( - "0000000000000000000000000000000000000000000000000000000000000000" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "ffffff77314763574f5cdbacf163f2d4ac8bd4a0ce6be2590000000000000000" - )), - c1: Bn254Fp(hex!( - "0000000000000000000000000000000000000000000000000000000000000000" - )), - }, - ], - [ - Fp2 { - c0: Bn254Fp(hex!( - "7fa6d41e397d6fe84ad255be8db34c8990aaacd08c60e9efbbe482cccf81dc19" - )), - c1: Bn254Fp(hex!( - "01c1c0f42baa9476ec39d497e3a5037f9d137635e3eecb06737de70bb6f8ab00" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "6dfbdc7be86e747bd342695d3dfd5f80ac259f95771cffba0aef55b778e05608" - )), - c1: Bn254Fp(hex!( - "de86a5aa2bab0c383126ff98bf31df0f4f0926ec6d0ef3a96f76d1b341def104" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "ede9dc66d08acc5ff470a8bea389d6bba35e9eca1d7ff1db4caa96986d5b272a" - )), - c1: Bn254Fp(hex!( - "644c59b2b30c4db9ba6ecfd8c7ec007632e907950e904bb18f9bf034b611a428" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "66f0cb3cbc921a0ecb6bb075450933e64e44b2b5f7e0be19ab8dc011668cc50b" - )), - c1: Bn254Fp(hex!( - "9f230c739dede35fe5967f73089e4aa4041dd20ceff6b0fe120a91e199e9d523" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "431b26767084deeba5847c969880d62e693f4d3bfa99167105092c954490c413" - )), - c1: Bn254Fp(hex!( - "992428841304251f21800220eada2d3e3d63482a28b2b19f0bddb1596a36db16" - )), - }, - ], - [ - Fp2 { - c0: Bn254Fp(hex!( - "48fd7c60e544bde43d6e96bb9f068fc2b0ccace0e7d96d5e29a031e1724e6430" - )), - c1: Bn254Fp(hex!( - "0000000000000000000000000000000000000000000000000000000000000000" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "feffff77314763574f5cdbacf163f2d4ac8bd4a0ce6be2590000000000000000" - )), - c1: Bn254Fp(hex!( - "0000000000000000000000000000000000000000000000000000000000000000" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "0100000000000000000000000000000000000000000000000000000000000000" - )), - c1: Bn254Fp(hex!( - "0000000000000000000000000000000000000000000000000000000000000000" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "48fd7c60e544bde43d6e96bb9f068fc2b0ccace0e7d96d5e29a031e1724e6430" - )), - c1: Bn254Fp(hex!( - "0000000000000000000000000000000000000000000000000000000000000000" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "feffff77314763574f5cdbacf163f2d4ac8bd4a0ce6be2590000000000000000" - )), - c1: Bn254Fp(hex!( - "0000000000000000000000000000000000000000000000000000000000000000" - )), - }, - ], - [ - Fp2 { - c0: Bn254Fp(hex!( - "0fc20a425e476412d4b026958595fa2c301fc659afc02f07dc3c1da4b3ca5707" - )), - c1: Bn254Fp(hex!( - "9c5b4a4ce34558e8933c5771fd7d0ba26c60e2a49bb7e918b6351e3835b0a60c" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "e4a9ad1dee13e9623a1fb7b0d41416f7cad90978b8829569513f94bbd474be28" - )), - c1: Bn254Fp(hex!( - "c7aac7c9ce0baeed8d06f6c3b40ef4547a4701bebc6ab8c2997b74cbe08aa814" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "5a13a071460154dc9859c9a9ede0aadbb9f9e2b698c65edcdcf59a4805f33c06" - )), - c1: Bn254Fp(hex!( - "e3b02326637fd382d25ba28fc97d80212b6f79eca7b504079a0441acbc3cc007" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "7f65920905da7ba94f722c3454fb1ade89f5b67107a49d1d7d6a826aae72e91e" - )), - c1: Bn254Fp(hex!( - "c955c2707ee32157d136854130643254247725bbcd13b5d251abd4f86f54de10" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "14b26e8b5fbc3bbdd268d240fd3a7aec74ff17979863dc87bb82b2455dce4012" - )), - c1: Bn254Fp(hex!( - "4ef81b16254b5efa605574b8500fad8dbfc3d562e1ff31fd95d6b4e29f432e04" - )), - }, - ], - [ - Fp2 { - c0: Bn254Fp(hex!( - "46fd7cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e6430" - )), - c1: Bn254Fp(hex!( - "0000000000000000000000000000000000000000000000000000000000000000" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "0100000000000000000000000000000000000000000000000000000000000000" - )), - c1: Bn254Fp(hex!( - "0000000000000000000000000000000000000000000000000000000000000000" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "46fd7cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e6430" - )), - c1: Bn254Fp(hex!( - "0000000000000000000000000000000000000000000000000000000000000000" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "0100000000000000000000000000000000000000000000000000000000000000" - )), - c1: Bn254Fp(hex!( - "0000000000000000000000000000000000000000000000000000000000000000" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "46fd7cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e6430" - )), - c1: Bn254Fp(hex!( - "0000000000000000000000000000000000000000000000000000000000000000" - )), - }, - ], - [ - Fp2 { - c0: Bn254Fp(hex!( - "d718b3fb3b56156616a9423f894c2f3bfdcc9a0ad9a596cf49f8cbb85697df1d" - )), - c1: Bn254Fp(hex!( - "9b9a8957b79bc371a70283d919d80723cf4c6c6fb8c81d1243b8362c7fb7fa0b" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "3d556f175795e3990c33c3c210c38cb743b159f53cec0b4cf711794f9847b32f" - )), - c1: Bn254Fp(hex!( - "a2cb0f641cd56516ce9d7c0b1d2aae3294075ad78bcca44b20aeeb6150e5c916" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "ede9dc66d08acc5ff470a8bea389d6bba35e9eca1d7ff1db4caa96986d5b272a" - )), - c1: Bn254Fp(hex!( - "644c59b2b30c4db9ba6ecfd8c7ec007632e907950e904bb18f9bf034b611a428" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "62a71e92551f8a8472ec94bef76533d3841e185ab7c0f38001a8ee645e4fb505" - )), - c1: Bn254Fp(hex!( - "26812bcd11473bc163c7de1bead28536921c0b3bb0803a9fee8afde7db5e142c" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "1894c5ed05c47d0dbaaec712f624255569184cdd540f16cfdf19b8918b8ce02e" - )), - c1: Bn254Fp(hex!( - "fcd0706a28d35917cd9fe300f89e00e7dfb80eba6f93d015b499346aa85bb71d" - )), - }, - ], - [ - Fp2 { - c0: Bn254Fp(hex!( - "feffff77314763574f5cdbacf163f2d4ac8bd4a0ce6be2590000000000000000" - )), - c1: Bn254Fp(hex!( - "0000000000000000000000000000000000000000000000000000000000000000" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "48fd7c60e544bde43d6e96bb9f068fc2b0ccace0e7d96d5e29a031e1724e6430" - )), - c1: Bn254Fp(hex!( - "0000000000000000000000000000000000000000000000000000000000000000" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "0100000000000000000000000000000000000000000000000000000000000000" - )), - c1: Bn254Fp(hex!( - "0000000000000000000000000000000000000000000000000000000000000000" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "feffff77314763574f5cdbacf163f2d4ac8bd4a0ce6be2590000000000000000" - )), - c1: Bn254Fp(hex!( - "0000000000000000000000000000000000000000000000000000000000000000" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "48fd7c60e544bde43d6e96bb9f068fc2b0ccace0e7d96d5e29a031e1724e6430" - )), - c1: Bn254Fp(hex!( - "0000000000000000000000000000000000000000000000000000000000000000" - )), - }, - ], - [ - Fp2 { - c0: Bn254Fp(hex!( - "c856a8b9dd0eb15342f81baa03b7340ecdadd4b029e566c86dbbae14a3cc8716" - )), - c1: Bn254Fp(hex!( - "463cbce3eae18bc5a0909dd0adc47d18c0440b4cd35684b1b6224ad5bc55b82f" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "6dfbdc7be86e747bd342695d3dfd5f80ac259f95771cffba0aef55b778e05608" - )), - c1: Bn254Fp(hex!( - "de86a5aa2bab0c383126ff98bf31df0f4f0926ec6d0ef3a96f76d1b341def104" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "5a13a071460154dc9859c9a9ede0aadbb9f9e2b698c65edcdcf59a4805f33c06" - )), - c1: Bn254Fp(hex!( - "e3b02326637fd382d25ba28fc97d80212b6f79eca7b504079a0441acbc3cc007" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "66f0cb3cbc921a0ecb6bb075450933e64e44b2b5f7e0be19ab8dc011668cc50b" - )), - c1: Bn254Fp(hex!( - "9f230c739dede35fe5967f73089e4aa4041dd20ceff6b0fe120a91e199e9d523" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "04e25662a6074250e745f5d1f8e9aa68f4183446bcab39472497054c2ebe9f1c" - )), - c1: Bn254Fp(hex!( - "aed854540388fb1c6c4a6f48a78f535920f538578e939e181ec37f8708188919" - )), - }, - ], - [ - Fp2 { - c0: Bn254Fp(hex!( - "ffffff77314763574f5cdbacf163f2d4ac8bd4a0ce6be2590000000000000000" - )), - c1: Bn254Fp(hex!( - "0000000000000000000000000000000000000000000000000000000000000000" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "feffff77314763574f5cdbacf163f2d4ac8bd4a0ce6be2590000000000000000" - )), - c1: Bn254Fp(hex!( - "0000000000000000000000000000000000000000000000000000000000000000" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "46fd7cd8168c203c8dca7168916a81975d588181b64550b829a031e1724e6430" - )), - c1: Bn254Fp(hex!( - "0000000000000000000000000000000000000000000000000000000000000000" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "48fd7c60e544bde43d6e96bb9f068fc2b0ccace0e7d96d5e29a031e1724e6430" - )), - c1: Bn254Fp(hex!( - "0000000000000000000000000000000000000000000000000000000000000000" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "49fd7c60e544bde43d6e96bb9f068fc2b0ccace0e7d96d5e29a031e1724e6430" - )), - c1: Bn254Fp(hex!( - "0000000000000000000000000000000000000000000000000000000000000000" - )), - }, - ], - [ - Fp2 { - c0: Bn254Fp(hex!( - "383b7296b844bc29b9194bd30bd5866a2d39bb27078520b14d63143dbf830c29" - )), - c1: Bn254Fp(hex!( - "aba1328c3346c853f98d1af793ec75f5f0f79edc1a8e669f736a13a93d9ebd23" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "e4a9ad1dee13e9623a1fb7b0d41416f7cad90978b8829569513f94bbd474be28" - )), - c1: Bn254Fp(hex!( - "c7aac7c9ce0baeed8d06f6c3b40ef4547a4701bebc6ab8c2997b74cbe08aa814" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "ede9dc66d08acc5ff470a8bea389d6bba35e9eca1d7ff1db4caa96986d5b272a" - )), - c1: Bn254Fp(hex!( - "644c59b2b30c4db9ba6ecfd8c7ec007632e907950e904bb18f9bf034b611a428" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "7f65920905da7ba94f722c3454fb1ade89f5b67107a49d1d7d6a826aae72e91e" - )), - c1: Bn254Fp(hex!( - "c955c2707ee32157d136854130643254247725bbcd13b5d251abd4f86f54de10" - )), - }, - Fp2 { - c0: Bn254Fp(hex!( - "334b0e4db7cfe47eba619f27942f07abe85869ea1de273306e1d7f9b1580231e" - )), - c1: Bn254Fp(hex!( - "f90461c2f140c2412c75fdaf405bd4099e94ab1ed5451ebb93c97cfed20a362c" - )), - }, - ], - ]; -} diff --git a/extensions/pairing/guest/src/bn254/pairing.rs b/extensions/pairing/guest/src/bn254/pairing.rs deleted file mode 100644 index 25a0d6b7fe..0000000000 --- a/extensions/pairing/guest/src/bn254/pairing.rs +++ /dev/null @@ -1,379 +0,0 @@ -use alloc::vec::Vec; - -use itertools::izip; -use openvm_algebra_guest::{field::FieldExtension, DivUnsafe, Field}; -use openvm_ecc_guest::AffinePoint; -#[cfg(target_os = "zkvm")] -use { - crate::{PairingBaseFunct7, OPCODE, PAIRING_FUNCT3}, - core::mem::MaybeUninit, - openvm_platform::custom_insn_r, - openvm_rv32im_guest::hint_buffer_u32, -}; - -use super::{Bn254, Fp, Fp12, Fp2, BN254_PSEUDO_BINARY_ENCODING, BN254_SEED}; -use crate::pairing::{ - exp_check_fallback, Evaluatable, EvaluatedLine, FromLineDType, LineMulDType, MillerStep, - MultiMillerLoop, PairingCheck, PairingCheckError, PairingIntrinsics, UnevaluatedLine, -}; -#[cfg(all(feature = "halo2curves", not(target_os = "zkvm")))] -use crate::{ - bn254::utils::{ - convert_bn254_fp2_to_halo2_fq2, convert_bn254_fp_to_halo2_fq, - convert_bn254_halo2_fq12_to_fp12, - }, - halo2curves_shims::bn254::Bn254 as Halo2CurvesBn254, - pairing::FinalExp, -}; - -impl Evaluatable for UnevaluatedLine { - fn evaluate(&self, xy_frac: &(Fp, Fp)) -> EvaluatedLine { - let (x_over_y, y_inv) = xy_frac; - // Represents the line L(x,y) = 1 + b (x/y) w^1 + c (1/y) w^3 - EvaluatedLine { - b: self.b.mul_base(x_over_y), - c: self.c.mul_base(y_inv), - } - } -} - -impl FromLineDType for Fp12 { - fn from_evaluated_line_d_type(line: EvaluatedLine) -> Fp12 { - FieldExtension::::from_coeffs([ - Fp2::ONE, - line.b, - Fp2::ZERO, - line.c, - Fp2::ZERO, - Fp2::ZERO, - ]) - } -} - -// TODO[jpw]: make this into a macro depending on P::PAIRING_IDX when we have more curves -impl LineMulDType for Bn254 { - /// Multiplies two lines in 013-form to get an element in 01234-form - fn mul_013_by_013(l0: &EvaluatedLine, l1: &EvaluatedLine) -> [Fp2; 5] { - let b0 = &l0.b; - let c0 = &l0.c; - let b1 = &l1.b; - let c1 = &l1.c; - - // where w⁶ = xi - // l0 * l1 = 1 + (b0 + b1)w + (b0b1)w² + (c0 + c1)w³ + (b0c1 + b1c0)w⁴ + (c0c1)w⁶ - // = (1 + c0c1 * xi) + (b0 + b1)w + (b0b1)w² + (c0 + c1)w³ + (b0c1 + b1c0)w⁴ - let x0 = Fp2::ONE + c0 * c1 * &Bn254::XI; - let x1 = b0 + b1; - let x2 = b0 * b1; - let x3 = c0 + c1; - let x4 = b0 * c1 + b1 * c0; - - [x0, x1, x2, x3, x4] - } - - /// Multiplies a line in 013-form with a Fp12 element to get an Fp12 element - fn mul_by_013(f: &Fp12, l: &EvaluatedLine) -> Fp12 { - Fp12::from_evaluated_line_d_type(l.clone()) * f - } - - /// Multiplies a line in 01234-form with a Fp12 element to get an Fp12 element - fn mul_by_01234(f: &Fp12, x: &[Fp2; 5]) -> Fp12 { - // we update the order of the coefficients to match the Fp12 coefficient ordering: - // Fp12 { - // c0: Fp6 { - // c0: x0, - // c1: x2, - // c2: x4, - // }, - // c1: Fp6 { - // c0: x1, - // c1: x3, - // c2: x5, - // }, - // } - let o0 = &x[0]; - let o1 = &x[2]; - let o2 = &x[4]; - let o3 = &x[1]; - let o4 = &x[3]; - - let xi = &Bn254::XI; - - let self_coeffs = &f.c; - let s0 = &self_coeffs[0]; - let s1 = &self_coeffs[2]; - let s2 = &self_coeffs[4]; - let s3 = &self_coeffs[1]; - let s4 = &self_coeffs[3]; - let s5 = &self_coeffs[5]; - - // NOTE[yj]: Hand-calculated multiplication for Fp12 * 01234 ∈ Fp2; this is likely not the - // most efficient implementation c00 = cs0co0 + xi(cs1co2 + cs2co1 + cs4co4 + - // cs5co3) c01 = cs0co1 + cs1co0 + cs3co3 + xi(cs2co2 + cs5co4) - // c02 = cs0co2 + cs1co1 + cs2co0 + cs3co4 + cs4co3 - // c10 = cs0co3 + cs3co0 + xi(cs2co4 + cs4co2 + cs5co1) - // c11 = cs0co4 + cs1co3 + cs3co1 + cs4co0 + xi(cs5co2) - // c12 = cs1co4 + cs2co3 + cs3co2 + cs4co1 + cs5co0 - let c00 = s0 * o0 + xi * &(s1 * o2 + s2 * o1 + s4 * o4 + s5 * o3); - let c01 = s0 * o1 + s1 * o0 + s3 * o3 + xi * &(s2 * o2 + s5 * o4); - let c02 = s0 * o2 + s1 * o1 + s2 * o0 + s3 * o4 + s4 * o3; - let c10 = s0 * o3 + s3 * o0 + xi * &(s2 * o4 + s4 * o2 + s5 * o1); - let c11 = s0 * o4 + s1 * o3 + s3 * o1 + s4 * o0 + xi * &(s5 * o2); - let c12 = s1 * o4 + s2 * o3 + s3 * o2 + s4 * o1 + s5 * o0; - - Fp12::from_coeffs([c00, c10, c01, c11, c02, c12]) - } -} - -#[allow(non_snake_case)] -impl MultiMillerLoop for Bn254 { - type Fp = Fp; - type Fp12 = Fp12; - - const SEED_ABS: u64 = BN254_SEED; - const PSEUDO_BINARY_ENCODING: &[i8] = &BN254_PSEUDO_BINARY_ENCODING; - - fn evaluate_lines_vec(f: Self::Fp12, lines: Vec>) -> Self::Fp12 { - let mut f = f; - let mut lines = lines; - if lines.len() % 2 == 1 { - f = Self::mul_by_013(&f, &lines.pop().unwrap()); - } - for chunk in lines.chunks(2) { - if let [line0, line1] = chunk { - let prod = Self::mul_013_by_013(line0, line1); - f = Self::mul_by_01234(&f, &prod); - } else { - panic!("lines.len() % 2 should be 0 at this point"); - } - } - f - } - - /// The expected output of this function when running the Miller loop with embedded exponent is - /// c^2 * l_{2Q} - fn pre_loop( - Q_acc: Vec>, - _Q: &[AffinePoint], - c: Option, - xy_fracs: &[(Self::Fp, Self::Fp)], - ) -> (Self::Fp12, Vec>) { - let mut f = if let Some(mut c) = c { - // for the miller loop with embedded exponent, f will be set to c at the beginning of - // the function, and we will square c due to the last two values of the - // pseudo-binary encoding (BN254_PSEUDO_BINARY_ENCODING) being 0 and 1. - // Therefore, the final value of f at the end of this block is c^2. - c.square_assign(); - c - } else { - Self::Fp12::ONE - }; - - let mut Q_acc = Q_acc; - let mut initial_lines = Vec::>::new(); - - // We don't need to special case the first iteration for Bn254, but since we are using the - // same Miller loop implementation for both Bn254 and Bls12_381, we need to do the - // first iteration separately here. - let (Q_out_double, lines_2S) = Q_acc - .into_iter() - .map(|Q| Self::miller_double_step(&Q)) - .unzip::<_, _, Vec<_>, Vec<_>>(); - Q_acc = Q_out_double; - - let lines_iter = izip!(lines_2S.iter(), xy_fracs.iter()); - for (line_2S, xy_frac) in lines_iter { - let line = line_2S.evaluate(xy_frac); - initial_lines.push(line); - } - - f = Self::evaluate_lines_vec(f, initial_lines); - - (f, Q_acc) - } - - /// Compute f_{Miller,Q}(P) from f_{6x+2,Q}(P) - fn post_loop( - f: &Self::Fp12, - Q_acc: Vec>, // at this point, Q_acc = (6x+2)Q - Q: &[AffinePoint], - _c: Option, - xy_fracs: &[(Self::Fp, Self::Fp)], - ) -> (Self::Fp12, Vec>) { - let mut Q_acc = Q_acc; - let mut lines = Vec::>::new(); - - let x_to_q_minus_1_over_3 = &Self::FROBENIUS_COEFF_FQ6_C1[1]; - let x_to_q_sq_minus_1_over_3 = &Self::FROBENIUS_COEFF_FQ6_C1[2]; - - // For each q, compute q1 such that `frob_p(twist(q)) = twist(q1)` - let q1_vec = Q - .iter() - .map(|Q| { - let x = Q.x.frobenius_map(1); - let x = x * x_to_q_minus_1_over_3; - let y = Q.y.frobenius_map(1); - let y = y * &Self::XI_TO_Q_MINUS_1_OVER_2; - AffinePoint { x, y } - }) - .collect::>(); - - // compute l_{(6x+2)\Psi(Q), \phi_p(\Psi(Q))} where \phi_p is the Frobenius map - let (Q_out_add, lines_S_plus_Q) = Q_acc - .iter() - .zip(q1_vec.iter()) - .map(|(Q_acc, q1)| Self::miller_add_step(Q_acc, q1)) - .unzip::<_, _, Vec<_>, Vec<_>>(); - Q_acc = Q_out_add; - - let lines_iter = izip!(lines_S_plus_Q.iter(), xy_fracs.iter()); - for (lines_S_plus_Q, xy_frac) in lines_iter { - let line = lines_S_plus_Q.evaluate(xy_frac); - lines.push(line); - } - - // For each q, compute q2 such that `-frob_p^2(twist(q)) = twist(q2)` - let q2_vec = Q - .iter() - .map(|Q| { - // There is a frobenius mapping π²(Q) that we skip here since it is equivalent to - // the identity mapping - let x = &Q.x * x_to_q_sq_minus_1_over_3; - AffinePoint { x, y: Q.y.clone() } - }) - .collect::>(); - - // compute l_{(6x+2)\Psi(Q) + \phi_p(\Psi(Q)), -(\phi_p)^2(\Psi(Q))} where \phi_p is the - // Frobenius map - let (Q_out_add, lines_S_plus_Q) = Q_acc - .iter() - .zip(q2_vec.iter()) - .map(|(Q_acc, q2)| Self::miller_add_step(Q_acc, q2)) - .unzip::<_, _, Vec<_>, Vec<_>>(); - Q_acc = Q_out_add; - - let lines_iter = izip!(lines_S_plus_Q.iter(), xy_fracs.iter()); - for (lines_S_plus_Q, xy_frac) in lines_iter { - let line = lines_S_plus_Q.evaluate(xy_frac); - lines.push(line); - } - - let mut f = f.clone(); - f = Self::evaluate_lines_vec(f, lines); - - (f, Q_acc) - } -} - -#[allow(non_snake_case)] -impl PairingCheck for Bn254 { - type Fp = Fp; - type Fp2 = Fp2; - type Fp12 = Fp12; - - #[allow(unused_variables)] - fn pairing_check_hint( - P: &[AffinePoint], - Q: &[AffinePoint], - ) -> (Self::Fp12, Self::Fp12) { - #[cfg(not(target_os = "zkvm"))] - { - #[cfg(not(feature = "halo2curves"))] - panic!("`halo2curves` feature must be enabled to use pairing check hint on host"); - - #[cfg(feature = "halo2curves")] - { - let p_halo2 = P - .iter() - .map(|p| { - AffinePoint::new( - convert_bn254_fp_to_halo2_fq(p.x.clone()), - convert_bn254_fp_to_halo2_fq(p.y.clone()), - ) - }) - .collect::>(); - let q_halo2 = Q - .iter() - .map(|q| { - AffinePoint::new( - convert_bn254_fp2_to_halo2_fq2(q.x.clone()), - convert_bn254_fp2_to_halo2_fq2(q.y.clone()), - ) - }) - .collect::>(); - let fq12 = Halo2CurvesBn254::multi_miller_loop(&p_halo2, &q_halo2); - let (c_fq12, s_fq12) = Halo2CurvesBn254::final_exp_hint(&fq12); - let c = convert_bn254_halo2_fq12_to_fp12(c_fq12); - let s = convert_bn254_halo2_fq12_to_fp12(s_fq12); - (c, s) - } - } - #[cfg(target_os = "zkvm")] - { - let hint = MaybeUninit::<(Fp12, Fp12)>::uninit(); - // We do not rely on the slice P's memory layout since rust does not guarantee it across - // compiler versions. - let p_fat_ptr = (P.as_ptr() as u32, P.len() as u32); - let q_fat_ptr = (Q.as_ptr() as u32, Q.len() as u32); - unsafe { - custom_insn_r!( - opcode = OPCODE, - funct3 = PAIRING_FUNCT3, - funct7 = ((Bn254::PAIRING_IDX as u8) * PairingBaseFunct7::PAIRING_MAX_KINDS + PairingBaseFunct7::HintFinalExp as u8), - rd = Const "x0", - rs1 = In &p_fat_ptr, - rs2 = In &q_fat_ptr - ); - let ptr = hint.as_ptr() as *const u8; - hint_buffer_u32!(ptr, (32 * 12 * 2) / 4); - hint.assume_init() - } - } - } - - fn pairing_check( - P: &[AffinePoint], - Q: &[AffinePoint], - ) -> Result<(), PairingCheckError> { - Self::try_honest_pairing_check(P, Q).unwrap_or_else(|| { - let f = Self::multi_miller_loop(P, Q); - exp_check_fallback(&f, &Self::FINAL_EXPONENT) - }) - } -} - -#[allow(non_snake_case)] -impl Bn254 { - fn try_honest_pairing_check( - P: &[AffinePoint<::Fp>], - Q: &[AffinePoint<::Fp2>], - ) -> Option> { - let (c, u) = Self::pairing_check_hint(P, Q); - if c == Fp12::ZERO { - return None; - } - let c_inv = Fp12::ONE.div_unsafe(&c); - - // We follow Theorem 3 of https://eprint.iacr.org/2024/640.pdf to check that the pairing equals 1 - // By the theorem, it suffices to provide c and u such that f * u == c^λ. - // Since λ = 6x + 2 + q^3 - q^2 + q, we will check the equivalent condition: - // f * c^-{6x + 2} * u * c^-{q^3 - q^2 + q} == 1 - // This is because we can compute f * c^-{6x+2} by embedding the c^-{6x+2} computation in - // the miller loop. - - // c_mul = c^-{q^3 - q^2 + q} - let c_q3_inv = FieldExtension::frobenius_map(&c_inv, 3); - let c_q2 = FieldExtension::frobenius_map(&c, 2); - let c_q_inv = FieldExtension::frobenius_map(&c_inv, 1); - let c_mul = c_q3_inv * c_q2 * c_q_inv; - - // Pass c inverse into the miller loop so that we compute fc == f * c^-{6x + 2} - let fc = Self::multi_miller_loop_embedded_exp(P, Q, Some(c_inv)); - - if fc * c_mul * u == Fp12::ONE { - Some(Ok(())) - } else { - None - } - } -} diff --git a/extensions/pairing/guest/src/bn254/tests.rs b/extensions/pairing/guest/src/bn254/tests.rs index 331bb64692..54fa3fc840 100644 --- a/extensions/pairing/guest/src/bn254/tests.rs +++ b/extensions/pairing/guest/src/bn254/tests.rs @@ -1,308 +1,4 @@ -use group::{ff::Field, prime::PrimeCurveAffine}; -use halo2curves_axiom::bn256::{ - Fq, Fq12, Fq2, Fq6, G1Affine, G2Affine, G2Prepared, Gt, FROBENIUS_COEFF_FQ12_C1, - FROBENIUS_COEFF_FQ6_C1, XI_TO_Q_MINUS_1_OVER_2, -}; -use num_bigint::BigUint; -use num_traits::One; -use openvm_algebra_guest::{field::FieldExtension, IntMod}; -use openvm_ecc_guest::{weierstrass::WeierstrassPoint, AffinePoint}; -use rand::{rngs::StdRng, SeedableRng}; - -use super::{Fp, Fp12, Fp2}; -use crate::{ - bn254::{ - utils::{ - convert_bn254_fp12_to_halo2_fq12, convert_bn254_halo2_fq12_to_fp12, - convert_bn254_halo2_fq2_to_fp2, convert_bn254_halo2_fq_to_fp, - convert_g2_affine_halo2_to_openvm, - }, - Bn254, G2Affine as OpenVmG2Affine, BN254_MODULUS, BN254_ORDER, - BN254_PSEUDO_BINARY_ENCODING, BN254_SEED, - }, - pairing::{ - fp2_invert_assign, fp6_invert_assign, fp6_square_assign, FinalExp, MultiMillerLoop, - PairingCheck, PairingIntrinsics, - }, -}; - -#[test] -fn test_bn254_frobenius_coeffs() { - #[allow(clippy::needless_range_loop)] - for i in 0..12 { - for j in 0..5 { - assert_eq!( - Bn254::FROBENIUS_COEFFS[i][j], - convert_bn254_halo2_fq2_to_fp2(FROBENIUS_COEFF_FQ12_C1[i].pow([j as u64 + 1])), - "FROBENIUS_COEFFS[{}][{}] failed", - i, - j - ) - } - } -} - -#[test] -fn test_bn254_frobenius() { - let mut rng = StdRng::seed_from_u64(15); - for pow in 0..12 { - let fq = Fq12::random(&mut rng); - let fq_frob = fq.frobenius_map(pow); - - let fp = convert_bn254_halo2_fq12_to_fp12(fq); - let fp_frob = fp.frobenius_map(pow); - - assert_eq!(fp_frob, convert_bn254_halo2_fq12_to_fp12(fq_frob)); - } -} - -#[test] -fn test_fp12_invert() { - let mut rng = StdRng::seed_from_u64(15); - let fq = Fq12::random(&mut rng); - let fq_inv = fq.invert().unwrap(); - - let fp = convert_bn254_halo2_fq12_to_fp12(fq); - let fp_inv = fp.invert(); - assert_eq!(fp_inv, convert_bn254_halo2_fq12_to_fp12(fq_inv)); -} - -#[test] -fn test_fp6_invert() { - let mut rng = StdRng::seed_from_u64(20); - let fq6 = Fq6::random(&mut rng); - let fq6_inv = fq6.invert().unwrap(); - - let fp6c0 = convert_bn254_halo2_fq2_to_fp2(fq6.c0); - let fp6c1 = convert_bn254_halo2_fq2_to_fp2(fq6.c1); - let fp6c2 = convert_bn254_halo2_fq2_to_fp2(fq6.c2); - let mut fp6 = [fp6c0, fp6c1, fp6c2]; - fp6_invert_assign::(&mut fp6, &Bn254::XI); - - let fq6_invc0 = convert_bn254_halo2_fq2_to_fp2(fq6_inv.c0); - let fq6_invc1 = convert_bn254_halo2_fq2_to_fp2(fq6_inv.c1); - let fq6_invc2 = convert_bn254_halo2_fq2_to_fp2(fq6_inv.c2); - let fq6_inv = [fq6_invc0, fq6_invc1, fq6_invc2]; - assert_eq!(fp6, fq6_inv); -} - -#[test] -fn test_fp2_invert() { - let mut rng = StdRng::seed_from_u64(25); - let fq2 = Fq2::random(&mut rng); - let fq2_inv = fq2.invert().unwrap(); - - let mut fp2 = convert_bn254_halo2_fq2_to_fp2(fq2).to_coeffs(); - fp2_invert_assign::(&mut fp2); - assert_eq!(fp2, convert_bn254_halo2_fq2_to_fp2(fq2_inv).to_coeffs()); -} - -#[test] -fn test_fp6_square() { - let mut rng = StdRng::seed_from_u64(45); - let fq6 = Fq6::random(&mut rng); - let fq6_sq = fq6.square(); - - let fp6c0 = convert_bn254_halo2_fq2_to_fp2(fq6.c0); - let fp6c1 = convert_bn254_halo2_fq2_to_fp2(fq6.c1); - let fp6c2 = convert_bn254_halo2_fq2_to_fp2(fq6.c2); - let mut fp6 = [fp6c0, fp6c1, fp6c2]; - fp6_square_assign::(&mut fp6, &Bn254::XI); - - let fq6_sqc0 = convert_bn254_halo2_fq2_to_fp2(fq6_sq.c0); - let fq6_sqc1 = convert_bn254_halo2_fq2_to_fp2(fq6_sq.c1); - let fq6_sqc2 = convert_bn254_halo2_fq2_to_fp2(fq6_sq.c2); - let fq6_sq = [fq6_sqc0, fq6_sqc1, fq6_sqc2]; - assert_eq!(fp6, fq6_sq); -} - -#[test] -fn test_fp2_square() { - let mut rng = StdRng::seed_from_u64(55); - let fq2 = Fq2::random(&mut rng); - let fq2_sq = fq2.square(); - - let fp2 = convert_bn254_halo2_fq2_to_fp2(fq2); - let fp2_sq = &fp2 * &fp2; - assert_eq!(fp2_sq, convert_bn254_halo2_fq2_to_fp2(fq2_sq)); -} - -#[test] -fn test_fp_add() { - let mut rng = StdRng::seed_from_u64(65); - let fq = Fq::random(&mut rng); - let fq_res = fq + Fq::one(); - - let fp = convert_bn254_halo2_fq_to_fp(fq); - let fp_res = fp + Fp::ONE; - assert_eq!(fp_res, convert_bn254_halo2_fq_to_fp(fq_res)); -} - -#[test] -fn test_fp_one() { - let fp_one = Fp::ONE; - let fq_one = Fq::ONE; - assert_eq!(fp_one, convert_bn254_halo2_fq_to_fp(fq_one)); -} - -// Gt(Fq12) is not public -fn assert_miller_results_eq(a: Gt, b: Fp12) { - let b = convert_bn254_fp12_to_halo2_fq12(b); - crate::halo2curves_shims::bn254::tests::assert_miller_results_eq(a, b); -} - -#[test] -fn test_bn254_miller_loop() { - let mut rng = StdRng::seed_from_u64(53); - let h2c_p = G1Affine::random(&mut rng); - let h2c_q = G2Affine::random(&mut rng); - - let p = AffinePoint { - x: convert_bn254_halo2_fq_to_fp(h2c_p.x), - y: convert_bn254_halo2_fq_to_fp(h2c_p.y), - }; - let q = AffinePoint { - x: convert_bn254_halo2_fq2_to_fp2(h2c_q.x), - y: convert_bn254_halo2_fq2_to_fp2(h2c_q.y), - }; - - // Compare against halo2curves implementation - let h2c_q_prepared = G2Prepared::from(h2c_q); - let compare_miller = halo2curves_axiom::bn256::multi_miller_loop(&[(&h2c_p, &h2c_q_prepared)]); - let f = Bn254::multi_miller_loop(&[p], &[q]); - assert_miller_results_eq(compare_miller, f); -} - -#[test] -fn test_bn254_miller_loop_identity() { - let mut rng = StdRng::seed_from_u64(33); - let h2c_p = G1Affine::identity(); - let h2c_q = G2Affine::random(&mut rng); - - let p = AffinePoint { - x: convert_bn254_halo2_fq_to_fp(Fq::ZERO), - y: convert_bn254_halo2_fq_to_fp(Fq::ZERO), - }; - let q = AffinePoint { - x: convert_bn254_halo2_fq2_to_fp2(h2c_q.x), - y: convert_bn254_halo2_fq2_to_fp2(h2c_q.y), - }; - - let f = Bn254::multi_miller_loop(&[p], &[q]); - // halo2curves implementation - let h2c_q_prepared = G2Prepared::from(h2c_q); - let compare_miller = halo2curves_axiom::bn256::multi_miller_loop(&[(&h2c_p, &h2c_q_prepared)]); - assert_miller_results_eq(compare_miller, f); -} - -#[test] -fn test_bn254_miller_loop_identity_2() { - let mut rng = StdRng::seed_from_u64(33); - let h2c_p = G1Affine::random(&mut rng); - let h2c_q = G2Affine::identity(); - let p = AffinePoint { - x: convert_bn254_halo2_fq_to_fp(h2c_p.x), - y: convert_bn254_halo2_fq_to_fp(h2c_p.y), - }; - let q = AffinePoint { - x: convert_bn254_halo2_fq2_to_fp2(Fq2::ZERO), - y: convert_bn254_halo2_fq2_to_fp2(Fq2::ZERO), - }; - - let f = Bn254::multi_miller_loop(&[p], &[q]); - // halo2curves implementation - let h2c_q_prepared = G2Prepared::from(h2c_q); - let compare_miller = halo2curves_axiom::bn256::multi_miller_loop(&[(&h2c_p, &h2c_q_prepared)]); - assert_miller_results_eq(compare_miller, f); -} - -// test on host is enough since we are testing the curve formulas and not anything -// about intrinsic functions -#[test] -fn test_bn254_g2_affine() { - let mut rng = StdRng::seed_from_u64(34); - for _ in 0..10 { - let p = G2Affine::random(&mut rng); - let q = G2Affine::random(&mut rng); - let expected_add = G2Affine::from(p + q); - let expected_sub = G2Affine::from(p - q); - let expected_neg = -p; - let expected_double = G2Affine::from(p + p); - let [p, q] = [p, q].map(|p| { - let x = convert_bn254_halo2_fq2_to_fp2(p.x); - let y = convert_bn254_halo2_fq2_to_fp2(p.y); - // check on curve - OpenVmG2Affine::from_xy(x, y).unwrap() - }); - let r_add = &p + &q; - let r_sub = &p - &q; - let r_neg = -&p; - let r_double = &p + &p; - - for (expected, actual) in [ - (expected_add, r_add), - (expected_sub, r_sub), - (expected_neg, r_neg), - (expected_double, r_double), - ] { - assert_eq!(convert_g2_affine_halo2_to_openvm(expected), actual); - } - } -} - -#[test] -fn test_bn254_pairing_check_hint_host() { - let mut rng = StdRng::seed_from_u64(83); - let h2c_p = G1Affine::random(&mut rng); - let h2c_q = G2Affine::random(&mut rng); - - let p = AffinePoint { - x: convert_bn254_halo2_fq_to_fp(h2c_p.x), - y: convert_bn254_halo2_fq_to_fp(h2c_p.y), - }; - let q = AffinePoint { - x: convert_bn254_halo2_fq2_to_fp2(h2c_q.x), - y: convert_bn254_halo2_fq2_to_fp2(h2c_q.y), - }; - - let (c, u) = Bn254::pairing_check_hint(&[p], &[q]); - - let p_cmp = AffinePoint { - x: h2c_p.x, - y: h2c_p.y, - }; - let q_cmp = AffinePoint { - x: h2c_q.x, - y: h2c_q.y, - }; - - let f_cmp = crate::halo2curves_shims::bn254::Bn254::multi_miller_loop(&[p_cmp], &[q_cmp]); - let (c_cmp, u_cmp) = crate::halo2curves_shims::bn254::Bn254::final_exp_hint(&f_cmp); - let c_cmp = convert_bn254_halo2_fq12_to_fp12(c_cmp); - let u_cmp = convert_bn254_halo2_fq12_to_fp12(u_cmp); - - assert_eq!(c, c_cmp); - assert_eq!(u, u_cmp); -} - -#[test] -fn test_bn254_final_exponent() { - let final_exp = (BN254_MODULUS.pow(12) - BigUint::one()) / BN254_ORDER.clone(); - assert_eq!(Bn254::FINAL_EXPONENT.to_vec(), final_exp.to_bytes_be()); -} - -#[test] -fn test_bn254_frobenius_coeffs_fq6() { - #[allow(clippy::needless_range_loop)] - for i in 0..3 { - assert_eq!( - Bn254::FROBENIUS_COEFF_FQ6_C1[i], - convert_bn254_halo2_fq2_to_fp2(FROBENIUS_COEFF_FQ6_C1[i]), - "FROBENIUS_COEFFS_FQ6_C1[{}] failed", - i, - ) - } -} +use crate::bn254::{BN254_PSEUDO_BINARY_ENCODING, BN254_SEED}; #[test] fn test_bn254_pseudo_binary_encoding() { @@ -314,12 +10,3 @@ fn test_bn254_pseudo_binary_encoding() { } assert_eq!(x.unsigned_abs(), 6 * (BN254_SEED as u128) + 2); } - -#[test] -fn test_bn254_xi_to_q_minus_1_over_2() { - assert_eq!( - Bn254::XI_TO_Q_MINUS_1_OVER_2, - convert_bn254_halo2_fq2_to_fp2(XI_TO_Q_MINUS_1_OVER_2), - "XI_TO_Q_MINUS_1_OVER_2 failed", - ) -} diff --git a/extensions/pairing/guest/src/bn254/utils.rs b/extensions/pairing/guest/src/bn254/utils.rs deleted file mode 100644 index 9102e980db..0000000000 --- a/extensions/pairing/guest/src/bn254/utils.rs +++ /dev/null @@ -1,49 +0,0 @@ -use halo2curves_axiom::bn256::{Fq, Fq12, Fq2, G2Affine}; -use openvm_algebra_guest::{field::FieldExtension, IntMod}; -use openvm_ecc_guest::weierstrass::WeierstrassPoint; - -use super::{Fp, Fp12, Fp2}; -use crate::bn254::G2Affine as OpenVmG2Affine; - -pub(crate) fn convert_bn254_halo2_fq_to_fp(x: Fq) -> Fp { - let bytes = x.to_bytes(); - Fp::from_le_bytes(&bytes) -} - -pub(crate) fn convert_bn254_halo2_fq2_to_fp2(x: Fq2) -> Fp2 { - Fp2::new( - convert_bn254_halo2_fq_to_fp(x.c0), - convert_bn254_halo2_fq_to_fp(x.c1), - ) -} - -pub(crate) fn convert_bn254_halo2_fq12_to_fp12(x: Fq12) -> Fp12 { - Fp12 { - c: x.to_coeffs().map(convert_bn254_halo2_fq2_to_fp2), - } -} - -pub(crate) fn convert_bn254_fp_to_halo2_fq(x: Fp) -> Fq { - Fq::from_bytes(&x.0).unwrap() -} - -pub(crate) fn convert_bn254_fp2_to_halo2_fq2(x: Fp2) -> Fq2 { - Fq2 { - c0: convert_bn254_fp_to_halo2_fq(x.c0.clone()), - c1: convert_bn254_fp_to_halo2_fq(x.c1.clone()), - } -} - -#[allow(unused)] -pub(crate) fn convert_bn254_fp12_to_halo2_fq12(x: Fp12) -> Fq12 { - let c = x.to_coeffs(); - Fq12::from_coeffs(c.map(convert_bn254_fp2_to_halo2_fq2)) -} - -#[allow(unused)] -pub(crate) fn convert_g2_affine_halo2_to_openvm(p: G2Affine) -> OpenVmG2Affine { - OpenVmG2Affine::from_xy_unchecked( - convert_bn254_halo2_fq2_to_fp2(p.x), - convert_bn254_halo2_fq2_to_fp2(p.y), - ) -} diff --git a/extensions/pairing/guest/src/halo2curves_shims/bls12_381/miller_loop.rs b/extensions/pairing/guest/src/halo2curves_shims/bls12_381/miller_loop.rs index add12a4b23..f2720fb52b 100644 --- a/extensions/pairing/guest/src/halo2curves_shims/bls12_381/miller_loop.rs +++ b/extensions/pairing/guest/src/halo2curves_shims/bls12_381/miller_loop.rs @@ -8,13 +8,30 @@ use openvm_ecc_guest::{ }; use super::Bls12_381; -use crate::{ - bls12_381::{BLS12_381_PSEUDO_BINARY_ENCODING, BLS12_381_SEED_ABS}, - pairing::{ - Evaluatable, EvaluatedLine, LineMulMType, MillerStep, MultiMillerLoop, UnevaluatedLine, - }, +use crate::pairing::{ + Evaluatable, EvaluatedLine, LineMulMType, MillerStep, MultiMillerLoop, UnevaluatedLine, }; +pub const BLS12_381_SEED_ABS: u64 = 0xd201000000010000; +// Encodes the Bls12_381 seed, x. +// x = sum_i BLS12_381_PSEUDO_BINARY_ENCODING[i] * 2^i +// where BLS12_381_PSEUDO_BINARY_ENCODING[i] is in {-1, 0, 1} +pub const BLS12_381_PSEUDO_BINARY_ENCODING: [i8; 64] = [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, +]; + +#[test] +fn test_bls12381_pseudo_binary_encoding() { + let mut x: i128 = 0; + let mut power_of_2 = 1; + for b in BLS12_381_PSEUDO_BINARY_ENCODING.iter() { + x += (*b as i128) * power_of_2; + power_of_2 *= 2; + } + assert_eq!(x.unsigned_abs(), BLS12_381_SEED_ABS as u128); +} + impl MillerStep for Bls12_381 { type Fp2 = Fq2; diff --git a/extensions/pairing/guest/src/halo2curves_shims/bls12_381/tests/mod.rs b/extensions/pairing/guest/src/halo2curves_shims/bls12_381/tests/mod.rs index 56f7282612..72951a15be 100644 --- a/extensions/pairing/guest/src/halo2curves_shims/bls12_381/tests/mod.rs +++ b/extensions/pairing/guest/src/halo2curves_shims/bls12_381/tests/mod.rs @@ -2,15 +2,15 @@ use alloc::vec::Vec; use core::mem::transmute; use halo2curves_axiom::bls12_381::{Fq, Fq12, Fq2, G1Affine, G2Affine, MillerLoopResult}; +use hex_literal::hex; use itertools::izip; +use lazy_static::lazy_static; use num_bigint::BigUint; use num_traits::Pow; use openvm_algebra_guest::ExpBytes; use openvm_ecc_guest::AffinePoint; use rand::{rngs::StdRng, SeedableRng}; -use crate::bls12_381::{BLS12_381_MODULUS, BLS12_381_ORDER}; - #[cfg(test)] mod test_final_exp; #[cfg(test)] @@ -18,6 +18,17 @@ mod test_line; #[cfg(test)] mod test_miller_loop; +#[cfg(not(target_os = "zkvm"))] + +lazy_static! { + pub static ref BLS12_381_MODULUS: BigUint = BigUint::from_bytes_be(&hex!( + "1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab" + )); + pub static ref BLS12_381_ORDER: BigUint = BigUint::from_bytes_be(&hex!( + "73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001" + )); +} + // Manual final exponentiation because halo2curves `MillerLoopResult` doesn't have constructor pub fn final_exp(f: Fq12) -> Fq12 { let p = BLS12_381_MODULUS.clone(); diff --git a/extensions/pairing/guest/src/halo2curves_shims/bn254/miller_loop.rs b/extensions/pairing/guest/src/halo2curves_shims/bn254/miller_loop.rs index 9d4ff02557..9fc42b97ba 100644 --- a/extensions/pairing/guest/src/halo2curves_shims/bn254/miller_loop.rs +++ b/extensions/pairing/guest/src/halo2curves_shims/bn254/miller_loop.rs @@ -8,13 +8,31 @@ use openvm_ecc_guest::{ }; use super::Bn254; -use crate::{ - bn254::{BN254_PSEUDO_BINARY_ENCODING, BN254_SEED}, - pairing::{ - Evaluatable, EvaluatedLine, LineMulDType, MillerStep, MultiMillerLoop, UnevaluatedLine, - }, +use crate::pairing::{ + Evaluatable, EvaluatedLine, LineMulDType, MillerStep, MultiMillerLoop, UnevaluatedLine, }; +pub const BN254_SEED: u64 = 0x44e992b44a6909f1; +// Encodes 6x+2 where x is the BN254 seed. +// 6*x+2 = sum_i BN254_PSEUDO_BINARY_ENCODING[i] * 2^i +// where BN254_PSEUDO_BINARY_ENCODING[i] is in {-1, 0, 1} +pub const BN254_PSEUDO_BINARY_ENCODING: [i8; 66] = [ + 0, 0, 0, 1, 0, 1, 0, -1, 0, 0, -1, 0, 0, 0, 1, 0, 0, -1, 0, -1, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0, + -1, 0, 0, 1, 0, -1, 0, 0, 1, 0, 0, 0, 0, 0, -1, 0, 0, -1, 0, 1, 0, -1, 0, 0, 0, -1, 0, -1, 0, + 0, 0, 1, 0, -1, 0, 1, +]; + +#[test] +fn test_bn254_pseudo_binary_encoding() { + let mut x: i128 = 0; + let mut power_of_2 = 1; + for b in BN254_PSEUDO_BINARY_ENCODING.iter() { + x += (*b as i128) * power_of_2; + power_of_2 *= 2; + } + assert_eq!(x.unsigned_abs(), 6 * (BN254_SEED as u128) + 2); +} + impl MillerStep for Bn254 { type Fp2 = Fq2; diff --git a/extensions/pairing/guest/src/halo2curves_shims/bn254/tests/mod.rs b/extensions/pairing/guest/src/halo2curves_shims/bn254/tests/mod.rs index 70d484dc79..beecacac45 100644 --- a/extensions/pairing/guest/src/halo2curves_shims/bn254/tests/mod.rs +++ b/extensions/pairing/guest/src/halo2curves_shims/bn254/tests/mod.rs @@ -5,15 +5,15 @@ use halo2curves_axiom::{ bn256::{Fq, Fq12, Fq2, G1Affine, G2Affine, Gt}, pairing::MillerLoopResult, }; +use hex_literal::hex; use itertools::izip; +use lazy_static::lazy_static; use num_bigint::BigUint; use num_traits::Pow; use openvm_algebra_guest::ExpBytes; use openvm_ecc_guest::AffinePoint; use rand::{rngs::StdRng, SeedableRng}; -use crate::bn254::{BN254_MODULUS, BN254_ORDER}; - #[cfg(test)] mod test_final_exp; #[cfg(test)] @@ -21,6 +21,15 @@ mod test_line; #[cfg(test)] mod test_miller_loop; +lazy_static! { + pub static ref BN254_MODULUS: BigUint = BigUint::from_bytes_be(&hex!( + "30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47" + )); + pub static ref BN254_ORDER: BigUint = BigUint::from_bytes_be(&hex!( + "30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001" + )); +} + // Manual final exponentiation because halo2curves `MillerLoopResult` doesn't have constructor pub fn final_exp(f: Fq12) -> Fq12 { let p = BN254_MODULUS.clone(); diff --git a/extensions/pairing/guest/src/lib.rs b/extensions/pairing/guest/src/lib.rs index d0fdcf5eab..3570c36b22 100644 --- a/extensions/pairing/guest/src/lib.rs +++ b/extensions/pairing/guest/src/lib.rs @@ -28,6 +28,7 @@ pub use openvm_algebra_guest as algebra; /// These should **only** be importable on a host machine. #[cfg(all(feature = "halo2curves", not(target_os = "zkvm")))] pub mod halo2curves_shims; + /// Traits for optimal Ate pairing check using intrinsic functions. pub mod pairing; diff --git a/extensions/pairing/guest/src/pairing/mod.rs b/extensions/pairing/guest/src/pairing/mod.rs index 3a751025e4..0c59edd394 100644 --- a/extensions/pairing/guest/src/pairing/mod.rs +++ b/extensions/pairing/guest/src/pairing/mod.rs @@ -2,8 +2,6 @@ mod final_exp; mod line; mod miller_loop; mod miller_step; -mod operations; -mod sextic_ext_field; pub use final_exp::*; pub use line::*; @@ -14,9 +12,6 @@ use openvm_algebra_guest::{ ExpBytes, Field, IntMod, }; use openvm_ecc_guest::AffinePoint; -#[allow(unused_imports)] -pub(crate) use operations::*; -pub use sextic_ext_field::*; use crate::PairingBaseFunct7; diff --git a/extensions/pairing/guest/src/pairing/operations/fp12.rs b/extensions/pairing/guest/src/pairing/operations/fp12.rs deleted file mode 100644 index de72db0c0d..0000000000 --- a/extensions/pairing/guest/src/pairing/operations/fp12.rs +++ /dev/null @@ -1,34 +0,0 @@ -use openvm_algebra_guest::{field::FieldExtension, Field, IntMod}; - -use super::{ - fp6_invert_assign, fp6_mul_assign, fp6_mul_by_nonresidue_assign, fp6_square_assign, - fp6_sub_assign, -}; - -pub(crate) fn fp12_invert_assign< - Fp: IntMod + Field, - Fp2: Field + FieldExtension, ->( - c: &mut [Fp2; 6], - xi: &Fp2, -) { - let mut c0s = [c[0].clone(), c[2].clone(), c[4].clone()]; - let mut c1s = [c[1].clone(), c[3].clone(), c[5].clone()]; - - fp6_square_assign(&mut c0s, xi); - fp6_square_assign(&mut c1s, xi); - fp6_mul_by_nonresidue_assign(&mut c1s, xi); - fp6_sub_assign(&mut c0s, &c1s); - - fp6_invert_assign(&mut c0s, xi); - let mut t0 = c0s.clone(); - let mut t1 = c0s; - fp6_mul_assign(&mut t0, &[c[0].clone(), c[2].clone(), c[4].clone()], xi); - fp6_mul_assign(&mut t1, &[c[1].clone(), c[3].clone(), c[5].clone()], xi); - c[0] = t0[0].clone(); - c[2] = t0[1].clone(); - c[4] = t0[2].clone(); - c[1] = t1[0].clone().neg(); - c[3] = t1[1].clone().neg(); - c[5] = t1[2].clone().neg(); -} diff --git a/extensions/pairing/guest/src/pairing/operations/fp2.rs b/extensions/pairing/guest/src/pairing/operations/fp2.rs deleted file mode 100644 index 38a648b88b..0000000000 --- a/extensions/pairing/guest/src/pairing/operations/fp2.rs +++ /dev/null @@ -1,16 +0,0 @@ -use openvm_algebra_guest::{Field, IntMod}; - -pub(crate) fn fp2_invert_assign(c: &mut [F; 2]) { - let mut t1 = c[1].clone(); - ::square_assign(&mut t1); - let mut t0 = c[0].clone(); - ::square_assign(&mut t0); - t0 += &t1; - t0 = ::ONE.div_unsafe(&t0); - let mut tmp = [c[0].clone(), c[1].clone()]; - tmp[0] *= &t0; - tmp[1] *= &t0; - tmp[1].neg_assign(); - - *c = tmp; -} diff --git a/extensions/pairing/guest/src/pairing/operations/fp6.rs b/extensions/pairing/guest/src/pairing/operations/fp6.rs deleted file mode 100644 index 1896d1d1ee..0000000000 --- a/extensions/pairing/guest/src/pairing/operations/fp6.rs +++ /dev/null @@ -1,187 +0,0 @@ -use openvm_algebra_guest::{field::FieldExtension, Field, IntMod}; - -use super::fp2_invert_assign; - -pub(crate) fn fp6_invert_assign< - Fp: IntMod + Field, - Fp2: Field + FieldExtension, ->( - c: &mut [Fp2; 3], - xi: &Fp2, -) { - let mut c0 = c[2].clone(); - c0 *= xi; - c0 *= &c[1]; - c0 = c0.neg(); - { - let mut c0s = c[0].clone(); - ::square_assign(&mut c0s); - c0 += &c0s; - } - let mut c1 = c[2].clone(); - ::square_assign(&mut c1); - c1 *= xi; - { - let mut c01 = c[0].clone(); - c01 *= &c[1]; - c1 -= &c01; - } - let mut c2 = c[1].clone(); - ::square_assign(&mut c2); - { - let mut c02 = c[0].clone(); - c02 *= &c[2]; - c2 -= &c02; - } - - let mut tmp1 = c[2].clone(); - tmp1 *= &c1; - let mut tmp2 = c[1].clone(); - tmp2 *= &c2; - tmp1 += &tmp2; - tmp1 *= xi; - tmp2 = c[0].clone(); - tmp2 *= &c0; - tmp1 += &tmp2; - - let mut coeffs = tmp1.clone().to_coeffs(); - fp2_invert_assign::(&mut coeffs); - let tmp = Fp2::from_coeffs(coeffs); - let mut tmp = [tmp.clone(), tmp.clone(), tmp.clone()]; - tmp[0] *= &c0; - tmp[1] *= &c1; - tmp[2] *= &c2; - - *c = tmp; -} - -pub(crate) fn fp6_mul_by_nonresidue_assign< - Fp: IntMod + Field, - Fp2: Field + FieldExtension, ->( - c: &mut [Fp2; 3], - xi: &Fp2, -) { - // c0, c1, c2 -> c2, c0, c1 - c.swap(0, 1); - c.swap(0, 2); - c[0] *= xi; -} - -pub(crate) fn fp6_sub_assign< - Fp: IntMod + Field, - Fp2: Field + FieldExtension, ->( - a: &mut [Fp2; 3], - b: &[Fp2; 3], -) { - a.iter_mut().zip(b).for_each(|(a, b)| *a -= b); -} - -/// Squares 3 elements of `Fp2`, which represents as a single Fp6 element, in place -pub(crate) fn fp6_square_assign< - Fp: IntMod + Field, - Fp2: Field + FieldExtension, ->( - c: &mut [Fp2; 3], - xi: &Fp2, -) { - // s0 = a^2 - let mut s0 = c[0].clone(); - ::square_assign(&mut s0); - // s1 = 2ab - let mut ab = c[0].clone(); - ab *= &c[1]; - let mut s1 = ab; - ::double_assign(&mut s1); - // s2 = (a - b + c)^2 - let mut s2 = c[0].clone(); - s2 -= &c[1]; - s2 += &c[2]; - ::square_assign(&mut s2); - // bc - let mut bc = c[1].clone(); - bc *= &c[2]; - // s3 = 2bc - let mut s3 = bc; - ::double_assign(&mut s3); - // s4 = c^2 - let mut s4 = c[2].clone(); - ::square_assign(&mut s4); - - // new c0 = 2bc.mul_by_xi + a^2 - c[0] = s3.clone(); - c[0] *= xi; - c[0] += &s0; - - // new c1 = (c^2).mul_by_xi + 2ab - c[1] = s4.clone(); - c[1] *= xi; - c[1] += &s1; - - // new c2 = 2ab + (a - b + c)^2 + 2bc - a^2 - c^2 = b^2 + 2ac - c[2] = s1; - c[2] += &s2; - c[2] += &s3; - c[2] -= &s0; - c[2] -= &s4; -} - -pub(crate) fn fp6_mul_assign< - Fp: IntMod + Field, - Fp2: Field + FieldExtension, ->( - a: &mut [Fp2; 3], - b: &[Fp2; 3], - xi: &Fp2, -) { - let mut a_a = a[0].clone(); - let mut b_b = a[1].clone(); - let mut c_c = a[2].clone(); - - a_a *= &b[0]; - b_b *= &b[1]; - c_c *= &b[2]; - - let mut t1 = b[1].clone(); - t1 += &b[2]; - { - let mut tmp = a[1].clone(); - tmp += &a[2]; - - t1 *= &tmp; - t1 -= &b_b; - t1 -= &c_c; - t1 *= xi; - t1 += &a_a; - } - - let mut t3 = b[0].clone(); - t3 += &b[2]; - { - let mut tmp = a[0].clone(); - tmp += &a[2]; - - t3 *= &tmp; - t3 -= &a_a; - t3 += &b_b; - t3 -= &c_c; - } - - let mut t2 = b[0].clone(); - t2 += &b[1]; - { - let mut tmp = a[0].clone(); - tmp += &a[1]; - - t2 *= &tmp; - t2 -= &a_a; - t2 -= &b_b; - c_c *= xi; - t2 += &c_c; - } - - a[0] = t1; - a[1] = t2; - a[2] = t3; -} diff --git a/extensions/pairing/guest/src/pairing/operations/mod.rs b/extensions/pairing/guest/src/pairing/operations/mod.rs deleted file mode 100644 index 4a75c8a9d7..0000000000 --- a/extensions/pairing/guest/src/pairing/operations/mod.rs +++ /dev/null @@ -1,9 +0,0 @@ -#![allow(dead_code)] -mod fp12; -mod fp2; -mod fp6; - -#[allow(unused_imports)] -pub(crate) use fp12::*; -pub(crate) use fp2::*; -pub(crate) use fp6::*; diff --git a/extensions/pairing/guest/src/pairing/sextic_ext_field.rs b/extensions/pairing/guest/src/pairing/sextic_ext_field.rs deleted file mode 100644 index 7f75815e05..0000000000 --- a/extensions/pairing/guest/src/pairing/sextic_ext_field.rs +++ /dev/null @@ -1,158 +0,0 @@ -use core::{ - fmt::{Debug, Formatter, Result}, - ops::{Add, AddAssign, Sub, SubAssign}, -}; - -use openvm_algebra_guest::field::Field; - -/// Sextic extension field of `F` with irreducible polynomial `X^6 - \xi`. -/// Elements are represented as `c0 + c1 * w + ... + c5 * w^5` where `w^6 = \xi`, where `\xi in F`. -/// -/// Memory alignment follows alignment of `F`. -#[derive(Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] -#[repr(C)] -pub struct SexticExtField { - pub c: [F; 6], -} - -impl SexticExtField { - pub const fn new(c: [F; 6]) -> Self { - Self { c } - } -} - -impl<'a, F: Field> AddAssign<&'a SexticExtField> for SexticExtField { - #[inline(always)] - fn add_assign(&mut self, other: &'a SexticExtField) { - for i in 0..6 { - self.c[i] += &other.c[i]; - } - } -} - -impl<'a, F: Field> Add<&'a SexticExtField> for &SexticExtField { - type Output = SexticExtField; - #[inline(always)] - fn add(self, other: &'a SexticExtField) -> Self::Output { - let mut res = self.clone(); - res += other; - res - } -} - -impl<'a, F: Field> SubAssign<&'a SexticExtField> for SexticExtField { - #[inline(always)] - fn sub_assign(&mut self, other: &'a SexticExtField) { - for i in 0..6 { - self.c[i] -= &other.c[i]; - } - } -} - -impl<'a, F: Field> Sub<&'a SexticExtField> for &SexticExtField { - type Output = SexticExtField; - #[inline(always)] - fn sub(self, other: &'a SexticExtField) -> Self::Output { - let mut res = self.clone(); - res -= other; - res - } -} - -pub(crate) fn sextic_tower_mul( - lhs: &SexticExtField, - rhs: &SexticExtField, - xi: &F, -) -> SexticExtField -where - for<'a> &'a F: core::ops::Mul<&'a F, Output = F>, -{ - // The following multiplication is hand-derived with respect to the basis where degree 6 - // extension is composed of degree 3 extension followed by degree 2 extension. - - // c0 = cs0co0 + xi(cs1co2 + cs2co1 + cs3co5 + cs4co4 + cs5co3) - // c1 = cs0co1 + cs1co0 + cs3co3 + xi(cs2co2 + cs4co5 + cs5co4) - // c2 = cs0co2 + cs1co1 + cs2co0 + cs3co4 + cs4co3 + xi(cs5co5) - // c3 = cs0co3 + cs3co0 + xi(cs1co5 + cs2co4 + cs4co2 + cs5co1) - // c4 = cs0co4 + cs1co3 + cs3co1 + cs4co0 + xi(cs2co5 + cs5co2) - // c5 = cs0co5 + cs1co4 + cs2co3 + cs3co2 + cs4co1 + cs5co0 - // where cs*: lhs.c*, co*: rhs.c* - - let (s0, s1, s2, s3, s4, s5) = ( - &lhs.c[0], &lhs.c[2], &lhs.c[4], &lhs.c[1], &lhs.c[3], &lhs.c[5], - ); - let (o0, o1, o2, o3, o4, o5) = ( - &rhs.c[0], &rhs.c[2], &rhs.c[4], &rhs.c[1], &rhs.c[3], &rhs.c[5], - ); - - let c0 = s0 * o0 + xi * &(s1 * o2 + s2 * o1 + s3 * o5 + s4 * o4 + s5 * o3); - let c1 = s0 * o1 + s1 * o0 + s3 * o3 + xi * &(s2 * o2 + s4 * o5 + s5 * o4); - let c2 = s0 * o2 + s1 * o1 + s2 * o0 + s3 * o4 + s4 * o3 + xi * &(s5 * o5); - let c3 = s0 * o3 + s3 * o0 + xi * &(s1 * o5 + s2 * o4 + s4 * o2 + s5 * o1); - let c4 = s0 * o4 + s1 * o3 + s3 * o1 + s4 * o0 + xi * &(s2 * o5 + s5 * o2); - let c5 = s0 * o5 + s1 * o4 + s2 * o3 + s3 * o2 + s4 * o1 + s5 * o0; - - SexticExtField::new([c0, c3, c1, c4, c2, c5]) -} - -// Auto-derived implementations: - -impl AddAssign for SexticExtField { - #[inline(always)] - fn add_assign(&mut self, other: Self) { - self.add_assign(&other); - } -} - -impl Add for SexticExtField { - type Output = Self; - #[inline(always)] - fn add(mut self, other: Self) -> Self::Output { - self += other; - self - } -} - -impl<'a, F: Field> Add<&'a SexticExtField> for SexticExtField { - type Output = Self; - #[inline(always)] - fn add(mut self, other: &'a SexticExtField) -> Self::Output { - self += other; - self - } -} - -impl SubAssign for SexticExtField { - #[inline(always)] - fn sub_assign(&mut self, other: Self) { - self.sub_assign(&other); - } -} - -impl Sub for SexticExtField { - type Output = Self; - #[inline(always)] - fn sub(mut self, other: Self) -> Self::Output { - self -= other; - self - } -} - -impl<'a, F: Field> Sub<&'a SexticExtField> for SexticExtField { - type Output = Self; - #[inline(always)] - fn sub(mut self, other: &'a SexticExtField) -> Self::Output { - self -= other; - self - } -} - -impl Debug for SexticExtField { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - write!( - f, - "{:?}, {:?}, {:?}, {:?}, {:?}, {:?}", - self.c[0], self.c[1], self.c[2], self.c[3], self.c[4], self.c[5] - ) - } -} diff --git a/extensions/pairing/tests/Cargo.toml b/extensions/pairing/tests/Cargo.toml deleted file mode 100644 index 6a337bfab0..0000000000 --- a/extensions/pairing/tests/Cargo.toml +++ /dev/null @@ -1,34 +0,0 @@ -[package] -name = "openvm-pairing-integration-tests" -description = "Integration tests for the OpenVM pairing extension" -version.workspace = true -authors.workspace = true -edition.workspace = true -homepage.workspace = true -repository.workspace = true - -[dependencies] -openvm-instructions = { workspace = true } -openvm-stark-sdk.workspace = true -openvm-circuit = { workspace = true, features = ["test-utils"] } -openvm-transpiler.workspace = true -openvm-algebra-circuit.workspace = true -openvm-algebra-transpiler.workspace = true -openvm-pairing-circuit.workspace = true -openvm-pairing-transpiler.workspace = true -openvm-pairing-guest.workspace = true -openvm-ecc-circuit.workspace = true -openvm-ecc-guest.workspace = true -openvm-ecc-transpiler.workspace = true -openvm-rv32im-transpiler.workspace = true -openvm = { workspace = true } -openvm-toolchain-tests = { workspace = true } -eyre.workspace = true -rand.workspace = true -num-bigint.workspace = true -num-traits.workspace = true -halo2curves-axiom = { workspace = true } - -[features] -default = ["parallel"] -parallel = ["openvm-circuit/parallel"] diff --git a/extensions/pairing/tests/programs/Cargo.toml b/extensions/pairing/tests/programs/Cargo.toml deleted file mode 100644 index 6e5cd66984..0000000000 --- a/extensions/pairing/tests/programs/Cargo.toml +++ /dev/null @@ -1,51 +0,0 @@ -[workspace] -[package] -name = "openvm-pairing-test-programs" -version = "0.0.0" -edition = "2021" - -[dependencies] -openvm = { path = "../../../../crates/toolchain/openvm" } -openvm-platform = { path = "../../../../crates/toolchain/platform" } - -openvm-algebra-guest = { path = "../../../../extensions/algebra/guest", default-features = false } -openvm-algebra-moduli-macros = { path = "../../../../extensions/algebra/moduli-macros", default-features = false } -openvm-algebra-complex-macros = { path = "../../../../extensions/algebra/complex-macros", default-features = false } -openvm-ecc-guest = { path = "../../../../extensions/ecc/guest", default-features = false } -openvm-ecc-sw-macros = { path = "../../../../extensions/ecc/sw-macros", default-features = false } -openvm-pairing-guest = { path = "../../../../extensions/pairing/guest", default-features = false } - -serde = { version = "1.0", default-features = false, features = [ - "alloc", - "derive", -] } -hex = { version = "0.4.3", default-features = false, features = ["alloc"] } -hex-literal = { version = "0.4.1", default-features = false } -k256 = { version = "0.13.3", default-features = false, features = [ - "ecdsa-core", - "ecdsa", -], optional = true } - -[features] -default = [] -std = ["serde/std", "openvm/std"] - -bn254 = ["openvm-pairing-guest/bn254"] -bls12_381 = ["openvm-pairing-guest/bls12_381"] - -[profile.release] -panic = "abort" -lto = "thin" # turn on lto = fat to decrease binary size, but this optimizes out some missing extern links so we shouldn't use it for testing -# strip = "symbols" - -[[example]] -name = "bn_final_exp_hint" -required-features = ["bn254"] - -[[example]] -name = "bls_final_exp_hint" -required-features = ["bls12_381"] - -[[example]] -name = "bls_ec" -required-features = ["bls12_381"] diff --git a/extensions/pairing/tests/programs/examples/bls_ec.rs b/extensions/pairing/tests/programs/examples/bls_ec.rs deleted file mode 100644 index 93f72115cf..0000000000 --- a/extensions/pairing/tests/programs/examples/bls_ec.rs +++ /dev/null @@ -1,11 +0,0 @@ -#![cfg_attr(not(feature = "std"), no_main)] -#![cfg_attr(not(feature = "std"), no_std)] - -#[allow(unused_imports)] -use openvm_pairing_guest::bls12_381::Bls12_381G1Affine; - -openvm::init!("openvm_init_bls_ec_bls12_381.rs"); - -openvm::entry!(main); - -pub fn main() {} diff --git a/extensions/pairing/tests/programs/examples/bls_final_exp_hint.rs b/extensions/pairing/tests/programs/examples/bls_final_exp_hint.rs deleted file mode 100644 index 33660e0951..0000000000 --- a/extensions/pairing/tests/programs/examples/bls_final_exp_hint.rs +++ /dev/null @@ -1,24 +0,0 @@ -#![cfg_attr(not(feature = "std"), no_main)] -#![cfg_attr(not(feature = "std"), no_std)] - -extern crate alloc; - -use alloc::vec::Vec; - -use openvm::io::read; -use openvm_ecc_guest::AffinePoint; -use openvm_pairing_guest::{ - bls12_381::{Bls12_381, Fp, Fp12, Fp2}, - pairing::PairingCheck, -}; - -openvm::entry!(main); - -openvm::init!("openvm_init_bls_final_exp_hint_bls12_381.rs"); - -pub fn main() { - #[allow(clippy::type_complexity)] - let (p, q, expected): (Vec>, Vec>, (Fp12, Fp12)) = read(); - let actual = Bls12_381::pairing_check_hint(&p, &q); - assert_eq!(actual, expected); -} diff --git a/extensions/pairing/tests/programs/examples/bn_final_exp_hint.rs b/extensions/pairing/tests/programs/examples/bn_final_exp_hint.rs deleted file mode 100644 index 347afa72bb..0000000000 --- a/extensions/pairing/tests/programs/examples/bn_final_exp_hint.rs +++ /dev/null @@ -1,24 +0,0 @@ -#![cfg_attr(not(feature = "std"), no_main)] -#![cfg_attr(not(feature = "std"), no_std)] - -extern crate alloc; - -use alloc::vec::Vec; - -use openvm::io::read; -use openvm_ecc_guest::AffinePoint; -use openvm_pairing_guest::{ - bn254::{Bn254, Fp, Fp12, Fp2}, - pairing::PairingCheck, -}; - -openvm::entry!(main); - -openvm::init!("openvm_init_bn_final_exp_hint_bn254.rs"); - -pub fn main() { - #[allow(clippy::type_complexity)] - let (p, q, expected): (Vec>, Vec>, (Fp12, Fp12)) = read(); - let actual = Bn254::pairing_check_hint(&p, &q); - assert_eq!(actual, expected); -} diff --git a/extensions/pairing/tests/programs/examples/fp12_mul.rs b/extensions/pairing/tests/programs/examples/fp12_mul.rs deleted file mode 100644 index aeec2fb63d..0000000000 --- a/extensions/pairing/tests/programs/examples/fp12_mul.rs +++ /dev/null @@ -1,82 +0,0 @@ -#![cfg_attr(not(feature = "std"), no_main)] -#![cfg_attr(not(feature = "std"), no_std)] -#![allow(unused_imports)] - -use openvm::io::read_vec; -use openvm_algebra_guest::{field::FieldExtension, IntMod}; - -openvm::entry!(main); - -#[cfg(feature = "bn254")] -mod bn254 { - use openvm_pairing_guest::bn254::{Fp, Fp12}; - - use super::*; - - openvm::init!("openvm_init_fp12_mul_bn254.rs"); - - pub fn test_fp12_mul(io: &[u8]) { - assert_eq!(io.len(), 32 * 36); - - let f0 = &io[0..32 * 12]; - let f1 = &io[32 * 12..32 * 24]; - let r_cmp = &io[32 * 24..32 * 36]; - - let f0_cast = Fp12::from_bytes(f0); - let f1_cast = Fp12::from_bytes(f1); - - let r = f0_cast * f1_cast; - let mut r_bytes = [0u8; 32 * 12]; - r.to_coeffs() - .iter() - .flat_map(|fp2| fp2.clone().to_coeffs()) - .enumerate() - .for_each(|(i, fp)| r_bytes[i * 32..(i + 1) * 32].copy_from_slice(fp.as_le_bytes())); - - assert_eq!(r_bytes, r_cmp); - } -} - -#[cfg(feature = "bls12_381")] -mod bls12_381 { - use openvm_pairing_guest::bls12_381::{Fp, Fp12}; - - use super::*; - - openvm::init!("openvm_init_fp12_mul_bls12_381.rs"); - - pub fn test_fp12_mul(io: &[u8]) { - assert_eq!(io.len(), 48 * 36); - - let f0 = &io[0..48 * 12]; - let f1 = &io[48 * 12..48 * 24]; - let r_cmp = &io[48 * 24..48 * 36]; - - let f0_cast = Fp12::from_bytes(f0); - let f1_cast = Fp12::from_bytes(f1); - - let r = f0_cast * f1_cast; - let mut r_bytes = [0u8; 48 * 12]; - r.to_coeffs() - .iter() - .flat_map(|fp2| fp2.clone().to_coeffs()) - .enumerate() - .for_each(|(i, fp)| r_bytes[i * 48..(i + 1) * 48].copy_from_slice(fp.as_le_bytes())); - - assert_eq!(r_bytes, r_cmp); - } -} - -pub fn main() { - #[allow(unused_variables)] - let io = read_vec(); - - #[cfg(feature = "bn254")] - { - bn254::test_fp12_mul(&io) - } - #[cfg(feature = "bls12_381")] - { - bls12_381::test_fp12_mul(&io) - } -} diff --git a/extensions/pairing/tests/programs/examples/pairing_check.rs b/extensions/pairing/tests/programs/examples/pairing_check.rs deleted file mode 100644 index c01caded79..0000000000 --- a/extensions/pairing/tests/programs/examples/pairing_check.rs +++ /dev/null @@ -1,90 +0,0 @@ -#![cfg_attr(not(feature = "std"), no_main)] -#![cfg_attr(not(feature = "std"), no_std)] -#![allow(unused_imports)] - -extern crate alloc; - -use openvm::io::read_vec; -use openvm_ecc_guest::AffinePoint; -use openvm_pairing_guest::pairing::PairingCheck; - -openvm::entry!(main); - -#[cfg(feature = "bn254")] -mod bn254 { - use alloc::format; - - use openvm_algebra_guest::{field::FieldExtension, IntMod}; - use openvm_pairing_guest::bn254::{Bn254, Fp, Fp2}; - - use super::*; - - openvm::init!("openvm_init_pairing_check_bn254.rs"); - - pub fn test_pairing_check(io: &[u8]) { - let s0 = &io[0..32 * 2]; - let s1 = &io[32 * 2..32 * 4]; - let q0 = &io[32 * 4..32 * 8]; - let q1 = &io[32 * 8..32 * 12]; - - let s0_cast = - AffinePoint::new(Fp::from_le_bytes(&s0[..32]), Fp::from_le_bytes(&s0[32..64])); - let s1_cast = - AffinePoint::new(Fp::from_le_bytes(&s1[..32]), Fp::from_le_bytes(&s1[32..64])); - let q0_cast = AffinePoint::new(Fp2::from_bytes(&q0[..64]), Fp2::from_bytes(&q0[64..128])); - let q1_cast = AffinePoint::new(Fp2::from_bytes(&q1[..64]), Fp2::from_bytes(&q1[64..128])); - - let f = Bn254::pairing_check( - &[s0_cast.clone(), s1_cast.clone()], - &[q0_cast.clone(), q1_cast.clone()], - ); - assert_eq!(f, Ok(())); - } -} - -#[cfg(feature = "bls12_381")] -mod bls12_381 { - - use alloc::format; - - use openvm_algebra_guest::{field::FieldExtension, IntMod}; - use openvm_pairing_guest::bls12_381::{Bls12_381, Fp, Fp2}; - - use super::*; - - openvm::init!("openvm_init_pairing_check_bls12_381.rs"); - - pub fn test_pairing_check(io: &[u8]) { - let s0 = &io[0..48 * 2]; - let s1 = &io[48 * 2..48 * 4]; - let q0 = &io[48 * 4..48 * 8]; - let q1 = &io[48 * 8..48 * 12]; - - let s0_cast = - AffinePoint::new(Fp::from_le_bytes(&s0[..48]), Fp::from_le_bytes(&s0[48..96])); - let s1_cast = - AffinePoint::new(Fp::from_le_bytes(&s1[..48]), Fp::from_le_bytes(&s1[48..96])); - let q0_cast = AffinePoint::new(Fp2::from_bytes(&q0[..96]), Fp2::from_bytes(&q0[96..192])); - let q1_cast = AffinePoint::new(Fp2::from_bytes(&q1[..96]), Fp2::from_bytes(&q1[96..192])); - - let f = Bls12_381::pairing_check( - &[s0_cast.clone(), s1_cast.clone()], - &[q0_cast.clone(), q1_cast.clone()], - ); - assert_eq!(f, Ok(())); - } -} - -pub fn main() { - #[allow(unused_variables)] - let io = read_vec(); - - #[cfg(feature = "bn254")] - { - bn254::test_pairing_check(&io); - } - #[cfg(feature = "bls12_381")] - { - bls12_381::test_pairing_check(&io); - } -} diff --git a/extensions/pairing/tests/programs/examples/pairing_check_fallback.rs b/extensions/pairing/tests/programs/examples/pairing_check_fallback.rs deleted file mode 100644 index da3bcbb16f..0000000000 --- a/extensions/pairing/tests/programs/examples/pairing_check_fallback.rs +++ /dev/null @@ -1,241 +0,0 @@ -#![cfg_attr(not(feature = "std"), no_main)] -#![cfg_attr(not(feature = "std"), no_std)] -#![allow(unused_imports)] - -extern crate alloc; - -use openvm::io::read_vec; -use openvm_algebra_guest::{ - field::{ComplexConjugate, FieldExtension}, - DivUnsafe, Field, IntMod, -}; -use openvm_ecc_guest::AffinePoint; -use openvm_pairing_guest::pairing::{ - exp_check_fallback, MultiMillerLoop, PairingCheck, PairingCheckError, -}; - -openvm::entry!(main); - -#[cfg(feature = "bn254")] -mod bn254 { - use alloc::format; - - use openvm_pairing_guest::bn254::{Bn254, Fp, Fp12, Fp2}; - - use super::*; - - openvm::init!("openvm_init_pairing_check_fallback_bn254.rs"); - - // Wrapper so that we can override `pairing_check_hint` - struct Bn254Wrapper(Bn254); - - #[allow(non_snake_case)] - impl PairingCheck for Bn254Wrapper { - type Fp = Fp; - type Fp2 = Fp2; - type Fp12 = Fp12; - - fn pairing_check_hint( - _P: &[AffinePoint], - _Q: &[AffinePoint], - ) -> (Self::Fp12, Self::Fp12) { - // return dummy values - (Fp12::ZERO, Fp12::ZERO) - } - - // copied from Bn254::pairing_check - fn pairing_check( - P: &[AffinePoint], - Q: &[AffinePoint], - ) -> Result<(), PairingCheckError> { - Self::try_honest_pairing_check(P, Q).unwrap_or_else(|| { - let f = Bn254::multi_miller_loop(P, Q); - exp_check_fallback(&f, &Bn254::FINAL_EXPONENT) - }) - } - } - - #[allow(non_snake_case)] - impl Bn254Wrapper { - // copied from Bn254::try_honest_pairing_check - fn try_honest_pairing_check( - P: &[AffinePoint<::Fp>], - Q: &[AffinePoint<::Fp2>], - ) -> Option> { - let (c, s) = Self::pairing_check_hint(P, Q); - - // f * s = c^{q - x} - // f * s = c^q * c^-x - // f * c^x * c^-q * s = 1, - // where fc = f * c'^x (embedded Miller loop with c conjugate inverse), - // and the curve seed x = -0xd201000000010000 - // the miller loop computation includes a conjugation at the end because the value of - // the seed is negative, so we need to conjugate the miller loop input c - // as c'. We then substitute y = -x to get c^-y and finally compute c'^-y - // as input to the miller loop: f * c'^-y * c^-q * s = 1 - let c_q = FieldExtension::frobenius_map(&c, 1); - let c_conj = c.conjugate(); - if c_conj == Fp12::ZERO { - return None; - } - let c_conj_inv = Fp12::ONE.div_unsafe(&c_conj); - - // fc = f_{Miller,x,Q}(P) * c^{x} - // where - // fc = conjugate( f_{Miller,-x,Q}(P) * c'^{-x} ), with c' denoting the conjugate of c - let fc = Bn254::multi_miller_loop_embedded_exp(P, Q, Some(c_conj_inv)); - - if fc * s == c_q { - Some(Ok(())) - } else { - None - } - } - } - - pub fn test_pairing_check(io: &[u8]) { - let s0 = &io[0..32 * 2]; - let s1 = &io[32 * 2..32 * 4]; - let q0 = &io[32 * 4..32 * 8]; - let q1 = &io[32 * 8..32 * 12]; - - let s0_cast = - AffinePoint::new(Fp::from_le_bytes(&s0[..32]), Fp::from_le_bytes(&s0[32..64])); - let s1_cast = - AffinePoint::new(Fp::from_le_bytes(&s1[..32]), Fp::from_le_bytes(&s1[32..64])); - let q0_cast = AffinePoint::new(Fp2::from_bytes(&q0[..64]), Fp2::from_bytes(&q0[64..128])); - let q1_cast = AffinePoint::new(Fp2::from_bytes(&q1[..64]), Fp2::from_bytes(&q1[64..128])); - - let f = Bn254Wrapper::pairing_check( - &[s0_cast.clone(), s1_cast.clone()], - &[q0_cast.clone(), q1_cast.clone()], - ); - assert_eq!(f, Ok(())); - - let f = Bn254Wrapper::pairing_check( - &[-s0_cast.clone(), s1_cast.clone()], - &[q0_cast.clone(), q1_cast.clone()], - ); - assert_eq!(f, Err(PairingCheckError)); - } -} - -#[cfg(feature = "bls12_381")] -mod bls12_381 { - - use alloc::format; - - use openvm_pairing_guest::bls12_381::{Bls12_381, Fp, Fp12, Fp2}; - - use super::*; - - openvm::init!("openvm_init_pairing_check_fallback_bls12_381.rs"); - - // Wrapper so that we can override `pairing_check_hint` - struct Bls12_381Wrapper(Bls12_381); - - #[allow(non_snake_case)] - impl PairingCheck for Bls12_381Wrapper { - type Fp = Fp; - type Fp2 = Fp2; - type Fp12 = Fp12; - - #[allow(unused_variables)] - fn pairing_check_hint( - _P: &[AffinePoint], - _Q: &[AffinePoint], - ) -> (Self::Fp12, Self::Fp12) { - // return dummy values - (Fp12::ZERO, Fp12::ZERO) - } - - // copied from Bls12_381::pairing_check - fn pairing_check( - P: &[AffinePoint], - Q: &[AffinePoint], - ) -> Result<(), PairingCheckError> { - Self::try_honest_pairing_check(P, Q).unwrap_or_else(|| { - let f = Bls12_381::multi_miller_loop(P, Q); - exp_check_fallback(&f, &Bls12_381::FINAL_EXPONENT) - }) - } - } - - #[allow(non_snake_case)] - impl Bls12_381Wrapper { - // copied from Bls12_381::try_honest_pairing_check - fn try_honest_pairing_check( - P: &[AffinePoint<::Fp>], - Q: &[AffinePoint<::Fp2>], - ) -> Option> { - let (c, s) = Self::pairing_check_hint(P, Q); - - // f * s = c^{q - x} - // f * s = c^q * c^-x - // f * c^x * c^-q * s = 1, - // where fc = f * c'^x (embedded Miller loop with c conjugate inverse), - // and the curve seed x = -0xd201000000010000 - // the miller loop computation includes a conjugation at the end because the value of - // the seed is negative, so we need to conjugate the miller loop input c - // as c'. We then substitute y = -x to get c^-y and finally compute c'^-y - // as input to the miller loop: f * c'^-y * c^-q * s = 1 - let c_q = FieldExtension::frobenius_map(&c, 1); - let c_conj = c.conjugate(); - if c_conj == Fp12::ZERO { - return None; - } - let c_conj_inv = Fp12::ONE.div_unsafe(&c_conj); - - // fc = f_{Miller,x,Q}(P) * c^{x} - // where - // fc = conjugate( f_{Miller,-x,Q}(P) * c'^{-x} ), with c' denoting the conjugate of c - let fc = Bls12_381::multi_miller_loop_embedded_exp(P, Q, Some(c_conj_inv)); - - if fc * s == c_q { - Some(Ok(())) - } else { - None - } - } - } - - pub fn test_pairing_check(io: &[u8]) { - let s0 = &io[0..48 * 2]; - let s1 = &io[48 * 2..48 * 4]; - let q0 = &io[48 * 4..48 * 8]; - let q1 = &io[48 * 8..48 * 12]; - - let s0_cast = - AffinePoint::new(Fp::from_le_bytes(&s0[..48]), Fp::from_le_bytes(&s0[48..96])); - let s1_cast = - AffinePoint::new(Fp::from_le_bytes(&s1[..48]), Fp::from_le_bytes(&s1[48..96])); - let q0_cast = AffinePoint::new(Fp2::from_bytes(&q0[..96]), Fp2::from_bytes(&q0[96..192])); - let q1_cast = AffinePoint::new(Fp2::from_bytes(&q1[..96]), Fp2::from_bytes(&q1[96..192])); - - let f = Bls12_381Wrapper::pairing_check( - &[s0_cast.clone(), s1_cast.clone()], - &[q0_cast.clone(), q1_cast.clone()], - ); - assert_eq!(f, Ok(())); - - let f = Bls12_381Wrapper::pairing_check( - &[-s0_cast.clone(), s1_cast.clone()], - &[q0_cast.clone(), q1_cast.clone()], - ); - assert_eq!(f, Err(PairingCheckError)); - } -} - -pub fn main() { - #[allow(unused_variables)] - let io = read_vec(); - - #[cfg(feature = "bn254")] - { - bn254::test_pairing_check(&io); - } - #[cfg(feature = "bls12_381")] - { - bls12_381::test_pairing_check(&io); - } -} diff --git a/extensions/pairing/tests/programs/examples/pairing_line.rs b/extensions/pairing/tests/programs/examples/pairing_line.rs deleted file mode 100644 index b36c200391..0000000000 --- a/extensions/pairing/tests/programs/examples/pairing_line.rs +++ /dev/null @@ -1,147 +0,0 @@ -#![allow(unused_imports)] -#![cfg_attr(not(feature = "std"), no_main)] -#![cfg_attr(not(feature = "std"), no_std)] - -use openvm::io::read_vec; -use openvm_algebra_guest::{field::FieldExtension, IntMod}; -use openvm_pairing_guest::pairing::{EvaluatedLine, LineMulDType, LineMulMType}; - -openvm::entry!(main); - -#[cfg(feature = "bn254")] -mod bn254 { - use openvm_pairing_guest::bn254::{Bn254, Fp, Fp12, Fp2}; - - use super::*; - - openvm::init!("openvm_init_pairing_line_bn254.rs"); - - pub fn test_mul_013_by_013(io: &[u8]) { - assert_eq!(io.len(), 32 * 18); - let l0 = &io[..32 * 4]; - let l1 = &io[32 * 4..32 * 8]; - let expected = &io[32 * 8..32 * 18]; - - let l0_cast = EvaluatedLine { - b: Fp2::from_bytes(&l0[..64]), - c: Fp2::from_bytes(&l0[64..128]), - }; - let l1_cast = EvaluatedLine { - b: Fp2::from_bytes(&l1[..64]), - c: Fp2::from_bytes(&l1[64..128]), - }; - - let r = Bn254::mul_013_by_013(&l0_cast, &l1_cast); - let mut r_bytes = [0u8; 32 * 10]; - let mut i = 0; - for x in r { - r_bytes[i..i + 32].copy_from_slice(x.c0.as_le_bytes()); - r_bytes[i + 32..i + 64].copy_from_slice(x.c1.as_le_bytes()); - i += 64; - } - assert_eq!(r_bytes, expected); - } - - pub fn test_mul_by_01234(io: &[u8]) { - assert_eq!(io.len(), 32 * 34); - let f = &io[..32 * 12]; - let x = &io[32 * 12..32 * 22]; - let expected = &io[32 * 22..32 * 34]; - - let f_cast = Fp12::from_bytes(f); - let x_cast = [ - Fp2::from_bytes(&x[..64]), - Fp2::from_bytes(&x[64..128]), - Fp2::from_bytes(&x[128..192]), - Fp2::from_bytes(&x[192..256]), - Fp2::from_bytes(&x[256..320]), - ]; - - let r = Bn254::mul_by_01234(&f_cast, &x_cast); - let mut r_bytes = [0u8; 32 * 12]; - let mut i = 0; - for x in r.to_coeffs() { - r_bytes[i..i + 32].copy_from_slice(x.c0.as_le_bytes()); - r_bytes[i + 32..i + 64].copy_from_slice(x.c1.as_le_bytes()); - i += 64; - } - assert_eq!(r_bytes, expected); - } -} - -#[cfg(feature = "bls12_381")] -mod bls12_381 { - use openvm_pairing_guest::bls12_381::{Bls12_381, Fp, Fp12, Fp2}; - - use super::*; - - openvm::init!("openvm_init_pairing_line_bls12_381.rs"); - - pub fn test_mul_023_by_023(io: &[u8]) { - assert_eq!(io.len(), 48 * 18); - let l0 = &io[..48 * 4]; - let l1 = &io[48 * 4..48 * 8]; - let expected = &io[48 * 8..48 * 18]; - - let l0_cast = EvaluatedLine { - b: Fp2::from_bytes(&l0[..48 * 2]), - c: Fp2::from_bytes(&l0[48 * 2..48 * 4]), - }; - let l1_cast = EvaluatedLine { - b: Fp2::from_bytes(&l1[..48 * 2]), - c: Fp2::from_bytes(&l1[48 * 2..48 * 4]), - }; - - let r = Bls12_381::mul_023_by_023(&l0_cast, &l1_cast); - let mut r_bytes = [0u8; 48 * 10]; - let mut i = 0; - for x in r { - r_bytes[i..i + 48].copy_from_slice(x.c0.as_le_bytes()); - r_bytes[i + 48..i + 96].copy_from_slice(x.c1.as_le_bytes()); - i += 96; - } - assert_eq!(r_bytes, expected); - } - - pub fn test_mul_by_02345(io: &[u8]) { - assert_eq!(io.len(), 48 * 34); - let f = &io[..48 * 12]; - let x = &io[48 * 12..48 * 22]; - let expected = &io[48 * 22..48 * 34]; - - let f_cast = Fp12::from_bytes(f); - let x_cast = [ - Fp2::from_bytes(&x[..48 * 2]), - Fp2::from_bytes(&x[48 * 2..48 * 4]), - Fp2::from_bytes(&x[48 * 4..48 * 6]), - Fp2::from_bytes(&x[48 * 6..48 * 8]), - Fp2::from_bytes(&x[48 * 8..48 * 10]), - ]; - - let r = Bls12_381::mul_by_02345(&f_cast, &x_cast); - let mut r_bytes = [0u8; 48 * 12]; - let mut i = 0; - for x in r.to_coeffs() { - r_bytes[i..i + 48].copy_from_slice(x.c0.as_le_bytes()); - r_bytes[i + 48..i + 96].copy_from_slice(x.c1.as_le_bytes()); - i += 96; - } - assert_eq!(r_bytes, expected); - } -} - -pub fn main() { - #[allow(unused_variables)] - let io = read_vec(); - - #[cfg(feature = "bn254")] - { - bn254::test_mul_013_by_013(&io[..32 * 18]); - bn254::test_mul_by_01234(&io[32 * 18..32 * 52]); - } - #[cfg(feature = "bls12_381")] - { - bls12_381::test_mul_023_by_023(&io[..48 * 18]); - bls12_381::test_mul_by_02345(&io[48 * 18..48 * 52]); - } -} diff --git a/extensions/pairing/tests/programs/examples/pairing_miller_loop.rs b/extensions/pairing/tests/programs/examples/pairing_miller_loop.rs deleted file mode 100644 index a9d8f09dbd..0000000000 --- a/extensions/pairing/tests/programs/examples/pairing_miller_loop.rs +++ /dev/null @@ -1,97 +0,0 @@ -#![allow(unused_imports)] -#![cfg_attr(not(feature = "std"), no_main)] -#![cfg_attr(not(feature = "std"), no_std)] - -extern crate alloc; - -use openvm::io::read_vec; -use openvm_algebra_guest::{field::FieldExtension, IntMod}; -use openvm_ecc_guest::AffinePoint; -use openvm_pairing_guest::pairing::MultiMillerLoop; - -openvm::entry!(main); - -#[cfg(feature = "bn254")] -mod bn254 { - use openvm_pairing_guest::bn254::{Bn254, Fp, Fp2}; - - use super::*; - - openvm::init!("openvm_init_pairing_miller_loop_bn254.rs"); - - pub fn test_miller_loop(io: &[u8]) { - let s0 = &io[0..32 * 2]; - let s1 = &io[32 * 2..32 * 4]; - let q0 = &io[32 * 4..32 * 8]; - let q1 = &io[32 * 8..32 * 12]; - let f_cmp = &io[32 * 12..32 * 24]; - - let s0_cast = - AffinePoint::new(Fp::from_le_bytes(&s0[..32]), Fp::from_le_bytes(&s0[32..64])); - let s1_cast = - AffinePoint::new(Fp::from_le_bytes(&s1[..32]), Fp::from_le_bytes(&s1[32..64])); - let q0_cast = AffinePoint::new(Fp2::from_bytes(&q0[..64]), Fp2::from_bytes(&q0[64..128])); - let q1_cast = AffinePoint::new(Fp2::from_bytes(&q1[..64]), Fp2::from_bytes(&q1[64..128])); - - let f = Bn254::multi_miller_loop(&[s0_cast, s1_cast], &[q0_cast, q1_cast]); - let mut f_bytes = [0u8; 32 * 12]; - f.to_coeffs() - .iter() - .flat_map(|fp2| fp2.clone().to_coeffs()) - .enumerate() - .for_each(|(i, fp)| f_bytes[i * 32..(i + 1) * 32].copy_from_slice(fp.as_le_bytes())); - - assert_eq!(f_bytes, f_cmp); - } -} - -#[cfg(feature = "bls12_381")] -mod bls12_381 { - use openvm_pairing_guest::bls12_381::{Bls12_381, Fp, Fp2}; - - use super::*; - - openvm::init!("openvm_init_pairing_miller_loop_bls12_381.rs"); - - pub fn test_miller_loop(io: &[u8]) { - let s0 = &io[0..48 * 2]; - let s1 = &io[48 * 2..48 * 4]; - let q0 = &io[48 * 4..48 * 8]; - let q1 = &io[48 * 8..48 * 12]; - let f_cmp = &io[48 * 12..48 * 24]; - - let s0_cast = - AffinePoint::new(Fp::from_le_bytes(&s0[..48]), Fp::from_le_bytes(&s0[48..96])); - let s1_cast = - AffinePoint::new(Fp::from_le_bytes(&s1[..48]), Fp::from_le_bytes(&s1[48..96])); - let q0_cast = AffinePoint::new(Fp2::from_bytes(&q0[..96]), Fp2::from_bytes(&q0[96..192])); - let q1_cast = AffinePoint::new(Fp2::from_bytes(&q1[..96]), Fp2::from_bytes(&q1[96..192])); - - let f = Bls12_381::multi_miller_loop( - &[s0_cast.clone(), s1_cast.clone()], - &[q0_cast.clone(), q1_cast.clone()], - ); - let mut f_bytes = [0u8; 48 * 12]; - f.to_coeffs() - .iter() - .flat_map(|fp2| fp2.clone().to_coeffs()) - .enumerate() - .for_each(|(i, fp)| f_bytes[i * 48..(i + 1) * 48].copy_from_slice(fp.as_le_bytes())); - - assert_eq!(f_bytes, f_cmp); - } -} - -pub fn main() { - #[allow(unused_variables)] - let io = read_vec(); - - #[cfg(feature = "bn254")] - { - bn254::test_miller_loop(&io); - } - #[cfg(feature = "bls12_381")] - { - bls12_381::test_miller_loop(&io); - } -} diff --git a/extensions/pairing/tests/programs/examples/pairing_miller_step.rs b/extensions/pairing/tests/programs/examples/pairing_miller_step.rs deleted file mode 100644 index a2d4662cf4..0000000000 --- a/extensions/pairing/tests/programs/examples/pairing_miller_step.rs +++ /dev/null @@ -1,166 +0,0 @@ -#![allow(unused_imports)] -#![cfg_attr(not(feature = "std"), no_main)] -#![cfg_attr(not(feature = "std"), no_std)] - -use openvm::io::read_vec; -use openvm_algebra_guest::IntMod; -use openvm_ecc_guest::AffinePoint; -use openvm_pairing_guest::pairing::MillerStep; - -openvm::entry!(main); - -#[cfg(feature = "bn254")] -mod bn254 { - use openvm_algebra_guest::field::FieldExtension; - use openvm_pairing_guest::bn254::{Bn254, Fp, Fp2}; - - use super::*; - - openvm::init!("openvm_init_pairing_miller_step_bn254.rs"); - - pub fn test_miller_step(io: &[u8]) { - assert_eq!(io.len(), 32 * 12); - let s = &io[..32 * 4]; - let pt = &io[32 * 4..32 * 8]; - let l = &io[32 * 8..32 * 12]; - - let s_cast = AffinePoint::new(Fp2::from_bytes(&s[..64]), Fp2::from_bytes(&s[64..128])); - - let (pt_cmp, l_cmp) = Bn254::miller_double_step(&s_cast); - let mut pt_bytes = [0u8; 32 * 4]; - let mut l_bytes = [0u8; 32 * 4]; - - // TODO: if we ever need to change this, we should switch to using `StdIn::write` to - // serialize for us and use `read()` instead of `read_vec()` - pt_bytes[0..32].copy_from_slice(pt_cmp.x.c0.as_le_bytes()); - pt_bytes[32..2 * 32].copy_from_slice(pt_cmp.x.c1.as_le_bytes()); - pt_bytes[2 * 32..3 * 32].copy_from_slice(pt_cmp.y.c0.as_le_bytes()); - pt_bytes[3 * 32..4 * 32].copy_from_slice(pt_cmp.y.c1.as_le_bytes()); - l_bytes[0..32].copy_from_slice(l_cmp.b.c0.as_le_bytes()); - l_bytes[32..2 * 32].copy_from_slice(l_cmp.b.c1.as_le_bytes()); - l_bytes[2 * 32..3 * 32].copy_from_slice(l_cmp.c.c0.as_le_bytes()); - l_bytes[3 * 32..4 * 32].copy_from_slice(l_cmp.c.c1.as_le_bytes()); - - assert_eq!(pt_bytes, pt); - assert_eq!(l_bytes, l); - } - - pub fn test_miller_double_and_add_step(io: &[u8]) { - assert_eq!(io.len(), 32 * 20); - let s = &io[0..32 * 4]; - let q = &io[32 * 4..32 * 8]; - let pt = &io[32 * 8..32 * 12]; - let l0 = &io[32 * 12..32 * 16]; - let l1 = &io[32 * 16..32 * 20]; - - let s_cast = AffinePoint::new(Fp2::from_bytes(&s[..64]), Fp2::from_bytes(&s[64..128])); - let q_cast = AffinePoint::new(Fp2::from_bytes(&q[..64]), Fp2::from_bytes(&q[64..128])); - let (pt_cmp, l0_cmp, l1_cmp) = Bn254::miller_double_and_add_step(&s_cast, &q_cast); - let mut pt_bytes = [0u8; 32 * 4]; - let mut l0_bytes = [0u8; 32 * 4]; - let mut l1_bytes = [0u8; 32 * 4]; - - // TODO: if we ever need to change this, we should switch to using `StdIn::write` to - // serialize for us and use `read()` instead of `read_vec()` - pt_bytes[0..32].copy_from_slice(pt_cmp.x.c0.as_le_bytes()); - pt_bytes[32..2 * 32].copy_from_slice(pt_cmp.x.c1.as_le_bytes()); - pt_bytes[2 * 32..3 * 32].copy_from_slice(pt_cmp.y.c0.as_le_bytes()); - pt_bytes[3 * 32..4 * 32].copy_from_slice(pt_cmp.y.c1.as_le_bytes()); - l0_bytes[0..32].copy_from_slice(l0_cmp.b.c0.as_le_bytes()); - l0_bytes[32..2 * 32].copy_from_slice(l0_cmp.b.c1.as_le_bytes()); - l0_bytes[2 * 32..3 * 32].copy_from_slice(l0_cmp.c.c0.as_le_bytes()); - l0_bytes[3 * 32..4 * 32].copy_from_slice(l0_cmp.c.c1.as_le_bytes()); - l1_bytes[0..32].copy_from_slice(l1_cmp.b.c0.as_le_bytes()); - l1_bytes[32..2 * 32].copy_from_slice(l1_cmp.b.c1.as_le_bytes()); - l1_bytes[2 * 32..3 * 32].copy_from_slice(l1_cmp.c.c0.as_le_bytes()); - l1_bytes[3 * 32..4 * 32].copy_from_slice(l1_cmp.c.c1.as_le_bytes()); - - assert_eq!(pt_bytes, pt); - assert_eq!(l0_bytes, l0); - assert_eq!(l1_bytes, l1); - } -} - -#[cfg(feature = "bls12_381")] -mod bls12_381 { - use openvm_algebra_guest::field::FieldExtension; - use openvm_pairing_guest::bls12_381::{Bls12_381, Fp, Fp2}; - - use super::*; - - openvm::init!("openvm_init_pairing_miller_step_bls12_381.rs"); - - pub fn test_miller_step(io: &[u8]) { - assert_eq!(io.len(), 48 * 12); - let s = &io[..48 * 4]; - let pt = &io[48 * 4..48 * 8]; - let l = &io[48 * 8..48 * 12]; - - let s_cast = AffinePoint::new(Fp2::from_bytes(&s[..96]), Fp2::from_bytes(&s[96..192])); - - let (pt_cmp, l_cmp) = Bls12_381::miller_double_step(&s_cast); - let mut pt_bytes = [0u8; 48 * 4]; - let mut l_bytes = [0u8; 48 * 4]; - - pt_bytes[0..48].copy_from_slice(pt_cmp.x.c0.as_le_bytes()); - pt_bytes[48..2 * 48].copy_from_slice(pt_cmp.x.c1.as_le_bytes()); - pt_bytes[2 * 48..3 * 48].copy_from_slice(pt_cmp.y.c0.as_le_bytes()); - pt_bytes[3 * 48..4 * 48].copy_from_slice(pt_cmp.y.c1.as_le_bytes()); - l_bytes[0..48].copy_from_slice(l_cmp.b.c0.as_le_bytes()); - l_bytes[48..2 * 48].copy_from_slice(l_cmp.b.c1.as_le_bytes()); - l_bytes[2 * 48..3 * 48].copy_from_slice(l_cmp.c.c0.as_le_bytes()); - l_bytes[3 * 48..4 * 48].copy_from_slice(l_cmp.c.c1.as_le_bytes()); - - assert_eq!(pt_bytes, pt); - assert_eq!(l_bytes, l); - } - - pub fn test_miller_double_and_add_step(io: &[u8]) { - assert_eq!(io.len(), 48 * 20); - let s = &io[0..48 * 4]; - let q = &io[48 * 4..48 * 8]; - let pt = &io[48 * 8..48 * 12]; - let l0 = &io[48 * 12..48 * 16]; - let l1 = &io[48 * 16..48 * 20]; - - let s_cast = AffinePoint::new(Fp2::from_bytes(&s[..96]), Fp2::from_bytes(&s[96..192])); - let q_cast = AffinePoint::new(Fp2::from_bytes(&q[..96]), Fp2::from_bytes(&q[96..192])); - let (pt_cmp, l0_cmp, l1_cmp) = Bls12_381::miller_double_and_add_step(&s_cast, &q_cast); - let mut pt_bytes = [0u8; 48 * 4]; - let mut l0_bytes = [0u8; 48 * 4]; - let mut l1_bytes = [0u8; 48 * 4]; - - pt_bytes[0..48].copy_from_slice(pt_cmp.x.c0.as_le_bytes()); - pt_bytes[48..2 * 48].copy_from_slice(pt_cmp.x.c1.as_le_bytes()); - pt_bytes[2 * 48..3 * 48].copy_from_slice(pt_cmp.y.c0.as_le_bytes()); - pt_bytes[3 * 48..4 * 48].copy_from_slice(pt_cmp.y.c1.as_le_bytes()); - l0_bytes[0..48].copy_from_slice(l0_cmp.b.c0.as_le_bytes()); - l0_bytes[48..2 * 48].copy_from_slice(l0_cmp.b.c1.as_le_bytes()); - l0_bytes[2 * 48..3 * 48].copy_from_slice(l0_cmp.c.c0.as_le_bytes()); - l0_bytes[3 * 48..4 * 48].copy_from_slice(l0_cmp.c.c1.as_le_bytes()); - l1_bytes[0..48].copy_from_slice(l1_cmp.b.c0.as_le_bytes()); - l1_bytes[48..2 * 48].copy_from_slice(l1_cmp.b.c1.as_le_bytes()); - l1_bytes[2 * 48..3 * 48].copy_from_slice(l1_cmp.c.c0.as_le_bytes()); - l1_bytes[3 * 48..4 * 48].copy_from_slice(l1_cmp.c.c1.as_le_bytes()); - - assert_eq!(pt_bytes, pt); - assert_eq!(l0_bytes, l0); - assert_eq!(l1_bytes, l1); - } -} - -pub fn main() { - #[allow(unused_variables)] - let io = read_vec(); - - #[cfg(feature = "bn254")] - { - bn254::test_miller_step(&io[..32 * 12]); - bn254::test_miller_double_and_add_step(&io[32 * 12..]); - } - #[cfg(feature = "bls12_381")] - { - bls12_381::test_miller_step(&io[..48 * 12]); - bls12_381::test_miller_double_and_add_step(&io[48 * 12..]); - } -} diff --git a/extensions/pairing/tests/programs/openvm_init_bls_ec_bls12_381.rs b/extensions/pairing/tests/programs/openvm_init_bls_ec_bls12_381.rs deleted file mode 100644 index 95a4e46fd3..0000000000 --- a/extensions/pairing/tests/programs/openvm_init_bls_ec_bls12_381.rs +++ /dev/null @@ -1,3 +0,0 @@ -// This file is automatically generated by cargo openvm. Do not rename or edit. -openvm_algebra_guest::moduli_macros::moduli_init! { "4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787", "52435875175126190479447740508185965837690552500527637822603658699938581184513" } -openvm_ecc_guest::sw_macros::sw_init! { Bls12_381G1Affine } diff --git a/extensions/pairing/tests/programs/openvm_init_bls_final_exp_hint_bls12_381.rs b/extensions/pairing/tests/programs/openvm_init_bls_final_exp_hint_bls12_381.rs deleted file mode 100644 index 00181b03ef..0000000000 --- a/extensions/pairing/tests/programs/openvm_init_bls_final_exp_hint_bls12_381.rs +++ /dev/null @@ -1,4 +0,0 @@ -// This file is automatically generated by cargo openvm. Do not rename or edit. -openvm_algebra_guest::moduli_macros::moduli_init! { "4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787" } -openvm_algebra_guest::complex_macros::complex_init! { Bls12_381Fp2 { mod_idx = 0 } } -openvm_ecc_guest::sw_macros::sw_init! { } diff --git a/extensions/pairing/tests/programs/openvm_init_bn_final_exp_hint_bn254.rs b/extensions/pairing/tests/programs/openvm_init_bn_final_exp_hint_bn254.rs deleted file mode 100644 index c130859ad8..0000000000 --- a/extensions/pairing/tests/programs/openvm_init_bn_final_exp_hint_bn254.rs +++ /dev/null @@ -1,4 +0,0 @@ -// This file is automatically generated by cargo openvm. Do not rename or edit. -openvm_algebra_guest::moduli_macros::moduli_init! { "21888242871839275222246405745257275088696311157297823662689037894645226208583" } -openvm_algebra_guest::complex_macros::complex_init! { Bn254Fp2 { mod_idx = 0 } } -openvm_ecc_guest::sw_macros::sw_init! { } diff --git a/extensions/pairing/tests/programs/openvm_init_fp12_mul_bls12_381.rs b/extensions/pairing/tests/programs/openvm_init_fp12_mul_bls12_381.rs deleted file mode 100644 index c130859ad8..0000000000 --- a/extensions/pairing/tests/programs/openvm_init_fp12_mul_bls12_381.rs +++ /dev/null @@ -1,4 +0,0 @@ -// This file is automatically generated by cargo openvm. Do not rename or edit. -openvm_algebra_guest::moduli_macros::moduli_init! { "21888242871839275222246405745257275088696311157297823662689037894645226208583" } -openvm_algebra_guest::complex_macros::complex_init! { Bn254Fp2 { mod_idx = 0 } } -openvm_ecc_guest::sw_macros::sw_init! { } diff --git a/extensions/pairing/tests/programs/openvm_init_fp12_mul_bn254.rs b/extensions/pairing/tests/programs/openvm_init_fp12_mul_bn254.rs deleted file mode 100644 index c130859ad8..0000000000 --- a/extensions/pairing/tests/programs/openvm_init_fp12_mul_bn254.rs +++ /dev/null @@ -1,4 +0,0 @@ -// This file is automatically generated by cargo openvm. Do not rename or edit. -openvm_algebra_guest::moduli_macros::moduli_init! { "21888242871839275222246405745257275088696311157297823662689037894645226208583" } -openvm_algebra_guest::complex_macros::complex_init! { Bn254Fp2 { mod_idx = 0 } } -openvm_ecc_guest::sw_macros::sw_init! { } diff --git a/extensions/pairing/tests/programs/openvm_init_pairing_check_bls12_381.rs b/extensions/pairing/tests/programs/openvm_init_pairing_check_bls12_381.rs deleted file mode 100644 index 00181b03ef..0000000000 --- a/extensions/pairing/tests/programs/openvm_init_pairing_check_bls12_381.rs +++ /dev/null @@ -1,4 +0,0 @@ -// This file is automatically generated by cargo openvm. Do not rename or edit. -openvm_algebra_guest::moduli_macros::moduli_init! { "4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787" } -openvm_algebra_guest::complex_macros::complex_init! { Bls12_381Fp2 { mod_idx = 0 } } -openvm_ecc_guest::sw_macros::sw_init! { } diff --git a/extensions/pairing/tests/programs/openvm_init_pairing_check_bn254.rs b/extensions/pairing/tests/programs/openvm_init_pairing_check_bn254.rs deleted file mode 100644 index c130859ad8..0000000000 --- a/extensions/pairing/tests/programs/openvm_init_pairing_check_bn254.rs +++ /dev/null @@ -1,4 +0,0 @@ -// This file is automatically generated by cargo openvm. Do not rename or edit. -openvm_algebra_guest::moduli_macros::moduli_init! { "21888242871839275222246405745257275088696311157297823662689037894645226208583" } -openvm_algebra_guest::complex_macros::complex_init! { Bn254Fp2 { mod_idx = 0 } } -openvm_ecc_guest::sw_macros::sw_init! { } diff --git a/extensions/pairing/tests/programs/openvm_init_pairing_check_fallback_bls12_381.rs b/extensions/pairing/tests/programs/openvm_init_pairing_check_fallback_bls12_381.rs deleted file mode 100644 index 00181b03ef..0000000000 --- a/extensions/pairing/tests/programs/openvm_init_pairing_check_fallback_bls12_381.rs +++ /dev/null @@ -1,4 +0,0 @@ -// This file is automatically generated by cargo openvm. Do not rename or edit. -openvm_algebra_guest::moduli_macros::moduli_init! { "4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787" } -openvm_algebra_guest::complex_macros::complex_init! { Bls12_381Fp2 { mod_idx = 0 } } -openvm_ecc_guest::sw_macros::sw_init! { } diff --git a/extensions/pairing/tests/programs/openvm_init_pairing_check_fallback_bn254.rs b/extensions/pairing/tests/programs/openvm_init_pairing_check_fallback_bn254.rs deleted file mode 100644 index c130859ad8..0000000000 --- a/extensions/pairing/tests/programs/openvm_init_pairing_check_fallback_bn254.rs +++ /dev/null @@ -1,4 +0,0 @@ -// This file is automatically generated by cargo openvm. Do not rename or edit. -openvm_algebra_guest::moduli_macros::moduli_init! { "21888242871839275222246405745257275088696311157297823662689037894645226208583" } -openvm_algebra_guest::complex_macros::complex_init! { Bn254Fp2 { mod_idx = 0 } } -openvm_ecc_guest::sw_macros::sw_init! { } diff --git a/extensions/pairing/tests/programs/openvm_init_pairing_line_bls12_381.rs b/extensions/pairing/tests/programs/openvm_init_pairing_line_bls12_381.rs deleted file mode 100644 index 00181b03ef..0000000000 --- a/extensions/pairing/tests/programs/openvm_init_pairing_line_bls12_381.rs +++ /dev/null @@ -1,4 +0,0 @@ -// This file is automatically generated by cargo openvm. Do not rename or edit. -openvm_algebra_guest::moduli_macros::moduli_init! { "4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787" } -openvm_algebra_guest::complex_macros::complex_init! { Bls12_381Fp2 { mod_idx = 0 } } -openvm_ecc_guest::sw_macros::sw_init! { } diff --git a/extensions/pairing/tests/programs/openvm_init_pairing_line_bn254.rs b/extensions/pairing/tests/programs/openvm_init_pairing_line_bn254.rs deleted file mode 100644 index c130859ad8..0000000000 --- a/extensions/pairing/tests/programs/openvm_init_pairing_line_bn254.rs +++ /dev/null @@ -1,4 +0,0 @@ -// This file is automatically generated by cargo openvm. Do not rename or edit. -openvm_algebra_guest::moduli_macros::moduli_init! { "21888242871839275222246405745257275088696311157297823662689037894645226208583" } -openvm_algebra_guest::complex_macros::complex_init! { Bn254Fp2 { mod_idx = 0 } } -openvm_ecc_guest::sw_macros::sw_init! { } diff --git a/extensions/pairing/tests/programs/openvm_init_pairing_miller_loop_bls12_381.rs b/extensions/pairing/tests/programs/openvm_init_pairing_miller_loop_bls12_381.rs deleted file mode 100644 index 00181b03ef..0000000000 --- a/extensions/pairing/tests/programs/openvm_init_pairing_miller_loop_bls12_381.rs +++ /dev/null @@ -1,4 +0,0 @@ -// This file is automatically generated by cargo openvm. Do not rename or edit. -openvm_algebra_guest::moduli_macros::moduli_init! { "4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787" } -openvm_algebra_guest::complex_macros::complex_init! { Bls12_381Fp2 { mod_idx = 0 } } -openvm_ecc_guest::sw_macros::sw_init! { } diff --git a/extensions/pairing/tests/programs/openvm_init_pairing_miller_loop_bn254.rs b/extensions/pairing/tests/programs/openvm_init_pairing_miller_loop_bn254.rs deleted file mode 100644 index c130859ad8..0000000000 --- a/extensions/pairing/tests/programs/openvm_init_pairing_miller_loop_bn254.rs +++ /dev/null @@ -1,4 +0,0 @@ -// This file is automatically generated by cargo openvm. Do not rename or edit. -openvm_algebra_guest::moduli_macros::moduli_init! { "21888242871839275222246405745257275088696311157297823662689037894645226208583" } -openvm_algebra_guest::complex_macros::complex_init! { Bn254Fp2 { mod_idx = 0 } } -openvm_ecc_guest::sw_macros::sw_init! { } diff --git a/extensions/pairing/tests/programs/openvm_init_pairing_miller_step_bls12_381.rs b/extensions/pairing/tests/programs/openvm_init_pairing_miller_step_bls12_381.rs deleted file mode 100644 index 00181b03ef..0000000000 --- a/extensions/pairing/tests/programs/openvm_init_pairing_miller_step_bls12_381.rs +++ /dev/null @@ -1,4 +0,0 @@ -// This file is automatically generated by cargo openvm. Do not rename or edit. -openvm_algebra_guest::moduli_macros::moduli_init! { "4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787" } -openvm_algebra_guest::complex_macros::complex_init! { Bls12_381Fp2 { mod_idx = 0 } } -openvm_ecc_guest::sw_macros::sw_init! { } diff --git a/extensions/pairing/tests/programs/openvm_init_pairing_miller_step_bn254.rs b/extensions/pairing/tests/programs/openvm_init_pairing_miller_step_bn254.rs deleted file mode 100644 index c130859ad8..0000000000 --- a/extensions/pairing/tests/programs/openvm_init_pairing_miller_step_bn254.rs +++ /dev/null @@ -1,4 +0,0 @@ -// This file is automatically generated by cargo openvm. Do not rename or edit. -openvm_algebra_guest::moduli_macros::moduli_init! { "21888242871839275222246405745257275088696311157297823662689037894645226208583" } -openvm_algebra_guest::complex_macros::complex_init! { Bn254Fp2 { mod_idx = 0 } } -openvm_ecc_guest::sw_macros::sw_init! { } diff --git a/extensions/pairing/tests/src/lib.rs b/extensions/pairing/tests/src/lib.rs deleted file mode 100644 index 7f12f01241..0000000000 --- a/extensions/pairing/tests/src/lib.rs +++ /dev/null @@ -1,884 +0,0 @@ -#![allow(non_snake_case)] - -#[cfg(test)] -mod bn254 { - use std::iter; - - use eyre::Result; - use halo2curves_axiom::{ - bn256::{Fq12, Fq2, Fr, G1Affine, G2Affine}, - ff::Field, - }; - use openvm_algebra_circuit::{Fp2Extension, ModularExtension}; - use openvm_algebra_transpiler::{Fp2TranspilerExtension, ModularTranspilerExtension}; - use openvm_circuit::{ - arch::SystemConfig, - utils::{air_test_impl, air_test_with_min_segments}, - }; - use openvm_ecc_circuit::WeierstrassExtension; - use openvm_ecc_guest::{ - algebra::{field::FieldExtension, IntMod}, - AffinePoint, - }; - use openvm_instructions::exe::VmExe; - use openvm_pairing_circuit::{PairingCurve, PairingExtension, Rv32PairingConfig}; - use openvm_pairing_guest::{ - bn254::{BN254_COMPLEX_STRUCT_NAME, BN254_MODULUS}, - halo2curves_shims::bn254::Bn254, - pairing::{EvaluatedLine, FinalExp, LineMulDType, MillerStep, MultiMillerLoop}, - }; - use openvm_pairing_transpiler::PairingTranspilerExtension; - use openvm_rv32im_transpiler::{ - Rv32ITranspilerExtension, Rv32IoTranspilerExtension, Rv32MTranspilerExtension, - }; - use openvm_stark_sdk::{openvm_stark_backend::p3_field::FieldAlgebra, p3_baby_bear::BabyBear}; - use openvm_toolchain_tests::{build_example_program_at_path_with_features, get_programs_dir}; - use openvm_transpiler::{transpiler::Transpiler, FromElf}; - use rand::SeedableRng; - - type F = BabyBear; - - pub fn get_testing_config() -> Rv32PairingConfig { - let primes = [BN254_MODULUS.clone()]; - let complex_struct_names = [BN254_COMPLEX_STRUCT_NAME.to_string()]; - let primes_with_names = complex_struct_names - .into_iter() - .zip(primes.clone()) - .collect::>(); - Rv32PairingConfig { - system: SystemConfig::default().with_continuations(), - base: Default::default(), - mul: Default::default(), - io: Default::default(), - modular: ModularExtension::new(primes.to_vec()), - fp2: Fp2Extension::new(primes_with_names), - weierstrass: WeierstrassExtension::new(vec![]), - pairing: PairingExtension::new(vec![PairingCurve::Bn254]), - } - } - - #[test] - fn test_bn254_fp12_mul() -> Result<()> { - let config = get_testing_config(); - let elf = build_example_program_at_path_with_features( - get_programs_dir!(), - "fp12_mul", - ["bn254"], - &config, - )?; - let openvm_exe = VmExe::from_elf( - elf, - Transpiler::::default() - .with_extension(Rv32ITranspilerExtension) - .with_extension(Rv32MTranspilerExtension) - .with_extension(Rv32IoTranspilerExtension) - .with_extension(PairingTranspilerExtension) - .with_extension(ModularTranspilerExtension) - .with_extension(Fp2TranspilerExtension), - )?; - - let mut rng = rand::rngs::StdRng::seed_from_u64(2); - let f0 = Fq12::random(&mut rng); - let f1 = Fq12::random(&mut rng); - let r = f0 * f1; - - let io = [f0, f1, r] - .into_iter() - .flat_map(|fp12| fp12.to_coeffs()) - .flat_map(|fp2| fp2.to_bytes()) - .map(FieldAlgebra::from_canonical_u8) - .collect::>(); - - air_test_with_min_segments(config, openvm_exe, vec![io], 1); - Ok(()) - } - - #[test] - fn test_bn254_line_functions() -> Result<()> { - let config = get_testing_config(); - let elf = build_example_program_at_path_with_features( - get_programs_dir!(), - "pairing_line", - ["bn254"], - &config, - )?; - let openvm_exe = VmExe::from_elf( - elf, - Transpiler::::default() - .with_extension(Rv32ITranspilerExtension) - .with_extension(Rv32MTranspilerExtension) - .with_extension(Rv32IoTranspilerExtension) - .with_extension(PairingTranspilerExtension) - .with_extension(ModularTranspilerExtension) - .with_extension(Fp2TranspilerExtension), - )?; - - let mut rng = rand::rngs::StdRng::seed_from_u64(2); - let a = G2Affine::random(&mut rng); - let b = G2Affine::random(&mut rng); - let c = G2Affine::random(&mut rng); - - let f = Fq12::random(&mut rng); - let l0 = EvaluatedLine:: { b: a.x, c: a.y }; - let l1 = EvaluatedLine:: { b: b.x, c: b.y }; - - // Test mul_013_by_013 - let r0 = Bn254::mul_013_by_013(&l0, &l1); - let io0 = [l0, l1] - .into_iter() - .flat_map(|fp2| fp2.into_iter()) - .chain(r0) - .flat_map(|fp2| fp2.to_coeffs()) - .flat_map(|fp| fp.to_bytes()) - .map(FieldAlgebra::from_canonical_u8) - .collect::>(); - - // Test mul_by_01234 - let x = [c.x, c.y, b.x, b.y, a.x]; - let r1 = Bn254::mul_by_01234(&f, &x); - let io1 = iter::empty() - .chain(f.to_coeffs()) - .chain(x) - .chain(r1.to_coeffs()) - .flat_map(|fp2| fp2.to_coeffs()) - .flat_map(|fp| fp.to_bytes()) - .map(FieldAlgebra::from_canonical_u8) - .collect::>(); - - let io_all = io0.into_iter().chain(io1).collect::>(); - - air_test_with_min_segments(config, openvm_exe, vec![io_all], 1); - Ok(()) - } - - #[test] - fn test_bn254_miller_step() -> Result<()> { - let config = get_testing_config(); - let elf = build_example_program_at_path_with_features( - get_programs_dir!(), - "pairing_miller_step", - ["bn254"], - &config, - )?; - let openvm_exe = VmExe::from_elf( - elf, - Transpiler::::default() - .with_extension(Rv32ITranspilerExtension) - .with_extension(Rv32MTranspilerExtension) - .with_extension(Rv32IoTranspilerExtension) - .with_extension(PairingTranspilerExtension) - .with_extension(ModularTranspilerExtension) - .with_extension(Fp2TranspilerExtension), - )?; - - let mut rng = rand::rngs::StdRng::seed_from_u64(20); - let S = G2Affine::random(&mut rng); - let Q = G2Affine::random(&mut rng); - - let s = AffinePoint::new(S.x, S.y); - let q = AffinePoint::new(Q.x, Q.y); - - // Test miller_double_step - let (pt, l) = Bn254::miller_double_step(&s); - let io0 = [s.x, s.y, pt.x, pt.y, l.b, l.c] - .into_iter() - .flat_map(|fp| fp.to_bytes()) - .map(FieldAlgebra::from_canonical_u8) - .collect::>(); - - // Test miller_double_and_add_step - let (pt, l0, l1) = Bn254::miller_double_and_add_step(&s, &q); - let io1 = [s.x, s.y, q.x, q.y, pt.x, pt.y, l0.b, l0.c, l1.b, l1.c] - .into_iter() - .flat_map(|fp| fp.to_bytes()) - .map(FieldAlgebra::from_canonical_u8) - .collect::>(); - - let io_all = io0.into_iter().chain(io1).collect::>(); - - air_test_with_min_segments(config, openvm_exe, vec![io_all], 1); - Ok(()) - } - - #[test] - fn test_bn254_miller_loop() -> Result<()> { - let config = get_testing_config(); - let elf = build_example_program_at_path_with_features( - get_programs_dir!(), - "pairing_miller_loop", - ["bn254"], - &config, - )?; - let openvm_exe = VmExe::from_elf( - elf, - Transpiler::::default() - .with_extension(Rv32ITranspilerExtension) - .with_extension(Rv32MTranspilerExtension) - .with_extension(Rv32IoTranspilerExtension) - .with_extension(PairingTranspilerExtension) - .with_extension(ModularTranspilerExtension) - .with_extension(Fp2TranspilerExtension), - )?; - - let S = G1Affine::generator(); - let Q = G2Affine::generator(); - - let mut S_mul = [S * Fr::from(1), S * Fr::from(2)]; - S_mul[1].y = -S_mul[1].y; - let Q_mul = [Q * Fr::from(2), Q * Fr::from(1)]; - - let s = S_mul.map(|s| AffinePoint::new(s.x, s.y)); - let q = Q_mul.map(|p| AffinePoint::new(p.x, p.y)); - - // Test miller_loop - let f = Bn254::multi_miller_loop(&s, &q); - let io0 = s - .into_iter() - .flat_map(|pt| [pt.x, pt.y].into_iter().flat_map(|fp| fp.to_bytes())) - .map(FieldAlgebra::from_canonical_u8) - .collect::>(); - - let io1 = q - .into_iter() - .flat_map(|pt| [pt.x, pt.y].into_iter()) - .chain(f.to_coeffs()) - .flat_map(|fp2| fp2.to_coeffs()) - .flat_map(|fp| fp.to_bytes()) - .map(FieldAlgebra::from_canonical_u8) - .collect::>(); - - let io_all = io0.into_iter().chain(io1).collect::>(); - - air_test_with_min_segments(config, openvm_exe, vec![io_all], 1); - Ok(()) - } - - #[test] - fn test_bn254_pairing_check() -> Result<()> { - let config = get_testing_config(); - let elf = build_example_program_at_path_with_features( - get_programs_dir!(), - "pairing_check", - ["bn254"], - &config, - )?; - let openvm_exe = VmExe::from_elf( - elf, - Transpiler::::default() - .with_extension(Rv32ITranspilerExtension) - .with_extension(Rv32MTranspilerExtension) - .with_extension(Rv32IoTranspilerExtension) - .with_extension(PairingTranspilerExtension) - .with_extension(ModularTranspilerExtension) - .with_extension(Fp2TranspilerExtension), - )?; - - let S = G1Affine::generator(); - let Q = G2Affine::generator(); - - let mut S_mul = [ - G1Affine::from(S * Fr::from(1)), - G1Affine::from(S * Fr::from(2)), - ]; - S_mul[1].y = -S_mul[1].y; - let Q_mul = [ - G2Affine::from(Q * Fr::from(2)), - G2Affine::from(Q * Fr::from(1)), - ]; - - let s = S_mul.map(|s| AffinePoint::new(s.x, s.y)); - let q = Q_mul.map(|p| AffinePoint::new(p.x, p.y)); - - // Gather inputs - let io0 = s - .into_iter() - .flat_map(|pt| [pt.x, pt.y].into_iter().flat_map(|fp| fp.to_bytes())) - .map(FieldAlgebra::from_canonical_u8) - .collect::>(); - - let io1 = q - .into_iter() - .flat_map(|pt| [pt.x, pt.y].into_iter()) - .flat_map(|fp2| fp2.to_coeffs()) - .flat_map(|fp| fp.to_bytes()) - .map(FieldAlgebra::from_canonical_u8) - .collect::>(); - - let io_all = io0.into_iter().chain(io1).collect::>(); - - air_test_with_min_segments(config, openvm_exe, vec![io_all], 1); - Ok(()) - } - - #[test] - fn test_bn254_pairing_check_fallback() -> Result<()> { - let config = get_testing_config(); - let elf = build_example_program_at_path_with_features( - get_programs_dir!(), - "pairing_check_fallback", - ["bn254"], - &config, - )?; - let openvm_exe = VmExe::from_elf( - elf, - Transpiler::::default() - .with_extension(Rv32ITranspilerExtension) - .with_extension(Rv32MTranspilerExtension) - .with_extension(Rv32IoTranspilerExtension) - .with_extension(PairingTranspilerExtension) - .with_extension(ModularTranspilerExtension) - .with_extension(Fp2TranspilerExtension), - )?; - - let S = G1Affine::generator(); - let Q = G2Affine::generator(); - - let mut S_mul = [ - G1Affine::from(S * Fr::from(1)), - G1Affine::from(S * Fr::from(2)), - ]; - S_mul[1].y = -S_mul[1].y; - let Q_mul = [ - G2Affine::from(Q * Fr::from(2)), - G2Affine::from(Q * Fr::from(1)), - ]; - - let s = S_mul.map(|s| AffinePoint::new(s.x, s.y)); - let q = Q_mul.map(|p| AffinePoint::new(p.x, p.y)); - - // Gather inputs - let io0 = s - .into_iter() - .flat_map(|pt| [pt.x, pt.y].into_iter().flat_map(|fp| fp.to_bytes())) - .map(FieldAlgebra::from_canonical_u8) - .collect::>(); - - let io1 = q - .into_iter() - .flat_map(|pt| [pt.x, pt.y].into_iter()) - .flat_map(|fp2| fp2.to_coeffs()) - .flat_map(|fp| fp.to_bytes()) - .map(FieldAlgebra::from_canonical_u8) - .collect::>(); - - let io_all = io0.into_iter().chain(io1).collect::>(); - // Don't run debugger because it's slow - air_test_impl(get_testing_config(), openvm_exe, vec![io_all], 1, false); - Ok(()) - } - - #[test] - fn test_bn254_final_exp_hint() -> Result<()> { - let config = get_testing_config(); - let elf = build_example_program_at_path_with_features( - get_programs_dir!(), - "bn_final_exp_hint", - ["bn254"], - &config, - )?; - let openvm_exe = VmExe::from_elf( - elf, - Transpiler::::default() - .with_extension(Rv32ITranspilerExtension) - .with_extension(Rv32MTranspilerExtension) - .with_extension(Rv32IoTranspilerExtension) - .with_extension(PairingTranspilerExtension) - .with_extension(ModularTranspilerExtension) - .with_extension(Fp2TranspilerExtension), - )?; - - let P = G1Affine::generator(); - let Q = G2Affine::generator(); - let ps = vec![AffinePoint::new(P.x, P.y), AffinePoint::new(P.x, -P.y)]; - let qs = vec![AffinePoint::new(Q.x, Q.y), AffinePoint::new(Q.x, Q.y)]; - let f = Bn254::multi_miller_loop(&ps, &qs); - let (c, s) = Bn254::final_exp_hint(&f); - let ps = ps - .into_iter() - .map(|pt| { - let [x, y] = [pt.x, pt.y] - .map(|x| openvm_pairing_guest::bn254::Fp::from_le_bytes(&x.to_bytes())); - AffinePoint::new(x, y) - }) - .collect::>(); - let qs = qs - .into_iter() - .map(|pt| { - let [x, y] = [pt.x, pt.y] - .map(|x| openvm_pairing_guest::bn254::Fp2::from_bytes(&x.to_bytes())); - AffinePoint::new(x, y) - }) - .collect::>(); - let [c, s] = [c, s].map(|x| openvm_pairing_guest::bn254::Fp12::from_bytes(&x.to_bytes())); - let io = (ps, qs, (c, s)); - let io = openvm::serde::to_vec(&io).unwrap(); - let io = io - .into_iter() - .flat_map(|w| w.to_le_bytes()) - .map(F::from_canonical_u8) - .collect(); - air_test_with_min_segments(config, openvm_exe, vec![io], 1); - Ok(()) - } -} - -#[cfg(test)] -mod bls12_381 { - use eyre::Result; - use halo2curves_axiom::{ - bls12_381::{Fq12, Fq2, Fr, G1Affine, G2Affine}, - ff::Field, - }; - use num_bigint::BigUint; - use num_traits::{self, FromPrimitive}; - use openvm_algebra_circuit::{Fp2Extension, ModularExtension}; - use openvm_algebra_transpiler::{Fp2TranspilerExtension, ModularTranspilerExtension}; - use openvm_circuit::{ - arch::{instructions::exe::VmExe, SystemConfig}, - utils::{air_test, air_test_impl, air_test_with_min_segments}, - }; - use openvm_ecc_circuit::{CurveConfig, Rv32WeierstrassConfig, WeierstrassExtension}; - use openvm_ecc_guest::{ - algebra::{field::FieldExtension, IntMod}, - AffinePoint, - }; - use openvm_ecc_transpiler::EccTranspilerExtension; - use openvm_pairing_circuit::{PairingCurve, PairingExtension, Rv32PairingConfig}; - use openvm_pairing_guest::{ - bls12_381::{ - BLS12_381_COMPLEX_STRUCT_NAME, BLS12_381_ECC_STRUCT_NAME, BLS12_381_MODULUS, - BLS12_381_ORDER, - }, - halo2curves_shims::bls12_381::Bls12_381, - pairing::{EvaluatedLine, FinalExp, LineMulMType, MillerStep, MultiMillerLoop}, - }; - use openvm_pairing_transpiler::PairingTranspilerExtension; - use openvm_rv32im_transpiler::{ - Rv32ITranspilerExtension, Rv32IoTranspilerExtension, Rv32MTranspilerExtension, - }; - use openvm_stark_sdk::{openvm_stark_backend::p3_field::FieldAlgebra, p3_baby_bear::BabyBear}; - use openvm_toolchain_tests::{build_example_program_at_path_with_features, get_programs_dir}; - use openvm_transpiler::{transpiler::Transpiler, FromElf}; - use rand::SeedableRng; - - type F = BabyBear; - - pub fn get_testing_config() -> Rv32PairingConfig { - let primes = [BLS12_381_MODULUS.clone()]; - let complex_struct_names = [BLS12_381_COMPLEX_STRUCT_NAME.to_string()]; - let primes_with_names = complex_struct_names - .into_iter() - .zip(primes.clone()) - .collect::>(); - Rv32PairingConfig { - system: SystemConfig::default().with_continuations(), - base: Default::default(), - mul: Default::default(), - io: Default::default(), - modular: ModularExtension::new(primes.to_vec()), - fp2: Fp2Extension::new(primes_with_names), - weierstrass: WeierstrassExtension::new(vec![]), - pairing: PairingExtension::new(vec![PairingCurve::Bls12_381]), - } - } - - #[test] - fn test_bls_ec() -> Result<()> { - let curve = CurveConfig { - struct_name: BLS12_381_ECC_STRUCT_NAME.to_string(), - modulus: BLS12_381_MODULUS.clone(), - scalar: BLS12_381_ORDER.clone(), - a: BigUint::ZERO, - b: BigUint::from_u8(4).unwrap(), - }; - let config = Rv32WeierstrassConfig::new(vec![curve]); - let elf = build_example_program_at_path_with_features( - get_programs_dir!(), - "bls_ec", - ["bls12_381"], - &config, - )?; - let openvm_exe = VmExe::from_elf( - elf, - Transpiler::::default() - .with_extension(Rv32ITranspilerExtension) - .with_extension(Rv32MTranspilerExtension) - .with_extension(Rv32IoTranspilerExtension) - .with_extension(EccTranspilerExtension) - .with_extension(ModularTranspilerExtension), - )?; - air_test(config, openvm_exe); - Ok(()) - } - - #[test] - fn test_bls12_381_fp12_mul() -> Result<()> { - let config = get_testing_config(); - let elf = build_example_program_at_path_with_features( - get_programs_dir!(), - "fp12_mul", - ["bls12_381"], - &config, - )?; - let openvm_exe = VmExe::from_elf( - elf, - Transpiler::::default() - .with_extension(Rv32ITranspilerExtension) - .with_extension(Rv32MTranspilerExtension) - .with_extension(Rv32IoTranspilerExtension) - .with_extension(PairingTranspilerExtension) - .with_extension(ModularTranspilerExtension) - .with_extension(Fp2TranspilerExtension), - )?; - - let mut rng = rand::rngs::StdRng::seed_from_u64(50); - let f0 = Fq12::random(&mut rng); - let f1 = Fq12::random(&mut rng); - let r = f0 * f1; - - let io = [f0, f1, r] - .into_iter() - .flat_map(|fp12| fp12.to_coeffs()) - .flat_map(|fp2| fp2.to_bytes()) - .map(FieldAlgebra::from_canonical_u8) - .collect::>(); - - air_test_with_min_segments(config, openvm_exe, vec![io], 1); - Ok(()) - } - - #[test] - fn test_bls12_381_line_functions() -> Result<()> { - let config = get_testing_config(); - let elf = build_example_program_at_path_with_features( - get_programs_dir!(), - "pairing_line", - ["bls12_381"], - &config, - )?; - let openvm_exe = VmExe::from_elf( - elf, - Transpiler::::default() - .with_extension(Rv32ITranspilerExtension) - .with_extension(Rv32MTranspilerExtension) - .with_extension(Rv32IoTranspilerExtension) - .with_extension(PairingTranspilerExtension) - .with_extension(ModularTranspilerExtension) - .with_extension(Fp2TranspilerExtension), - )?; - - let mut rng = rand::rngs::StdRng::seed_from_u64(5); - let a = G2Affine::random(&mut rng); - let b = G2Affine::random(&mut rng); - let c = G2Affine::random(&mut rng); - - let f = Fq12::random(&mut rng); - let l0 = EvaluatedLine:: { b: a.x, c: a.y }; - let l1 = EvaluatedLine:: { b: b.x, c: b.y }; - - // Test mul_023_by_023 - let r0 = Bls12_381::mul_023_by_023(&l0, &l1); - let io0 = [l0, l1] - .into_iter() - .flat_map(|fp2| fp2.into_iter()) - .chain(r0) - .flat_map(|fp2| fp2.to_coeffs()) - .flat_map(|fp| fp.to_bytes()) - .map(FieldAlgebra::from_canonical_u8) - .collect::>(); - - // Test mul_by_02345 - let x = [c.x, c.y, b.x, b.y, a.x]; - let r1 = Bls12_381::mul_by_02345(&f, &x); - let io1 = f - .to_coeffs() - .into_iter() - .chain(x) - .chain(r1.to_coeffs()) - .flat_map(|fp2| fp2.to_coeffs()) - .flat_map(|fp| fp.to_bytes()) - .map(FieldAlgebra::from_canonical_u8) - .collect::>(); - - let io_all = io0.into_iter().chain(io1).collect::>(); - - air_test_with_min_segments(config, openvm_exe, vec![io_all], 1); - Ok(()) - } - - #[test] - fn test_bls12_381_miller_step() -> Result<()> { - let config = get_testing_config(); - let elf = build_example_program_at_path_with_features( - get_programs_dir!(), - "pairing_miller_step", - ["bls12_381"], - &config, - )?; - let openvm_exe = VmExe::from_elf( - elf, - Transpiler::::default() - .with_extension(Rv32ITranspilerExtension) - .with_extension(Rv32MTranspilerExtension) - .with_extension(Rv32IoTranspilerExtension) - .with_extension(PairingTranspilerExtension) - .with_extension(ModularTranspilerExtension) - .with_extension(Fp2TranspilerExtension), - )?; - - let mut rng = rand::rngs::StdRng::seed_from_u64(88); - let S = G2Affine::random(&mut rng); - let Q = G2Affine::random(&mut rng); - - let s = AffinePoint::new(S.x, S.y); - let q = AffinePoint::new(Q.x, Q.y); - - // Test miller_double_step - let (pt, l) = Bls12_381::miller_double_step(&s); - let io0 = [s.x, s.y, pt.x, pt.y, l.b, l.c] - .into_iter() - .flat_map(|fp| fp.to_bytes()) - .map(FieldAlgebra::from_canonical_u8) - .collect::>(); - - // Test miller_double_and_add_step - let (pt, l0, l1) = Bls12_381::miller_double_and_add_step(&s, &q); - let io1 = [s.x, s.y, q.x, q.y, pt.x, pt.y, l0.b, l0.c, l1.b, l1.c] - .into_iter() - .flat_map(|fp| fp.to_bytes()) - .map(FieldAlgebra::from_canonical_u8) - .collect::>(); - - let io_all = io0.into_iter().chain(io1).collect::>(); - - air_test_with_min_segments(config, openvm_exe, vec![io_all], 1); - Ok(()) - } - - #[test] - fn test_bls12_381_miller_loop() -> Result<()> { - let config = get_testing_config(); - let elf = build_example_program_at_path_with_features( - get_programs_dir!(), - "pairing_miller_loop", - ["bls12_381"], - &config, - )?; - let openvm_exe = VmExe::from_elf( - elf, - Transpiler::::default() - .with_extension(Rv32ITranspilerExtension) - .with_extension(Rv32MTranspilerExtension) - .with_extension(Rv32IoTranspilerExtension) - .with_extension(PairingTranspilerExtension) - .with_extension(ModularTranspilerExtension) - .with_extension(Fp2TranspilerExtension), - )?; - - let S = G1Affine::generator(); - let Q = G2Affine::generator(); - - let mut S_mul = [ - G1Affine::from(S * Fr::from(1)), - G1Affine::from(S * Fr::from(2)), - ]; - S_mul[1].y = -S_mul[1].y; - let Q_mul = [ - G2Affine::from(Q * Fr::from(2)), - G2Affine::from(Q * Fr::from(1)), - ]; - - let s = S_mul.map(|s| AffinePoint::new(s.x, s.y)); - let q = Q_mul.map(|p| AffinePoint::new(p.x, p.y)); - - // Test miller_loop - let f = Bls12_381::multi_miller_loop(&s, &q); - let io0 = s - .into_iter() - .flat_map(|pt| [pt.x, pt.y].into_iter().flat_map(|fp| fp.to_bytes())) - .map(FieldAlgebra::from_canonical_u8) - .collect::>(); - - let io1 = q - .into_iter() - .flat_map(|pt| [pt.x, pt.y].into_iter()) - .chain(f.to_coeffs()) - .flat_map(|fp2| fp2.to_coeffs()) - .flat_map(|fp| fp.to_bytes()) - .map(FieldAlgebra::from_canonical_u8) - .collect::>(); - - let io_all = io0.into_iter().chain(io1).collect::>(); - - air_test_with_min_segments(config, openvm_exe, vec![io_all], 1); - Ok(()) - } - - #[test] - fn test_bls12_381_pairing_check() -> Result<()> { - let config = get_testing_config(); - let elf = build_example_program_at_path_with_features( - get_programs_dir!(), - "pairing_check", - ["bls12_381"], - &config, - )?; - let openvm_exe = VmExe::from_elf( - elf, - Transpiler::::default() - .with_extension(Rv32ITranspilerExtension) - .with_extension(Rv32MTranspilerExtension) - .with_extension(Rv32IoTranspilerExtension) - .with_extension(PairingTranspilerExtension) - .with_extension(ModularTranspilerExtension) - .with_extension(Fp2TranspilerExtension), - )?; - - let S = G1Affine::generator(); - let Q = G2Affine::generator(); - - let mut S_mul = [ - G1Affine::from(S * Fr::from(1)), - G1Affine::from(S * Fr::from(2)), - ]; - S_mul[1].y = -S_mul[1].y; - let Q_mul = [ - G2Affine::from(Q * Fr::from(2)), - G2Affine::from(Q * Fr::from(1)), - ]; - let s = S_mul.map(|s| AffinePoint::new(s.x, s.y)); - let q = Q_mul.map(|p| AffinePoint::new(p.x, p.y)); - - // Gather inputs - let io0 = s - .into_iter() - .flat_map(|pt| [pt.x, pt.y].into_iter().flat_map(|fp| fp.to_bytes())) - .map(FieldAlgebra::from_canonical_u8) - .collect::>(); - - let io1 = q - .into_iter() - .flat_map(|pt| [pt.x, pt.y].into_iter()) - .flat_map(|fp2| fp2.to_coeffs()) - .flat_map(|fp| fp.to_bytes()) - .map(FieldAlgebra::from_canonical_u8) - .collect::>(); - - let io_all = io0.into_iter().chain(io1).collect::>(); - - air_test_with_min_segments(config, openvm_exe, vec![io_all], 1); - Ok(()) - } - - #[ignore] - #[test] - fn test_bls12_381_pairing_check_fallback() -> Result<()> { - let config = get_testing_config(); - let elf = build_example_program_at_path_with_features( - get_programs_dir!(), - "pairing_check_fallback", - ["bls12_381"], - &config, - )?; - let openvm_exe = VmExe::from_elf( - elf, - Transpiler::::default() - .with_extension(Rv32ITranspilerExtension) - .with_extension(Rv32MTranspilerExtension) - .with_extension(Rv32IoTranspilerExtension) - .with_extension(PairingTranspilerExtension) - .with_extension(ModularTranspilerExtension) - .with_extension(Fp2TranspilerExtension), - )?; - - let S = G1Affine::generator(); - let Q = G2Affine::generator(); - - let mut S_mul = [ - G1Affine::from(S * Fr::from(1)), - G1Affine::from(S * Fr::from(2)), - ]; - S_mul[1].y = -S_mul[1].y; - let Q_mul = [ - G2Affine::from(Q * Fr::from(2)), - G2Affine::from(Q * Fr::from(1)), - ]; - let s = S_mul.map(|s| AffinePoint::new(s.x, s.y)); - let q = Q_mul.map(|p| AffinePoint::new(p.x, p.y)); - - // Gather inputs - let io0 = s - .into_iter() - .flat_map(|pt| [pt.x, pt.y].into_iter().flat_map(|fp| fp.to_bytes())) - .map(FieldAlgebra::from_canonical_u8) - .collect::>(); - - let io1 = q - .into_iter() - .flat_map(|pt| [pt.x, pt.y].into_iter()) - .flat_map(|fp2| fp2.to_coeffs()) - .flat_map(|fp| fp.to_bytes()) - .map(FieldAlgebra::from_canonical_u8) - .collect::>(); - - let io_all = io0.into_iter().chain(io1).collect::>(); - // Don't run debugger because it's slow - air_test_impl(get_testing_config(), openvm_exe, vec![io_all], 1, false); - Ok(()) - } - - #[test] - fn test_bls12_381_final_exp_hint() -> Result<()> { - let config = get_testing_config(); - let elf = build_example_program_at_path_with_features( - get_programs_dir!(), - "bls_final_exp_hint", - ["bls12_381"], - &config, - )?; - let openvm_exe = VmExe::from_elf( - elf, - Transpiler::::default() - .with_extension(Rv32ITranspilerExtension) - .with_extension(Rv32MTranspilerExtension) - .with_extension(Rv32IoTranspilerExtension) - .with_extension(PairingTranspilerExtension) - .with_extension(ModularTranspilerExtension) - .with_extension(Fp2TranspilerExtension), - )?; - - let P = G1Affine::generator(); - let Q = G2Affine::generator(); - let ps = vec![AffinePoint::new(P.x, P.y), AffinePoint::new(P.x, -P.y)]; - let qs = vec![AffinePoint::new(Q.x, Q.y), AffinePoint::new(Q.x, Q.y)]; - let f = Bls12_381::multi_miller_loop(&ps, &qs); - let (c, s) = Bls12_381::final_exp_hint(&f); - let ps = ps - .into_iter() - .map(|pt| { - let [x, y] = [pt.x, pt.y] - .map(|x| openvm_pairing_guest::bls12_381::Fp::from_le_bytes(&x.to_bytes())); - AffinePoint::new(x, y) - }) - .collect::>(); - let qs = qs - .into_iter() - .map(|pt| { - let [x, y] = [pt.x, pt.y] - .map(|x| openvm_pairing_guest::bls12_381::Fp2::from_bytes(&x.to_bytes())); - AffinePoint::new(x, y) - }) - .collect::>(); - let [c, s] = - [c, s].map(|x| openvm_pairing_guest::bls12_381::Fp12::from_bytes(&x.to_bytes())); - let io = (ps, qs, (c, s)); - let io = openvm::serde::to_vec(&io).unwrap(); - let io = io - .into_iter() - .flat_map(|w| w.to_le_bytes()) - .map(F::from_canonical_u8) - .collect(); - air_test_with_min_segments(config, openvm_exe, vec![io], 1); - Ok(()) - } -} From a59cf128417cf334675e5cd95c1bd604934c9f13 Mon Sep 17 00:00:00 2001 From: Avaneesh Kulkarni Date: Wed, 30 Apr 2025 22:10:58 -0400 Subject: [PATCH 25/41] Use rust-runtime feature in openvm-platform --- extensions/keccak256/guest/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/keccak256/guest/Cargo.toml b/extensions/keccak256/guest/Cargo.toml index 593412bdc9..4b4b1ea518 100644 --- a/extensions/keccak256/guest/Cargo.toml +++ b/extensions/keccak256/guest/Cargo.toml @@ -8,7 +8,7 @@ homepage.workspace = true repository.workspace = true [dependencies] -openvm-platform = { workspace = true } +openvm-platform = { workspace = true, features = ["rust-runtime"] } [target.'cfg(not(target_os = "zkvm"))'.dependencies] tiny-keccak.workspace = true From 438a3573449489fe2d69239fe8bd4a91b75c431e Mon Sep 17 00:00:00 2001 From: Avaneesh Kulkarni Date: Wed, 30 Apr 2025 22:22:52 -0400 Subject: [PATCH 26/41] Fix bugs --- extensions/algebra/moduli-macros/src/lib.rs | 2 + .../algebra/tests/programs/examples/sqrt.rs | 4 +- extensions/ecc/guest/src/k256.rs | 2 +- extensions/ecc/guest/src/p256.rs | 2 +- extensions/ecc/tests/programs/Cargo.toml | 4 +- .../ecc/tests/programs/examples/decompress.rs | 2 +- extensions/ecc/tests/programs/examples/ec.rs | 2 +- .../tests/programs/examples/ec_nonzero_a.rs | 2 +- .../tests/programs/examples/ec_two_curves.rs | 2 +- .../ecc/tests/programs/examples/ecdsa.rs | 4 +- .../programs/openvm_init_decompress_k256.rs | 3 + .../ecc/tests/programs/openvm_init_ec_k256.rs | 3 + .../programs/openvm_init_ec_nonzero_a_p256.rs | 3 + .../openvm_init_ec_two_curves_k256_p256.rs | 3 + .../tests/programs/openvm_init_ecdsa_k256.rs | 3 + extensions/ecc/tests/src/lib.rs | 68 ++++++++++--------- 16 files changed, 65 insertions(+), 44 deletions(-) create mode 100644 extensions/ecc/tests/programs/openvm_init_decompress_k256.rs create mode 100644 extensions/ecc/tests/programs/openvm_init_ec_k256.rs create mode 100644 extensions/ecc/tests/programs/openvm_init_ec_nonzero_a_p256.rs create mode 100644 extensions/ecc/tests/programs/openvm_init_ec_two_curves_k256_p256.rs create mode 100644 extensions/ecc/tests/programs/openvm_init_ecdsa_k256.rs diff --git a/extensions/algebra/moduli-macros/src/lib.rs b/extensions/algebra/moduli-macros/src/lib.rs index 75b73e0899..02d96d5ef9 100644 --- a/extensions/algebra/moduli-macros/src/lib.rs +++ b/extensions/algebra/moduli-macros/src/lib.rs @@ -151,6 +151,8 @@ pub fn moduli_declare(input: TokenStream) -> TokenStream { fn #mul_extern_func(rd: usize, rs1: usize, rs2: usize); fn #div_extern_func(rd: usize, rs1: usize, rs2: usize); fn #is_eq_extern_func(rs1: usize, rs2: usize) -> bool; + fn #hint_sqrt_extern_func(rs1: usize); + fn #hint_non_qr_extern_func(); fn #moduli_setup_extern_func(); } diff --git a/extensions/algebra/tests/programs/examples/sqrt.rs b/extensions/algebra/tests/programs/examples/sqrt.rs index 194e522b97..a4ddfe4e23 100644 --- a/extensions/algebra/tests/programs/examples/sqrt.rs +++ b/extensions/algebra/tests/programs/examples/sqrt.rs @@ -10,11 +10,11 @@ openvm::entry!(main); openvm_algebra_moduli_macros::moduli_declare! { Secp256k1Coord { modulus = "0xFFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE FFFFFC2F", - prime = true + impl_field = true } } -openvm::init!(); +openvm::init!("openvm_init_sqrt.rs"); pub fn main() { let a = Secp256k1Coord::from_u32(4); diff --git a/extensions/ecc/guest/src/k256.rs b/extensions/ecc/guest/src/k256.rs index 1b8b406eee..1ecbaa7293 100644 --- a/extensions/ecc/guest/src/k256.rs +++ b/extensions/ecc/guest/src/k256.rs @@ -7,7 +7,7 @@ use hex_literal::hex; use lazy_static::lazy_static; #[cfg(not(target_os = "zkvm"))] use num_bigint::BigUint; -use openvm_algebra_guest::{Field, IntMod}; +use openvm_algebra_guest::IntMod; use openvm_algebra_moduli_macros::moduli_declare; use openvm_ecc_sw_macros::sw_declare; diff --git a/extensions/ecc/guest/src/p256.rs b/extensions/ecc/guest/src/p256.rs index 01af6e4b17..ed0bfe2bb2 100644 --- a/extensions/ecc/guest/src/p256.rs +++ b/extensions/ecc/guest/src/p256.rs @@ -5,7 +5,7 @@ use hex_literal::hex; use lazy_static::lazy_static; #[cfg(not(target_os = "zkvm"))] use num_bigint::BigUint; -use openvm_algebra_guest::{Field, IntMod}; +use openvm_algebra_guest::IntMod; use super::group::{CyclicGroup, Group}; use crate::weierstrass::{CachedMulTable, IntrinsicCurve}; diff --git a/extensions/ecc/tests/programs/Cargo.toml b/extensions/ecc/tests/programs/Cargo.toml index ccc3ba7387..bc8f78ee29 100644 --- a/extensions/ecc/tests/programs/Cargo.toml +++ b/extensions/ecc/tests/programs/Cargo.toml @@ -13,9 +13,11 @@ openvm-ecc-guest = { path = "../../guest", default-features = false } openvm-ecc-sw-macros = { path = "../../../../extensions/ecc/sw-macros", default-features = false } openvm-algebra-guest = { path = "../../../algebra/guest", default-features = false } openvm-algebra-moduli-macros = { path = "../../../algebra/moduli-macros", default-features = false } -openvm-keccak256-guest = { path = "../../../keccak256/guest", default-features = false } openvm-rv32im-guest = { path = "../../../../extensions/rv32im/guest", default-features = false } +# TODO: change to https after openvm-primitives is public +openvm-keccak256 = { git = "ssh://git@github.com/openvm-org/openvm-primitives.git", branch = "feat/primitive-libs" } + serde = { version = "1.0", default-features = false, features = [ "alloc", "derive", diff --git a/extensions/ecc/tests/programs/examples/decompress.rs b/extensions/ecc/tests/programs/examples/decompress.rs index 206562ef90..b7f8f9c072 100644 --- a/extensions/ecc/tests/programs/examples/decompress.rs +++ b/extensions/ecc/tests/programs/examples/decompress.rs @@ -73,7 +73,7 @@ openvm_ecc_sw_macros::sw_declare! { }, } -openvm::init!("openvm_init_decompress.rs"); +openvm::init!("openvm_init_decompress_k256.rs"); // test decompression under an honest host pub fn main() { diff --git a/extensions/ecc/tests/programs/examples/ec.rs b/extensions/ecc/tests/programs/examples/ec.rs index b4a03bea6c..bd9238add1 100644 --- a/extensions/ecc/tests/programs/examples/ec.rs +++ b/extensions/ecc/tests/programs/examples/ec.rs @@ -10,7 +10,7 @@ use openvm_ecc_guest::{ Group, }; -openvm::init!("openvm_init_ec.rs"); +openvm::init!("openvm_init_ec_k256.rs"); openvm::entry!(main); diff --git a/extensions/ecc/tests/programs/examples/ec_nonzero_a.rs b/extensions/ecc/tests/programs/examples/ec_nonzero_a.rs index 2402151bdf..74e384d61b 100644 --- a/extensions/ecc/tests/programs/examples/ec_nonzero_a.rs +++ b/extensions/ecc/tests/programs/examples/ec_nonzero_a.rs @@ -11,7 +11,7 @@ use openvm_ecc_guest::{ openvm::entry!(main); -openvm::init!("openvm_init_ec_nonzero_a.rs"); +openvm::init!("openvm_init_ec_nonzero_a_p256.rs"); pub fn main() { // Sample points got from https://asecuritysite.com/ecc/p256p diff --git a/extensions/ecc/tests/programs/examples/ec_two_curves.rs b/extensions/ecc/tests/programs/examples/ec_two_curves.rs index 21cdb2eb51..5cf3711c1f 100644 --- a/extensions/ecc/tests/programs/examples/ec_two_curves.rs +++ b/extensions/ecc/tests/programs/examples/ec_two_curves.rs @@ -11,7 +11,7 @@ use openvm_ecc_guest::{ Group, }; -openvm::init!("openvm_init_ec_two_curves.rs"); +openvm::init!("openvm_init_ec_two_curves_k256_p256.rs"); openvm::entry!(main); diff --git a/extensions/ecc/tests/programs/examples/ecdsa.rs b/extensions/ecc/tests/programs/examples/ecdsa.rs index da556cd096..df623a9d8d 100644 --- a/extensions/ecc/tests/programs/examples/ecdsa.rs +++ b/extensions/ecc/tests/programs/examples/ecdsa.rs @@ -12,10 +12,10 @@ use k256::{ use openvm_ecc_guest::{ algebra::IntMod, ecdsa::VerifyingKey, k256::Secp256k1Point, weierstrass::WeierstrassPoint, }; -use openvm_keccak256_guest::keccak256; +use openvm_keccak256::keccak256; openvm::entry!(main); -openvm::init!("openvm_init_ecdsa.rs"); +openvm::init!("openvm_init_ecdsa_k256.rs"); // Ref: https://docs.rs/k256/latest/k256/ecdsa/index.html pub fn main() { diff --git a/extensions/ecc/tests/programs/openvm_init_decompress_k256.rs b/extensions/ecc/tests/programs/openvm_init_decompress_k256.rs new file mode 100644 index 0000000000..b6137ae9ee --- /dev/null +++ b/extensions/ecc/tests/programs/openvm_init_decompress_k256.rs @@ -0,0 +1,3 @@ +// This file is automatically generated by cargo openvm. Do not rename or edit. +openvm_algebra_guest::moduli_macros::moduli_init! { "115792089237316195423570985008687907853269984665640564039457584007908834671663", "115792089237316195423570985008687907852837564279074904382605163141518161494337", "115792089237316195423570985008687907853269984665640564039457584007913129639501", "1000000007", "26959946667150639794667015087019630673557916260026308143510066298881", "26959946667150639794667015087019625940457807714424391721682722368061" } +openvm_ecc_guest::sw_macros::sw_init! { Secp256k1Point, CurvePoint5mod8, CurvePoint1mod4 } diff --git a/extensions/ecc/tests/programs/openvm_init_ec_k256.rs b/extensions/ecc/tests/programs/openvm_init_ec_k256.rs new file mode 100644 index 0000000000..bec9f527e9 --- /dev/null +++ b/extensions/ecc/tests/programs/openvm_init_ec_k256.rs @@ -0,0 +1,3 @@ +// This file is automatically generated by cargo openvm. Do not rename or edit. +openvm_algebra_guest::moduli_macros::moduli_init! { "115792089237316195423570985008687907853269984665640564039457584007908834671663", "115792089237316195423570985008687907852837564279074904382605163141518161494337" } +openvm_ecc_guest::sw_macros::sw_init! { Secp256k1Point } diff --git a/extensions/ecc/tests/programs/openvm_init_ec_nonzero_a_p256.rs b/extensions/ecc/tests/programs/openvm_init_ec_nonzero_a_p256.rs new file mode 100644 index 0000000000..02f8b5c05d --- /dev/null +++ b/extensions/ecc/tests/programs/openvm_init_ec_nonzero_a_p256.rs @@ -0,0 +1,3 @@ +// This file is automatically generated by cargo openvm. Do not rename or edit. +openvm_algebra_guest::moduli_macros::moduli_init! { "115792089210356248762697446949407573530086143415290314195533631308867097853951", "115792089210356248762697446949407573529996955224135760342422259061068512044369" } +openvm_ecc_guest::sw_macros::sw_init! { P256Point } diff --git a/extensions/ecc/tests/programs/openvm_init_ec_two_curves_k256_p256.rs b/extensions/ecc/tests/programs/openvm_init_ec_two_curves_k256_p256.rs new file mode 100644 index 0000000000..8689190544 --- /dev/null +++ b/extensions/ecc/tests/programs/openvm_init_ec_two_curves_k256_p256.rs @@ -0,0 +1,3 @@ +// This file is automatically generated by cargo openvm. Do not rename or edit. +openvm_algebra_guest::moduli_macros::moduli_init! { "115792089237316195423570985008687907853269984665640564039457584007908834671663", "115792089237316195423570985008687907852837564279074904382605163141518161494337", "115792089210356248762697446949407573530086143415290314195533631308867097853951", "115792089210356248762697446949407573529996955224135760342422259061068512044369" } +openvm_ecc_guest::sw_macros::sw_init! { Secp256k1Point, P256Point } diff --git a/extensions/ecc/tests/programs/openvm_init_ecdsa_k256.rs b/extensions/ecc/tests/programs/openvm_init_ecdsa_k256.rs new file mode 100644 index 0000000000..bec9f527e9 --- /dev/null +++ b/extensions/ecc/tests/programs/openvm_init_ecdsa_k256.rs @@ -0,0 +1,3 @@ +// This file is automatically generated by cargo openvm. Do not rename or edit. +openvm_algebra_guest::moduli_macros::moduli_init! { "115792089237316195423570985008687907853269984665640564039457584007908834671663", "115792089237316195423570985008687907852837564279074904382605163141518161494337" } +openvm_ecc_guest::sw_macros::sw_init! { Secp256k1Point } diff --git a/extensions/ecc/tests/src/lib.rs b/extensions/ecc/tests/src/lib.rs index 37a71c9e3b..d23b785893 100644 --- a/extensions/ecc/tests/src/lib.rs +++ b/extensions/ecc/tests/src/lib.rs @@ -262,40 +262,42 @@ mod tests { test_decompress_invalid_specific_test("test_curvepoint1mod4_impossible") } - #[test] - fn test_ecdsa() -> Result<()> { - let config = SdkVmConfig::builder() - .system(SystemConfig::default().with_continuations().into()) - .rv32i(Default::default()) - .rv32m(Default::default()) - .io(Default::default()) - .modular(ModularExtension::new(vec![ - SECP256K1_CONFIG.modulus.clone(), - SECP256K1_CONFIG.scalar.clone(), - ])) - .keccak(Default::default()) - .ecc(WeierstrassExtension::new(vec![SECP256K1_CONFIG.clone()])) - .build(); + // Commenting out for now since ecdsa requires keccak256 which is now removed from openvm + // And openvm-keccak256 depends on openvm-platform which cause weird linking issues + // #[test] + // fn test_ecdsa() -> Result<()> { + // let config = SdkVmConfig::builder() + // .system(SystemConfig::default().with_continuations().into()) + // .rv32i(Default::default()) + // .rv32m(Default::default()) + // .io(Default::default()) + // .modular(ModularExtension::new(vec![ + // SECP256K1_CONFIG.modulus.clone(), + // SECP256K1_CONFIG.scalar.clone(), + // ])) + // .keccak(Default::default()) + // .ecc(WeierstrassExtension::new(vec![SECP256K1_CONFIG.clone()])) + // .build(); - let elf = build_example_program_at_path_with_features( - get_programs_dir!(), - "ecdsa", - ["k256"], - &config, - )?; - let openvm_exe = VmExe::from_elf( - elf, - Transpiler::::default() - .with_extension(Rv32ITranspilerExtension) - .with_extension(Rv32MTranspilerExtension) - .with_extension(Rv32IoTranspilerExtension) - .with_extension(Keccak256TranspilerExtension) - .with_extension(EccTranspilerExtension) - .with_extension(ModularTranspilerExtension), - )?; - air_test(config, openvm_exe); - Ok(()) - } + // let elf = build_example_program_at_path_with_features( + // get_programs_dir!(), + // "ecdsa", + // ["k256"], + // &config, + // )?; + // let openvm_exe = VmExe::from_elf( + // elf, + // Transpiler::::default() + // .with_extension(Rv32ITranspilerExtension) + // .with_extension(Rv32MTranspilerExtension) + // .with_extension(Rv32IoTranspilerExtension) + // .with_extension(Keccak256TranspilerExtension) + // .with_extension(EccTranspilerExtension) + // .with_extension(ModularTranspilerExtension), + // )?; + // air_test(config, openvm_exe); + // Ok(()) + // } #[test] #[should_panic] From 7034ad66b2282f91cb12caec6a9c15ff401069a7 Mon Sep 17 00:00:00 2001 From: Avaneesh Kulkarni Date: Wed, 30 Apr 2025 22:32:41 -0400 Subject: [PATCH 27/41] Delete pairing benchmark --- benchmarks/guest/pairing/Cargo.toml | 27 ------ .../pairing/elf/openvm-pairing-program.elf | Bin 155380 -> 0 bytes benchmarks/guest/pairing/openvm.toml | 25 ------ benchmarks/guest/pairing/src/main.rs | 77 ------------------ benchmarks/prove/src/bin/pairing.rs | 41 ---------- 5 files changed, 170 deletions(-) delete mode 100644 benchmarks/guest/pairing/Cargo.toml delete mode 100755 benchmarks/guest/pairing/elf/openvm-pairing-program.elf delete mode 100644 benchmarks/guest/pairing/openvm.toml delete mode 100644 benchmarks/guest/pairing/src/main.rs delete mode 100644 benchmarks/prove/src/bin/pairing.rs diff --git a/benchmarks/guest/pairing/Cargo.toml b/benchmarks/guest/pairing/Cargo.toml deleted file mode 100644 index 47e0889148..0000000000 --- a/benchmarks/guest/pairing/Cargo.toml +++ /dev/null @@ -1,27 +0,0 @@ -[workspace] -[package] -name = "openvm-pairing-program" -version = "0.0.0" -edition = "2021" - -[dependencies] -openvm = { path = "../../../crates/toolchain/openvm", features = ["std"] } -openvm-algebra-guest = { path = "../../../extensions/algebra/guest", default-features = false } -openvm-ecc-guest = { path = "../../../extensions/ecc/guest", default-features = false } -openvm-pairing-guest = { path = "../../../extensions/pairing/guest", default-features = false, features = [ - "bn254", -] } -hex = { version = "0.4.3", default-features = false, features = ["alloc"] } - -[features] -default = [] -halo2curves = ["openvm-pairing-guest/halo2curves"] - -[profile.release] -panic = "abort" -lto = "thin" # faster compile time - -[profile.profiling] -inherits = "release" -debug = 2 -strip = false diff --git a/benchmarks/guest/pairing/elf/openvm-pairing-program.elf b/benchmarks/guest/pairing/elf/openvm-pairing-program.elf deleted file mode 100755 index bf30d5a0034e7ed173020237aaa86150e7279514..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 155380 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

Eb_0aG5v9kMSiQsB7b>}r>0-8vB)L7&{ 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 Result<()> { - let args = BenchmarkCli::parse(); - - let elf = args.build_bench_program("pairing")?; - let vm_config = SdkVmConfig::builder() - .system(SystemConfig::default().with_continuations().into()) - .rv32i(Default::default()) - .rv32m(Default::default()) - .io(Default::default()) - .keccak(Default::default()) - .modular(ModularExtension::new(vec![ - BN254_MODULUS.clone(), - BN254_ORDER.clone(), - ])) - .fp2(Fp2Extension::new(vec![( - BN254_COMPLEX_STRUCT_NAME.to_string(), - BN254_MODULUS.clone(), - )])) - .ecc(WeierstrassExtension::new(vec![ - PairingCurve::Bn254.curve_config() - ])) - .pairing(PairingExtension::new(vec![PairingCurve::Bn254])) - .build(); - let sdk = Sdk::new(); - let exe = sdk.transpile(elf, vm_config.transpiler()).unwrap(); - - run_with_metric_collection("OUTPUT_PATH", || -> Result<()> { - args.bench_from_exe("pairing", vm_config, exe, StdIn::default()) - }) -} From f8f8ac1a1f46e6082001e92c9d768743a0ef456e Mon Sep 17 00:00:00 2001 From: Avaneesh Kulkarni Date: Wed, 30 Apr 2025 22:47:50 -0400 Subject: [PATCH 28/41] Add complex struct name to openvm-pairing-guest --- extensions/pairing/guest/src/bls12_381/mod.rs | 4 ++++ extensions/pairing/guest/src/bn254/mod.rs | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/extensions/pairing/guest/src/bls12_381/mod.rs b/extensions/pairing/guest/src/bls12_381/mod.rs index 898370a532..08808e10da 100644 --- a/extensions/pairing/guest/src/bls12_381/mod.rs +++ b/extensions/pairing/guest/src/bls12_381/mod.rs @@ -35,3 +35,7 @@ pub const BLS12_381_PSEUDO_BINARY_ENCODING: [i8; 64] = [ #[cfg(not(target_os = "zkvm"))] // Used in WeierstrassExtension config pub const BLS12_381_ECC_STRUCT_NAME: &str = "Bls12_381G1Affine"; + +#[cfg(not(target_os = "zkvm"))] +// Used in Fp2Extension config +pub const BLS12_381_COMPLEX_STRUCT_NAME: &str = "Bls12_381Fp2"; diff --git a/extensions/pairing/guest/src/bn254/mod.rs b/extensions/pairing/guest/src/bn254/mod.rs index 5116e4e053..f4af8bd242 100644 --- a/extensions/pairing/guest/src/bn254/mod.rs +++ b/extensions/pairing/guest/src/bn254/mod.rs @@ -36,3 +36,7 @@ pub const BN254_PSEUDO_BINARY_ENCODING: [i8; 66] = [ #[cfg(not(target_os = "zkvm"))] // Used in WeierstrassExtension config pub const BN254_ECC_STRUCT_NAME: &str = "Bn254G1Affine"; + +#[cfg(not(target_os = "zkvm"))] +// Used in Fp2Extension config +pub const BN254_COMPLEX_STRUCT_NAME: &str = "Bn254Fp2"; From f5be75739ae688dc45e1966037e6d19b86564baa Mon Sep 17 00:00:00 2001 From: Avaneesh Kulkarni Date: Wed, 30 Apr 2025 23:03:42 -0400 Subject: [PATCH 29/41] Delete unused code --- extensions/ecc/tests/programs/Cargo.toml | 3 --- extensions/ecc/tests/src/lib.rs | 9 ++------- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/extensions/ecc/tests/programs/Cargo.toml b/extensions/ecc/tests/programs/Cargo.toml index bc8f78ee29..2b01df997d 100644 --- a/extensions/ecc/tests/programs/Cargo.toml +++ b/extensions/ecc/tests/programs/Cargo.toml @@ -15,9 +15,6 @@ openvm-algebra-guest = { path = "../../../algebra/guest", default-features = fal openvm-algebra-moduli-macros = { path = "../../../algebra/moduli-macros", default-features = false } openvm-rv32im-guest = { path = "../../../../extensions/rv32im/guest", default-features = false } -# TODO: change to https after openvm-primitives is public -openvm-keccak256 = { git = "ssh://git@github.com/openvm-org/openvm-primitives.git", branch = "feat/primitive-libs" } - serde = { version = "1.0", default-features = false, features = [ "alloc", "derive", diff --git a/extensions/ecc/tests/src/lib.rs b/extensions/ecc/tests/src/lib.rs index d23b785893..223cd530cb 100644 --- a/extensions/ecc/tests/src/lib.rs +++ b/extensions/ecc/tests/src/lib.rs @@ -5,21 +5,16 @@ mod tests { use eyre::Result; use hex_literal::hex; use num_bigint::BigUint; - use openvm_algebra_circuit::ModularExtension; use openvm_algebra_transpiler::ModularTranspilerExtension; use openvm_circuit::{ - arch::{instructions::exe::VmExe, SystemConfig}, + arch::instructions::exe::VmExe, utils::{air_test, air_test_with_min_segments}, }; - use openvm_ecc_circuit::{ - CurveConfig, Rv32WeierstrassConfig, WeierstrassExtension, P256_CONFIG, SECP256K1_CONFIG, - }; + use openvm_ecc_circuit::{CurveConfig, Rv32WeierstrassConfig, P256_CONFIG, SECP256K1_CONFIG}; use openvm_ecc_transpiler::EccTranspilerExtension; - use openvm_keccak256_transpiler::Keccak256TranspilerExtension; use openvm_rv32im_transpiler::{ Rv32ITranspilerExtension, Rv32IoTranspilerExtension, Rv32MTranspilerExtension, }; - use openvm_sdk::config::SdkVmConfig; use openvm_stark_backend::p3_field::FieldAlgebra; use openvm_stark_sdk::{openvm_stark_backend, p3_baby_bear::BabyBear}; use openvm_toolchain_tests::{ From 63e56c6c7f1270fdaff1f9a225c80ed8bfe37d6e Mon Sep 17 00:00:00 2001 From: Avaneesh Kulkarni Date: Wed, 30 Apr 2025 23:17:14 -0400 Subject: [PATCH 30/41] Fix ecc example --- examples/ecc/openvm.toml | 2 +- examples/ecc/openvm/app.vmexe | Bin 0 -> 417761 bytes examples/ecc/openvm/committed_app_exe.bc | Bin 0 -> 2351140 bytes examples/ecc/openvm/exe_commit.bytes | Bin 0 -> 32 bytes 4 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 examples/ecc/openvm/app.vmexe create mode 100644 examples/ecc/openvm/committed_app_exe.bc create mode 100644 examples/ecc/openvm/exe_commit.bytes diff --git a/examples/ecc/openvm.toml b/examples/ecc/openvm.toml index 47980b422d..1dc6cf25f2 100644 --- a/examples/ecc/openvm.toml +++ b/examples/ecc/openvm.toml @@ -2,7 +2,7 @@ [app_vm_config.rv32m] [app_vm_config.io] [app_vm_config.modular] -supported_modulus = ["115792089237316195423570985008687907853269984665640564039457584007908834671663", "115792089237316195423570985008687907852837564279074904382605163141518161494337"] +supported_moduli = ["115792089237316195423570985008687907853269984665640564039457584007908834671663", "115792089237316195423570985008687907852837564279074904382605163141518161494337"] [[app_vm_config.ecc.supported_curves]] struct_name = "Secp256k1Point" diff --git a/examples/ecc/openvm/app.vmexe b/examples/ecc/openvm/app.vmexe new file mode 100644 index 0000000000000000000000000000000000000000..19d83388bd090f285f09780ef2291b2c214bacaa GIT binary patch literal 417761 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 literal 0 HcmV?d00001 diff --git a/examples/ecc/openvm/committed_app_exe.bc b/examples/ecc/openvm/committed_app_exe.bc new file mode 100644 index 0000000000000000000000000000000000000000..823f559f9f8138c33202d14d360dc15520478ef0 GIT binary patch literal 2351140 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�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%0DrguO@dCfh-4GmP$ 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

U4;+#W1$3Kof6Ha?$E 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°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�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�Ac$W>uMK74ae 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%0DB-Y2;qMmJBu{SA*nJ| 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