diff --git a/crates/executor/src/concurrent_block.rs b/crates/executor/src/concurrent_block.rs index 015a6563ec..72b48d853a 100644 --- a/crates/executor/src/concurrent_block.rs +++ b/crates/executor/src/concurrent_block.rs @@ -327,7 +327,7 @@ fn create_state_reader_components( let block_context = BlockContext::new( starknet_block_info, chain_info, - versioned_constants.into_owned(), + versioned_constants.clone(), BouncerConfig::max(), ); diff --git a/crates/executor/src/execution_state.rs b/crates/executor/src/execution_state.rs index a51b64e0e9..a7d8d3e785 100644 --- a/crates/executor/src/execution_state.rs +++ b/crates/executor/src/execution_state.rs @@ -1,4 +1,3 @@ -use std::borrow::Cow; use std::collections::BTreeMap; use std::sync::Arc; @@ -21,147 +20,80 @@ use crate::state_reader::{NativeClassCache, StorageAdapter}; use crate::types::BlockInfo; use crate::IntoStarkFelt; -mod versions { - use pathfinder_common::StarknetVersion; - - pub(super) const STARKNET_VERSION_0_13_1: StarknetVersion = StarknetVersion::new(0, 13, 1, 0); - - pub(super) const STARKNET_VERSION_0_13_1_1: StarknetVersion = StarknetVersion::new(0, 13, 1, 1); - - pub(super) const STARKNET_VERSION_0_13_2: StarknetVersion = StarknetVersion::new(0, 13, 2, 0); - - pub(super) const STARKNET_VERSION_0_13_2_1: StarknetVersion = StarknetVersion::new(0, 13, 2, 1); - - pub(super) const STARKNET_VERSION_0_13_3: StarknetVersion = StarknetVersion::new(0, 13, 3, 0); - - pub(super) const STARKNET_VERSION_0_13_4: StarknetVersion = StarknetVersion::new(0, 13, 4, 0); - - pub(super) const STARKNET_VERSION_0_13_5: StarknetVersion = StarknetVersion::new(0, 13, 5, 0); - - pub(super) const STARKNET_VERSION_0_14_0: StarknetVersion = StarknetVersion::new(0, 14, 0, 0); - - pub(super) const STARKNET_VERSION_0_14_1: StarknetVersion = StarknetVersion::new(0, 14, 1, 0); - - pub(super) const STARKNET_VERSION_0_14_2: StarknetVersion = StarknetVersion::new(0, 14, 2, 0); - - pub(super) const STARKNET_VERSION_0_14_3: StarknetVersion = StarknetVersion::new(0, 14, 3, 0); -} - #[derive(Clone, Debug)] pub struct VersionedConstantsMap { - data: BTreeMap>, + /// Operator-supplied overrides, keyed by the exact Starknet version they + /// target. Empty unless `--versioned-constants-file` is configured; every + /// other version resolves to the constants bundled with our blockifier + /// dependency. + overrides: BTreeMap, } impl VersionedConstantsMap { pub fn new() -> Self { - let mut data = BTreeMap::new(); - Self::fill_default(&mut data); - Self { data } + Self { + overrides: BTreeMap::new(), + } } - pub fn custom(mut data: BTreeMap>) -> Self { - Self::fill_default(&mut data); - Self { data } + pub fn custom(overrides: BTreeMap) -> Self { + Self { overrides } } + /// The latest Starknet version our blockifier dependency ships constants + /// for. pub fn latest_version() -> StarknetVersion { - versions::STARKNET_VERSION_0_14_3 + starknet_api::block::StarknetVersion::LATEST + .to_string() + .parse() + .expect("blockifier's latest Starknet version is a valid version string") } - fn fill_default(data: &mut BTreeMap>) { - use versions::*; - - Self::insert_default( - data, - &STARKNET_VERSION_0_13_1, - VersionedConstants::get(&starknet_api::block::StarknetVersion::V0_13_1) - .expect("Failed to get versioned constants for 0.13.1"), - ); - Self::insert_default( - data, - &STARKNET_VERSION_0_13_1_1, - VersionedConstants::get(&starknet_api::block::StarknetVersion::V0_13_1_1) - .expect("Failed to get versioned constants for 0.13.1.1"), - ); - Self::insert_default( - data, - &STARKNET_VERSION_0_13_2, - VersionedConstants::get(&starknet_api::block::StarknetVersion::V0_13_2) - .expect("Failed to get versioned constants for 0.13.2"), - ); - Self::insert_default( - data, - &STARKNET_VERSION_0_13_2_1, - VersionedConstants::get(&starknet_api::block::StarknetVersion::V0_13_2_1) - .expect("Failed to get versioned constants for 0.13.2.1"), - ); - Self::insert_default( - data, - &STARKNET_VERSION_0_13_3, - VersionedConstants::get(&starknet_api::block::StarknetVersion::V0_13_3) - .expect("Failed to get versioned constants for 0.13.3"), - ); - Self::insert_default( - data, - &STARKNET_VERSION_0_13_4, - VersionedConstants::get(&starknet_api::block::StarknetVersion::V0_13_4) - .expect("Failed to get versioned constants for 0.13.4"), - ); - Self::insert_default( - data, - &STARKNET_VERSION_0_13_5, - VersionedConstants::get(&starknet_api::block::StarknetVersion::V0_13_5) - .expect("Failed to get versioned constants for 0.13.5"), - ); - Self::insert_default( - data, - &STARKNET_VERSION_0_14_0, - VersionedConstants::get(&starknet_api::block::StarknetVersion::V0_14_0) - .expect("Failed to get versioned constants for 0.14.0"), - ); - Self::insert_default( - data, - &STARKNET_VERSION_0_14_1, - VersionedConstants::get(&starknet_api::block::StarknetVersion::V0_14_1) - .expect("Failed to get versioned constants for 0.14.1"), - ); - Self::insert_default( - data, - &STARKNET_VERSION_0_14_2, - VersionedConstants::get(&starknet_api::block::StarknetVersion::V0_14_2) - .expect("Failed to get versioned constants for 0.14.2"), - ); - Self::insert_default( - data, - &STARKNET_VERSION_0_14_3, - VersionedConstants::get(&starknet_api::block::StarknetVersion::V0_14_3) - .expect("Failed to get versioned constants for 0.14.3"), - ); + pub fn for_version(&self, version: &StarknetVersion) -> &VersionedConstants { + // An operator override for this exact version wins. + if let Some(constants) = self.overrides.get(version) { + return constants; + } + Self::bundled_for_version(version) } - fn insert_default( - data: &mut BTreeMap>, - key: &StarknetVersion, - default_value: &'static VersionedConstants, - ) { - // should be try_insert, but that's still experimental... - if !data.contains_key(key) { - data.insert(*key, Cow::Borrowed(default_value)); + /// Resolves the versioned constants bundled with our blockifier dependency. + /// + /// Blockifier ships an exact constants set per released Starknet version + /// (0.13.0 through [`Self::latest_version`]). For a version it doesn't + /// recognise we fall back: blocks older than 0.13.0 use 0.13.0, while + /// blocks newer than this dependency use the latest known constants — + /// the latter also warns, since execution may be inaccurate until + /// Pathfinder is upgraded. + fn bundled_for_version(version: &StarknetVersion) -> &'static VersionedConstants { + use starknet_api::block::StarknetVersion as ApiVersion; + + if let Some(constants) = + Self::api_version(version).and_then(|version| VersionedConstants::get(&version).ok()) + { + return constants; } - } - pub fn for_version(&self, version: &StarknetVersion) -> Cow<'static, VersionedConstants> { - let mut rng = self.data.range(..=version); - if let Some(kv) = rng.next_back() { - kv.1.clone() + if version > &Self::latest_version() { + tracing::warn!( + block_version = %version, + latest_known = %Self::latest_version(), + "Block's Starknet version is newer than this Pathfinder release supports; \ + executing with the latest known constants. Upgrade Pathfinder for accurate \ + execution." + ); + VersionedConstants::latest_constants() } else { - // We use 0.13.0 for all blocks before 0.13.1. - Cow::Borrowed( - VersionedConstants::get(&starknet_api::block::StarknetVersion::V0_13_0) - .expect("Failed to get versioned constants for 0.13.0"), - ) + VersionedConstants::get(&ApiVersion::V0_13_0) + .expect("blockifier bundles 0.13.0 versioned constants") } } + + /// Maps a Pathfinder Starknet version onto blockifier's version enum, when + /// it names a version blockifier knows about. + fn api_version(version: &StarknetVersion) -> Option { + version.to_string().try_into().ok() + } } impl Default for VersionedConstantsMap { @@ -296,7 +228,7 @@ impl ExecutionState { let block_context = BlockContext::new( block_info, chain_info, - versioned_constants.into_owned(), + versioned_constants.clone(), BouncerConfig::max(), ); @@ -507,20 +439,75 @@ pub enum L1BlobDataAvailability { #[cfg(test)] mod tests { - use blockifier::blockifier_versioned_constants::ResourceCost; + use std::collections::BTreeMap; + + use blockifier::blockifier_versioned_constants::VersionedConstants; + use pathfinder_common::StarknetVersion; + use starknet_api::block::StarknetVersion as ApiVersion; + use starknet_api::transaction::fields::ProofVersion; + use starknet_api::versioned_constants_logic::VersionedConstantsTrait; - use super::versions::*; use super::VersionedConstantsMap; + fn bundled(version: ApiVersion) -> &'static VersionedConstants { + VersionedConstants::get(&version).unwrap() + } + + /// Each released version — including patch releases — resolves to its own + /// exact bundled constants, never an adjacent version's. #[test] - fn query_versioned_constants() { + fn resolves_exact_bundled_version() { let vcm = VersionedConstantsMap::default(); - let constants = vcm.for_version(&STARKNET_VERSION_0_13_2); - let value = constants.deprecated_l2_resource_gas_costs.gas_per_code_byte; - assert_eq!(value, ResourceCost::new(875, 1000)); + for version in ["0.14.3", "0.13.2.1"] { + let parsed: StarknetVersion = version.parse().unwrap(); + let api: ApiVersion = version.to_string().try_into().unwrap(); + assert!( + std::ptr::eq(vcm.for_version(&parsed), bundled(api)), + "{version} should resolve to its own bundled constants", + ); + } + } - let constants = vcm.for_version(&STARKNET_VERSION_0_13_2_1); - let value = constants.deprecated_l2_resource_gas_costs.gas_per_code_byte; - assert_eq!(value, ResourceCost::new(32, 1000)); + /// Versions outside blockifier's range fall back: older than 0.13.0 to + /// 0.13.0, newer than our dependency to the latest known constants. + #[test] + fn resolves_out_of_range_versions() { + let vcm = VersionedConstantsMap::default(); + + let ancient = "0.12.0".parse().unwrap(); + assert!(std::ptr::eq( + vcm.for_version(&ancient), + bundled(ApiVersion::V0_13_0), + )); + + let future = StarknetVersion::new(0, 99, 0, 0); + assert!(std::ptr::eq( + vcm.for_version(&future), + VersionedConstants::latest_constants(), + )); + } + + /// An operator override wins over the bundled constants for its exact + /// version. + #[test] + fn override_takes_precedence() { + let target: StarknetVersion = "0.14.3".parse().unwrap(); + let proof1 = ProofVersion::V1.as_felt(); + + // 0.14.3 normally allows PROOF1... + assert!(VersionedConstantsMap::default() + .for_version(&target) + .os_constants + .allowed_proof_versions + .contains(&proof1)); + + // ...so overriding it with 0.13.0's constants (which allow no proof + // versions) proves the override is what gets used. + let overrides = BTreeMap::from([(target, bundled(ApiVersion::V0_13_0).clone())]); + assert!(!VersionedConstantsMap::custom(overrides) + .for_version(&target) + .os_constants + .allowed_proof_versions + .contains(&proof1)); } } diff --git a/crates/pathfinder/src/config.rs b/crates/pathfinder/src/config.rs index 168ad74348..fc3cf5aa72 100644 --- a/crates/pathfinder/src/config.rs +++ b/crates/pathfinder/src/config.rs @@ -1,4 +1,3 @@ -use std::borrow::Cow; use std::collections::{BTreeMap, HashMap, HashSet}; use std::fs::File; use std::net::SocketAddr; @@ -1100,16 +1099,13 @@ fn parse_versioned_constants( })?; let abs_path = std::fs::canonicalize(dir_path.join(rel_path))?; let constants = VersionedConstants::from_path(&abs_path)?; - target.insert(version, Cow::Owned(constants)); + target.insert(version, constants); } } else { // logging isn't set up yet... eprintln!("Unknown versioned constants map file format - trying legacy..."); let constants = VersionedConstants::from_path(path)?; - target.insert( - VersionedConstantsMap::latest_version(), - Cow::Owned(constants), - ); + target.insert(VersionedConstantsMap::latest_version(), constants); } if target.is_empty() {