|
| 1 | +use apollo_infra_utils::cairo0_compiler::{cairo0_format, verify_cairo0_compiler_deps}; |
| 2 | +use blockifier::blockifier_versioned_constants::{OsConstants, VersionedConstants}; |
| 3 | +use blockifier::execution::syscalls::vm_syscall_utils::SyscallSelector; |
| 4 | +use starknet_api::core::{ClassHash, ContractAddress, EntryPointSelector}; |
| 5 | +use starknet_types_core::felt::Felt; |
| 6 | + |
| 7 | +const CONSTANTS_CONTENTS: &str = include_str!("cairo/starkware/starknet/core/os/constants.cairo"); |
| 8 | + |
| 9 | +fn selector_to_hex(selector: &EntryPointSelector) -> String { |
| 10 | + format!("{:#?}", selector.0) |
| 11 | +} |
| 12 | + |
| 13 | +fn contract_address_to_hex(address: &ContractAddress) -> String { |
| 14 | + format!("{:#?}", address.0.key()) |
| 15 | +} |
| 16 | + |
| 17 | +fn base_only_syscall_cost(selector: SyscallSelector, os_constants: &OsConstants) -> u64 { |
| 18 | + let syscall_cost = |
| 19 | + os_constants.gas_costs.syscalls.get_syscall_gas_cost(&selector).unwrap_or_else(|error| { |
| 20 | + panic!("Selector {selector:?} should have a gas cost. Error: {error}") |
| 21 | + }); |
| 22 | + assert_eq!(syscall_cost.linear_syscall_cost(), 0, "Syscall {selector:?} has a linear cost."); |
| 23 | + syscall_cost.base_syscall_cost() |
| 24 | +} |
| 25 | + |
| 26 | +fn quote_string(s: &str) -> String { |
| 27 | + format!("'{s}'") |
| 28 | +} |
| 29 | + |
| 30 | +/// Create constants from a list of class hashes. Example: |
| 31 | +/// ``` |
| 32 | +/// let expected = #" |
| 33 | +/// X_0 = 0x1; |
| 34 | +/// X_1 = 0x2; |
| 35 | +/// X_LEN = 2; |
| 36 | +/// "#; |
| 37 | +/// assert_eq!(stringify_class_hash_list("X", &[ClassHash(1), ClassHash(2)]), expected); |
| 38 | +/// ``` |
| 39 | +fn stringify_class_hash_list(name: &str, class_hashes: &[ClassHash]) -> String { |
| 40 | + class_hashes |
| 41 | + .iter() |
| 42 | + .enumerate() |
| 43 | + .map(|(i, class_hash)| { |
| 44 | + // If the line ends up longer than 100 chars, wrap the value in parenthesis, so the |
| 45 | + // formatter can split the lines. |
| 46 | + let line = format!("const {name}_{i} = {:#064x};", class_hash.0); |
| 47 | + if line.len() > 100 { |
| 48 | + format!("const {name}_{i} = ({:#064x});", class_hash.0) |
| 49 | + } else { |
| 50 | + line |
| 51 | + } |
| 52 | + }) |
| 53 | + .chain(std::iter::once(format!("const {name}_LEN = {};", class_hashes.len()))) |
| 54 | + .collect::<Vec<String>>() |
| 55 | + .join("\n") |
| 56 | +} |
| 57 | + |
| 58 | +fn generate_constants_file() -> String { |
| 59 | + let os_constants = &VersionedConstants::latest_constants().os_constants; |
| 60 | + |
| 61 | + // Replace the template with the actual values. |
| 62 | + let unformatted = format!( |
| 63 | + include_str!("cairo/starkware/starknet/core/os/constants_template.txt"), |
| 64 | + // Miscellaneous constants. |
| 65 | + NOP_ENTRY_POINT_OFFSET = os_constants.nop_entry_point_offset, |
| 66 | + STORED_BLOCK_HASH_BUFFER = os_constants.stored_block_hash_buffer, |
| 67 | + L1_HANDLER_VERSION = os_constants.l1_handler_version, |
| 68 | + L1_HANDLER_L2_GAS_MAX_AMOUNT = os_constants.l1_handler_max_amount_bounds.l2_gas.0, |
| 69 | + SIERRA_ARRAY_LEN_BOUND = os_constants.sierra_array_len_bound, |
| 70 | + // Entry point types. |
| 71 | + ENTRY_POINT_TYPE_EXTERNAL = os_constants.entry_point_type_external, |
| 72 | + ENTRY_POINT_TYPE_L1_HANDLER = os_constants.entry_point_type_l1_handler, |
| 73 | + ENTRY_POINT_TYPE_CONSTRUCTOR = os_constants.entry_point_type_constructor, |
| 74 | + // Entry point selectors. |
| 75 | + CONSTRUCTOR_ENTRY_POINT_SELECTOR = |
| 76 | + selector_to_hex(&os_constants.constructor_entry_point_selector), |
| 77 | + EXECUTE_ENTRY_POINT_SELECTOR = selector_to_hex(&os_constants.execute_entry_point_selector), |
| 78 | + VALIDATE_ENTRY_POINT_SELECTOR = |
| 79 | + selector_to_hex(&os_constants.validate_entry_point_selector), |
| 80 | + VALIDATE_DECLARE_ENTRY_POINT_SELECTOR = |
| 81 | + selector_to_hex(&os_constants.validate_declare_entry_point_selector), |
| 82 | + VALIDATE_DEPLOY_ENTRY_POINT_SELECTOR = |
| 83 | + selector_to_hex(&os_constants.validate_deploy_entry_point_selector), |
| 84 | + TRANSFER_ENTRY_POINT_SELECTOR = |
| 85 | + selector_to_hex(&os_constants.transfer_entry_point_selector), |
| 86 | + DEFAULT_ENTRY_POINT_SELECTOR = selector_to_hex(&os_constants.default_entry_point_selector), |
| 87 | + // OS addresses. |
| 88 | + BLOCK_HASH_CONTRACT_ADDRESS = contract_address_to_hex( |
| 89 | + &os_constants.os_contract_addresses.block_hash_contract_address() |
| 90 | + ), |
| 91 | + ALIAS_CONTRACT_ADDRESS = |
| 92 | + contract_address_to_hex(&os_constants.os_contract_addresses.alias_contract_address()), |
| 93 | + RESERVED_CONTRACT_ADDRESS = contract_address_to_hex( |
| 94 | + &os_constants.os_contract_addresses.reserved_contract_address() |
| 95 | + ), |
| 96 | + // Base costs. |
| 97 | + STEP_GAS_COST = os_constants.gas_costs.base.step_gas_cost, |
| 98 | + MEMORY_HOLE_GAS_COST = os_constants.gas_costs.base.memory_hole_gas_cost, |
| 99 | + // Builtin costs. |
| 100 | + RANGE_CHECK_GAS_COST = os_constants.gas_costs.builtins.range_check, |
| 101 | + RANGE_CHECK96_GAS_COST = os_constants.gas_costs.builtins.range_check96, |
| 102 | + KECCAK_BUILTIN_GAS_COST = os_constants.gas_costs.builtins.keccak, |
| 103 | + PEDERSEN_GAS_COST = os_constants.gas_costs.builtins.pedersen, |
| 104 | + BITWISE_BUILTIN_GAS_COST = os_constants.gas_costs.builtins.bitwise, |
| 105 | + ECOP_GAS_COST = os_constants.gas_costs.builtins.ecop, |
| 106 | + POSEIDON_GAS_COST = os_constants.gas_costs.builtins.poseidon, |
| 107 | + ADD_MOD_GAS_COST = os_constants.gas_costs.builtins.add_mod, |
| 108 | + MUL_MOD_GAS_COST = os_constants.gas_costs.builtins.mul_mod, |
| 109 | + ECDSA_GAS_COST = os_constants.gas_costs.builtins.ecdsa, |
| 110 | + // Initial costs and gas limits. |
| 111 | + DEFAULT_INITIAL_GAS_COST = os_constants.default_initial_gas_cost, |
| 112 | + VALIDATE_MAX_SIERRA_GAS = os_constants.validate_max_sierra_gas, |
| 113 | + EXECUTE_MAX_SIERRA_GAS = os_constants.execute_max_sierra_gas, |
| 114 | + ENTRY_POINT_INITIAL_BUDGET = os_constants.entry_point_initial_budget, |
| 115 | + // Syscall costs. |
| 116 | + // Costs without a linear factor use `base_only_syscall_cost`; costs with a linear factor |
| 117 | + // use `get_syscall_cost(0)` for the base cost (0 linear factor), and `linear_syscall_cost` |
| 118 | + // for the linear factor. |
| 119 | + SYSCALL_BASE_GAS_COST = os_constants.gas_costs.base.syscall_base_gas_cost, |
| 120 | + CALL_CONTRACT_GAS_COST = |
| 121 | + base_only_syscall_cost(SyscallSelector::CallContract, os_constants), |
| 122 | + DEPLOY_GAS_COST = os_constants.gas_costs.syscalls.deploy.get_syscall_cost(0), |
| 123 | + DEPLOY_CALLDATA_FACTOR_GAS_COST = |
| 124 | + os_constants.gas_costs.syscalls.deploy.linear_syscall_cost(), |
| 125 | + GET_BLOCK_HASH_GAS_COST = |
| 126 | + base_only_syscall_cost(SyscallSelector::GetBlockHash, os_constants), |
| 127 | + GET_CLASS_HASH_AT_GAS_COST = |
| 128 | + base_only_syscall_cost(SyscallSelector::GetClassHashAt, os_constants), |
| 129 | + GET_EXECUTION_INFO_GAS_COST = |
| 130 | + base_only_syscall_cost(SyscallSelector::GetExecutionInfo, os_constants), |
| 131 | + LIBRARY_CALL_GAS_COST = base_only_syscall_cost(SyscallSelector::LibraryCall, os_constants), |
| 132 | + REPLACE_CLASS_GAS_COST = |
| 133 | + base_only_syscall_cost(SyscallSelector::ReplaceClass, os_constants), |
| 134 | + STORAGE_READ_GAS_COST = base_only_syscall_cost(SyscallSelector::StorageRead, os_constants), |
| 135 | + STORAGE_WRITE_GAS_COST = |
| 136 | + base_only_syscall_cost(SyscallSelector::StorageWrite, os_constants), |
| 137 | + EMIT_EVENT_GAS_COST = base_only_syscall_cost(SyscallSelector::EmitEvent, os_constants), |
| 138 | + SEND_MESSAGE_TO_L1_GAS_COST = |
| 139 | + base_only_syscall_cost(SyscallSelector::SendMessageToL1, os_constants), |
| 140 | + META_TX_V0_GAS_COST = os_constants.gas_costs.syscalls.meta_tx_v0.get_syscall_cost(0), |
| 141 | + META_TX_V0_CALLDATA_FACTOR_GAS_COST = |
| 142 | + os_constants.gas_costs.syscalls.meta_tx_v0.linear_syscall_cost(), |
| 143 | + SECP256K1_ADD_GAS_COST = |
| 144 | + base_only_syscall_cost(SyscallSelector::Secp256k1Add, os_constants), |
| 145 | + SECP256K1_GET_POINT_FROM_X_GAS_COST = |
| 146 | + base_only_syscall_cost(SyscallSelector::Secp256k1GetPointFromX, os_constants), |
| 147 | + SECP256K1_GET_XY_GAS_COST = |
| 148 | + base_only_syscall_cost(SyscallSelector::Secp256k1GetXy, os_constants), |
| 149 | + SECP256K1_MUL_GAS_COST = |
| 150 | + base_only_syscall_cost(SyscallSelector::Secp256k1Mul, os_constants), |
| 151 | + SECP256K1_NEW_GAS_COST = |
| 152 | + base_only_syscall_cost(SyscallSelector::Secp256k1New, os_constants), |
| 153 | + SECP256R1_ADD_GAS_COST = |
| 154 | + base_only_syscall_cost(SyscallSelector::Secp256r1Add, os_constants), |
| 155 | + SECP256R1_GET_POINT_FROM_X_GAS_COST = |
| 156 | + base_only_syscall_cost(SyscallSelector::Secp256r1GetPointFromX, os_constants), |
| 157 | + SECP256R1_GET_XY_GAS_COST = |
| 158 | + base_only_syscall_cost(SyscallSelector::Secp256r1GetXy, os_constants), |
| 159 | + SECP256R1_MUL_GAS_COST = |
| 160 | + base_only_syscall_cost(SyscallSelector::Secp256r1Mul, os_constants), |
| 161 | + SECP256R1_NEW_GAS_COST = |
| 162 | + base_only_syscall_cost(SyscallSelector::Secp256r1New, os_constants), |
| 163 | + KECCAK_GAS_COST = base_only_syscall_cost(SyscallSelector::Keccak, os_constants), |
| 164 | + KECCAK_ROUND_COST_GAS_COST = |
| 165 | + base_only_syscall_cost(SyscallSelector::KeccakRound, os_constants), |
| 166 | + SHA256_PROCESS_BLOCK_GAS_COST = |
| 167 | + base_only_syscall_cost(SyscallSelector::Sha256ProcessBlock, os_constants), |
| 168 | + // Short-strings. |
| 169 | + ERROR_BLOCK_NUMBER_OUT_OF_RANGE = |
| 170 | + quote_string(&os_constants.error_block_number_out_of_range), |
| 171 | + ERROR_OUT_OF_GAS = quote_string(&os_constants.error_out_of_gas), |
| 172 | + ERROR_ENTRY_POINT_FAILED = quote_string(&os_constants.error_entry_point_failed), |
| 173 | + ERROR_ENTRY_POINT_NOT_FOUND = quote_string(&os_constants.error_entry_point_not_found), |
| 174 | + ERROR_INVALID_INPUT_LEN = quote_string(&os_constants.error_invalid_input_len), |
| 175 | + ERROR_INVALID_ARGUMENT = quote_string(&os_constants.error_invalid_argument), |
| 176 | + VALIDATED = quote_string(&os_constants.validated), |
| 177 | + // Resource bounds. |
| 178 | + L1_GAS = quote_string(&os_constants.l1_gas), |
| 179 | + L2_GAS = quote_string(&os_constants.l2_gas), |
| 180 | + L1_DATA_GAS = quote_string(&os_constants.l1_data_gas), |
| 181 | + L1_GAS_INDEX = os_constants.l1_gas_index, |
| 182 | + L2_GAS_INDEX = os_constants.l2_gas_index, |
| 183 | + L1_DATA_GAS_INDEX = os_constants.l1_data_gas_index, |
| 184 | + // Syscall rounding. |
| 185 | + VALIDATE_BLOCK_NUMBER_ROUNDING = |
| 186 | + os_constants.validate_rounding_consts.validate_block_number_rounding, |
| 187 | + VALIDATE_TIMESTAMP_ROUNDING = |
| 188 | + os_constants.validate_rounding_consts.validate_timestamp_rounding, |
| 189 | + // Backward compatibility accounts. |
| 190 | + V1_BOUND_ACCOUNTS_CAIRO0 = stringify_class_hash_list( |
| 191 | + "V1_BOUND_ACCOUNTS_CAIRO0", |
| 192 | + &os_constants.v1_bound_accounts_cairo0 |
| 193 | + ), |
| 194 | + V1_BOUND_ACCOUNTS_CAIRO1 = stringify_class_hash_list( |
| 195 | + "V1_BOUND_ACCOUNTS_CAIRO1", |
| 196 | + &os_constants.v1_bound_accounts_cairo1 |
| 197 | + ), |
| 198 | + V1_BOUND_ACCOUNTS_MAX_TIP = |
| 199 | + format!("{:#?}", Felt::from(os_constants.v1_bound_accounts_max_tip)), |
| 200 | + DATA_GAS_ACCOUNTS = |
| 201 | + stringify_class_hash_list("DATA_GAS_ACCOUNTS", &os_constants.data_gas_accounts), |
| 202 | + ); |
| 203 | + |
| 204 | + // Format and return. |
| 205 | + cairo0_format(&unformatted) |
| 206 | +} |
| 207 | + |
| 208 | +#[test] |
| 209 | +fn test_os_constants() { |
| 210 | + verify_cairo0_compiler_deps(); |
| 211 | + assert_eq!(CONSTANTS_CONTENTS, generate_constants_file()); |
| 212 | +} |
0 commit comments