Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: improve convertion errors and handle invalid ABI #387

Merged
merged 10 commits into from
Oct 14, 2024
14 changes: 6 additions & 8 deletions crates/bin/prove_block/src/rpc_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::collections::HashMap;
use blockifier::transaction::objects::TransactionExecutionInfo;
use cairo_vm::Felt252;
use num_bigint::BigInt;
use rpc_client::pathfinder::client::ClientError;
use rpc_client::pathfinder::proofs::{
ContractData, EdgePath, PathfinderClassProof, PathfinderProof, ProofVerificationError, TrieNode,
};
Expand All @@ -25,7 +26,7 @@ async fn fetch_storage_proof_for_contract(
contract_address: Felt,
keys: &[Felt],
block_number: u64,
) -> Result<PathfinderProof, reqwest::Error> {
) -> Result<PathfinderProof, ClientError> {
let storage_proof = if keys.is_empty() {
rpc_client.pathfinder_rpc().get_proof(block_number, contract_address, &[]).await?
} else {
Expand All @@ -50,7 +51,7 @@ async fn get_storage_proof_for_contract<KeyIter: Iterator<Item = StorageKey>>(
contract_address: ContractAddress,
storage_keys: KeyIter,
block_number: u64,
) -> Result<PathfinderProof, reqwest::Error> {
) -> Result<PathfinderProof, ClientError> {
let contract_address_felt = *contract_address.key();
let keys: Vec<_> = storage_keys.map(|storage_key| *storage_key.key()).collect();

Expand Down Expand Up @@ -113,7 +114,7 @@ pub(crate) async fn get_storage_proofs(
block_number: u64,
tx_execution_infos: &[TransactionExecutionInfo],
old_block_number: Felt,
) -> Result<HashMap<Felt, PathfinderProof>, reqwest::Error> {
) -> Result<HashMap<Felt, PathfinderProof>, ClientError> {
let accessed_keys_by_address = {
let mut keys = get_all_accessed_keys(tx_execution_infos);
// We need to fetch the storage proof for the block hash contract
Expand All @@ -124,11 +125,8 @@ pub(crate) async fn get_storage_proofs(
let mut storage_proofs = HashMap::new();

log::info!("Contracts we're fetching proofs for:");
for contract_address in accessed_keys_by_address.keys() {
log::info!(" {}", contract_address.to_string());
}

for (contract_address, storage_keys) in accessed_keys_by_address {
log::info!(" {}", contract_address.to_string());
whichqua marked this conversation as resolved.
Show resolved Hide resolved
let contract_address_felt = *contract_address.key();
let storage_proof =
get_storage_proof_for_contract(client, contract_address, storage_keys.into_iter(), block_number).await?;
Expand Down Expand Up @@ -196,7 +194,7 @@ pub(crate) async fn get_class_proofs(
rpc_client: &RpcClient,
block_number: u64,
class_hashes: &[&Felt],
) -> Result<HashMap<Felt252, PathfinderClassProof>, reqwest::Error> {
) -> Result<HashMap<Felt252, PathfinderClassProof>, ClientError> {
let mut proofs: HashMap<Felt252, PathfinderClassProof> = HashMap::with_capacity(class_hashes.len());
for class_hash in class_hashes {
let proof = rpc_client.pathfinder_rpc().get_class_proof(block_number, class_hash).await?;
Expand Down
30 changes: 24 additions & 6 deletions crates/rpc-client/src/pathfinder/client.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
use reqwest::{Response, StatusCode};
use serde::de::DeserializeOwned;
use serde::Deserialize;
use serde_json::json;
use starknet_types_core::felt::Felt;

use crate::pathfinder::proofs::{PathfinderClassProof, PathfinderProof};

#[derive(Debug, thiserror::Error)]
pub enum ClientError {
#[error("Encountered a request error: {0}")]
ReqwestError(#[from] reqwest::Error),
shamsasari marked this conversation as resolved.
Show resolved Hide resolved
#[error("Encountered a custom error: {0}")]
Msg(String),
whichqua marked this conversation as resolved.
Show resolved Hide resolved
}

fn jsonrpc_request(method: &str, params: serde_json::Value) -> serde_json::Value {
json!({
"jsonrpc": "2.0",
Expand All @@ -19,7 +28,7 @@ async fn post_jsonrpc_request<T: DeserializeOwned>(
rpc_provider: &str,
method: &str,
params: serde_json::Value,
) -> Result<T, reqwest::Error> {
) -> Result<T, ClientError> {
let request = jsonrpc_request(method, params);
let response = client.post(format!("{}/rpc/pathfinder/v0.1", rpc_provider)).json(&request).send().await?;

Expand All @@ -28,12 +37,21 @@ async fn post_jsonrpc_request<T: DeserializeOwned>(
result: T,
}

let response_text = response.text().await?;
let response: TransactionReceiptResponse<T> =
serde_json::from_str(&response_text).unwrap_or_else(|_| panic!("Error: {}", response_text));
let response: TransactionReceiptResponse<T> = handle_error(response).await?;

Ok(response.result)
}

async fn handle_error<T: DeserializeOwned>(response: Response) -> Result<T, ClientError> {
match response.status() {
StatusCode::OK => Ok(response.json().await?),
s => {
let error = response.text().await?;
Err(ClientError::Msg(format!("Received response: {s:?} Error: {error}")))
}
}
}

pub struct PathfinderRpcClient {
/// A raw client to access endpoints not covered by starknet-rs.
http_client: reqwest::Client,
Expand All @@ -56,7 +74,7 @@ impl PathfinderRpcClient {
block_number: u64,
contract_address: Felt,
keys: &[Felt],
) -> Result<PathfinderProof, reqwest::Error> {
) -> Result<PathfinderProof, ClientError> {
post_jsonrpc_request(
&self.http_client,
&self.rpc_base_url,
Expand All @@ -70,7 +88,7 @@ impl PathfinderRpcClient {
&self,
block_number: u64,
class_hash: &Felt,
) -> Result<PathfinderClassProof, reqwest::Error> {
) -> Result<PathfinderClassProof, ClientError> {
log::debug!("querying pathfinder_getClassProof for {:x}", class_hash);
post_jsonrpc_request(
&self.http_client,
Expand Down
11 changes: 6 additions & 5 deletions crates/starknet-os-types/src/casm_contract_class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::sync::Arc;

use serde::{Deserialize, Deserializer, Serialize, Serializer};

use crate::error::ContractClassError;
use crate::error::{ContractClassError, ConversionError};
use crate::hash::GenericClassHash;

pub type CairoLangCasmClass = cairo_lang_starknet_classes::casm_contract_class::CasmContractClass;
Expand All @@ -25,8 +25,9 @@ pub struct GenericCasmContractClass {
fn blockifier_contract_class_from_cairo_lang_class(
cairo_lang_class: CairoLangCasmClass,
) -> Result<BlockifierCasmClass, ContractClassError> {
let blockifier_class: BlockifierCasmClass =
cairo_lang_class.try_into().map_err(|_| ContractClassError::BlockifierConversionError)?;
let blockifier_class: BlockifierCasmClass = cairo_lang_class
.try_into()
.map_err(|e| ContractClassError::ConversionError(ConversionError::BlockifierError(Box::new(e))))?;
Ok(blockifier_class)
}

Expand All @@ -51,7 +52,7 @@ impl GenericCasmContractClass {
return Ok(contract_class);
}

Err(ContractClassError::NoPossibleConversion)
Err(ContractClassError::ConversionError(ConversionError::CairoLangClassMissing))
}

fn build_blockifier_class(&self) -> Result<BlockifierCasmClass, ContractClassError> {
Expand All @@ -67,7 +68,7 @@ impl GenericCasmContractClass {
return blockifier_contract_class_from_cairo_lang_class(cairo_lang_class);
}

Err(ContractClassError::NoPossibleConversion)
Err(ContractClassError::ConversionError(ConversionError::BlockifierClassMissing))
}
pub fn get_cairo_lang_contract_class(&self) -> Result<&CairoLangCasmClass, ContractClassError> {
self.cairo_lang_contract_class
Expand Down
4 changes: 2 additions & 2 deletions crates/starknet-os-types/src/deprecated_compiled_class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::sync::Arc;
use pathfinder_gateway_types::class_hash::compute_class_hash;
use serde::{Deserialize, Deserializer, Serialize, Serializer};

use crate::error::ContractClassError;
use crate::error::{ContractClassError, ConversionError};
use crate::hash::GenericClassHash;
use crate::starknet_core_addons::{decompress_starknet_core_contract_class, LegacyContractDecompressionError};

Expand Down Expand Up @@ -44,7 +44,7 @@ impl GenericDeprecatedCompiledClass {
return Ok(contract_class);
}

Err(ContractClassError::NoPossibleConversion)
Err(ContractClassError::ConversionError(ConversionError::StarknetClassMissing))
}

fn build_blockifier_class(&self) -> Result<BlockifierDeprecatedClass, ContractClassError> {
Expand Down
24 changes: 19 additions & 5 deletions crates/starknet-os-types/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
use std::error::Error;

use cairo_lang_starknet_classes::casm_contract_class::StarknetSierraCompilationError;

#[derive(thiserror::Error, Debug)]
pub enum ContractClassError {
#[error("Internal error: no type conversion is possible to generate the desired effect.")]
NoPossibleConversion,

#[error("Could not build Blockifier contract class")]
BlockifierConversionError,
#[error(transparent)]
ConversionError(#[from] ConversionError),

#[error(transparent)]
SerdeError(#[from] serde_json::Error),
Expand All @@ -17,3 +16,18 @@ pub enum ContractClassError {
#[error(transparent)]
CompilationError(#[from] StarknetSierraCompilationError),
}

#[derive(thiserror::Error, Debug)]
pub enum ConversionError {
#[error("Could not build Blockifier contract class: {0}")]
BlockifierError(Box<dyn Error + 'static>),

#[error("Missing Starknet serialized class")]
StarknetClassMissing,

#[error("Missing Blockifier serialized class")]
BlockifierClassMissing,

#[error("Missing CairoLang serialized class")]
CairoLangClassMissing,
}
14 changes: 8 additions & 6 deletions crates/starknet-os-types/src/sierra_contract_class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,13 @@ impl GenericSierraContractClass {
}

fn build_cairo_lang_class(&self) -> Result<CairoLangSierraContractClass, ContractClassError> {
if let Ok(serialized_class) = self.get_serialized_contract_class() {
let contract_class = serde_json::from_slice(serialized_class)?;
return Ok(contract_class);
match self.get_serialized_contract_class() {
Ok(serialized_class) => {
let contract_class = serde_json::from_slice(serialized_class)?;
Ok(contract_class)
}
Err(e) => Err(e),
whichqua marked this conversation as resolved.
Show resolved Hide resolved
}

Err(ContractClassError::NoPossibleConversion)
}

pub fn get_serialized_contract_class(&self) -> Result<&Vec<u8>, ContractClassError> {
Expand Down Expand Up @@ -140,7 +141,8 @@ impl TryFrom<&FlattenedSierraClass> for FlattenedSierraClassWithAbi {
type Error = serde_json::error::Error;

fn try_from(sierra_class: &FlattenedSierraClass) -> Result<Self, Self::Error> {
let abi: Option<cairo_lang_starknet_classes::abi::Contract> = serde_json::from_str(&sierra_class.abi)?;
let abi: Option<cairo_lang_starknet_classes::abi::Contract> =
serde_json::from_str(&sierra_class.abi).unwrap_or_default();
HermanObst marked this conversation as resolved.
Show resolved Hide resolved

Ok(Self {
sierra_program: sierra_class.sierra_program.clone(),
Expand Down
Loading