diff --git a/crates/apollo_gateway/src/errors.rs b/crates/apollo_gateway/src/errors.rs index 96995b60b9d..9be7f11f512 100644 --- a/crates/apollo_gateway/src/errors.rs +++ b/crates/apollo_gateway/src/errors.rs @@ -436,7 +436,8 @@ fn convert_sn_api_error(err: StarknetApiError) -> StarknetError { | StarknetApiError::BlockHashVersion { .. } | StarknetApiError::ParseSierraVersionError(..) | StarknetApiError::ResourceHexToFeltConversion(..) - | StarknetApiError::OutOfRange { .. } => StarknetError { + | StarknetApiError::OutOfRange { .. } + | StarknetApiError::InvalidChainIdHex(..) => StarknetError { code: StarknetErrorCode::KnownErrorCode(KnownStarknetErrorCode::MalformedRequest), message: err.to_string(), }, diff --git a/crates/blockifier_reexecution/README.md b/crates/blockifier_reexecution/README.md index b5da9ea7131..2171963ffc8 100644 --- a/crates/blockifier_reexecution/README.md +++ b/crates/blockifier_reexecution/README.md @@ -7,6 +7,29 @@ The blockier reexecution crate is intended to verify blockifier changes do not b ## CLI Commands Using the different CLI commands, it is possible to run reexecution tests in different modes, to download (permisionless) files for offline reexecution from the GC bucket, and to upload (permissioned) files for offline reexecution to the GC bucket. +### Chain ID Option +Commands that use RPC (`rpc-test`, `reexecute-single-tx`, `write-to-file`) accept optional chain ID arguments. If not provided, the chain ID is guessed from the node URL. + +There are two mutually exclusive options for specifying the chain ID: + +**Option 1: `-c/--chain-id` (enum)** +For standard chain IDs, use the `-c` flag with one of the supported values: +- `mainnet` - Starknet Mainnet (SN_MAIN) +- `testnet` - Sepolia testnet (SN_SEPOLIA) +- `integration` - Integration Sepolia (SN_INTEGRATION_SEPOLIA) + +``` +cargo run --bin blockifier_reexecution rpc-test -n -b -c mainnet +``` + +**Option 2: `--custom-chain-id` (hex string)** +For custom/private networks, use the `--custom-chain-id` flag with a hex-encoded chain ID string: +``` +cargo run --bin blockifier_reexecution rpc-test -n -b --custom-chain-id 0x505249564154455f534e +``` +Note: The hex string is the ASCII encoding of the chain ID name. For example, `0x534e5f4d41494e` decodes to `SN_MAIN`. + + ### Reexecution Modes Reexecution can be run via CLI in the following modes: diff --git a/crates/blockifier_reexecution/src/state_reader/cli.rs b/crates/blockifier_reexecution/src/state_reader/cli.rs index 93c3e02b230..2af0f5d6d9b 100644 --- a/crates/blockifier_reexecution/src/state_reader/cli.rs +++ b/crates/blockifier_reexecution/src/state_reader/cli.rs @@ -3,7 +3,7 @@ use std::fs::read_to_string; use clap::{Args, Parser, Subcommand}; use starknet_api::block::BlockNumber; -use starknet_api::core::ChainId; +use starknet_api::core::{chain_id_from_hex_str, ChainId}; use crate::state_reader::errors::{ReexecutionError, ReexecutionResult}; @@ -38,22 +38,34 @@ impl From for ChainId { } #[derive(Clone, Debug, Args)] +#[clap(group( + clap::ArgGroup::new("chain_id_group") + .args(&["chain_id", "custom_chain_id"]) +))] pub struct RpcArgs { /// Node url. #[clap(long, short = 'n')] pub node_url: String, /// Optional chain ID (if not provided, it will be guessed from the node url). + /// Supported values: mainnet, testnet, integration. #[clap(long, short = 'c')] pub chain_id: Option, + + /// Optional custom chain ID as hex string (e.g., "0x534e5f4d41494e"). + #[clap(long)] + pub custom_chain_id: Option, } impl RpcArgs { pub fn parse_chain_id(&self) -> ChainId { - self.chain_id - .clone() - .map(ChainId::from) - .unwrap_or(guess_chain_id_from_node_url(self.node_url.as_str()).unwrap()) + if let Some(chain_id) = &self.chain_id { + return chain_id.clone().into(); + } + if let Some(hex_str) = &self.custom_chain_id { + return chain_id_from_hex_str(hex_str).expect("Failed to parse hex chain id"); + } + guess_chain_id_from_node_url(self.node_url.as_str()).unwrap() } } diff --git a/crates/starknet_api/src/core.rs b/crates/starknet_api/src/core.rs index 653ed0bce61..41169939597 100644 --- a/crates/starknet_api/src/core.rs +++ b/crates/starknet_api/src/core.rs @@ -18,7 +18,7 @@ use crate::crypto::utils::PublicKey; use crate::hash::{PoseidonHash, StarkHash}; use crate::serde_utils::{BytesAsHex, PrefixedBytesAsHex}; use crate::transaction::fields::{Calldata, ContractAddressSalt}; -use crate::{impl_from_through_intermediate, StarknetApiError}; +use crate::{impl_from_through_intermediate, StarknetApiError, StarknetApiResult}; /// Felt. pub fn ascii_as_felt(ascii_str: &str) -> Result { @@ -87,20 +87,31 @@ impl ChainId { } } -pub fn deserialize_chain_id_from_hex<'de, D>(deserializer: D) -> Result -where - D: Deserializer<'de>, -{ - let hex_str = String::deserialize(deserializer)?; +/// Parses a hex string (e.g., "0x534e5f4d41494e") into a ChainId. +pub fn chain_id_from_hex_str(hex_str: &str) -> StarknetApiResult { let chain_id_str = std::str::from_utf8(&hex::decode(hex_str.trim_start_matches("0x")).map_err(|e| { - D::Error::custom(format!("Failed to decode the hex string {hex_str}. Error: {e:?}")) + StarknetApiError::InvalidChainIdHex(format!( + "Failed to decode the hex string {hex_str}. Error: {e:?}" + )) })?) - .map_err(|e| D::Error::custom(format!("Failed to convert to UTF-8 string. Error: {e:?}")))? + .map_err(|e| { + StarknetApiError::InvalidChainIdHex(format!( + "Failed to convert to UTF-8 string. Error: {e}" + )) + })? .to_string(); Ok(ChainId::from(chain_id_str)) } +pub fn deserialize_chain_id_from_hex<'de, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, +{ + let hex_str = String::deserialize(deserializer)?; + chain_id_from_hex_str(&hex_str).map_err(D::Error::custom) +} + /// The address of a contract, used for example in [StateDiff](`crate::state::StateDiff`), /// [DeclareTransaction](`crate::transaction::DeclareTransaction`), and /// [BlockHeader](`crate::block::BlockHeader`). diff --git a/crates/starknet_api/src/lib.rs b/crates/starknet_api/src/lib.rs index 5278a727e80..cc252968102 100644 --- a/crates/starknet_api/src/lib.rs +++ b/crates/starknet_api/src/lib.rs @@ -88,6 +88,8 @@ pub enum StarknetApiError { version {cairo_version:?}.", **declare_version )] ContractClassVersionMismatch { declare_version: TransactionVersion, cairo_version: u64 }, + #[error("Failed to parse chain ID from hex: {0}")] + InvalidChainIdHex(String), #[error("Failed to parse Sierra version: {0}")] ParseSierraVersionError(String), #[error("Unsupported transaction type: {0}")]