Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions crates/starknet_os/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ deserialize = [
]
include_program_output = []
testing = [
"apollo_starknet_os_program/test_programs",
"blockifier/testing",
"expect-test",
"itertools",
Expand Down
71 changes: 6 additions & 65 deletions crates/starknet_os/src/proof_fact_fold_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ use std::collections::HashMap;

use apollo_starknet_os_program::test_programs::PROOF_FACT_FOLD_BYTES;
use cairo_vm::types::builtin_name::BuiltinName;
use cairo_vm::types::layout_name::LayoutName;
use cairo_vm::types::relocatable::MaybeRelocatable;
use cairo_vm::vm::runners::cairo_runner::ExecutionResources;
use expect_test::expect;
Expand All @@ -23,74 +22,16 @@ use super::{
use crate::test_utils::cairo_runner::{
initialize_and_run_cairo_0_entry_point,
EndpointArg,
EntryPointRunnerConfig,
ImplicitArg,
PointerArg,
ValueArg,
};

fn entrypoint_runner_config() -> EntryPointRunnerConfig {
EntryPointRunnerConfig {
layout: LayoutName::all_cairo,
trace_enabled: false,
verify_secure: false,
proof_mode: false,
add_main_prefix_to_entrypoint: true,
validate_builtins_offset: true,
}
}

fn felt_array_arg(felts: &[Felt]) -> EndpointArg {
EndpointArg::Pointer(PointerArg::Array(
felts.iter().map(|felt| MaybeRelocatable::Int(*felt)).collect(),
))
}

fn run_cairo_function_returning_words(
function_name: &str,
explicit_args: &[EndpointArg],
implicit_args: &[ImplicitArg],
n_returned_words: usize,
) -> Vec<u32> {
let expected_return_values = vec![EndpointArg::Pointer(PointerArg::Array(vec![
MaybeRelocatable::from(Felt::ZERO);
n_returned_words
]))];
let (_, explicit_return_values, _) = initialize_and_run_cairo_0_entry_point(
&entrypoint_runner_config(),
PROOF_FACT_FOLD_BYTES,
function_name,
explicit_args,
implicit_args,
&expected_return_values,
HashMap::new(),
None,
)
.unwrap_or_else(|error| panic!("Failed to run Cairo function {function_name}: {error:?}"));
let [EndpointArg::Pointer(PointerArg::Array(returned_words))] =
explicit_return_values.as_slice()
else {
panic!("Expected {function_name} to return a single words-array pointer.");
};
returned_words
.iter()
.map(|returned_word| {
let MaybeRelocatable::Int(word_felt) = returned_word else {
panic!("Expected a felt digest word, got {returned_word:?}.");
};
u32::try_from(word_felt.to_biguint()).expect("A digest word must fit in a u32.")
})
.collect()
}

fn run_cairo_processed_proof_output_digest(proof_facts: &[Felt]) -> Vec<u32> {
run_cairo_function_returning_words(
"compute_processed_proof_output_digest",
&[EndpointArg::from(Felt::from(proof_facts.len())), felt_array_arg(proof_facts)],
&[ImplicitArg::Builtin(BuiltinName::range_check)],
BLAKE2S_DIGEST_N_WORDS,
)
}
use crate::test_utils::proof_fact_fold_runner::{
entrypoint_runner_config,
felt_array_arg,
run_cairo_function_returning_words,
run_cairo_processed_proof_output_digest,
};

fn run_cairo_processed_proof_output_digest_resources(proof_facts: &[Felt]) -> ExecutionResources {
let expected_return_values = vec![EndpointArg::Pointer(PointerArg::Array(vec![
Expand Down
1 change: 1 addition & 0 deletions crates/starknet_os/src/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ pub mod cairo_dict;
pub mod cairo_runner;
pub mod coverage;
pub mod errors;
pub mod proof_fact_fold_runner;
#[cfg(test)]
pub mod utils;
pub mod validations;
Expand Down
79 changes: 79 additions & 0 deletions crates/starknet_os/src/test_utils/proof_fact_fold_runner.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
use std::collections::HashMap;

use apollo_starknet_os_program::test_programs::PROOF_FACT_FOLD_BYTES;
use cairo_vm::types::builtin_name::BuiltinName;
use cairo_vm::types::layout_name::LayoutName;
use cairo_vm::types::relocatable::MaybeRelocatable;
use starknet_types_core::felt::Felt;

use crate::proof_fact_fold::BLAKE2S_DIGEST_N_WORDS;
use crate::test_utils::cairo_runner::{
initialize_and_run_cairo_0_entry_point,
EndpointArg,
EntryPointRunnerConfig,
ImplicitArg,
PointerArg,
};

pub fn entrypoint_runner_config() -> EntryPointRunnerConfig {
EntryPointRunnerConfig {
layout: LayoutName::all_cairo,
trace_enabled: false,
verify_secure: false,
proof_mode: false,
add_main_prefix_to_entrypoint: true,
validate_builtins_offset: true,
}
}

pub fn felt_array_arg(felts: &[Felt]) -> EndpointArg {
EndpointArg::Pointer(PointerArg::Array(
felts.iter().map(|felt| MaybeRelocatable::Int(*felt)).collect(),
))
}

pub fn run_cairo_function_returning_words(
function_name: &str,
explicit_args: &[EndpointArg],
implicit_args: &[ImplicitArg],
n_returned_words: usize,
) -> Vec<u32> {
let expected_return_values = vec![EndpointArg::Pointer(PointerArg::Array(vec![
MaybeRelocatable::from(Felt::ZERO);
n_returned_words
]))];
let (_, explicit_return_values, _) = initialize_and_run_cairo_0_entry_point(
&entrypoint_runner_config(),
PROOF_FACT_FOLD_BYTES,
function_name,
explicit_args,
implicit_args,
&expected_return_values,
HashMap::new(),
None,
)
.unwrap_or_else(|error| panic!("Failed to run Cairo function {function_name}: {error:?}"));
let [EndpointArg::Pointer(PointerArg::Array(returned_words))] =
explicit_return_values.as_slice()
else {
panic!("Expected {function_name} to return a single words-array pointer.");
};
returned_words
.iter()
.map(|returned_word| {
let MaybeRelocatable::Int(word_felt) = returned_word else {
panic!("Expected a felt digest word, got {returned_word:?}.");
};
u32::try_from(word_felt.to_biguint()).expect("A digest word must fit in a u32.")
})
.collect()
}

pub fn run_cairo_processed_proof_output_digest(proof_facts: &[Felt]) -> Vec<u32> {
run_cairo_function_returning_words(
"compute_processed_proof_output_digest",
&[EndpointArg::from(Felt::from(proof_facts.len())), felt_array_arg(proof_facts)],
&[ImplicitArg::Builtin(BuiltinName::range_check)],
BLAKE2S_DIGEST_N_WORDS,
)
}
2 changes: 1 addition & 1 deletion crates/starknet_os_flow_tests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ starknet_committer = { workspace = true, features = ["testing"] }
starknet_os = { workspace = true, features = ["include_program_output", "testing"] }
starknet_patricia_storage = { workspace = true, features = ["testing"] }
starknet_proof_verifier.workspace = true
starknet_transaction_prover.workspace = true
starknet_transaction_prover = { workspace = true, features = ["testing"] }
strum.workspace = true
tokio.workspace = true

Expand Down
1 change: 1 addition & 0 deletions crates/starknet_os_flow_tests/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
pub(crate) mod fuzz_tests;
pub(crate) mod initial_state;
pub(crate) mod os_resources_test;
pub(crate) mod proof_fact_verifier_test;
pub(crate) mod special_contracts;
pub(crate) mod test_manager;
pub(crate) mod tests;
Expand Down
75 changes: 75 additions & 0 deletions crates/starknet_os_flow_tests/src/proof_fact_verifier_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
use blockifier::test_utils::get_valid_virtual_os_program_hash;
use blockifier_test_utils::cairo_versions::{CairoVersion, RunnableCairo1};
use blockifier_test_utils::calldata::create_calldata;
use blockifier_test_utils::contracts::FeatureContract;
use starknet_api::transaction::fields::ProofFacts;
use starknet_api::{calldata, invoke_tx_args};
use starknet_os::proof_fact_fold::{compute_processed_proof_output_digest, pack_output_digest};
use starknet_os::test_utils::proof_fact_fold_runner::run_cairo_processed_proof_output_digest;
use starknet_transaction_prover::verifier_task::test_utils::{
one_leaf_proof_facts,
run_one_leaf_verifier_task,
FIXTURE_VERIFIER_PROGRAM_HASH,
};
use starknet_transaction_prover::verifier_task::{
verify_circuit_verifier_task_output,
VerifierTaskError,
};
use starknet_types_core::felt::Felt;

use crate::test_manager::TestBuilder;

#[test]
fn test_cairo_processed_proof_digest_matches_circuit_verifier_output() {
let task_output = run_one_leaf_verifier_task().unwrap();
let cairo_output_digest: Vec<u32> =
run_cairo_processed_proof_output_digest(&one_leaf_proof_facts());
let (packed_output_low, packed_output_high) =
pack_output_digest(&cairo_output_digest.try_into().unwrap());
verify_circuit_verifier_task_output(
&task_output,
FIXTURE_VERIFIER_PROGRAM_HASH,
packed_output_low,
packed_output_high,
)
.unwrap();
}

#[tokio::test]
async fn test_os_emitted_output_feeds_verifier_task_comparison() {
let test_contract = FeatureContract::TestContract(CairoVersion::Cairo1(RunnableCairo1::Casm));
let (mut test_builder, [test_contract_address]) =
TestBuilder::create_standard([(test_contract, calldata![Felt::ZERO, Felt::ZERO])]).await;
let proof_facts = ProofFacts::custom_proof_facts_for_testing(
get_valid_virtual_os_program_hash(),
test_builder.compute_virtual_os_config_hash(),
);
let calldata = create_calldata(test_contract_address, "empty_function", &[]);
test_builder.add_funded_account_invoke(
invoke_tx_args! { calldata: calldata.clone(), proof_facts: proof_facts.clone() },
);

let test_output = test_builder.build_and_run().await;
test_output.perform_default_validations();
let os_output = test_output
.runner_output
.get_os_output(test_output.private_keys.as_ref())
.expect("Getting OsOutput from raw OS output should not fail.");
assert_eq!(os_output.common_os_output.n_proof_facts_transactions, 1);

let expected_output_digest = compute_processed_proof_output_digest(&proof_facts.0);
let (expected_low, expected_high) = pack_output_digest(&expected_output_digest);
assert_eq!(os_output.common_os_output.processed_proof_output_low, expected_low);
assert_eq!(os_output.common_os_output.processed_proof_output_high, expected_high);

let task_output = run_one_leaf_verifier_task().unwrap();
assert!(matches!(
verify_circuit_verifier_task_output(
&task_output,
FIXTURE_VERIFIER_PROGRAM_HASH,
os_output.common_os_output.processed_proof_output_low,
os_output.common_os_output.processed_proof_output_high,
),
Err(VerifierTaskError::VerificationDigestMismatch { .. })
));
}
2 changes: 2 additions & 0 deletions crates/starknet_transaction_prover/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ cairo_native = ["blockifier/cairo_native"]
# Enables in-memory stwo proving. Requires a nightly Rust toolchain because the
# stwo prover crate uses unstable features (array_chunks, portable_simd, …).
stwo_proving = ["dep:privacy-prove"]
testing = ["dep:flate2"]

[dependencies]
anyhow.workspace = true
Expand All @@ -24,6 +25,7 @@ cairo-program-runner-lib.workspace = true
cairo-lang-utils.workspace = true
cairo-vm.workspace = true
clap = { workspace = true, features = ["derive", "env"] }
flate2 = { workspace = true, optional = true }
futures.workspace = true
http.workspace = true
http-body-util.workspace = true
Expand Down
3 changes: 3 additions & 0 deletions crates/starknet_transaction_prover/src/verifier_task.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ use starknet_os::proof_fact_fold::{
};
use starknet_types_core::felt::Felt;

#[cfg(any(test, feature = "testing"))]
pub mod test_utils;

#[cfg(test)]
#[path = "verifier_task_test.rs"]
mod verifier_task_test;
Expand Down
57 changes: 57 additions & 0 deletions crates/starknet_transaction_prover/src/verifier_task/test_utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
use std::io::Read;

use flate2::read::GzDecoder;
use starknet_types_core::felt::Felt;

use crate::verifier_task::{
run_circuit_verifier_task,
CircuitVerifierTaskOutput,
VerifierTaskError,
};

pub const SIMPLE_BOOTLOADER_PROGRAM_GZ: &[u8] =
include_bytes!("../../resources/simple_bootloader_compiled.json.gz");

pub const VERIFIER_EXECUTABLE_GZ: &[u8] =
include_bytes!("../../resources/stwo_circuit_verifier_canonical_small.executable.json.gz");

pub const PROCESSED_PROOF_GZ: &[u8] = include_bytes!("../../resources/one_leaf_root_proof.json.gz");

pub const FIXTURE_VERIFIER_PROGRAM_HASH: Felt =
Felt::from_hex_unchecked("0x764dc214c7f45a6899b05d42ba4d23d5849e0bf0383ec951f000b1742107fdd");

pub fn gunzip(compressed_bytes: &[u8]) -> Vec<u8> {
let mut decompressed_bytes = Vec::new();
GzDecoder::new(compressed_bytes).read_to_end(&mut decompressed_bytes).unwrap();
decompressed_bytes
}

pub fn processed_proof_felts() -> Vec<Felt> {
let processed_proof_hex: Vec<String> =
serde_json::from_slice(&gunzip(PROCESSED_PROOF_GZ)).unwrap();
processed_proof_hex
.iter()
.map(|proof_felt_hex| Felt::from_hex(proof_felt_hex).unwrap())
.collect()
}

pub fn one_leaf_proof_facts() -> Vec<Felt> {
[
Felt::ZERO,
Felt::ZERO,
Felt::from_hex("0x32b88272d54b83880ebebd9c4292a650bee27d1575e82123391b6df2932e843")
.unwrap(),
Felt::from_hex("0xb").unwrap(),
Felt::from_hex("0xd").unwrap(),
Felt::from_hex("0x11").unwrap(),
]
.to_vec()
}

pub fn run_one_leaf_verifier_task() -> Result<CircuitVerifierTaskOutput, VerifierTaskError> {
run_circuit_verifier_task(
&gunzip(SIMPLE_BOOTLOADER_PROGRAM_GZ),
&gunzip(VERIFIER_EXECUTABLE_GZ),
&processed_proof_felts(),
)
}
Loading
Loading