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

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
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
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());
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),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From what I've seen of Rust errors, shouldn't this just be Reqwest? The enum is already named "Error".

#[error("Encountered a custom error: {0}")]
Msg(String),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Custom would be a better variant name

}

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 @@ -26,8 +26,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 @@ -52,7 +53,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 @@ -68,7 +69,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 @@ -45,7 +45,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 @@ -40,12 +40,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),
Comment on lines +43 to +48
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can improve this by using Result.and_then

}

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();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@whichqua what default would represent here?


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