Skip to content

Commit caac33d

Browse files
test(apollo_starknet_os_program): add OS constants test
1 parent 15ded16 commit caac33d

File tree

4 files changed

+222
-0
lines changed

4 files changed

+222
-0
lines changed

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/apollo_starknet_os_program/Cargo.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,9 @@ thiserror.workspace = true
2323
[build-dependencies]
2424
apollo_infra_utils.workspace = true
2525
serde_json.workspace = true
26+
27+
[dev-dependencies]
28+
apollo_infra_utils = { workspace = true, features = ["testing"] }
29+
blockifier.workspace = true
30+
starknet-types-core.workspace = true
31+
starknet_api.workspace = true
Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
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+
}

crates/apollo_starknet_os_program/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ use cairo_vm::types::program::Program;
66

77
use crate::program_hash::ProgramHash;
88

9+
#[cfg(test)]
10+
mod constants_test;
911
pub mod program_hash;
1012

1113
#[cfg(feature = "dump_source_files")]

0 commit comments

Comments
 (0)