Skip to content
Draft
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
2 changes: 1 addition & 1 deletion crates/executor/src/concurrent_block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,7 @@ fn create_state_reader_components<S: StorageAdapter + Clone>(
let block_context = BlockContext::new(
starknet_block_info,
chain_info,
versioned_constants.into_owned(),
versioned_constants.clone(),
BouncerConfig::max(),
);

Expand Down
251 changes: 119 additions & 132 deletions crates/executor/src/execution_state.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use std::borrow::Cow;
use std::collections::BTreeMap;
use std::sync::Arc;

Expand All @@ -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<StarknetVersion, Cow<'static, VersionedConstants>>,
/// 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<StarknetVersion, VersionedConstants>,
}

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<StarknetVersion, Cow<'static, VersionedConstants>>) -> Self {
Self::fill_default(&mut data);
Self { data }
pub fn custom(overrides: BTreeMap<StarknetVersion, VersionedConstants>) -> 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<StarknetVersion, Cow<'static, VersionedConstants>>) {
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<StarknetVersion, Cow<'static, VersionedConstants>>,
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<starknet_api::block::StarknetVersion> {
version.to_string().try_into().ok()
}
}

impl Default for VersionedConstantsMap {
Expand Down Expand Up @@ -296,7 +228,7 @@ impl ExecutionState {
let block_context = BlockContext::new(
block_info,
chain_info,
versioned_constants.into_owned(),
versioned_constants.clone(),
BouncerConfig::max(),
);

Expand Down Expand Up @@ -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));
}
}
8 changes: 2 additions & 6 deletions crates/pathfinder/src/config.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use std::borrow::Cow;
use std::collections::{BTreeMap, HashMap, HashSet};
use std::fs::File;
use std::net::SocketAddr;
Expand Down Expand Up @@ -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() {
Expand Down
Loading