From 08467656672ca8397b74ebabb7492aa94d5cd38c Mon Sep 17 00:00:00 2001 From: einat-starkware Date: Thu, 25 Dec 2025 12:05:23 +0200 Subject: [PATCH] apollo_gateway: use proof manager --- Cargo.lock | 1 + crates/apollo_class_manager_types/Cargo.toml | 1 + .../src/transaction_converter.rs | 28 ++++- .../src/transaction_converter_test.rs | 101 +++++++++++++++++- crates/apollo_gateway/src/errors.rs | 3 + .../src/stateless_transaction_validator.rs | 6 -- crates/apollo_proof_manager_types/src/lib.rs | 2 +- 7 files changed, 131 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b099f86632a..2fa75b5517f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1049,6 +1049,7 @@ dependencies = [ "assert_matches", "async-trait", "blockifier", + "blockifier_test_utils", "mempool_test_utils", "mockall", "rstest", diff --git a/crates/apollo_class_manager_types/Cargo.toml b/crates/apollo_class_manager_types/Cargo.toml index 27346c24fb2..ff16ce988e7 100644 --- a/crates/apollo_class_manager_types/Cargo.toml +++ b/crates/apollo_class_manager_types/Cargo.toml @@ -31,6 +31,7 @@ tokio.workspace = true apollo_proof_manager_types = { workspace = true, features = ["testing"] } assert_matches.workspace = true blockifier = { workspace = true, features = ["testing"] } +blockifier_test_utils.workspace = true mempool_test_utils.workspace = true mockall.workspace = true rstest.workspace = true diff --git a/crates/apollo_class_manager_types/src/transaction_converter.rs b/crates/apollo_class_manager_types/src/transaction_converter.rs index 3866badb582..3c0d773ff9b 100644 --- a/crates/apollo_class_manager_types/src/transaction_converter.rs +++ b/crates/apollo_class_manager_types/src/transaction_converter.rs @@ -1,4 +1,4 @@ -use apollo_proof_manager_types::SharedProofManagerClient; +use apollo_proof_manager_types::{ProofManagerClientError, SharedProofManagerClient}; use async_trait::async_trait; #[cfg(any(feature = "testing", test))] use mockall::automock; @@ -41,6 +41,8 @@ pub enum TransactionConverterError { #[error("Class of hash: {class_hash} not found")] ClassNotFound { class_hash: ClassHash }, #[error(transparent)] + ProofManagerClientError(#[from] ProofManagerClientError), + #[error(transparent)] StarknetApiError(#[from] StarknetApiError), #[error(transparent)] ValidateCompiledClassHashError(#[from] ValidateCompiledClassHashError), @@ -200,7 +202,10 @@ impl TransactionConverterTrait for TransactionConverter { tx: RpcTransaction, ) -> TransactionConverterResult { let tx_without_hash = match tx { - RpcTransaction::Invoke(tx) => InternalRpcTransactionWithoutTxHash::Invoke(tx.into()), + RpcTransaction::Invoke(RpcInvokeTransaction::V3(tx)) => { + self._handle_proof_verification(&tx.proof_facts, &tx.proof).await?; + InternalRpcTransactionWithoutTxHash::Invoke(tx.into()) + } RpcTransaction::Declare(RpcDeclareTransaction::V3(tx)) => { let ClassHashes { class_hash, executable_class_hash_v2 } = self.class_manager_client.add_class(tx.contract_class).await?; @@ -310,6 +315,25 @@ impl TransactionConverter { )?) } + async fn _handle_proof_verification( + &self, + proof_facts: &ProofFacts, + proof: &Proof, + ) -> TransactionConverterResult<()> { + if proof_facts.is_empty() { + return Ok(()); + } + + let contains_proof = self.proof_manager_client.contains_proof(proof_facts.clone()).await?; + if contains_proof { + return Ok(()); + } + + self._verify_proof(proof_facts.clone(), proof.clone())?; + self.proof_manager_client.set_proof(proof_facts.clone(), proof.clone()).await?; + Ok(()) + } + fn _verify_proof( &self, _proof_facts: ProofFacts, diff --git a/crates/apollo_class_manager_types/src/transaction_converter_test.rs b/crates/apollo_class_manager_types/src/transaction_converter_test.rs index 2276a446032..7c31f460d0e 100644 --- a/crates/apollo_class_manager_types/src/transaction_converter_test.rs +++ b/crates/apollo_class_manager_types/src/transaction_converter_test.rs @@ -3,12 +3,18 @@ use std::sync::Arc; use apollo_proof_manager_types::MockProofManagerClient; use assert_matches::assert_matches; use blockifier::context::ChainInfo; -use mempool_test_utils::starknet_api_test_utils::declare_tx; +use blockifier_test_utils::cairo_versions::{CairoVersion, RunnableCairo1}; +use mempool_test_utils::starknet_api_test_utils::{ + declare_tx, + invoke_tx, + invoke_tx_client_side_proving, +}; use mockall::predicate::eq; use rstest::rstest; -use starknet_api::compiled_class_hash; use starknet_api::executable_transaction::ValidateCompiledClassHashError; use starknet_api::rpc_transaction::{RpcDeclareTransaction, RpcTransaction}; +use starknet_api::transaction::fields::Proof; +use starknet_api::{compiled_class_hash, felt, proof_facts}; use crate::transaction_converter::{ TransactionConverter, @@ -56,3 +62,94 @@ async fn test_compiled_class_hash_mismatch() { ); assert_eq!(err, expected_code); } + +#[rstest] +#[tokio::test] +async fn test_proof_verification_called_for_invoke_v3_with_proof_facts() { + // Create an invoke transaction with proof_facts and proof. + let proof_facts = proof_facts![felt!("0x1"), felt!("0x2"), felt!("0x3")]; + let proof = Proof::from(vec![1u32, 2u32, 3u32]); + let invoke_tx = invoke_tx_client_side_proving( + CairoVersion::Cairo1(RunnableCairo1::Casm), + proof_facts.clone(), + proof.clone(), + ); + + let mut mock_proof_manager_client = MockProofManagerClient::new(); + mock_proof_manager_client + .expect_contains_proof() + .once() + .with(eq(proof_facts.clone())) + .return_once(|_| Ok(false)); + mock_proof_manager_client + .expect_set_proof() + .once() + .with(eq(proof_facts), eq(proof)) + .return_once(|_, _| Ok(())); + + let mock_class_manager_client = MockClassManagerClient::new(); + + let transaction_converter = TransactionConverter::new( + Arc::new(mock_class_manager_client), + Arc::new(mock_proof_manager_client), + ChainInfo::create_for_testing().chain_id, + ); + + // Convert the RPC transaction to an internal RPC transaction. + transaction_converter.convert_rpc_tx_to_internal_rpc_tx(invoke_tx).await.unwrap(); +} + +#[rstest] +#[tokio::test] +async fn test_proof_verification_skipped_for_invoke_v3_without_proof_facts() { + // Create an invoke transaction without proof_facts. + let invoke_tx = invoke_tx(CairoVersion::Cairo1(RunnableCairo1::Casm)); + + // Mock proof manager client expects NO calls to contains_proof or set_proof. + let mock_proof_manager_client = MockProofManagerClient::new(); + let mock_class_manager_client = MockClassManagerClient::new(); + + let transaction_converter = TransactionConverter::new( + Arc::new(mock_class_manager_client), + Arc::new(mock_proof_manager_client), + ChainInfo::create_for_testing().chain_id, + ); + + // Convert the RPC transaction to an internal RPC transaction. + // This should succeed without calling contains_proof or set_proof. + transaction_converter.convert_rpc_tx_to_internal_rpc_tx(invoke_tx).await.unwrap(); +} + +#[rstest] +#[tokio::test] +async fn test_proof_verification_skipped_when_proof_already_exists() { + // Create an invoke transaction with proof_facts and proof. + let proof_facts = proof_facts![felt!("0x1"), felt!("0x2"), felt!("0x3")]; + let proof = Proof::from(vec![1u32, 2u32, 3u32]); + let invoke_tx = invoke_tx_client_side_proving( + CairoVersion::Cairo1(RunnableCairo1::Casm), + proof_facts.clone(), + proof.clone(), + ); + + let mut mock_proof_manager_client = MockProofManagerClient::new(); + // Expect contains_proof to be called and return true (proof already exists). + mock_proof_manager_client + .expect_contains_proof() + .once() + .with(eq(proof_facts)) + .return_once(|_| Ok(true)); + // Since proof already exists, expect set_proof to NOT be called. + + let mock_class_manager_client = MockClassManagerClient::new(); + + let transaction_converter = TransactionConverter::new( + Arc::new(mock_class_manager_client), + Arc::new(mock_proof_manager_client), + ChainInfo::create_for_testing().chain_id, + ); + + // Convert the RPC transaction to an internal RPC transaction. + // This should succeed and only call contains_proof, not set_proof. + transaction_converter.convert_rpc_tx_to_internal_rpc_tx(invoke_tx).await.unwrap(); +} diff --git a/crates/apollo_gateway/src/errors.rs b/crates/apollo_gateway/src/errors.rs index 89b7aa9439d..fa7eb99bfd8 100644 --- a/crates/apollo_gateway/src/errors.rs +++ b/crates/apollo_gateway/src/errors.rs @@ -339,6 +339,9 @@ pub fn transaction_converter_err_to_deprecated_gw_err( err: TransactionConverterError, ) -> StarknetError { match err { + TransactionConverterError::ProofManagerClientError(err) => { + StarknetError::internal_with_logging("Proof manager client error", err) + } TransactionConverterError::ValidateCompiledClassHashError(err) => { convert_compiled_class_hash_error(err) } diff --git a/crates/apollo_gateway/src/stateless_transaction_validator.rs b/crates/apollo_gateway/src/stateless_transaction_validator.rs index 41b44959c95..9d801b754a3 100644 --- a/crates/apollo_gateway/src/stateless_transaction_validator.rs +++ b/crates/apollo_gateway/src/stateless_transaction_validator.rs @@ -41,7 +41,6 @@ impl StatelessTransactionValidator { self.validate_tx_size(tx)?; self.validate_nonce_data_availability_mode(tx)?; self.validate_fee_data_availability_mode(tx)?; - self.validate_proof(tx)?; if let RpcTransaction::Declare(declare_tx) = tx { self.validate_declare_tx(declare_tx)?; @@ -219,11 +218,6 @@ impl StatelessTransactionValidator { Ok(()) } - fn validate_proof(&self, _: &RpcTransaction) -> StatelessTransactionValidatorResult<()> { - // TODO(Einat): Implement proof validation. - Ok(()) - } - fn validate_declare_tx( &self, declare_tx: &RpcDeclareTransaction, diff --git a/crates/apollo_proof_manager_types/src/lib.rs b/crates/apollo_proof_manager_types/src/lib.rs index 5d34b938c18..155852df862 100644 --- a/crates/apollo_proof_manager_types/src/lib.rs +++ b/crates/apollo_proof_manager_types/src/lib.rs @@ -51,7 +51,7 @@ pub enum ProofManagerError { Io(String), } -#[derive(Clone, Debug, Error)] +#[derive(Clone, Debug, Error, PartialEq)] pub enum ProofManagerClientError { #[error(transparent)] ClientError(#[from] ClientError),