Skip to content

Commit e158327

Browse files
starknet_os_flow_tests: make fuzz test Cairo1 scenarios an enum
1 parent f11c982 commit e158327

4 files changed

Lines changed: 320 additions & 283 deletions

File tree

crates/blockifier_test_utils/resources/feature_contracts/cairo1/fuzz_revert.cairo

Lines changed: 155 additions & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ trait IOrchestrator<TContractState> {
33
fn pop_front(ref self: TContractState) -> felt252;
44
fn get_index(ref self: TContractState) -> felt252;
55
fn set_index(ref self: TContractState, index: felt252);
6+
fn should_fail_invalid_scenario_panic_message(ref self: TContractState) -> felt252;
67
fn should_fail_undeployed_panic_message(ref self: TContractState) -> felt252;
78
fn should_fail_call_no_entrypoint_panic_message(ref self: TContractState) -> felt252;
89
fn should_fail_libcall_no_entrypoint_panic_message(ref self: TContractState) -> felt252;
@@ -20,25 +21,51 @@ mod FuzzRevertContract {
2021
use starknet::info::SyscallResultTrait;
2122

2223
// Scenarios.
23-
// The RETURN scenario *must* be zero, as the zero value also indicates end of scenario stream
24-
// (when cairo0 fuzz contracts get the None value from the orchestrator).
25-
// TODO(Dori): Convert to enum.
26-
const SCENARIO_RETURN: felt252 = 0;
27-
const SCENARIO_CALL: felt252 = 1;
28-
const SCENARIO_LIBRARY_CALL: felt252 = 2;
29-
const SCENARIO_WRITE: felt252 = 3;
30-
const SCENARIO_REPLACE_CLASS: felt252 = 4;
31-
const SCENARIO_DEPLOY: felt252 = 5;
32-
const SCENARIO_PANIC: felt252 = 6;
33-
const SCENARIO_INCREMENT_COUNTER: felt252 = 7;
34-
const SCENARIO_SEND_MESSAGE: felt252 = 8;
35-
const SCENARIO_DEPLOY_NON_EXISTING: felt252 = 9;
36-
const SCENARIO_LIBRARY_CALL_NON_EXISTING: felt252 = 10;
37-
const SCENARIO_SHA256: felt252 = 11;
38-
const SCENARIO_KECCAK: felt252 = 12;
39-
const SCENARIO_CALL_UNDEPLOYED: felt252 = 13;
40-
const SCENARIO_CALL_NON_EXISTING_ENTRY_POINT: felt252 = 14;
41-
const SCENARIO_LIBRARY_CALL_NON_EXISTING_ENTRY_POINT: felt252 = 15;
24+
enum Scenario {
25+
Return,
26+
Call,
27+
LibraryCall,
28+
Write,
29+
ReplaceClass,
30+
Deploy,
31+
Panic,
32+
IncrementCounter,
33+
SendMessage,
34+
DeployNonExisting,
35+
LibraryCallNonExisting,
36+
Sha256,
37+
Keccak,
38+
CallUndeployed,
39+
CallNonExistingEntryPoint,
40+
LibraryCallNonExistingEntryPoint,
41+
}
42+
43+
impl FeltTryIntoScenario of TryInto<felt252, Scenario> {
44+
fn try_into(self: felt252) -> Option<Scenario> {
45+
match self {
46+
// The RETURN scenario *must* be zero, as the zero value also indicates end of
47+
// scenario stream (when cairo0 fuzz contracts get the None value from the
48+
// orchestrator).
49+
0 => Some(Scenario::Return),
50+
1 => Some(Scenario::Call),
51+
2 => Some(Scenario::LibraryCall),
52+
3 => Some(Scenario::Write),
53+
4 => Some(Scenario::ReplaceClass),
54+
5 => Some(Scenario::Deploy),
55+
6 => Some(Scenario::Panic),
56+
7 => Some(Scenario::IncrementCounter),
57+
8 => Some(Scenario::SendMessage),
58+
9 => Some(Scenario::DeployNonExisting),
59+
10 => Some(Scenario::LibraryCallNonExisting),
60+
11 => Some(Scenario::Sha256),
61+
12 => Some(Scenario::Keccak),
62+
13 => Some(Scenario::CallUndeployed),
63+
14 => Some(Scenario::CallNonExistingEntryPoint),
64+
15 => Some(Scenario::LibraryCallNonExistingEntryPoint),
65+
_ => None,
66+
}
67+
}
68+
}
4269

4370
#[storage]
4471
struct Storage {
@@ -122,128 +149,116 @@ mod FuzzRevertContract {
122149
let orchestrator = self.orchestrator();
123150

124151
// Get next scenario; None means done.
125-
let scenario = orchestrator.pop_front();
126-
127-
if scenario == SCENARIO_RETURN {
128-
return;
129-
}
130-
131-
if scenario == SCENARIO_CALL {
132-
let address: ContractAddress = orchestrator.pop_front().try_into().unwrap();
133-
let selector = orchestrator.pop_front();
134-
let should_unwrap: bool = orchestrator.pop_front() != 0;
135-
let result = syscalls::call_contract_syscall(address, selector, array![].span());
136-
self.handle_error_catch(result, should_unwrap);
137-
}
138-
139-
if scenario == SCENARIO_LIBRARY_CALL {
140-
let class_hash: ClassHash = orchestrator.pop_front().try_into().unwrap();
141-
let selector = orchestrator.pop_front();
142-
let should_unwrap: bool = orchestrator.pop_front() != 0;
143-
let result = syscalls::library_call_syscall(class_hash, selector, array![].span());
144-
self.handle_error_catch(result, should_unwrap);
145-
}
146-
147-
if scenario == SCENARIO_WRITE {
148-
let key: StorageAddress = orchestrator.pop_front().try_into().unwrap();
149-
let value = orchestrator.pop_front();
150-
let address_domain = 0;
151-
syscalls::storage_write_syscall(address_domain, key, value).unwrap_syscall();
152-
}
153-
154-
if scenario == SCENARIO_REPLACE_CLASS {
155-
let class_hash: ClassHash = orchestrator.pop_front().try_into().unwrap();
156-
syscalls::replace_class_syscall(class_hash).unwrap_syscall();
157-
}
158-
159-
if scenario == SCENARIO_DEPLOY {
160-
// The class hash is assumed to be a fuzz test class hash.
161-
// Deploy it with a non-trivial orchestrator address.
162-
let class_hash: ClassHash = orchestrator.pop_front().try_into().unwrap();
163-
let salt = orchestrator.pop_front();
164-
let deploy_from_zero: bool = true;
165-
let ctor_calldata = array![self.orchestrator_address.read().into()];
166-
// Deploy errors cannot be caught. Just unwrap the syscall.
167-
syscalls::deploy_syscall(class_hash, salt, ctor_calldata.span(), deploy_from_zero)
168-
.unwrap_syscall();
169-
}
170-
171-
if scenario == SCENARIO_PANIC {
172-
// Panic message is part of the scenario data.
173-
let message = orchestrator.pop_front();
174-
panic(array![orchestrator.get_index(), message]);
175-
}
176-
177-
if scenario == SCENARIO_INCREMENT_COUNTER {
178-
let value = self.counter.read();
179-
self.counter.write(value + 1);
180-
}
181-
182-
if scenario == SCENARIO_SEND_MESSAGE {
183-
let payload = array![orchestrator.pop_front()];
184-
syscalls::send_message_to_l1_syscall(0xadd1, payload.span()).unwrap_syscall();
185-
}
186-
187-
if scenario == SCENARIO_DEPLOY_NON_EXISTING {
188-
let class_hash: ClassHash = 0xde6107000c1.try_into().unwrap();
189-
let salt = 0;
190-
let deploy_from_zero: bool = true;
191-
// Unrecoverable error (we do not prove class hashes do not exist), no option to catch
192-
// error.
193-
syscalls::deploy_syscall(class_hash, salt, array![].span(), deploy_from_zero)
194-
.unwrap_syscall();
195-
}
196-
197-
if scenario == SCENARIO_LIBRARY_CALL_NON_EXISTING {
198-
let class_hash: ClassHash = 0x11bca11000c1.try_into().unwrap();
199-
// Unrecoverable error (we do not prove class hashes do not exist), no option to catch
200-
// error.
201-
syscalls::library_call_syscall(class_hash, 0, array![].span()).unwrap_syscall();
202-
}
203-
204-
if scenario == SCENARIO_SHA256 {
205-
let preimage: u32 = orchestrator.pop_front().try_into().unwrap();
206-
compute_sha256_u32_array(array![preimage], 0, 0);
207-
}
208-
209-
if scenario == SCENARIO_KECCAK {
210-
let preimage: u128 = orchestrator.pop_front().try_into().unwrap();
211-
let mut input: Array::<u256> = Default::default();
212-
input.append(u256 { low: preimage, high: preimage });
213-
keccak::keccak_u256s_le_inputs(input.span());
214-
}
215-
216-
if scenario == SCENARIO_CALL_UNDEPLOYED {
217-
let address: ContractAddress = orchestrator.pop_front().try_into().unwrap();
218-
let selector = orchestrator.pop_front();
219-
let _should_unwrap = orchestrator.pop_front();
220-
// Calling an undeployed contract should be an uncatchable fail.
221-
syscalls::call_contract_syscall(address, selector, array![].span()).unwrap_err();
222-
panic_with_felt252(orchestrator.should_fail_undeployed_panic_message());
223-
}
224-
225-
if scenario == SCENARIO_CALL_NON_EXISTING_ENTRY_POINT {
226-
let address: ContractAddress = orchestrator.pop_front().try_into().unwrap();
227-
let selector = orchestrator.pop_front();
228-
let should_unwrap = orchestrator.pop_front();
229-
self
230-
.handle_syscall_immediate_failure(
231-
syscalls::call_contract_syscall(address, selector, array![].span()),
232-
orchestrator.should_fail_call_no_entrypoint_panic_message(),
233-
should_unwrap != 0
234-
);
235-
}
152+
let scenario: Scenario = match orchestrator.pop_front().try_into() {
153+
Some(scenario) => scenario,
154+
None => panic_with_felt252(orchestrator.should_fail_invalid_scenario_panic_message()),
155+
};
236156

237-
if scenario == SCENARIO_LIBRARY_CALL_NON_EXISTING_ENTRY_POINT {
238-
let class_hash: ClassHash = orchestrator.pop_front().try_into().unwrap();
239-
let selector = orchestrator.pop_front();
240-
let should_unwrap = orchestrator.pop_front();
241-
self
242-
.handle_syscall_immediate_failure(
243-
syscalls::library_call_syscall(class_hash, selector, array![].span()),
244-
orchestrator.should_fail_libcall_no_entrypoint_panic_message(),
245-
should_unwrap != 0
246-
);
157+
match scenario {
158+
Scenario::Return => { return; },
159+
Scenario::Call => {
160+
let address: ContractAddress = orchestrator.pop_front().try_into().unwrap();
161+
let selector = orchestrator.pop_front();
162+
let should_unwrap: bool = orchestrator.pop_front() != 0;
163+
let result = syscalls::call_contract_syscall(address, selector, array![].span());
164+
self.handle_error_catch(result, should_unwrap);
165+
},
166+
Scenario::LibraryCall => {
167+
let class_hash: ClassHash = orchestrator.pop_front().try_into().unwrap();
168+
let selector = orchestrator.pop_front();
169+
let should_unwrap: bool = orchestrator.pop_front() != 0;
170+
let result = syscalls::library_call_syscall(class_hash, selector, array![].span());
171+
self.handle_error_catch(result, should_unwrap);
172+
},
173+
Scenario::Write => {
174+
let key: StorageAddress = orchestrator.pop_front().try_into().unwrap();
175+
let value = orchestrator.pop_front();
176+
let address_domain = 0;
177+
syscalls::storage_write_syscall(address_domain, key, value).unwrap_syscall();
178+
},
179+
Scenario::ReplaceClass => {
180+
let class_hash: ClassHash = orchestrator.pop_front().try_into().unwrap();
181+
syscalls::replace_class_syscall(class_hash).unwrap_syscall();
182+
},
183+
Scenario::Deploy => {
184+
// The class hash is assumed to be a fuzz test class hash.
185+
// Deploy it with a non-trivial orchestrator address.
186+
let class_hash: ClassHash = orchestrator.pop_front().try_into().unwrap();
187+
let salt = orchestrator.pop_front();
188+
let deploy_from_zero: bool = true;
189+
let ctor_calldata = array![self.orchestrator_address.read().into()];
190+
// Deploy errors cannot be caught. Just unwrap the syscall.
191+
syscalls::deploy_syscall(class_hash, salt, ctor_calldata.span(), deploy_from_zero)
192+
.unwrap_syscall();
193+
},
194+
Scenario::Panic => {
195+
// Panic message is part of the scenario data.
196+
let message = orchestrator.pop_front();
197+
panic(array![orchestrator.get_index(), message]);
198+
},
199+
Scenario::IncrementCounter => {
200+
let value = self.counter.read();
201+
self.counter.write(value + 1);
202+
},
203+
Scenario::SendMessage => {
204+
let payload = array![orchestrator.pop_front()];
205+
syscalls::send_message_to_l1_syscall(0xadd1, payload.span()).unwrap_syscall();
206+
},
207+
Scenario::DeployNonExisting => {
208+
let class_hash: ClassHash = 0xde6107000c1.try_into().unwrap();
209+
let salt = 0;
210+
let deploy_from_zero: bool = true;
211+
// Unrecoverable error (we do not prove class hashes do not exist), no option to
212+
// catch error.
213+
syscalls::deploy_syscall(class_hash, salt, array![].span(), deploy_from_zero)
214+
.unwrap_syscall();
215+
},
216+
Scenario::LibraryCallNonExisting => {
217+
let class_hash: ClassHash = 0x11bca11000c1.try_into().unwrap();
218+
// Unrecoverable error (we do not prove class hashes do not exist), no option to
219+
// catch error.
220+
syscalls::library_call_syscall(class_hash, 0, array![].span()).unwrap_syscall();
221+
},
222+
Scenario::Sha256 => {
223+
let preimage: u32 = orchestrator.pop_front().try_into().unwrap();
224+
compute_sha256_u32_array(array![preimage], 0, 0);
225+
},
226+
Scenario::Keccak => {
227+
let preimage: u128 = orchestrator.pop_front().try_into().unwrap();
228+
let mut input: Array::<u256> = Default::default();
229+
input.append(u256 { low: preimage, high: preimage });
230+
keccak::keccak_u256s_le_inputs(input.span());
231+
},
232+
Scenario::CallUndeployed => {
233+
let address: ContractAddress = orchestrator.pop_front().try_into().unwrap();
234+
let selector = orchestrator.pop_front();
235+
let _should_unwrap = orchestrator.pop_front();
236+
// Calling an undeployed contract should be an uncatchable fail.
237+
syscalls::call_contract_syscall(address, selector, array![].span()).unwrap_err();
238+
panic_with_felt252(orchestrator.should_fail_undeployed_panic_message());
239+
},
240+
Scenario::CallNonExistingEntryPoint => {
241+
let address: ContractAddress = orchestrator.pop_front().try_into().unwrap();
242+
let selector = orchestrator.pop_front();
243+
let should_unwrap = orchestrator.pop_front();
244+
self
245+
.handle_syscall_immediate_failure(
246+
syscalls::call_contract_syscall(address, selector, array![].span()),
247+
orchestrator.should_fail_call_no_entrypoint_panic_message(),
248+
should_unwrap != 0
249+
);
250+
},
251+
Scenario::LibraryCallNonExistingEntryPoint => {
252+
let class_hash: ClassHash = orchestrator.pop_front().try_into().unwrap();
253+
let selector = orchestrator.pop_front();
254+
let should_unwrap = orchestrator.pop_front();
255+
self
256+
.handle_syscall_immediate_failure(
257+
syscalls::library_call_syscall(class_hash, selector, array![].span()),
258+
orchestrator.should_fail_libcall_no_entrypoint_panic_message(),
259+
should_unwrap != 0
260+
);
261+
},
247262
}
248263

249264
// Unless explicitly stated otherwise, the next operation should be in the current call

0 commit comments

Comments
 (0)