Skip to content
This repository has been archived by the owner on Aug 21, 2024. It is now read-only.

Commit

Permalink
chore: move validate consts into os constants (#1523)
Browse files Browse the repository at this point in the history
* chore: move validate consts into os constants

Had to bump serde_json due to some apparent bug with the recent stable
rust.

* feat: make 13_1 consts backwards compatible

Assign the following defaults for keys missing in versioned_constants
files (for example, they are missing in versioned_constants_13_0.json).

- EventSizeLimit => max values
- L2ResourceGasCosts => 0 values
- validate rounding numbers => 1 (to preserve past no-rounding
  behavior). This required bundling them up in a specialized struct in
  order to define a custom default as 1 (rather than 0).
- Add test for default values: required extracting validation logic of
  `OsResources` so it won't trigger automatically in tests.

Co-Authored-By: Gilad Chase <[email protected]>
  • Loading branch information
giladchase and Gilad Chase authored Feb 18, 2024
1 parent 61f0b38 commit 9c0c99a
Show file tree
Hide file tree
Showing 6 changed files with 157 additions and 73 deletions.
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 5 additions & 3 deletions crates/blockifier/resources/versioned_constants.json
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,11 @@
"l1_gas": "L1_GAS",
"l2_gas": "L2_GAS",
"l1_gas_index": 0,
"l2_gas_index": 1
"l2_gas_index": 1,
"validate_rounding_consts": {
"validate_block_number_rounding": 100,
"validate_timestamp_rounding": 3600
}
},
"os_resources": {
"execute_syscalls": {
Expand Down Expand Up @@ -449,9 +453,7 @@
}
}
},
"validate_block_number_rounding": 100,
"validate_max_n_steps": 1000000,
"validate_timestamp_rounding": 3600,
"vm_resource_fee_cost": {
"bitwise_builtin": 0.16,
"ec_op_builtin": 2.56,
Expand Down
21 changes: 13 additions & 8 deletions crates/blockifier/src/execution/deprecated_syscalls/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -407,10 +407,13 @@ pub fn get_block_number(
let versioned_constants = syscall_handler.context.versioned_constants();
let block_number = syscall_handler.get_block_info().block_number;
let block_number = match syscall_handler.execution_mode() {
ExecutionMode::Validate => BlockNumber(
(block_number.0 / versioned_constants.validate_block_number_rounding)
* versioned_constants.validate_block_number_rounding,
),
ExecutionMode::Validate => {
let validate_block_number_rounding =
versioned_constants.get_validate_block_number_rounding();
BlockNumber(
(block_number.0 / validate_block_number_rounding) * validate_block_number_rounding,
)
}
ExecutionMode::Execute => block_number,
};
Ok(GetBlockNumberResponse { block_number })
Expand Down Expand Up @@ -440,10 +443,12 @@ pub fn get_block_timestamp(
let versioned_constants = syscall_handler.context.versioned_constants();
let block_timestamp = syscall_handler.get_block_info().block_timestamp;
let block_timestamp = match syscall_handler.execution_mode() {
ExecutionMode::Validate => BlockTimestamp(
(block_timestamp.0 / versioned_constants.validate_timestamp_rounding)
* versioned_constants.validate_timestamp_rounding,
),
ExecutionMode::Validate => {
let validate_timestamp_rounding = versioned_constants.get_validate_timestamp_rounding();
BlockTimestamp(
(block_timestamp.0 / validate_timestamp_rounding) * validate_timestamp_rounding,
)
}
ExecutionMode::Execute => block_timestamp,
};
Ok(GetBlockTimestampResponse { block_timestamp })
Expand Down
13 changes: 7 additions & 6 deletions crates/blockifier/src/execution/syscalls/hint_processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -504,13 +504,14 @@ impl<'a> SyscallHintProcessor<'a> {
let versioned_constants = self.context.versioned_constants();
let block_data: Vec<StarkFelt> = if self.is_validate_mode() {
// Round down to the nearest multiple of validate_block_number_rounding.
let rounded_block_number = (block_number
/ versioned_constants.validate_block_number_rounding)
* versioned_constants.validate_block_number_rounding;
let validate_block_number_rounding =
versioned_constants.get_validate_block_number_rounding();
let rounded_block_number =
(block_number / validate_block_number_rounding) * validate_block_number_rounding;
// Round down to the nearest multiple of validate_timestamp_rounding.
let rounded_timestamp = (block_timestamp
/ versioned_constants.validate_timestamp_rounding)
* versioned_constants.validate_timestamp_rounding;
let validate_timestamp_rounding = versioned_constants.get_validate_timestamp_rounding();
let rounded_timestamp =
(block_timestamp / validate_timestamp_rounding) * validate_timestamp_rounding;

vec![
StarkFelt::from(rounded_block_number),
Expand Down
153 changes: 99 additions & 54 deletions crates/blockifier/src/versioned_constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,13 @@ static DEFAULT_CONSTANTS: Lazy<VersionedConstants> = Lazy::new(|| {
#[derive(Clone, Debug, Default, Deserialize)]
pub struct VersionedConstants {
// Limits.
#[serde(default = "EventSizeLimit::max")]
pub event_size_limit: EventSizeLimit,
pub invoke_tx_max_n_steps: u32,
#[serde(default)]
pub l2_resource_gas_costs: L2ResourceGasCosts,
pub max_recursion_depth: usize,
// Flooring factor for block number in validate mode.
pub validate_block_number_rounding: u64,
pub validate_max_n_steps: u32,
// Flooring factor for timestamp in validate mode.
pub validate_timestamp_rounding: u64,

// Cairo OS constants.
// Note: if loaded from a json file, there are some assumptions made on its structure.
Expand Down Expand Up @@ -119,6 +117,14 @@ impl VersionedConstants {
self.os_resources.get_additional_os_syscall_resources(syscall_counter)
}

pub fn get_validate_block_number_rounding(&self) -> u64 {
self.os_constants.validate_rounding_consts.validate_block_number_rounding
}

pub fn get_validate_timestamp_rounding(&self) -> u64 {
self.os_constants.validate_rounding_consts.validate_timestamp_rounding
}

#[cfg(any(feature = "testing", test))]
pub fn create_for_account_testing() -> Self {
let vm_resource_fee_cost = Arc::new(HashMap::from([
Expand All @@ -144,7 +150,7 @@ impl TryFrom<&Path> for VersionedConstants {
}
}

#[derive(Clone, Debug, Default, Deserialize)]
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Eq)]
pub struct L2ResourceGasCosts {
// TODO(barak, 18/03/2024): Once we start charging per byte change to milligas_per_data_byte,
// divide the value by 32 in the JSON file.
Expand All @@ -153,13 +159,23 @@ pub struct L2ResourceGasCosts {
pub milligas_per_code_byte: u128,
}

#[derive(Clone, Debug, Default, Deserialize)]
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Eq)]
pub struct EventSizeLimit {
pub max_data_length: usize,
pub max_keys_length: usize,
pub max_n_emitted_events: usize,
}

impl EventSizeLimit {
fn max() -> Self {
Self {
max_data_length: usize::MAX,
max_keys_length: usize::MAX,
max_n_emitted_events: usize::MAX,
}
}
}

#[derive(Clone, Debug, Default, Deserialize)]
// Serde trick for adding validations via a customr deserializer, without forgoing the derive.
// See: https://github.com/serde-rs/serde/issues/1220.
Expand All @@ -178,6 +194,58 @@ pub struct OsResources {
}

impl OsResources {
pub fn validate<'de, D: Deserializer<'de>>(
&self,
) -> Result<(), <D as Deserializer<'de>>::Error> {
for tx_type in TransactionType::iter() {
if !self.execute_txs_inner.contains_key(&tx_type) {
return Err(DeserializationError::custom(format!(
"ValidationError: os_resources.execute_tx_inner is missing transaction_type: \
{tx_type:?}"
)));
}
}

for syscall_handler in DeprecatedSyscallSelector::iter() {
if !self.execute_syscalls.contains_key(&syscall_handler) {
return Err(DeserializationError::custom(format!(
"ValidationError: os_resources.execute_syscalls are missing syscall handler: \
{syscall_handler:?}"
)));
}
}

let known_builtin_names: HashSet<&str> = HashSet::from([
builtin_runner::OUTPUT_BUILTIN_NAME,
builtin_runner::HASH_BUILTIN_NAME,
builtin_runner::RANGE_CHECK_BUILTIN_NAME,
builtin_runner::SIGNATURE_BUILTIN_NAME,
builtin_runner::BITWISE_BUILTIN_NAME,
builtin_runner::EC_OP_BUILTIN_NAME,
builtin_runner::KECCAK_BUILTIN_NAME,
builtin_runner::POSEIDON_BUILTIN_NAME,
builtin_runner::SEGMENT_ARENA_BUILTIN_NAME,
]);

let execution_resources = self
.execute_txs_inner
.values()
.flat_map(|resources_vector| {
[&resources_vector.constant, &resources_vector.calldata_factor]
})
.chain(self.execute_syscalls.values());
let builtin_names =
execution_resources.flat_map(|resources| resources.builtin_instance_counter.keys());
for builtin_name in builtin_names {
if !(known_builtin_names.contains(builtin_name.as_str())) {
return Err(DeserializationError::custom(format!(
"ValidationError: unknown os resource {builtin_name}"
)));
}
}

Ok(())
}
/// Calculates the additional resources needed for the OS to run the given transaction;
/// i.e., the resources of the Starknet OS function `execute_transactions_inner`.
/// Also adds the resources needed for the fee transfer execution, performed in the end·
Expand Down Expand Up @@ -233,52 +301,8 @@ impl<'de> Deserialize<'de> for OsResources {

// Validations.

for tx_type in TransactionType::iter() {
if !os_resources.execute_txs_inner.contains_key(&tx_type) {
return Err(DeserializationError::custom(format!(
"ValidationError: os_resources.execute_tx_inner is missing transaction_type: \
{tx_type:?}"
)));
}
}

for syscall_handler in DeprecatedSyscallSelector::iter() {
if !os_resources.execute_syscalls.contains_key(&syscall_handler) {
return Err(DeserializationError::custom(format!(
"ValidationError: os_resources.execute_syscalls are missing syscall handler: \
{syscall_handler:?}"
)));
}
}

let known_builtin_names: HashSet<&str> = HashSet::from([
builtin_runner::OUTPUT_BUILTIN_NAME,
builtin_runner::HASH_BUILTIN_NAME,
builtin_runner::RANGE_CHECK_BUILTIN_NAME,
builtin_runner::SIGNATURE_BUILTIN_NAME,
builtin_runner::BITWISE_BUILTIN_NAME,
builtin_runner::EC_OP_BUILTIN_NAME,
builtin_runner::KECCAK_BUILTIN_NAME,
builtin_runner::POSEIDON_BUILTIN_NAME,
builtin_runner::SEGMENT_ARENA_BUILTIN_NAME,
]);

let execution_resources = os_resources
.execute_txs_inner
.values()
.flat_map(|resources_vector| {
[&resources_vector.constant, &resources_vector.calldata_factor]
})
.chain(os_resources.execute_syscalls.values());
let builtin_names =
execution_resources.flat_map(|resources| resources.builtin_instance_counter.keys());
for builtin_name in builtin_names {
if !(known_builtin_names.contains(builtin_name.as_str())) {
return Err(DeserializationError::custom(format!(
"ValidationError: unknown os resource {builtin_name}"
)));
}
}
#[cfg(not(any(feature = "testing", test)))]
validate(&os_resources);

Ok(os_resources)
}
Expand All @@ -299,6 +323,9 @@ impl<'de> Deserialize<'de> for OsResources {
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(try_from = "OSConstantsRawJSON")]
pub struct OSConstants {
validate_rounding_consts: ValidateRoundingConsts,

// Invariant: fixed keys.
gas_costs: IndexMap<String, u64>,
}

Expand Down Expand Up @@ -370,7 +397,9 @@ impl TryFrom<OSConstantsRawJSON> for OSConstants {

let gas_cost_whitelist: IndexSet<_> =
Self::ALLOWED_GAS_COST_NAMES.iter().copied().collect();
for (key, value) in raw_json_data.raw_json_file_as_dict {

let OSConstantsRawJSON { raw_json_file_as_dict, validate_rounding_consts } = raw_json_data;
for (key, value) in raw_json_file_as_dict {
if !gas_cost_whitelist.contains(key.as_str()) {
// Ignore non-whitelist consts.
continue;
Expand Down Expand Up @@ -413,7 +442,7 @@ impl TryFrom<OSConstantsRawJSON> for OSConstants {
}
}

let os_constants = OSConstants { gas_costs };
let os_constants = OSConstants { gas_costs, validate_rounding_consts };

// Skip validation in testing: to test validation run validate manually.
#[cfg(not(any(feature = "testing", test)))]
Expand All @@ -429,6 +458,8 @@ impl TryFrom<OSConstantsRawJSON> for OSConstants {
struct OSConstantsRawJSON {
#[serde(flatten)]
raw_json_file_as_dict: IndexMap<String, Value>,
#[serde(default)]
validate_rounding_consts: ValidateRoundingConsts,
}

#[derive(Debug, Error)]
Expand Down Expand Up @@ -463,3 +494,17 @@ pub struct ResourcesParams {
pub constant: ExecutionResources,
pub calldata_factor: ExecutionResources,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
struct ValidateRoundingConsts {
// Flooring factor for block number in validate mode.
pub validate_block_number_rounding: u64,
// Flooring factor for timestamp in validate mode.
pub validate_timestamp_rounding: u64,
}

impl Default for ValidateRoundingConsts {
fn default() -> Self {
Self { validate_block_number_rounding: 1, validate_timestamp_rounding: 1 }
}
}
31 changes: 31 additions & 0 deletions crates/blockifier/src/versioned_constants_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ fn test_successful_parsing() {
"entry_point_initial_budget": 4,
"step_gas_cost": 5
},
"validate_block_number_rounding": 111,
"validate_timestamp_rounding": 222,
"ignore the gas string": "GAS!",
"I look like a gas cost but my name is all wrong": 0
}"#;
Expand All @@ -32,6 +34,29 @@ fn test_successful_parsing() {
assert_eq!(versioned_constants.os_constants.gas_costs.len(), 3);
}

#[test]
fn test_default_values() {
let json_data = r#"
{
"invoke_tx_max_n_steps": 2,
"validate_max_n_steps": 1,
"os_constants": {},
"os_resources": {
"execute_syscalls":{},
"execute_txs_inner": {}
},
"vm_resource_fee_cost": {},
"max_recursion_depth": 2
}"#;
let versioned_constants: VersionedConstants = serde_json::from_str(json_data).unwrap();

assert_eq!(versioned_constants.get_validate_block_number_rounding(), 1);
assert_eq!(versioned_constants.get_validate_timestamp_rounding(), 1);

assert_eq!(versioned_constants.event_size_limit, EventSizeLimit::max());
assert_eq!(versioned_constants.l2_resource_gas_costs, L2ResourceGasCosts::default());
}

#[test]
fn test_string_inside_composed_field() {
let json_data = r#"
Expand All @@ -50,6 +75,12 @@ fn test_string_inside_composed_field() {
}

fn check_constants_serde_error(json_data: &str, expected_error_message: &str) {
let mut json_data_raw: IndexMap<String, Value> = serde_json::from_str(json_data).unwrap();
json_data_raw.insert("validate_block_number_rounding".to_string(), 0.into());
json_data_raw.insert("validate_timestamp_rounding".to_string(), 0.into());

let json_data = &serde_json::to_string(&json_data_raw).unwrap();

let error = serde_json::from_str::<OSConstants>(json_data).unwrap_err();
assert_eq!(error.to_string(), expected_error_message);
}
Expand Down

0 comments on commit 9c0c99a

Please sign in to comment.