Skip to content

Commit 5db3b08

Browse files
test(apollo_starknet_os_program): OS constants test
1 parent 49a4272 commit 5db3b08

File tree

4 files changed

+239
-0
lines changed

4 files changed

+239
-0
lines changed

Cargo.lock

Lines changed: 3 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
@@ -19,3 +19,9 @@ serde_json.workspace = true
1919
[build-dependencies]
2020
apollo_infra_utils.workspace = true
2121
serde_json.workspace = true
22+
23+
[dev-dependencies]
24+
apollo_infra_utils = { workspace = true, features = ["testing"] }
25+
blockifier.workspace = true
26+
starknet-types-core.workspace = true
27+
starknet_api.workspace = true
Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
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::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 = match selector {
19+
SyscallSelector::CallContract => os_constants.gas_costs.syscalls.call_contract,
20+
SyscallSelector::Deploy => os_constants.gas_costs.syscalls.deploy,
21+
SyscallSelector::GetBlockHash => os_constants.gas_costs.syscalls.get_block_hash,
22+
SyscallSelector::GetExecutionInfo => os_constants.gas_costs.syscalls.get_execution_info,
23+
SyscallSelector::LibraryCall => os_constants.gas_costs.syscalls.library_call,
24+
SyscallSelector::ReplaceClass => os_constants.gas_costs.syscalls.replace_class,
25+
SyscallSelector::StorageRead => os_constants.gas_costs.syscalls.storage_read,
26+
SyscallSelector::StorageWrite => os_constants.gas_costs.syscalls.storage_write,
27+
SyscallSelector::GetClassHashAt => os_constants.gas_costs.syscalls.get_class_hash_at,
28+
SyscallSelector::EmitEvent => os_constants.gas_costs.syscalls.emit_event,
29+
SyscallSelector::SendMessageToL1 => os_constants.gas_costs.syscalls.send_message_to_l1,
30+
SyscallSelector::Secp256k1Add => os_constants.gas_costs.syscalls.secp256k1_add,
31+
SyscallSelector::Secp256k1GetPointFromX => {
32+
os_constants.gas_costs.syscalls.secp256k1_get_point_from_x
33+
}
34+
SyscallSelector::Secp256k1GetXy => os_constants.gas_costs.syscalls.secp256k1_get_xy,
35+
SyscallSelector::Secp256k1Mul => os_constants.gas_costs.syscalls.secp256k1_mul,
36+
SyscallSelector::Secp256k1New => os_constants.gas_costs.syscalls.secp256k1_new,
37+
SyscallSelector::Secp256r1Add => os_constants.gas_costs.syscalls.secp256r1_add,
38+
SyscallSelector::Secp256r1GetPointFromX => {
39+
os_constants.gas_costs.syscalls.secp256r1_get_point_from_x
40+
}
41+
SyscallSelector::Secp256r1GetXy => os_constants.gas_costs.syscalls.secp256r1_get_xy,
42+
SyscallSelector::Secp256r1Mul => os_constants.gas_costs.syscalls.secp256r1_mul,
43+
SyscallSelector::Secp256r1New => os_constants.gas_costs.syscalls.secp256r1_new,
44+
SyscallSelector::Keccak => os_constants.gas_costs.syscalls.keccak,
45+
SyscallSelector::KeccakRound => os_constants.gas_costs.syscalls.keccak_round,
46+
SyscallSelector::MetaTxV0 => os_constants.gas_costs.syscalls.meta_tx_v0,
47+
SyscallSelector::Sha256ProcessBlock => os_constants.gas_costs.syscalls.sha256_process_block,
48+
_ => {
49+
panic!("Unsupported syscall selector: {selector:?}");
50+
}
51+
};
52+
assert_eq!(syscall_cost.linear_syscall_cost(), 0, "Syscall {selector:?} has a linear cost.");
53+
syscall_cost.base_syscall_cost()
54+
}
55+
56+
fn quote_string(s: &str) -> String {
57+
format!("'{s}'")
58+
}
59+
60+
/// Create constants from a list of class hashes. Example:
61+
/// ```
62+
/// let expected = #"
63+
/// X_0 = 0x1;
64+
/// X_1 = 0x2;
65+
/// X_LEN = 2;
66+
/// "#;
67+
/// assert_eq!(stringify_class_hash_list("X", &[ClassHash(1), ClassHash(2)]), expected);
68+
/// ```
69+
fn stringify_class_hash_list(name: &str, class_hashes: &[ClassHash]) -> String {
70+
let strings: Vec<String> = class_hashes
71+
.iter()
72+
.enumerate()
73+
.map(|(i, class_hash)| {
74+
// If the line ends up longer than 100 chars, wrap the value in parenthesis, so the
75+
// formatter can split the lines.
76+
let line = format!("const {name}_{i} = {:#064x};", class_hash.0);
77+
if line.len() > 100 {
78+
format!("const {name}_{i} = ({:#064x});", class_hash.0)
79+
} else {
80+
line
81+
}
82+
})
83+
.chain(std::iter::once(format!("const {name}_LEN = {};", class_hashes.len())))
84+
.collect();
85+
strings.join("\n")
86+
}
87+
88+
fn generate_constants_file() -> String {
89+
let os_constants = &VersionedConstants::latest_constants().os_constants;
90+
91+
// Replace the template with the actual values.
92+
let unformatted = format!(
93+
include_str!("constants_template.txt"),
94+
NOP_ENTRY_POINT_OFFSET = os_constants.nop_entry_point_offset,
95+
ENTRY_POINT_TYPE_EXTERNAL = os_constants.entry_point_type_external,
96+
ENTRY_POINT_TYPE_L1_HANDLER = os_constants.entry_point_type_l1_handler,
97+
ENTRY_POINT_TYPE_CONSTRUCTOR = os_constants.entry_point_type_constructor,
98+
L1_HANDLER_VERSION = os_constants.l1_handler_version,
99+
L1_HANDLER_L2_GAS_MAX_AMOUNT = os_constants.l1_handler_max_amount_bounds.l2_gas.0,
100+
SIERRA_ARRAY_LEN_BOUND = os_constants.sierra_array_len_bound,
101+
CONSTRUCTOR_ENTRY_POINT_SELECTOR =
102+
selector_to_hex(&os_constants.constructor_entry_point_selector),
103+
EXECUTE_ENTRY_POINT_SELECTOR = selector_to_hex(&os_constants.execute_entry_point_selector),
104+
VALIDATE_ENTRY_POINT_SELECTOR =
105+
selector_to_hex(&os_constants.validate_entry_point_selector),
106+
VALIDATE_DECLARE_ENTRY_POINT_SELECTOR =
107+
selector_to_hex(&os_constants.validate_declare_entry_point_selector),
108+
VALIDATE_DEPLOY_ENTRY_POINT_SELECTOR =
109+
selector_to_hex(&os_constants.validate_deploy_entry_point_selector),
110+
TRANSFER_ENTRY_POINT_SELECTOR =
111+
selector_to_hex(&os_constants.transfer_entry_point_selector),
112+
DEFAULT_ENTRY_POINT_SELECTOR = selector_to_hex(&os_constants.default_entry_point_selector),
113+
BLOCK_HASH_CONTRACT_ADDRESS = contract_address_to_hex(
114+
&os_constants.os_contract_addresses.block_hash_contract_address()
115+
),
116+
ALIAS_CONTRACT_ADDRESS =
117+
contract_address_to_hex(&os_constants.os_contract_addresses.alias_contract_address()),
118+
RESERVED_CONTRACT_ADDRESS = contract_address_to_hex(
119+
&os_constants.os_contract_addresses.reserved_contract_address()
120+
),
121+
STORED_BLOCK_HASH_BUFFER = os_constants.stored_block_hash_buffer,
122+
STEP_GAS_COST = os_constants.gas_costs.base.step_gas_cost,
123+
RANGE_CHECK_GAS_COST = os_constants.gas_costs.builtins.range_check,
124+
RANGE_CHECK96_GAS_COST = os_constants.gas_costs.builtins.range_check96,
125+
KECCAK_BUILTIN_GAS_COST = os_constants.gas_costs.builtins.keccak,
126+
PEDERSEN_GAS_COST = os_constants.gas_costs.builtins.pedersen,
127+
BITWISE_BUILTIN_GAS_COST = os_constants.gas_costs.builtins.bitwise,
128+
ECOP_GAS_COST = os_constants.gas_costs.builtins.ecop,
129+
POSEIDON_GAS_COST = os_constants.gas_costs.builtins.poseidon,
130+
ADD_MOD_GAS_COST = os_constants.gas_costs.builtins.add_mod,
131+
MUL_MOD_GAS_COST = os_constants.gas_costs.builtins.mul_mod,
132+
ECDSA_GAS_COST = os_constants.gas_costs.builtins.ecdsa,
133+
MEMORY_HOLE_GAS_COST = os_constants.gas_costs.base.memory_hole_gas_cost,
134+
DEFAULT_INITIAL_GAS_COST = os_constants.default_initial_gas_cost,
135+
VALIDATE_MAX_SIERRA_GAS = os_constants.validate_max_sierra_gas,
136+
EXECUTE_MAX_SIERRA_GAS = os_constants.execute_max_sierra_gas,
137+
ENTRY_POINT_INITIAL_BUDGET = os_constants.entry_point_initial_budget,
138+
SYSCALL_BASE_GAS_COST = os_constants.gas_costs.base.syscall_base_gas_cost,
139+
CALL_CONTRACT_GAS_COST =
140+
base_only_syscall_cost(SyscallSelector::CallContract, os_constants),
141+
DEPLOY_GAS_COST = os_constants.gas_costs.syscalls.deploy.get_syscall_cost(0),
142+
DEPLOY_CALLDATA_FACTOR_GAS_COST =
143+
os_constants.gas_costs.syscalls.deploy.linear_syscall_cost(),
144+
GET_BLOCK_HASH_GAS_COST =
145+
base_only_syscall_cost(SyscallSelector::GetBlockHash, os_constants),
146+
GET_CLASS_HASH_AT_GAS_COST =
147+
base_only_syscall_cost(SyscallSelector::GetClassHashAt, os_constants),
148+
GET_EXECUTION_INFO_GAS_COST =
149+
base_only_syscall_cost(SyscallSelector::GetExecutionInfo, os_constants),
150+
LIBRARY_CALL_GAS_COST = base_only_syscall_cost(SyscallSelector::LibraryCall, os_constants),
151+
REPLACE_CLASS_GAS_COST =
152+
base_only_syscall_cost(SyscallSelector::ReplaceClass, os_constants),
153+
STORAGE_READ_GAS_COST = base_only_syscall_cost(SyscallSelector::StorageRead, os_constants),
154+
STORAGE_WRITE_GAS_COST =
155+
base_only_syscall_cost(SyscallSelector::StorageWrite, os_constants),
156+
EMIT_EVENT_GAS_COST = base_only_syscall_cost(SyscallSelector::EmitEvent, os_constants),
157+
SEND_MESSAGE_TO_L1_GAS_COST =
158+
base_only_syscall_cost(SyscallSelector::SendMessageToL1, os_constants),
159+
META_TX_V0_GAS_COST = os_constants.gas_costs.syscalls.meta_tx_v0.get_syscall_cost(0),
160+
META_TX_V0_CALLDATA_FACTOR_GAS_COST =
161+
os_constants.gas_costs.syscalls.meta_tx_v0.linear_syscall_cost(),
162+
SECP256K1_ADD_GAS_COST =
163+
base_only_syscall_cost(SyscallSelector::Secp256k1Add, os_constants),
164+
SECP256K1_GET_POINT_FROM_X_GAS_COST =
165+
base_only_syscall_cost(SyscallSelector::Secp256k1GetPointFromX, os_constants),
166+
SECP256K1_GET_XY_GAS_COST =
167+
base_only_syscall_cost(SyscallSelector::Secp256k1GetXy, os_constants),
168+
SECP256K1_MUL_GAS_COST =
169+
base_only_syscall_cost(SyscallSelector::Secp256k1Mul, os_constants),
170+
SECP256K1_NEW_GAS_COST =
171+
base_only_syscall_cost(SyscallSelector::Secp256k1New, os_constants),
172+
SECP256R1_ADD_GAS_COST =
173+
base_only_syscall_cost(SyscallSelector::Secp256r1Add, os_constants),
174+
SECP256R1_GET_POINT_FROM_X_GAS_COST =
175+
base_only_syscall_cost(SyscallSelector::Secp256r1GetPointFromX, os_constants),
176+
SECP256R1_GET_XY_GAS_COST =
177+
base_only_syscall_cost(SyscallSelector::Secp256r1GetXy, os_constants),
178+
SECP256R1_MUL_GAS_COST =
179+
base_only_syscall_cost(SyscallSelector::Secp256r1Mul, os_constants),
180+
SECP256R1_NEW_GAS_COST =
181+
base_only_syscall_cost(SyscallSelector::Secp256r1New, os_constants),
182+
KECCAK_GAS_COST = base_only_syscall_cost(SyscallSelector::Keccak, os_constants),
183+
KECCAK_ROUND_COST_GAS_COST =
184+
base_only_syscall_cost(SyscallSelector::KeccakRound, os_constants),
185+
SHA256_PROCESS_BLOCK_GAS_COST =
186+
base_only_syscall_cost(SyscallSelector::Sha256ProcessBlock, os_constants),
187+
ERROR_BLOCK_NUMBER_OUT_OF_RANGE =
188+
quote_string(&os_constants.error_block_number_out_of_range),
189+
ERROR_OUT_OF_GAS = quote_string(&os_constants.error_out_of_gas),
190+
ERROR_ENTRY_POINT_FAILED = quote_string(&os_constants.error_entry_point_failed),
191+
ERROR_ENTRY_POINT_NOT_FOUND = quote_string(&os_constants.error_entry_point_not_found),
192+
ERROR_INVALID_INPUT_LEN = quote_string(&os_constants.error_invalid_input_len),
193+
ERROR_INVALID_ARGUMENT = quote_string(&os_constants.error_invalid_argument),
194+
VALIDATED = quote_string(&os_constants.validated),
195+
L1_GAS = quote_string(&os_constants.l1_gas),
196+
L2_GAS = quote_string(&os_constants.l2_gas),
197+
L1_DATA_GAS = quote_string(&os_constants.l1_data_gas),
198+
L1_GAS_INDEX = os_constants.l1_gas_index,
199+
L2_GAS_INDEX = os_constants.l2_gas_index,
200+
L1_DATA_GAS_INDEX = os_constants.l1_data_gas_index,
201+
VALIDATE_BLOCK_NUMBER_ROUNDING =
202+
os_constants.validate_rounding_consts.validate_block_number_rounding,
203+
VALIDATE_TIMESTAMP_ROUNDING =
204+
os_constants.validate_rounding_consts.validate_timestamp_rounding,
205+
V1_BOUND_ACCOUNTS_CAIRO0 = stringify_class_hash_list(
206+
"V1_BOUND_ACCOUNTS_CAIRO0",
207+
&os_constants.v1_bound_accounts_cairo0
208+
),
209+
V1_BOUND_ACCOUNTS_CAIRO1 = stringify_class_hash_list(
210+
"V1_BOUND_ACCOUNTS_CAIRO1",
211+
&os_constants.v1_bound_accounts_cairo1
212+
),
213+
V1_BOUND_ACCOUNTS_MAX_TIP =
214+
format!("{:#?}", Felt::from(os_constants.v1_bound_accounts_max_tip)),
215+
DATA_GAS_ACCOUNTS =
216+
stringify_class_hash_list("DATA_GAS_ACCOUNTS", &os_constants.data_gas_accounts),
217+
);
218+
219+
// Format and return.
220+
cairo0_format(&unformatted)
221+
}
222+
223+
#[test]
224+
fn test_os_constants() {
225+
verify_cairo0_compiler_deps();
226+
assert_eq!(CONSTANTS_CONTENTS, generate_constants_file());
227+
}

crates/apollo_starknet_os_program/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ use std::collections::HashMap;
33
#[cfg(feature = "dump_source_files")]
44
use std::sync::LazyLock;
55

6+
#[cfg(test)]
7+
mod constants_test;
8+
69
#[cfg(feature = "dump_source_files")]
710
pub static CAIRO_FILES_MAP: LazyLock<HashMap<String, String>> = LazyLock::new(|| {
811
serde_json::from_str(include_str!(concat!(env!("OUT_DIR"), "/cairo_files_map.json")))

0 commit comments

Comments
 (0)