From edc3520bf10d3cb5fd720e3181168c1a15875f2d Mon Sep 17 00:00:00 2001 From: Xuyang Song Date: Fri, 22 Nov 2024 19:19:48 +0800 Subject: [PATCH 1/9] move juvix files to juvix directory --- bench/compliance_circuit_bench.exs | 4 ++-- bench/logic_circuit_bench.exs | 4 ++-- {native/cairo_vm => juvix}/cairo.json | 0 {native/cairo_vm => juvix}/cairo.juvix | 0 {native/cairo_vm => juvix}/cairo_input.json | 0 {native/cairo_vm => juvix}/compliance.json | 0 {native/cairo_vm => juvix}/compliance.juvix | 0 {native/cairo_vm => juvix}/compliance_input.json | 0 {native/cairo_vm => juvix}/encryption.json | 0 {native/cairo_vm => juvix}/encryption.juvix | 0 {native/cairo_vm => juvix}/encryption_input.json | 0 {native/cairo_vm => juvix}/trivial_resource_logic.json | 0 {native/cairo_vm => juvix}/trivial_resource_logic.juvix | 0 .../cairo_vm => juvix}/trivial_resource_logic_input.json | 0 native/cairo_vm/README.md | 4 ++-- test/cairo_compliance_test.exs | 4 ++-- test/cairo_encryption.exs | 7 ++++--- test/cairo_logic_test.exs | 4 ++-- test/cairo_negative_test.exs | 4 ++-- test/cairo_test.exs | 4 ++-- 20 files changed, 18 insertions(+), 17 deletions(-) rename {native/cairo_vm => juvix}/cairo.json (100%) rename {native/cairo_vm => juvix}/cairo.juvix (100%) rename {native/cairo_vm => juvix}/cairo_input.json (100%) rename {native/cairo_vm => juvix}/compliance.json (100%) rename {native/cairo_vm => juvix}/compliance.juvix (100%) rename {native/cairo_vm => juvix}/compliance_input.json (100%) rename {native/cairo_vm => juvix}/encryption.json (100%) rename {native/cairo_vm => juvix}/encryption.juvix (100%) rename {native/cairo_vm => juvix}/encryption_input.json (100%) rename {native/cairo_vm => juvix}/trivial_resource_logic.json (100%) rename {native/cairo_vm => juvix}/trivial_resource_logic.juvix (100%) rename {native/cairo_vm => juvix}/trivial_resource_logic_input.json (100%) diff --git a/bench/compliance_circuit_bench.exs b/bench/compliance_circuit_bench.exs index b3491e4..16c3f72 100644 --- a/bench/compliance_circuit_bench.exs +++ b/bench/compliance_circuit_bench.exs @@ -1,5 +1,5 @@ -{:ok, program} = File.read("./native/cairo_vm/compliance.json") -{:ok, input} = File.read("./native/cairo_vm/compliance_input.json") +{:ok, program} = File.read("./juvix/compliance.json") +{:ok, input} = File.read("./juvix/compliance_input.json") {_output, trace, memory, public_inputs} = Cairo.cairo_vm_runner( diff --git a/bench/logic_circuit_bench.exs b/bench/logic_circuit_bench.exs index 3c7a0df..fd94909 100644 --- a/bench/logic_circuit_bench.exs +++ b/bench/logic_circuit_bench.exs @@ -1,7 +1,7 @@ -{:ok, program} = File.read("./native/cairo_vm/trivial_resource_logic.json") +{:ok, program} = File.read("./juvix/trivial_resource_logic.json") {:ok, input} = - File.read("./native/cairo_vm/trivial_resource_logic_input.json") + File.read("./juvix/trivial_resource_logic_input.json") {_output, trace, memory, public_inputs} = Cairo.cairo_vm_runner( diff --git a/native/cairo_vm/cairo.json b/juvix/cairo.json similarity index 100% rename from native/cairo_vm/cairo.json rename to juvix/cairo.json diff --git a/native/cairo_vm/cairo.juvix b/juvix/cairo.juvix similarity index 100% rename from native/cairo_vm/cairo.juvix rename to juvix/cairo.juvix diff --git a/native/cairo_vm/cairo_input.json b/juvix/cairo_input.json similarity index 100% rename from native/cairo_vm/cairo_input.json rename to juvix/cairo_input.json diff --git a/native/cairo_vm/compliance.json b/juvix/compliance.json similarity index 100% rename from native/cairo_vm/compliance.json rename to juvix/compliance.json diff --git a/native/cairo_vm/compliance.juvix b/juvix/compliance.juvix similarity index 100% rename from native/cairo_vm/compliance.juvix rename to juvix/compliance.juvix diff --git a/native/cairo_vm/compliance_input.json b/juvix/compliance_input.json similarity index 100% rename from native/cairo_vm/compliance_input.json rename to juvix/compliance_input.json diff --git a/native/cairo_vm/encryption.json b/juvix/encryption.json similarity index 100% rename from native/cairo_vm/encryption.json rename to juvix/encryption.json diff --git a/native/cairo_vm/encryption.juvix b/juvix/encryption.juvix similarity index 100% rename from native/cairo_vm/encryption.juvix rename to juvix/encryption.juvix diff --git a/native/cairo_vm/encryption_input.json b/juvix/encryption_input.json similarity index 100% rename from native/cairo_vm/encryption_input.json rename to juvix/encryption_input.json diff --git a/native/cairo_vm/trivial_resource_logic.json b/juvix/trivial_resource_logic.json similarity index 100% rename from native/cairo_vm/trivial_resource_logic.json rename to juvix/trivial_resource_logic.json diff --git a/native/cairo_vm/trivial_resource_logic.juvix b/juvix/trivial_resource_logic.juvix similarity index 100% rename from native/cairo_vm/trivial_resource_logic.juvix rename to juvix/trivial_resource_logic.juvix diff --git a/native/cairo_vm/trivial_resource_logic_input.json b/juvix/trivial_resource_logic_input.json similarity index 100% rename from native/cairo_vm/trivial_resource_logic_input.json rename to juvix/trivial_resource_logic_input.json diff --git a/native/cairo_vm/README.md b/native/cairo_vm/README.md index b944144..4e09a26 100644 --- a/native/cairo_vm/README.md +++ b/native/cairo_vm/README.md @@ -32,10 +32,10 @@ An example can be found in "cairo_api_test" # Run cairo-vm test "cairo_api_test" do // The file cairo.json is the output of Juvix compiler - {:ok, program} = File.read("./native/cairo_vm/cairo.json") + {:ok, program} = File.read("./juvix/cairo.json") // The file cairo_input.json is what we use to input data into the program. If there's no input, it'll just be an empty string. - {:ok, input} = File.read("./native/cairo_vm/cairo_input.json") + {:ok, input} = File.read("./juvix/cairo_input.json") // Run cairo vm {output, trace, memory, public_inputs} = diff --git a/test/cairo_compliance_test.exs b/test/cairo_compliance_test.exs index 351cdf2..9053760 100644 --- a/test/cairo_compliance_test.exs +++ b/test/cairo_compliance_test.exs @@ -5,8 +5,8 @@ defmodule CairoComplianceTest do doctest Cairo.CairoVM test "compliance_circuit" do - {:ok, program} = File.read("./native/cairo_vm/compliance.json") - # {:ok, input} = File.read("./native/cairo_vm/compliance_input.json") + {:ok, program} = File.read("./juvix/compliance.json") + # {:ok, input} = File.read("./juvix/compliance_input.json") input_resource = List.duplicate(1, 225) output_resource = List.duplicate(2, 225) path = List.duplicate(Cairo.random_felt(), 32) diff --git a/test/cairo_encryption.exs b/test/cairo_encryption.exs index 582db47..1a1f248 100644 --- a/test/cairo_encryption.exs +++ b/test/cairo_encryption.exs @@ -6,8 +6,8 @@ defmodule NifTest do test "cairo_encryption_test" do # encryption circuit test - {:ok, program} = File.read("./native/cairo_vm/encryption.json") - {:ok, input} = File.read("./native/cairo_vm/encryption_input.json") + {:ok, program} = File.read("./juvix/encryption.json") + {:ok, input} = File.read("./juvix/encryption_input.json") {_output, trace, memory, vm_public_input} = Cairo.cairo_vm_runner( @@ -41,7 +41,8 @@ defmodule NifTest do assert plaintext == expected_plaintext # decryption: wrong sk - assert {:error, "Invalid DH key"} = Cairo.decrypt(expected_cipher, felt_bytes_0) + assert {:error, "Invalid DH key"} = + Cairo.decrypt(expected_cipher, felt_bytes_0) end test "cairo_encryption_invalid_input_test" do diff --git a/test/cairo_logic_test.exs b/test/cairo_logic_test.exs index d03f7eb..23be093 100644 --- a/test/cairo_logic_test.exs +++ b/test/cairo_logic_test.exs @@ -6,10 +6,10 @@ defmodule CairoResourceLogicTest do test "resource_logic_circuit" do {:ok, program} = - File.read("./native/cairo_vm/trivial_resource_logic.json") + File.read("./juvix/trivial_resource_logic.json") {:ok, input} = - File.read("./native/cairo_vm/trivial_resource_logic_input.json") + File.read("./juvix/trivial_resource_logic_input.json") {_output, trace, memory, public_inputs} = Cairo.cairo_vm_runner( diff --git a/test/cairo_negative_test.exs b/test/cairo_negative_test.exs index 5a97083..6967190 100644 --- a/test/cairo_negative_test.exs +++ b/test/cairo_negative_test.exs @@ -6,7 +6,7 @@ defmodule NegativeTest do test "cairo_vm_runner with invalid program content" do invalid_program = "This is not valid JSON" - {:ok, input} = File.read("./native/cairo_vm/cairo_input.json") + {:ok, input} = File.read("./juvix/cairo_input.json") assert {:error, error_message} = Cairo.cairo_vm_runner(invalid_program, input) @@ -15,7 +15,7 @@ defmodule NegativeTest do end test "cairo_vm_runner with invalid input JSON" do - {:ok, program} = File.read("./native/cairo_vm/cairo.json") + {:ok, program} = File.read("./juvix/cairo.json") invalid_input = "This is not valid JSON" assert {:error, error_message} = diff --git a/test/cairo_test.exs b/test/cairo_test.exs index 5d44ae1..555b276 100644 --- a/test/cairo_test.exs +++ b/test/cairo_test.exs @@ -5,8 +5,8 @@ defmodule NifTest do doctest Cairo.CairoVM test "cairo_prove_test" do - {:ok, program} = File.read("./native/cairo_vm/cairo.json") - {:ok, input} = File.read("./native/cairo_vm/cairo_input.json") + {:ok, program} = File.read("./juvix/cairo.json") + {:ok, input} = File.read("./juvix/cairo_input.json") {output, trace, memory, vm_public_input} = Cairo.cairo_vm_runner( From 64af8fdb321430613a4a6d297be53a53dc3e6614 Mon Sep 17 00:00:00 2001 From: Xuyang Song Date: Fri, 22 Nov 2024 22:13:42 +0800 Subject: [PATCH 2/9] refine utils --- native/cairo_prover/src/lib.rs | 17 +++-------------- native/cairo_prover/src/utils.rs | 12 ++++++++++++ 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/native/cairo_prover/src/lib.rs b/native/cairo_prover/src/lib.rs index b08e80a..09879c8 100644 --- a/native/cairo_prover/src/lib.rs +++ b/native/cairo_prover/src/lib.rs @@ -9,7 +9,7 @@ use crate::{ compliance_input::ComplianceInputJson, encryption::Ciphertext, error::CairoError, - utils::{bytes_to_affine, bytes_to_felt, bytes_to_felt_vec, felt_to_string, random_felt}, + utils::{bytes_to_affine, bytes_to_felt, bytes_to_felt_vec}, }; use cairo_platinum_prover::{ air::{generate_cairo_proof, verify_cairo_proof, PublicInputs, Segment, SegmentName}, @@ -306,12 +306,6 @@ fn cairo_binding_sig_verify( verify(&pub_key_x, &msg, &r, &s).map_err(|_| CairoError::SigVerifyError.into()) } -// random_felt can help create private key in signature -#[rustler::nif] -fn cairo_random_felt() -> NifResult> { - Ok(random_felt()) -} - #[rustler::nif] fn get_public_key(priv_key: Vec) -> NifResult> { let priv_key_felt = bytes_to_felt(priv_key)?; @@ -384,11 +378,6 @@ fn program_hash(public_inputs: Vec) -> NifResult> { Ok(program_hash.to_bytes_be().to_vec()) } -#[rustler::nif] -fn cairo_felt_to_string(felt: Vec) -> NifResult { - Ok(felt_to_string(felt)?) -} - #[rustler::nif] fn cairo_generate_compliance_input_json( input_resource: Vec, @@ -463,13 +452,13 @@ rustler::init!( cairo_get_output, cairo_binding_sig_sign, cairo_binding_sig_verify, - cairo_random_felt, get_public_key, poseidon_single, poseidon, poseidon_many, program_hash, - cairo_felt_to_string, + utils::cairo_random_felt, + utils::cairo_felt_to_string, cairo_generate_compliance_input_json, encrypt, decrypt, diff --git a/native/cairo_prover/src/utils.rs b/native/cairo_prover/src/utils.rs index 2b9b23e..e4d4e44 100644 --- a/native/cairo_prover/src/utils.rs +++ b/native/cairo_prover/src/utils.rs @@ -1,8 +1,20 @@ use crate::error::CairoError; use rand::{thread_rng, RngCore}; +use rustler::NifResult; use starknet_types_core::curve::AffinePoint; use starknet_types_core::felt::Felt; +#[rustler::nif] +fn cairo_felt_to_string(felt: Vec) -> NifResult { + Ok(felt_to_string(felt)?) +} + +// random_felt can help create private key in signature +#[rustler::nif] +fn cairo_random_felt() -> NifResult> { + Ok(random_felt()) +} + pub fn felt_to_string(bytes: Vec) -> Result { let felt: [u8; 32] = bytes .try_into() From 2342b18a12dd0be2b7d4de2efb1488e8190def17 Mon Sep 17 00:00:00 2001 From: Xuyang Song Date: Fri, 22 Nov 2024 22:26:17 +0800 Subject: [PATCH 3/9] refine prove and verify --- native/cairo_prover/src/lib.rs | 244 +--------------------------- native/cairo_prover/src/prover.rs | 157 ++++++++++++++++++ native/cairo_prover/src/verifier.rs | 88 ++++++++++ 3 files changed, 251 insertions(+), 238 deletions(-) create mode 100644 native/cairo_prover/src/prover.rs create mode 100644 native/cairo_prover/src/verifier.rs diff --git a/native/cairo_prover/src/lib.rs b/native/cairo_prover/src/lib.rs index 09879c8..af02781 100644 --- a/native/cairo_prover/src/lib.rs +++ b/native/cairo_prover/src/lib.rs @@ -3,7 +3,9 @@ mod compliance_input; mod encryption; mod error; +mod prover; mod utils; +mod verifier; use crate::{ compliance_input::ComplianceInputJson, @@ -11,219 +13,16 @@ use crate::{ error::CairoError, utils::{bytes_to_affine, bytes_to_felt, bytes_to_felt_vec}, }; -use cairo_platinum_prover::{ - air::{generate_cairo_proof, verify_cairo_proof, PublicInputs, Segment, SegmentName}, - cairo_mem::CairoMemory, - execution_trace::build_main_trace, - register_states::RegisterStates, - Felt252, -}; -use hashbrown::HashMap; -use lambdaworks_math::traits::ByteConversion; use num_bigint::BigInt; use num_integer::Integer; use num_traits::Zero; use rand::{thread_rng, RngCore}; use rustler::NifResult; -use stark_platinum_prover::proof::options::{ProofOptions, SecurityLevel}; use starknet_crypto::{poseidon_hash, poseidon_hash_many, poseidon_hash_single, sign, verify}; use starknet_curve::curve_params::{EC_ORDER, GENERATOR}; use starknet_types_core::{curve::ProjectivePoint, felt::Felt}; use std::ops::Add; -#[rustler::nif(schedule = "DirtyCpu")] -fn cairo_prove( - trace: Vec, - memory: Vec, - public_input: Vec, -) -> NifResult<(Vec, Vec)> { - if trace.is_empty() || memory.is_empty() || public_input.is_empty() { - return Err(CairoError::EmptyInputs.into()); - } - // Generating the prover args - let register_states = - RegisterStates::from_bytes_le(&trace).map_err(|_| CairoError::CairoImportError)?; - - let memory = CairoMemory::from_bytes_le(&memory).map_err(|_| CairoError::CairoImportError)?; - - // Handle public inputs - let (rc_min, rc_max, public_memory, memory_segments) = parse_public_input(&public_input) - .map_err(|e| CairoError::ParsePublicInputError(e.to_string()))?; - - let num_steps = register_states.steps(); - let mut pub_inputs = PublicInputs { - pc_init: Felt252::from(register_states.rows[0].pc), - ap_init: Felt252::from(register_states.rows[0].ap), - fp_init: Felt252::from(register_states.rows[0].fp), - pc_final: Felt252::from(register_states.rows[num_steps - 1].pc), - ap_final: Felt252::from(register_states.rows[num_steps - 1].ap), - range_check_min: Some(rc_min), - range_check_max: Some(rc_max), - memory_segments, - public_memory, - num_steps, - }; - - // Build main trace - let main_trace = build_main_trace(®ister_states, &memory, &mut pub_inputs); - - // Generating proof - let proof_options = ProofOptions::new_secure(SecurityLevel::Conjecturable100Bits, 3); - let proof = generate_cairo_proof(&main_trace, &pub_inputs, &proof_options) - .map_err(|_| CairoError::ProvingError)?; - - // Encode proof and pub_inputs - let proof_bytes = bincode::serde::encode_to_vec(proof, bincode::config::standard()) - .map_err(CairoError::from)?; - let pub_input_bytes = bincode::serde::encode_to_vec(&pub_inputs, bincode::config::standard()) - .map_err(CairoError::from)?; - - Ok((proof_bytes, pub_input_bytes)) -} - -#[allow(clippy::type_complexity)] -fn parse_public_input( - public_input: &[u8], -) -> Result< - ( - u16, - u16, - HashMap, - HashMap, - ), - &'static str, -> { - let rc_min = u16::from_le_bytes( - public_input - .get(0..2) - .ok_or("Input must be at least 2 bytes long for rc_min")? - .try_into() - .map_err(|_| "Failed to convert rc_min bytes")?, - ); - - let rc_max = u16::from_le_bytes( - public_input - .get(2..4) - .ok_or("Input must be at least 4 bytes long for rc_max")? - .try_into() - .map_err(|_| "Failed to convert rc_max bytes")?, - ); - - let mem_len = u64::from_le_bytes( - public_input - .get(4..12) - .ok_or("Input must be at least 12 bytes long for mem_len")? - .try_into() - .map_err(|_| "Failed to convert mem_len bytes")?, - ) as usize; - - let mut public_memory: HashMap = HashMap::new(); - for i in 0..mem_len { - let start_index = 12 + i * 40; - let addr = Felt252::from(u64::from_le_bytes( - public_input - .get(start_index..start_index + 8) - .ok_or("Input too short for public memory address")? - .try_into() - .map_err(|_| "Failed to convert public memory address bytes")?, - )); - let value = Felt252::from_bytes_le( - public_input - .get(start_index + 8..start_index + 40) - .ok_or("Input too short for public memory value")?, - ) - .map_err(|_| "Failed to create Felt252 from bytes")?; - public_memory.insert(addr, value); - } - - let memory_segments_len = *public_input - .get(12 + 40 * mem_len) - .ok_or("Input too short for memory segments length")? - as usize; - let mut memory_segments = HashMap::new(); - for i in 0..memory_segments_len { - let start_index = 12 + 40 * mem_len + 1 + i * 17; - let segment_type = match public_input - .get(start_index) - .ok_or("Input too short for segment type")? - { - 0u8 => SegmentName::RangeCheck, - 1u8 => SegmentName::Output, - 2u8 => SegmentName::Program, - 3u8 => SegmentName::Execution, - 4u8 => SegmentName::Ecdsa, - 5u8 => SegmentName::Pedersen, - _ => continue, // skip unknown type - }; - - let segment_begin = u64::from_le_bytes( - public_input - .get(start_index + 1..start_index + 9) - .ok_or("Input too short for segment begin")? - .try_into() - .map_err(|_| "Failed to convert segment begin bytes")?, - ); - let segment_stop = u64::from_le_bytes( - public_input - .get(start_index + 9..start_index + 17) - .ok_or("Input too short for segment stop")? - .try_into() - .map_err(|_| "Failed to convert segment stop bytes")?, - ); - memory_segments.insert(segment_type, Segment::new(segment_begin, segment_stop)); - } - - Ok((rc_min, rc_max, public_memory, memory_segments)) -} - -#[rustler::nif(schedule = "DirtyCpu")] -fn cairo_verify(proof: Vec, public_input: Vec) -> NifResult { - let proof_options = ProofOptions::new_secure(SecurityLevel::Conjecturable100Bits, 3); - - // Decode proof - let proof = bincode::serde::decode_from_slice(&proof, bincode::config::standard()) - .map_err(CairoError::from)? - .0; - - // Decode public inputs - let pub_inputs = bincode::serde::decode_from_slice(&public_input, bincode::config::standard()) - .map_err(CairoError::from)? - .0; - - Ok(verify_cairo_proof(&proof, &pub_inputs, &proof_options)) -} - -#[rustler::nif()] -fn cairo_get_output(public_input: Vec) -> NifResult>> { - // Decode public inputs - let (pub_inputs, _): (PublicInputs, usize) = - bincode::serde::decode_from_slice(&public_input, bincode::config::standard()) - .map_err(CairoError::from)?; - - // Get output segments - let output_segments = pub_inputs - .memory_segments - .get(&SegmentName::Output) - .ok_or_else(|| CairoError::SegmentNotFound)?; - - let begin_addr: u64 = output_segments.begin_addr as u64; - let stop_addr: u64 = output_segments.stop_ptr as u64; - - let mut output_values = Vec::new(); - for addr in begin_addr..stop_addr { - // Convert addr to FieldElement (assuming this is the correct way to create a FieldElement from an address) - let addr_field_element = Felt252::from(addr); - - if let Some(value) = pub_inputs.public_memory.get(&addr_field_element) { - output_values.push(value.clone().to_bytes_be().to_vec()); - } else { - return Err(CairoError::AddressNotFound(addr).into()); - } - } - - Ok(output_values) -} - // The private_key_segments are random values used in delta commitments. // The messages are nullifiers and resource commitments in the transaction. #[rustler::nif] @@ -347,37 +146,6 @@ fn poseidon_many(inputs: Vec>) -> NifResult> { Ok(result_fe.to_bytes_be().to_vec()) } -// Get the program from public inputs and return the program hash as the -// resource label -#[rustler::nif] -fn program_hash(public_inputs: Vec) -> NifResult> { - let (pub_inputs, _): (PublicInputs, usize) = - bincode::serde::decode_from_slice(&public_inputs, bincode::config::standard()) - .map_err(CairoError::from)?; - let program_segments = pub_inputs - .memory_segments - .get(&SegmentName::Program) - .ok_or_else(|| CairoError::SegmentNotFound)?; - - let begin_addr: u64 = program_segments.begin_addr as u64; - let stop_addr: u64 = program_segments.stop_ptr as u64; - - let mut program = Vec::new(); - for addr in begin_addr..stop_addr { - // Convert addr to FieldElement (assuming this is the correct way to create a FieldElement from an address) - let addr_field_element = Felt252::from(addr); - let value = pub_inputs - .public_memory - .get(&addr_field_element) - .ok_or_else(|| CairoError::AddressNotFound(addr))?; - program.push(Felt::from_raw(value.to_raw().limbs)); - } - - let program_hash = poseidon_hash_many(&program); - - Ok(program_hash.to_bytes_be().to_vec()) -} - #[rustler::nif] fn cairo_generate_compliance_input_json( input_resource: Vec, @@ -447,16 +215,16 @@ fn decrypt(cihper: Vec>, sk: Vec) -> NifResult>> { rustler::init!( "Elixir.Cairo.CairoProver", [ - cairo_prove, - cairo_verify, - cairo_get_output, + prover::cairo_prove, + verifier::cairo_verify, + verifier::cairo_get_output, + verifier::program_hash, cairo_binding_sig_sign, cairo_binding_sig_verify, get_public_key, poseidon_single, poseidon, poseidon_many, - program_hash, utils::cairo_random_felt, utils::cairo_felt_to_string, cairo_generate_compliance_input_json, diff --git a/native/cairo_prover/src/prover.rs b/native/cairo_prover/src/prover.rs new file mode 100644 index 0000000..6648d7c --- /dev/null +++ b/native/cairo_prover/src/prover.rs @@ -0,0 +1,157 @@ +use crate::error::CairoError; +use cairo_platinum_prover::{ + air::{generate_cairo_proof, PublicInputs, Segment, SegmentName}, + cairo_mem::CairoMemory, + execution_trace::build_main_trace, + register_states::RegisterStates, + Felt252, +}; +use hashbrown::HashMap; +use lambdaworks_math::traits::ByteConversion; +use rustler::NifResult; +use stark_platinum_prover::proof::options::{ProofOptions, SecurityLevel}; + +#[rustler::nif(schedule = "DirtyCpu")] +fn cairo_prove( + trace: Vec, + memory: Vec, + public_input: Vec, +) -> NifResult<(Vec, Vec)> { + if trace.is_empty() || memory.is_empty() || public_input.is_empty() { + return Err(CairoError::EmptyInputs.into()); + } + // Generating the prover args + let register_states = + RegisterStates::from_bytes_le(&trace).map_err(|_| CairoError::CairoImportError)?; + + let memory = CairoMemory::from_bytes_le(&memory).map_err(|_| CairoError::CairoImportError)?; + + // Handle public inputs + let (rc_min, rc_max, public_memory, memory_segments) = parse_public_input(&public_input) + .map_err(|e| CairoError::ParsePublicInputError(e.to_string()))?; + + let num_steps = register_states.steps(); + let mut pub_inputs = PublicInputs { + pc_init: Felt252::from(register_states.rows[0].pc), + ap_init: Felt252::from(register_states.rows[0].ap), + fp_init: Felt252::from(register_states.rows[0].fp), + pc_final: Felt252::from(register_states.rows[num_steps - 1].pc), + ap_final: Felt252::from(register_states.rows[num_steps - 1].ap), + range_check_min: Some(rc_min), + range_check_max: Some(rc_max), + memory_segments, + public_memory, + num_steps, + }; + + // Build main trace + let main_trace = build_main_trace(®ister_states, &memory, &mut pub_inputs); + + // Generating proof + let proof_options = ProofOptions::new_secure(SecurityLevel::Conjecturable100Bits, 3); + let proof = generate_cairo_proof(&main_trace, &pub_inputs, &proof_options) + .map_err(|_| CairoError::ProvingError)?; + + // Encode proof and pub_inputs + let proof_bytes = bincode::serde::encode_to_vec(proof, bincode::config::standard()) + .map_err(CairoError::from)?; + let pub_input_bytes = bincode::serde::encode_to_vec(&pub_inputs, bincode::config::standard()) + .map_err(CairoError::from)?; + + Ok((proof_bytes, pub_input_bytes)) +} + +#[allow(clippy::type_complexity)] +fn parse_public_input( + public_input: &[u8], +) -> Result< + ( + u16, + u16, + HashMap, + HashMap, + ), + &'static str, +> { + let rc_min = u16::from_le_bytes( + public_input + .get(0..2) + .ok_or("Input must be at least 2 bytes long for rc_min")? + .try_into() + .map_err(|_| "Failed to convert rc_min bytes")?, + ); + + let rc_max = u16::from_le_bytes( + public_input + .get(2..4) + .ok_or("Input must be at least 4 bytes long for rc_max")? + .try_into() + .map_err(|_| "Failed to convert rc_max bytes")?, + ); + + let mem_len = u64::from_le_bytes( + public_input + .get(4..12) + .ok_or("Input must be at least 12 bytes long for mem_len")? + .try_into() + .map_err(|_| "Failed to convert mem_len bytes")?, + ) as usize; + + let mut public_memory: HashMap = HashMap::new(); + for i in 0..mem_len { + let start_index = 12 + i * 40; + let addr = Felt252::from(u64::from_le_bytes( + public_input + .get(start_index..start_index + 8) + .ok_or("Input too short for public memory address")? + .try_into() + .map_err(|_| "Failed to convert public memory address bytes")?, + )); + let value = Felt252::from_bytes_le( + public_input + .get(start_index + 8..start_index + 40) + .ok_or("Input too short for public memory value")?, + ) + .map_err(|_| "Failed to create Felt252 from bytes")?; + public_memory.insert(addr, value); + } + + let memory_segments_len = *public_input + .get(12 + 40 * mem_len) + .ok_or("Input too short for memory segments length")? + as usize; + let mut memory_segments = HashMap::new(); + for i in 0..memory_segments_len { + let start_index = 12 + 40 * mem_len + 1 + i * 17; + let segment_type = match public_input + .get(start_index) + .ok_or("Input too short for segment type")? + { + 0u8 => SegmentName::RangeCheck, + 1u8 => SegmentName::Output, + 2u8 => SegmentName::Program, + 3u8 => SegmentName::Execution, + 4u8 => SegmentName::Ecdsa, + 5u8 => SegmentName::Pedersen, + _ => continue, // skip unknown type + }; + + let segment_begin = u64::from_le_bytes( + public_input + .get(start_index + 1..start_index + 9) + .ok_or("Input too short for segment begin")? + .try_into() + .map_err(|_| "Failed to convert segment begin bytes")?, + ); + let segment_stop = u64::from_le_bytes( + public_input + .get(start_index + 9..start_index + 17) + .ok_or("Input too short for segment stop")? + .try_into() + .map_err(|_| "Failed to convert segment stop bytes")?, + ); + memory_segments.insert(segment_type, Segment::new(segment_begin, segment_stop)); + } + + Ok((rc_min, rc_max, public_memory, memory_segments)) +} diff --git a/native/cairo_prover/src/verifier.rs b/native/cairo_prover/src/verifier.rs new file mode 100644 index 0000000..b491929 --- /dev/null +++ b/native/cairo_prover/src/verifier.rs @@ -0,0 +1,88 @@ +use crate::error::CairoError; +use cairo_platinum_prover::{ + air::{verify_cairo_proof, PublicInputs, SegmentName}, + Felt252, +}; +use rustler::NifResult; +use stark_platinum_prover::proof::options::{ProofOptions, SecurityLevel}; +use starknet_crypto::poseidon_hash_many; +use starknet_types_core::felt::Felt; + +#[rustler::nif(schedule = "DirtyCpu")] +fn cairo_verify(proof: Vec, public_input: Vec) -> NifResult { + let proof_options = ProofOptions::new_secure(SecurityLevel::Conjecturable100Bits, 3); + + // Decode proof + let proof = bincode::serde::decode_from_slice(&proof, bincode::config::standard()) + .map_err(CairoError::from)? + .0; + + // Decode public inputs + let pub_inputs = bincode::serde::decode_from_slice(&public_input, bincode::config::standard()) + .map_err(CairoError::from)? + .0; + + Ok(verify_cairo_proof(&proof, &pub_inputs, &proof_options)) +} + +#[rustler::nif()] +fn cairo_get_output(public_input: Vec) -> NifResult>> { + // Decode public inputs + let (pub_inputs, _): (PublicInputs, usize) = + bincode::serde::decode_from_slice(&public_input, bincode::config::standard()) + .map_err(CairoError::from)?; + + // Get output segments + let output_segments = pub_inputs + .memory_segments + .get(&SegmentName::Output) + .ok_or_else(|| CairoError::SegmentNotFound)?; + + let begin_addr: u64 = output_segments.begin_addr as u64; + let stop_addr: u64 = output_segments.stop_ptr as u64; + + let mut output_values = Vec::new(); + for addr in begin_addr..stop_addr { + // Convert addr to FieldElement (assuming this is the correct way to create a FieldElement from an address) + let addr_field_element = Felt252::from(addr); + + if let Some(value) = pub_inputs.public_memory.get(&addr_field_element) { + output_values.push(value.clone().to_bytes_be().to_vec()); + } else { + return Err(CairoError::AddressNotFound(addr).into()); + } + } + + Ok(output_values) +} + +// Get the program from public inputs and return the program hash as the +// resource label +#[rustler::nif] +fn program_hash(public_inputs: Vec) -> NifResult> { + let (pub_inputs, _): (PublicInputs, usize) = + bincode::serde::decode_from_slice(&public_inputs, bincode::config::standard()) + .map_err(CairoError::from)?; + let program_segments = pub_inputs + .memory_segments + .get(&SegmentName::Program) + .ok_or_else(|| CairoError::SegmentNotFound)?; + + let begin_addr: u64 = program_segments.begin_addr as u64; + let stop_addr: u64 = program_segments.stop_ptr as u64; + + let mut program = Vec::new(); + for addr in begin_addr..stop_addr { + // Convert addr to FieldElement (assuming this is the correct way to create a FieldElement from an address) + let addr_field_element = Felt252::from(addr); + let value = pub_inputs + .public_memory + .get(&addr_field_element) + .ok_or_else(|| CairoError::AddressNotFound(addr))?; + program.push(Felt::from_raw(value.to_raw().limbs)); + } + + let program_hash = poseidon_hash_many(&program); + + Ok(program_hash.to_bytes_be().to_vec()) +} From b12c0ce791c3eef32179d4bafc85cca47d035192 Mon Sep 17 00:00:00 2001 From: Xuyang Song Date: Fri, 22 Nov 2024 22:34:54 +0800 Subject: [PATCH 4/9] refine binding signature --- native/cairo_prover/src/binding_signature.rs | 117 ++++++++++++++++++ native/cairo_prover/src/lib.rs | 122 ++----------------- 2 files changed, 124 insertions(+), 115 deletions(-) create mode 100644 native/cairo_prover/src/binding_signature.rs diff --git a/native/cairo_prover/src/binding_signature.rs b/native/cairo_prover/src/binding_signature.rs new file mode 100644 index 0000000..4d8ac96 --- /dev/null +++ b/native/cairo_prover/src/binding_signature.rs @@ -0,0 +1,117 @@ +use crate::{ + error::CairoError, + utils::{bytes_to_affine, bytes_to_felt, bytes_to_felt_vec}, +}; +use num_bigint::BigInt; +use num_integer::Integer; +use num_traits::Zero; +use rand::{thread_rng, RngCore}; +use rustler::NifResult; +use starknet_crypto::{poseidon_hash_many, sign, verify}; +use starknet_curve::curve_params::{EC_ORDER, GENERATOR}; +use starknet_types_core::{curve::ProjectivePoint, felt::Felt}; +use std::ops::Add; + +// The private_key_segments are random values used in delta commitments. +// The messages are nullifiers and resource commitments in the transaction. +#[rustler::nif] +fn cairo_binding_sig_sign( + private_key_segments: Vec, + messages: Vec>, +) -> NifResult> { + if private_key_segments.is_empty() || private_key_segments.len() % 32 != 0 { + return Err(CairoError::InvalidInputs.into()); + } + // Compute private key + let private_key = { + let result = private_key_segments + .chunks(32) + .fold(BigInt::zero(), |acc, key_segment| { + let key = BigInt::from_bytes_be(num_bigint::Sign::Plus, key_segment); + acc.add(key) + }) + .mod_floor(&EC_ORDER.to_bigint()); + + let (_, buffer) = result.to_bytes_be(); + let mut result = [0u8; 32]; + result[(32 - buffer.len())..].copy_from_slice(&buffer[..]); + + Felt::from_bytes_be(&result) + }; + + // Message digest + let sig_hash = message_digest(messages)?; + + // ECDSA sign + let mut rng = thread_rng(); + let k = { + let mut felt: [u8; 32] = Default::default(); + rng.fill_bytes(&mut felt); + Felt::from_bytes_be(&felt) + }; + let signature = sign(&private_key, &sig_hash, &k).map_err(CairoError::from)?; + + // Serialize signature + let mut ret = Vec::new(); + ret.extend(signature.r.to_bytes_be()); + ret.extend(signature.s.to_bytes_be()); + // We don't need the v to recover pubkey + // ret.extend(signature.v.to_bytes_be()); + Ok(ret) +} + +// The pub_key_segments are delta commitments in compliance input inputs. +#[rustler::nif] +fn cairo_binding_sig_verify( + pub_key_segments: Vec>, + messages: Vec>, + signature: Vec, +) -> NifResult { + // Generate the public key + let mut pub_key = ProjectivePoint::identity(); + for pk_seg_bytes in pub_key_segments.into_iter() { + let pk_seg = bytes_to_affine(pk_seg_bytes)?; + pub_key += pk_seg; + } + let pub_key_x = pub_key + .to_affine() + .map_err(|_| CairoError::InvalidAffinePoint)? + .x(); + + // Message digest + let msg = message_digest(messages)?; + + // Decode the signature + if signature.len() != 64 { + return Err(CairoError::InvalidSignatureFormat.into()); + } + + let (r_bytes, s_bytes) = signature.split_at(32); + let r = bytes_to_felt(r_bytes.to_vec())?; + let s = bytes_to_felt(s_bytes.to_vec())?; + + // Verify the signature + verify(&pub_key_x, &msg, &r, &s).map_err(|_| CairoError::SigVerifyError.into()) +} + +#[rustler::nif] +fn get_public_key(priv_key: Vec) -> NifResult> { + let priv_key_felt = bytes_to_felt(priv_key)?; + + let generator = ProjectivePoint::from_affine(GENERATOR.x(), GENERATOR.y()) + .map_err(|_| CairoError::InvalidAffinePoint)?; + + let pub_key = (&generator * priv_key_felt) + .to_affine() + .map_err(|_| CairoError::InvalidAffinePoint)?; + + let mut ret = pub_key.x().to_bytes_be().to_vec(); + let mut y = pub_key.y().to_bytes_be().to_vec(); + ret.append(&mut y); + Ok(ret) +} + +fn message_digest(msg: Vec>) -> NifResult { + let felt_msg_vec: Vec = bytes_to_felt_vec(msg)?; + Ok(poseidon_hash_many(&felt_msg_vec)) +} diff --git a/native/cairo_prover/src/lib.rs b/native/cairo_prover/src/lib.rs index af02781..0192b21 100644 --- a/native/cairo_prover/src/lib.rs +++ b/native/cairo_prover/src/lib.rs @@ -1,5 +1,6 @@ #![allow(dead_code)] +mod binding_signature; mod compliance_input; mod encryption; mod error; @@ -10,121 +11,10 @@ mod verifier; use crate::{ compliance_input::ComplianceInputJson, encryption::Ciphertext, - error::CairoError, utils::{bytes_to_affine, bytes_to_felt, bytes_to_felt_vec}, }; -use num_bigint::BigInt; -use num_integer::Integer; -use num_traits::Zero; -use rand::{thread_rng, RngCore}; use rustler::NifResult; -use starknet_crypto::{poseidon_hash, poseidon_hash_many, poseidon_hash_single, sign, verify}; -use starknet_curve::curve_params::{EC_ORDER, GENERATOR}; -use starknet_types_core::{curve::ProjectivePoint, felt::Felt}; -use std::ops::Add; - -// The private_key_segments are random values used in delta commitments. -// The messages are nullifiers and resource commitments in the transaction. -#[rustler::nif] -fn cairo_binding_sig_sign( - private_key_segments: Vec, - messages: Vec>, -) -> NifResult> { - if private_key_segments.is_empty() || private_key_segments.len() % 32 != 0 { - return Err(CairoError::InvalidInputs.into()); - } - // Compute private key - let private_key = { - let result = private_key_segments - .chunks(32) - .fold(BigInt::zero(), |acc, key_segment| { - let key = BigInt::from_bytes_be(num_bigint::Sign::Plus, key_segment); - acc.add(key) - }) - .mod_floor(&EC_ORDER.to_bigint()); - - let (_, buffer) = result.to_bytes_be(); - let mut result = [0u8; 32]; - result[(32 - buffer.len())..].copy_from_slice(&buffer[..]); - - Felt::from_bytes_be(&result) - }; - - // Message digest - let sig_hash = message_digest(messages)?; - - // ECDSA sign - let mut rng = thread_rng(); - let k = { - let mut felt: [u8; 32] = Default::default(); - rng.fill_bytes(&mut felt); - Felt::from_bytes_be(&felt) - }; - let signature = sign(&private_key, &sig_hash, &k).map_err(CairoError::from)?; - - // Serialize signature - let mut ret = Vec::new(); - ret.extend(signature.r.to_bytes_be()); - ret.extend(signature.s.to_bytes_be()); - // We don't need the v to recover pubkey - // ret.extend(signature.v.to_bytes_be()); - Ok(ret) -} - -// The pub_key_segments are delta commitments in compliance input inputs. -#[rustler::nif] -fn cairo_binding_sig_verify( - pub_key_segments: Vec>, - messages: Vec>, - signature: Vec, -) -> NifResult { - // Generate the public key - let mut pub_key = ProjectivePoint::identity(); - for pk_seg_bytes in pub_key_segments.into_iter() { - let pk_seg = bytes_to_affine(pk_seg_bytes)?; - pub_key += pk_seg; - } - let pub_key_x = pub_key - .to_affine() - .map_err(|_| CairoError::InvalidAffinePoint)? - .x(); - - // Message digest - let msg = message_digest(messages)?; - - // Decode the signature - if signature.len() != 64 { - return Err(CairoError::InvalidSignatureFormat.into()); - } - - let (r_bytes, s_bytes) = signature.split_at(32); - let r = bytes_to_felt(r_bytes.to_vec())?; - let s = bytes_to_felt(s_bytes.to_vec())?; - - // Verify the signature - verify(&pub_key_x, &msg, &r, &s).map_err(|_| CairoError::SigVerifyError.into()) -} - -#[rustler::nif] -fn get_public_key(priv_key: Vec) -> NifResult> { - let priv_key_felt = bytes_to_felt(priv_key)?; - - let generator = ProjectivePoint::from_affine(GENERATOR.x(), GENERATOR.y()) - .map_err(|_| CairoError::InvalidAffinePoint)?; - - let pub_key = (&generator * priv_key_felt) - .to_affine() - .map_err(|_| CairoError::InvalidAffinePoint)?; - - let mut ret = pub_key.x().to_bytes_be().to_vec(); - let mut y = pub_key.y().to_bytes_be().to_vec(); - ret.append(&mut y); - Ok(ret) -} -fn message_digest(msg: Vec>) -> NifResult { - let felt_msg_vec: Vec = bytes_to_felt_vec(msg)?; - Ok(poseidon_hash_many(&felt_msg_vec)) -} +use starknet_crypto::{poseidon_hash, poseidon_hash_many, poseidon_hash_single}; #[rustler::nif] fn poseidon_single(x: Vec) -> NifResult> { @@ -219,9 +109,9 @@ rustler::init!( verifier::cairo_verify, verifier::cairo_get_output, verifier::program_hash, - cairo_binding_sig_sign, - cairo_binding_sig_verify, - get_public_key, + binding_signature::cairo_binding_sig_sign, + binding_signature::cairo_binding_sig_verify, + binding_signature::get_public_key, poseidon_single, poseidon, poseidon_many, @@ -249,6 +139,7 @@ lazy_static! { #[test] fn test_prf_expand_personalization() { + use starknet_types_core::felt::Felt; println!( "PRF_EXPAND_PERSONALIZATION_FELT bytes: {:?}", *PRF_EXPAND_PERSONALIZATION_FELT @@ -268,6 +159,7 @@ fn test_prf_expand_personalization() { #[test] fn generate_compliance_input_test_params() { + use starknet_types_core::felt::Felt; println!("Felf one hex: {:?}", Felt::ONE.to_hex_string()); let input_nf_key = Felt::ONE; let input_npk = poseidon_hash(input_nf_key, Felt::ZERO); From 61e2a126f0f01dab4e08e7863fa45f58a9be2d76 Mon Sep 17 00:00:00 2001 From: Xuyang Song Date: Fri, 22 Nov 2024 22:39:14 +0800 Subject: [PATCH 5/9] refine poseidon --- native/cairo_prover/src/lib.rs | 30 ++++++----------------------- native/cairo_prover/src/poseidon.rs | 23 ++++++++++++++++++++++ 2 files changed, 29 insertions(+), 24 deletions(-) create mode 100644 native/cairo_prover/src/poseidon.rs diff --git a/native/cairo_prover/src/lib.rs b/native/cairo_prover/src/lib.rs index 0192b21..e79b143 100644 --- a/native/cairo_prover/src/lib.rs +++ b/native/cairo_prover/src/lib.rs @@ -4,6 +4,7 @@ mod binding_signature; mod compliance_input; mod encryption; mod error; +mod poseidon; mod prover; mod utils; mod verifier; @@ -14,27 +15,6 @@ use crate::{ utils::{bytes_to_affine, bytes_to_felt, bytes_to_felt_vec}, }; use rustler::NifResult; -use starknet_crypto::{poseidon_hash, poseidon_hash_many, poseidon_hash_single}; - -#[rustler::nif] -fn poseidon_single(x: Vec) -> NifResult> { - let x_field = bytes_to_felt(x)?; - Ok(poseidon_hash_single(x_field).to_bytes_be().to_vec()) -} - -#[rustler::nif] -fn poseidon(x: Vec, y: Vec) -> NifResult> { - let x_field = bytes_to_felt(x)?; - let y_field = bytes_to_felt(y)?; - Ok(poseidon_hash(x_field, y_field).to_bytes_be().to_vec()) -} - -#[rustler::nif] -fn poseidon_many(inputs: Vec>) -> NifResult> { - let vec_fe = bytes_to_felt_vec(inputs)?; - let result_fe = poseidon_hash_many(&vec_fe); - Ok(result_fe.to_bytes_be().to_vec()) -} #[rustler::nif] fn cairo_generate_compliance_input_json( @@ -112,9 +92,9 @@ rustler::init!( binding_signature::cairo_binding_sig_sign, binding_signature::cairo_binding_sig_verify, binding_signature::get_public_key, - poseidon_single, - poseidon, - poseidon_many, + poseidon::poseidon_single, + poseidon::poseidon, + poseidon::poseidon_many, utils::cairo_random_felt, utils::cairo_felt_to_string, cairo_generate_compliance_input_json, @@ -159,7 +139,9 @@ fn test_prf_expand_personalization() { #[test] fn generate_compliance_input_test_params() { + use starknet_crypto::poseidon_hash; use starknet_types_core::felt::Felt; + println!("Felf one hex: {:?}", Felt::ONE.to_hex_string()); let input_nf_key = Felt::ONE; let input_npk = poseidon_hash(input_nf_key, Felt::ZERO); diff --git a/native/cairo_prover/src/poseidon.rs b/native/cairo_prover/src/poseidon.rs new file mode 100644 index 0000000..a9d8bd8 --- /dev/null +++ b/native/cairo_prover/src/poseidon.rs @@ -0,0 +1,23 @@ +use crate::utils::{bytes_to_felt, bytes_to_felt_vec}; +use rustler::NifResult; +use starknet_crypto::{poseidon_hash, poseidon_hash_many, poseidon_hash_single}; + +#[rustler::nif] +fn poseidon_single(x: Vec) -> NifResult> { + let x_field = bytes_to_felt(x)?; + Ok(poseidon_hash_single(x_field).to_bytes_be().to_vec()) +} + +#[rustler::nif] +fn poseidon(x: Vec, y: Vec) -> NifResult> { + let x_field = bytes_to_felt(x)?; + let y_field = bytes_to_felt(y)?; + Ok(poseidon_hash(x_field, y_field).to_bytes_be().to_vec()) +} + +#[rustler::nif] +fn poseidon_many(inputs: Vec>) -> NifResult> { + let vec_fe = bytes_to_felt_vec(inputs)?; + let result_fe = poseidon_hash_many(&vec_fe); + Ok(result_fe.to_bytes_be().to_vec()) +} From 911db832c22f5cd61acc0405004a9b56afb8e351 Mon Sep 17 00:00:00 2001 From: Xuyang Song Date: Fri, 22 Nov 2024 22:41:59 +0800 Subject: [PATCH 6/9] refine compliance input --- native/cairo_prover/src/compliance_input.rs | 34 +++++++++++++++++++ native/cairo_prover/src/lib.rs | 36 +-------------------- 2 files changed, 35 insertions(+), 35 deletions(-) diff --git a/native/cairo_prover/src/compliance_input.rs b/native/cairo_prover/src/compliance_input.rs index 66690b5..6111988 100644 --- a/native/cairo_prover/src/compliance_input.rs +++ b/native/cairo_prover/src/compliance_input.rs @@ -1,6 +1,28 @@ use crate::{error::CairoError, utils::felt_to_string}; +use rustler::NifResult; use serde::{Deserialize, Serialize}; +#[rustler::nif] +fn cairo_generate_compliance_input_json( + input_resource: Vec, + output_resource: Vec, + path: Vec>, + pos: u64, + input_nf_key: Vec, + eph_root: Vec, + rcv: Vec, +) -> NifResult { + Ok(ComplianceInputJson::to_json_string( + input_resource, + output_resource, + path, + pos, + input_nf_key, + eph_root, + rcv, + )?) +} + #[derive(Debug, Default, Clone, Serialize, Deserialize)] pub struct ComplianceInputJson { input: ResourceJson, @@ -108,3 +130,15 @@ fn test_compliance_input_json() { println!("compliance_input_json: {}", json); } + +#[test] +fn generate_compliance_input_test_params() { + use starknet_crypto::poseidon_hash; + use starknet_types_core::felt::Felt; + + println!("Felf one hex: {:?}", Felt::ONE.to_hex_string()); + let input_nf_key = Felt::ONE; + let input_npk = poseidon_hash(input_nf_key, Felt::ZERO); + println!("input_npk: {:?}", input_npk.to_bytes_be()); + println!("input_npk: {:?}", input_npk.to_hex_string()); +} diff --git a/native/cairo_prover/src/lib.rs b/native/cairo_prover/src/lib.rs index e79b143..c69797f 100644 --- a/native/cairo_prover/src/lib.rs +++ b/native/cairo_prover/src/lib.rs @@ -10,33 +10,11 @@ mod utils; mod verifier; use crate::{ - compliance_input::ComplianceInputJson, encryption::Ciphertext, utils::{bytes_to_affine, bytes_to_felt, bytes_to_felt_vec}, }; use rustler::NifResult; -#[rustler::nif] -fn cairo_generate_compliance_input_json( - input_resource: Vec, - output_resource: Vec, - path: Vec>, - pos: u64, - input_nf_key: Vec, - eph_root: Vec, - rcv: Vec, -) -> NifResult { - Ok(ComplianceInputJson::to_json_string( - input_resource, - output_resource, - path, - pos, - input_nf_key, - eph_root, - rcv, - )?) -} - #[rustler::nif] fn encrypt( messages: Vec>, @@ -97,7 +75,7 @@ rustler::init!( poseidon::poseidon_many, utils::cairo_random_felt, utils::cairo_felt_to_string, - cairo_generate_compliance_input_json, + compliance_input::cairo_generate_compliance_input_json, encrypt, decrypt, ] @@ -136,15 +114,3 @@ fn test_prf_expand_personalization() { .to_hex_string() ); } - -#[test] -fn generate_compliance_input_test_params() { - use starknet_crypto::poseidon_hash; - use starknet_types_core::felt::Felt; - - println!("Felf one hex: {:?}", Felt::ONE.to_hex_string()); - let input_nf_key = Felt::ONE; - let input_npk = poseidon_hash(input_nf_key, Felt::ZERO); - println!("input_npk: {:?}", input_npk.to_bytes_be()); - println!("input_npk: {:?}", input_npk.to_hex_string()); -} From 16da2b5e0db4c2e20b970dd7a3bfd490ed851001 Mon Sep 17 00:00:00 2001 From: Xuyang Song Date: Fri, 22 Nov 2024 22:45:19 +0800 Subject: [PATCH 7/9] refine encryption --- native/cairo_prover/src/encryption.rs | 51 ++++++++++++++++++++++++- native/cairo_prover/src/lib.rs | 55 +-------------------------- 2 files changed, 52 insertions(+), 54 deletions(-) diff --git a/native/cairo_prover/src/encryption.rs b/native/cairo_prover/src/encryption.rs index 03d6cec..0791aa3 100644 --- a/native/cairo_prover/src/encryption.rs +++ b/native/cairo_prover/src/encryption.rs @@ -1,4 +1,8 @@ -use crate::{error::CairoError, utils::bytes_to_felt_vec}; +use crate::{ + error::CairoError, + utils::{bytes_to_affine, bytes_to_felt, bytes_to_felt_vec}, +}; +use rustler::NifResult; use starknet_crypto::{poseidon_hash, poseidon_hash_many}; use starknet_curve::curve_params::GENERATOR; use starknet_types_core::{ @@ -6,6 +10,51 @@ use starknet_types_core::{ felt::Felt, }; +#[rustler::nif] +fn encrypt( + messages: Vec>, + pk: Vec, + sk: Vec, + nonce: Vec, +) -> NifResult>> { + // Decode messages + let msgs_felt = bytes_to_felt_vec(messages)?; + + // Decode pk + let pk_affine = bytes_to_affine(pk)?; + + // Decode sk + let sk_felt = bytes_to_felt(sk)?; + + // Decode nonce + let nonce_felt = bytes_to_felt(nonce)?; + + // Encrypt + let cipher = Ciphertext::encrypt(&msgs_felt, &pk_affine, &sk_felt, &nonce_felt)?; + let cipher_bytes = cipher + .inner() + .iter() + .map(|x| x.to_bytes_be().to_vec()) + .collect(); + + Ok(cipher_bytes) +} + +#[rustler::nif] +fn decrypt(cihper: Vec>, sk: Vec) -> NifResult>> { + // Decode messages + let cipher = Ciphertext::from_bytes(cihper)?; + + // Decode sk + let sk_felt = bytes_to_felt(sk)?; + + // Encrypt + let plaintext = cipher.decrypt(&sk_felt)?; + let plaintext_bytes = plaintext.iter().map(|x| x.to_bytes_be().to_vec()).collect(); + + Ok(plaintext_bytes) +} + // The PLAINTEXT_NUM should be fixed to achieve the indistinguishability of resource logics // Make it 10 pub const PLAINTEXT_NUM: usize = 10; diff --git a/native/cairo_prover/src/lib.rs b/native/cairo_prover/src/lib.rs index c69797f..9addf7b 100644 --- a/native/cairo_prover/src/lib.rs +++ b/native/cairo_prover/src/lib.rs @@ -9,57 +9,6 @@ mod prover; mod utils; mod verifier; -use crate::{ - encryption::Ciphertext, - utils::{bytes_to_affine, bytes_to_felt, bytes_to_felt_vec}, -}; -use rustler::NifResult; - -#[rustler::nif] -fn encrypt( - messages: Vec>, - pk: Vec, - sk: Vec, - nonce: Vec, -) -> NifResult>> { - // Decode messages - let msgs_felt = bytes_to_felt_vec(messages)?; - - // Decode pk - let pk_affine = bytes_to_affine(pk)?; - - // Decode sk - let sk_felt = bytes_to_felt(sk)?; - - // Decode nonce - let nonce_felt = bytes_to_felt(nonce)?; - - // Encrypt - let cipher = Ciphertext::encrypt(&msgs_felt, &pk_affine, &sk_felt, &nonce_felt)?; - let cipher_bytes = cipher - .inner() - .iter() - .map(|x| x.to_bytes_be().to_vec()) - .collect(); - - Ok(cipher_bytes) -} - -#[rustler::nif] -fn decrypt(cihper: Vec>, sk: Vec) -> NifResult>> { - // Decode messages - let cipher = Ciphertext::from_bytes(cihper)?; - - // Decode sk - let sk_felt = bytes_to_felt(sk)?; - - // Encrypt - let plaintext = cipher.decrypt(&sk_felt)?; - let plaintext_bytes = plaintext.iter().map(|x| x.to_bytes_be().to_vec()).collect(); - - Ok(plaintext_bytes) -} - rustler::init!( "Elixir.Cairo.CairoProver", [ @@ -76,8 +25,8 @@ rustler::init!( utils::cairo_random_felt, utils::cairo_felt_to_string, compliance_input::cairo_generate_compliance_input_json, - encrypt, - decrypt, + encryption::encrypt, + encryption::decrypt, ] ); From 71b1da1a43c418ab122970e86029dad4a3978fbc Mon Sep 17 00:00:00 2001 From: Xuyang Song Date: Fri, 22 Nov 2024 22:48:54 +0800 Subject: [PATCH 8/9] refine constants --- native/cairo_prover/src/constants.rs | 43 +++++++++++++++++++++++++++ native/cairo_prover/src/encryption.rs | 13 +++----- native/cairo_prover/src/lib.rs | 35 +--------------------- 3 files changed, 48 insertions(+), 43 deletions(-) create mode 100644 native/cairo_prover/src/constants.rs diff --git a/native/cairo_prover/src/constants.rs b/native/cairo_prover/src/constants.rs new file mode 100644 index 0000000..9600cc7 --- /dev/null +++ b/native/cairo_prover/src/constants.rs @@ -0,0 +1,43 @@ +use lazy_static::lazy_static; + +// The PLAINTEXT_NUM should be fixed to achieve the indistinguishability of resource logics +// Make it 10 +pub const PLAINTEXT_NUM: usize = 10; +pub const CIPHERTEXT_MAC: usize = PLAINTEXT_NUM; +pub const CIPHERTEXT_PK_X: usize = PLAINTEXT_NUM + 1; +pub const CIPHERTEXT_PK_Y: usize = PLAINTEXT_NUM + 2; +pub const CIPHERTEXT_NONCE: usize = PLAINTEXT_NUM + 3; +pub const CIPHERTEXT_NUM: usize = PLAINTEXT_NUM + 4; + +lazy_static! { + // Bytes: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 97, 105, 114, 111, 95, 69, 120, 112, 97, 110, 100, 83, 101, 101, 100] + // Hexstring: "0x436169726f5f457870616e6453656564" + // Decimal string(used in juvix): "89564067232354163924078705540990330212" + pub static ref PRF_EXPAND_PERSONALIZATION_FELT: Vec = { + let personalization: Vec = b"Cairo_ExpandSeed".to_vec(); + let mut result = [0u8; 32]; + result[(32 - personalization.len())..].copy_from_slice(&personalization[..]); + + result.to_vec() + }; +} + +#[test] +fn test_prf_expand_personalization() { + use starknet_types_core::felt::Felt; + println!( + "PRF_EXPAND_PERSONALIZATION_FELT bytes: {:?}", + *PRF_EXPAND_PERSONALIZATION_FELT + ); + + println!( + "hex: {:?}", + Felt::from_bytes_be( + &PRF_EXPAND_PERSONALIZATION_FELT + .as_slice() + .try_into() + .unwrap() + ) + .to_hex_string() + ); +} diff --git a/native/cairo_prover/src/encryption.rs b/native/cairo_prover/src/encryption.rs index 0791aa3..40f66b8 100644 --- a/native/cairo_prover/src/encryption.rs +++ b/native/cairo_prover/src/encryption.rs @@ -1,4 +1,8 @@ use crate::{ + constants::{ + CIPHERTEXT_MAC, CIPHERTEXT_NONCE, CIPHERTEXT_NUM, CIPHERTEXT_PK_X, CIPHERTEXT_PK_Y, + PLAINTEXT_NUM, + }, error::CairoError, utils::{bytes_to_affine, bytes_to_felt, bytes_to_felt_vec}, }; @@ -55,15 +59,6 @@ fn decrypt(cihper: Vec>, sk: Vec) -> NifResult>> { Ok(plaintext_bytes) } -// The PLAINTEXT_NUM should be fixed to achieve the indistinguishability of resource logics -// Make it 10 -pub const PLAINTEXT_NUM: usize = 10; -pub const CIPHERTEXT_MAC: usize = PLAINTEXT_NUM; -pub const CIPHERTEXT_PK_X: usize = PLAINTEXT_NUM + 1; -pub const CIPHERTEXT_PK_Y: usize = PLAINTEXT_NUM + 2; -pub const CIPHERTEXT_NONCE: usize = PLAINTEXT_NUM + 3; -pub const CIPHERTEXT_NUM: usize = PLAINTEXT_NUM + 4; - #[derive(Debug, Clone)] pub struct Ciphertext([Felt; CIPHERTEXT_NUM]); diff --git a/native/cairo_prover/src/lib.rs b/native/cairo_prover/src/lib.rs index 9addf7b..8d1b6e3 100644 --- a/native/cairo_prover/src/lib.rs +++ b/native/cairo_prover/src/lib.rs @@ -2,6 +2,7 @@ mod binding_signature; mod compliance_input; +mod constants; mod encryption; mod error; mod poseidon; @@ -29,37 +30,3 @@ rustler::init!( encryption::decrypt, ] ); - -use lazy_static::lazy_static; -lazy_static! { - // Bytes: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 97, 105, 114, 111, 95, 69, 120, 112, 97, 110, 100, 83, 101, 101, 100] - // Hexstring: "0x436169726f5f457870616e6453656564" - // Decimal string(used in juvix): "89564067232354163924078705540990330212" - pub static ref PRF_EXPAND_PERSONALIZATION_FELT: Vec = { - let personalization: Vec = b"Cairo_ExpandSeed".to_vec(); - let mut result = [0u8; 32]; - result[(32 - personalization.len())..].copy_from_slice(&personalization[..]); - - result.to_vec() - }; -} - -#[test] -fn test_prf_expand_personalization() { - use starknet_types_core::felt::Felt; - println!( - "PRF_EXPAND_PERSONALIZATION_FELT bytes: {:?}", - *PRF_EXPAND_PERSONALIZATION_FELT - ); - - println!( - "hex: {:?}", - Felt::from_bytes_be( - &PRF_EXPAND_PERSONALIZATION_FELT - .as_slice() - .try_into() - .unwrap() - ) - .to_hex_string() - ); -} From ccaff1788ab71094c01aea5d2ff2411155073c7c Mon Sep 17 00:00:00 2001 From: Xuyang Song Date: Fri, 22 Nov 2024 22:52:13 +0800 Subject: [PATCH 9/9] fix dead code --- native/cairo_prover/src/encryption.rs | 6 +----- native/cairo_prover/src/lib.rs | 2 -- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/native/cairo_prover/src/encryption.rs b/native/cairo_prover/src/encryption.rs index 40f66b8..02bc966 100644 --- a/native/cairo_prover/src/encryption.rs +++ b/native/cairo_prover/src/encryption.rs @@ -176,10 +176,6 @@ impl Plaintext { &self.0 } - pub fn to_vec(&self) -> Vec { - self.0.to_vec() - } - pub fn padding(msg: &[Felt]) -> Self { let mut plaintext = msg.to_owned(); let padding = std::iter::repeat(Felt::ZERO).take(PLAINTEXT_NUM - msg.len()); @@ -230,5 +226,5 @@ fn test_encryption() { let decryption = cipher.decrypt(&Felt::ONE).unwrap(); let padded_plaintext = Plaintext::padding(&messages); - assert_eq!(padded_plaintext.to_vec(), decryption); + assert_eq!(padded_plaintext.inner().to_vec(), decryption); } diff --git a/native/cairo_prover/src/lib.rs b/native/cairo_prover/src/lib.rs index 8d1b6e3..64d649c 100644 --- a/native/cairo_prover/src/lib.rs +++ b/native/cairo_prover/src/lib.rs @@ -1,5 +1,3 @@ -#![allow(dead_code)] - mod binding_signature; mod compliance_input; mod constants;