diff --git a/Cargo.lock b/Cargo.lock index 5956675eb9..85357f624f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3033,9 +3033,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.110" +version = "1.0.113" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fbd975230bada99c8bb618e0c365c2eefa219158d5c6c29610fd09ff1833257" +checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79" dependencies = [ "itoa", "ryu", diff --git a/crates/blockifier/resources/versioned_constants.json b/crates/blockifier/resources/versioned_constants.json index 135a148773..61c4dee653 100644 --- a/crates/blockifier/resources/versioned_constants.json +++ b/crates/blockifier/resources/versioned_constants.json @@ -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": { @@ -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, diff --git a/crates/blockifier/src/execution/deprecated_syscalls/mod.rs b/crates/blockifier/src/execution/deprecated_syscalls/mod.rs index 4a11e057e8..8e338c78ee 100644 --- a/crates/blockifier/src/execution/deprecated_syscalls/mod.rs +++ b/crates/blockifier/src/execution/deprecated_syscalls/mod.rs @@ -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 }) @@ -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 }) diff --git a/crates/blockifier/src/execution/syscalls/hint_processor.rs b/crates/blockifier/src/execution/syscalls/hint_processor.rs index 8af903d3d1..f75f0d3145 100644 --- a/crates/blockifier/src/execution/syscalls/hint_processor.rs +++ b/crates/blockifier/src/execution/syscalls/hint_processor.rs @@ -504,13 +504,14 @@ impl<'a> SyscallHintProcessor<'a> { let versioned_constants = self.context.versioned_constants(); let block_data: Vec = 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), diff --git a/crates/blockifier/src/versioned_constants.rs b/crates/blockifier/src/versioned_constants.rs index 008c8f5931..d5db6c8b3b 100644 --- a/crates/blockifier/src/versioned_constants.rs +++ b/crates/blockifier/src/versioned_constants.rs @@ -35,15 +35,13 @@ static DEFAULT_CONSTANTS: Lazy = 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. @@ -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([ @@ -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. @@ -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. @@ -178,6 +194,58 @@ pub struct OsResources { } impl OsResources { + pub fn validate<'de, D: Deserializer<'de>>( + &self, + ) -> Result<(), >::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· @@ -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) } @@ -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, } @@ -370,7 +397,9 @@ impl TryFrom 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; @@ -413,7 +442,7 @@ impl TryFrom 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)))] @@ -429,6 +458,8 @@ impl TryFrom for OSConstants { struct OSConstantsRawJSON { #[serde(flatten)] raw_json_file_as_dict: IndexMap, + #[serde(default)] + validate_rounding_consts: ValidateRoundingConsts, } #[derive(Debug, Error)] @@ -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 } + } +} diff --git a/crates/blockifier/src/versioned_constants_test.rs b/crates/blockifier/src/versioned_constants_test.rs index 06ab6530ef..62cdedc108 100644 --- a/crates/blockifier/src/versioned_constants_test.rs +++ b/crates/blockifier/src/versioned_constants_test.rs @@ -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 }"#; @@ -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#" @@ -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 = 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::(json_data).unwrap_err(); assert_eq!(error.to_string(), expected_error_message); }