Skip to content
Merged
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
17 changes: 10 additions & 7 deletions crates/circuit_multiverifier/src/backward_compatibility_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ use stwo::core::fields::qm31::QM31;
use crate::test_utils::{
MULTIVERIFIER_OF_TWO_CAIRO_PROOFS_PATH, MULTIVERIFIER_PREPROCESSED_ROOT, PCS_CONFIG,
PRIVACY_CAIRO_VERIFIER_OUTPUT_VALUES, PRIVACY_CAIRO_VERIFIER_PREPROCESSED_ROOT,
TARGET_PADDING_SIZES, blake2s_u32s_host, multiverifier_preprocessed_column_log_sizes,
TARGET_PADDING_SIZES, leaf_circuit_hash, multiverifier_preprocessed_column_log_sizes,
native_blake_u32s,
};
use crate::verify::{MultiverifierInput, SharedConfig, build_multiverifier_circuit};

Expand Down Expand Up @@ -50,14 +51,16 @@ fn test_backward_compatibility() {
preprocessed_root: BACKWARD_COMPATIBILITY_CAIRO_VERIFIER_PREPROCESSED_ROOT.into(),
};

// Slot 1: the current multiverifier proof.
let cairo_input_preimage_words: Vec<u32> = PRIVACY_CAIRO_VERIFIER_PREPROCESSED_ROOT
.into_iter()
.chain(PRIVACY_CAIRO_VERIFIER_OUTPUT_VALUES)
.collect();
// Slot 1: the current multiverifier proof. Its inputs are hashed as
// `circuit_hash(preprocessed_root) || output_values`.
let cairo_input_preimage_words: Vec<u32> =
leaf_circuit_hash(PRIVACY_CAIRO_VERIFIER_PREPROCESSED_ROOT.into(), &shared_config)
.into_iter()
.chain(PRIVACY_CAIRO_VERIFIER_OUTPUT_VALUES)
.collect();
let payload_words: Vec<u32> =
chain!(&cairo_input_preimage_words, &cairo_input_preimage_words).copied().collect();
let current_output_values: [u32; N_RESERVED] = blake2s_u32s_host(&payload_words);
let current_output_values: [u32; N_RESERVED] = native_blake_u32s(&payload_words);

let current_bytes = std::fs::read(MULTIVERIFIER_OF_TWO_CAIRO_PROOFS_PATH).unwrap();
let current_proof =
Expand Down
26 changes: 23 additions & 3 deletions crates/circuit_multiverifier/src/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ use circuit_cairo_verifier::privacy::get_pcs_config;
use circuit_common::N_RESERVED;
use circuit_common::finalize::{ComponentSizes, pad_to_targets};
use circuit_common::preprocessed::PreprocessedCircuit;
use circuit_verifier::statement::circuit_verifier_proof_config;
use circuit_prover::circuit_hash::compute_circuit_hash;
use circuit_verifier::statement::{
all_circuit_components, circuit_component_log_sizes, circuit_verifier_proof_config,
};
use circuit_verifier::verify::CircuitConfig;
use circuits::blake::HashValue;
use circuits::context::FinalizedContext;
Expand All @@ -14,6 +17,7 @@ use circuits_stark_verifier::proof::empty_proof;
use stwo::core::fields::qm31::QM31;
use stwo::core::pcs::PcsConfig;
use stwo::core::poly::circle::CanonicCoset;
use stwo::core::vcs::blake2_hash::Blake2sHash;
use stwo::core::vcs_lifted::blake2_merkle::Blake2sM31MerkleChannel;
use stwo::prover::CommitmentTreeProver;
use stwo::prover::backend::simd::SimdBackend;
Expand Down Expand Up @@ -50,13 +54,13 @@ pub const PRIVACY_CAIRO_VERIFIER_PREPROCESSED_ROOT: [u32; 8] =

/// The preprocessed root of the multiverifier circuit.
pub const MULTIVERIFIER_PREPROCESSED_ROOT: [u32; 8] =
[933544165, 1273803072, 152539421, 3171454012, 281718454, 2434086603, 4253372773, 2721160983];
[2541539887, 35459119, 1319006936, 3404609071, 1500509682, 2093690248, 3400881069, 4203903858];
/// A multiverifier proof verifying two identical Cairo verifier proofs.
pub const MULTIVERIFIER_OF_TWO_CAIRO_PROOFS_PATH: &str =
concat!(env!("CARGO_MANIFEST_DIR"), "/../../test_data/circuit_multiverifier/proof.bin");

/// Out-of-circuit implementation of [`circuits::blake::blake2s_u32s`].
pub fn blake2s_u32s_host(words: &[u32]) -> [u32; 8] {
pub fn native_blake_u32s(words: &[u32]) -> [u32; 8] {
let mut hasher = Blake2s256::new();
for word in words {
hasher.update(word.to_le_bytes());
Expand All @@ -65,6 +69,22 @@ pub fn blake2s_u32s_host(words: &[u32]) -> [u32; 8] {
le_u32s_from_bytes(hash)
}

/// Computes the circuit hash for a given preprocessed root and shared config.
///
/// Returns the hash as a 32-bit word array as it is more convenient at the call sites.
pub fn leaf_circuit_hash(preprocessed_root: Blake2sHash, shared_config: &SharedConfig) -> [u32; 8] {
let component_log_sizes = circuit_component_log_sizes(
&all_circuit_components::<QM31>(),
&shared_config.preprocessed_column_log_sizes,
);
let hash = compute_circuit_hash(
&component_log_sizes,
shared_config.pcs_config.fri_config.log_blowup_factor,
preprocessed_root,
);
le_u32s_from_bytes(hash.0)
}

pub fn multiverifier_preprocessed_column_log_sizes() -> OrderedHashMap<PreProcessedColumnId, u32> {
[
("bitwise_xor_4_0", 8),
Expand Down
18 changes: 10 additions & 8 deletions crates/circuit_multiverifier/src/verify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,10 @@ pub struct SharedConfig {
/// [`CircuitStatement`] from `shared_config` and the per-proof
/// `preprocessed_root` and `outputs`, and runs the STARK verifier.
///
/// After both proofs are verified, the preprocessed roots and the inner
/// circuits' output values are concatenated and hashed. The resulting unreduced Blake2s digest
/// is written into the `N_RESERVED` reserved output variables of the outer circuit.
/// After both proofs are verified, each circuit's [`CircuitStatement::circuit_hash`] (the hash of
/// its shared config and preprocessed root) is concatenated with the inner circuit's output values,
/// and both circuits' words are hashed together. The resulting unreduced Blake2s digest is written
/// into the outer circuit's `N_RESERVED` reserved output variables.
/// The circuit is then finalized.
///
/// Both proofs must have been produced with the same [`SharedConfig`].
Expand Down Expand Up @@ -85,14 +86,15 @@ pub fn build_multiverifier_circuit<Value: IValue>(
let proof_vars = proof.guess(&mut context);

verify(&mut context, &proof_vars, &shared_config.proof_config, &statement);
let preprocessed_root = statement.preprocessed_root.clone();
outer_verifier_output_preimage.extend(chain!(preprocessed_root.into_iter(), output_values));
// Append (circuit_hash, outputs) to the preimage of the outer hash.
outer_verifier_output_preimage
.extend(chain!(statement.circuit_hash.iter().copied(), output_values));
}
// The payload to be hashed is, for each of the two circuits A and B, the eight 32-bit words of
// its preprocessed root followed by its `N_RESERVED` raw output words:
// its circuit hash followed by its `N_RESERVED` raw output words:
// [
// preprocessed_rootA (8 words), outputsA (N_RESERVED words),
// preprocessed_rootB (8 words), outputsB (N_RESERVED words),
// circuit_hashA (8 words), outputsA (N_RESERVED words),
// circuit_hashB (8 words), outputsB (N_RESERVED words),
// ]
// where A, B are the two circuits being verified.
let n_bytes = 4 * outer_verifier_output_preimage.len();
Expand Down
20 changes: 11 additions & 9 deletions crates/circuit_multiverifier/src/verify_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ use stwo::prover::mempool::BaseColumnPool;
use crate::test_utils::{
CIRCUIT_N_PREPROCESSED_COLUMNS, LOG_BLOWUP_FACTOR, MULTIVERIFIER_OF_TWO_CAIRO_PROOFS_PATH,
MULTIVERIFIER_PREPROCESSED_ROOT, PCS_CONFIG, PRIVACY_CAIRO_VERIFIER_OUTPUT_VALUES,
PRIVACY_CAIRO_VERIFIER_PREPROCESSED_ROOT, TARGET_PADDING_SIZES, blake2s_u32s_host,
get_preprocessed_multiverifier_from_circuit, get_preprocessed_root,
multiverifier_preprocessed_column_log_sizes,
PRIVACY_CAIRO_VERIFIER_PREPROCESSED_ROOT, TARGET_PADDING_SIZES,
get_preprocessed_multiverifier_from_circuit, get_preprocessed_root, leaf_circuit_hash,
multiverifier_preprocessed_column_log_sizes, native_blake_u32s,
};
use crate::verify::{MultiverifierInput, SharedConfig, build_multiverifier_circuit};

Expand Down Expand Up @@ -353,16 +353,18 @@ fn test_verify_cairo_proof_and_multiverifier_proof() {
let multiverifier_proof =
deserialize_proof_with_config(&mut bytes.as_slice(), &shared_config.proof_config).unwrap();
// Mirror the in-circuit preimage `build_multiverifier_circuit` hashes for each verified input:
// the eight full 32-bit words of the preprocessed root, followed by the eight raw output words.
let cairo_input_preimage_words: Vec<u32> = PRIVACY_CAIRO_VERIFIER_PREPROCESSED_ROOT
.into_iter()
.chain(PRIVACY_CAIRO_VERIFIER_OUTPUT_VALUES)
.collect();
// the eight full 32-bit words of the circuit hash `blake2s(config_words || preprocessed_root)`,
// followed by the eight raw output words.
let cairo_input_preimage_words: Vec<u32> =
leaf_circuit_hash(PRIVACY_CAIRO_VERIFIER_PREPROCESSED_ROOT.into(), &shared_config)
.into_iter()
.chain(PRIVACY_CAIRO_VERIFIER_OUTPUT_VALUES)
.collect();
// The proven multiverifier verified two identical Cairo verifier inputs.
let payload_words: Vec<u32> =
chain!(&cairo_input_preimage_words, &cairo_input_preimage_words).copied().collect();
// The circuit keeps the full unreduced eight-word digest as its output.
let hash_of_payload: [u32; N_RESERVED] = blake2s_u32s_host(&payload_words);
let hash_of_payload: [u32; N_RESERVED] = native_blake_u32s(&payload_words);

let multiverifier_of_two_cairo_input = MultiverifierInput {
proof: multiverifier_proof,
Expand Down
Loading
Loading