Skip to content

Commit f5edab0

Browse files
starknet_os_flow_tests: move deployed fuzz test map to be static
1 parent 787adf3 commit f5edab0

1 file changed

Lines changed: 63 additions & 45 deletions

File tree

crates/starknet_os_flow_tests/src/fuzz_tests.rs

Lines changed: 63 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,15 @@ static FUZZ_ADDRESS_CAIRO0_A: LazyLock<ContractAddress> = LazyLock::new(|| {
7373
static FUZZ_ADDRESS_CAIRO0_B: LazyLock<ContractAddress> = LazyLock::new(|| {
7474
ContractAddress::try_from(felt!(FUZZ_ADDRESS_CAIRO0_B_EXPECT.data())).unwrap()
7575
});
76+
static FUZZ_ADDRESS_TO_CLASS_HASH: LazyLock<BTreeMap<ContractAddress, ClassHash>> =
77+
LazyLock::new(|| {
78+
BTreeMap::from([
79+
(*FUZZ_ADDRESS_CAIRO1_A, *CAIRO1_CONTRACT_CLASS_HASH),
80+
(*FUZZ_ADDRESS_CAIRO1_B, *CAIRO1_CONTRACT_CLASS_HASH),
81+
(*FUZZ_ADDRESS_CAIRO0_A, *CAIRO0_CONTRACT_CLASS_HASH),
82+
(*FUZZ_ADDRESS_CAIRO0_B, *CAIRO0_CONTRACT_CLASS_HASH),
83+
])
84+
});
7685

7786
/// Storage key that can be written to.
7887
static VALID_STORAGE_KEYS: LazyLock<Vec<Felt>> =
@@ -300,26 +309,27 @@ struct RevertInfo {
300309
/// Addresses of deployed contracts that were deployed in the reverted call tree.
301310
pub deployed_addresses: BTreeSet<ContractAddress>,
302311

303-
/// If a class was replaced in the reverted call tree, the address and original class hash of
304-
/// the replaced class.
305-
pub class_replaced_and_original_class_hash: Option<(ContractAddress, ClassHash)>,
312+
/// If a class was replaced in the reverted call tree, the original class hash of the replaced
313+
/// class.
314+
pub class_replaced: Option<ClassHash>,
306315
}
307316

308317
impl RevertInfo {
309318
pub fn combine(others: Vec<Self>) -> Self {
310319
// If there is a replace-class, there should be only one.
311-
let mut class_replaced_and_original_class_hash = None;
320+
let mut class_replaced = None;
312321
for other in others.iter() {
313-
if let Some(inner_replacement) = other.class_replaced_and_original_class_hash {
314-
class_replaced_and_original_class_hash = Some(inner_replacement);
322+
if other.class_replaced.is_some() {
323+
assert!(class_replaced.is_none());
324+
class_replaced = other.class_replaced;
315325
}
316326
}
317327
Self {
318328
deployed_addresses: others
319329
.into_iter()
320330
.flat_map(|other| other.deployed_addresses)
321331
.collect(),
322-
class_replaced_and_original_class_hash,
332+
class_replaced,
323333
}
324334
}
325335
}
@@ -341,11 +351,11 @@ struct FuzzTestContext {
341351
/// List of operations applied to the fuzz test so far.
342352
pub operations: Vec<FuzzOperationData>,
343353

344-
/// Map from contract address to class hash.
345-
pub deployed_contracts: BTreeMap<ContractAddress, ClassHash>,
354+
/// Map from newly deployed contract address to class hash.
355+
pub newly_deployed_contracts: BTreeMap<ContractAddress, ClassHash>,
346356

347-
/// We only allow one replace class per test.
348-
pub class_replaced: bool,
357+
/// We only allow one replace class per test. Track which contract was replaced.
358+
pub replaced_address: Option<ContractAddress>,
349359

350360
/// Next value to write in a storage-write operation.
351361
pub next_storage_write_value: StarknetStorageValue,
@@ -357,18 +367,14 @@ struct FuzzTestContext {
357367
}
358368

359369
impl FuzzTestContext {
360-
pub fn init(
361-
seed: u64,
362-
first_call: FuzzCallInfo,
363-
deployed_fuzz_contracts: BTreeMap<ContractAddress, ClassHash>,
364-
) -> Self {
370+
pub fn init(seed: u64, first_call: FuzzCallInfo) -> Self {
365371
Self {
366372
calls: vec![first_call],
367373
current_call: vec![0],
368374
final_state: FinalizedState::Ongoing,
369375
operations: vec![],
370-
deployed_contracts: deployed_fuzz_contracts,
371-
class_replaced: false,
376+
newly_deployed_contracts: BTreeMap::new(),
377+
replaced_address: None,
372378
next_storage_write_value: StarknetStorageValue(Felt::from(1u16 << 12)),
373379
next_salt: ContractAddressSalt(Felt::from(1u32 << 16)),
374380
rng: ChaCha8Rng::seed_from_u64(seed),
@@ -391,15 +397,8 @@ impl FuzzTestManager {
391397
// - two cairo0 fuzz test contracts.
392398
let mut test_manager = Self::init_deployment(false).await;
393399

394-
let deployed_fuzz_contracts = BTreeMap::from([
395-
(*FUZZ_ADDRESS_CAIRO1_A, *CAIRO1_CONTRACT_CLASS_HASH),
396-
(*FUZZ_ADDRESS_CAIRO1_B, *CAIRO1_CONTRACT_CLASS_HASH),
397-
(*FUZZ_ADDRESS_CAIRO0_A, *CAIRO0_CONTRACT_CLASS_HASH),
398-
(*FUZZ_ADDRESS_CAIRO0_B, *CAIRO0_CONTRACT_CLASS_HASH),
399-
]);
400-
401400
// Initialize the fuzz testing contracts with the orchestrator address.
402-
for address in deployed_fuzz_contracts.keys() {
401+
for address in FUZZ_ADDRESS_TO_CLASS_HASH.keys() {
403402
let calldata = create_calldata(*address, "initialize", &[***FUZZ_ADDRESS_ORCHESTRATOR]);
404403
test_manager.add_funded_account_invoke(invoke_tx_args! { calldata });
405404
}
@@ -413,7 +412,7 @@ impl FuzzTestManager {
413412
ParentFailureBehavior::Cairo1Catching,
414413
);
415414
Self {
416-
context: FuzzTestContext::init(seed, first_call, deployed_fuzz_contracts),
415+
context: FuzzTestContext::init(seed, first_call),
417416
test_manager,
418417
first_called_address,
419418
}
@@ -494,6 +493,26 @@ impl FuzzTestManager {
494493
self.is_cairo1_class(&self.current_class_hash())
495494
}
496495

496+
pub fn deployed_contracts(&self) -> impl Iterator<Item = &ContractAddress> {
497+
self.context.newly_deployed_contracts.keys().chain(FUZZ_ADDRESS_TO_CLASS_HASH.keys())
498+
}
499+
500+
pub fn try_class_hash_of(&self, address: &ContractAddress) -> Option<ClassHash> {
501+
match self.context.replaced_address {
502+
Some(replaced_address) if &replaced_address == address => {
503+
Some(*CAIRO1_REPLACEMENT_CLASS_HASH)
504+
}
505+
_ => FUZZ_ADDRESS_TO_CLASS_HASH
506+
.get(address)
507+
.copied()
508+
.or_else(|| self.context.newly_deployed_contracts.get(address).copied()),
509+
}
510+
}
511+
512+
pub fn class_hash_of(&self, address: &ContractAddress) -> ClassHash {
513+
self.try_class_hash_of(address).unwrap()
514+
}
515+
497516
/// Returns a vector of operations of the given type that can be applied on the current context.
498517
pub fn valid_operations_of_type(
499518
&self,
@@ -510,9 +529,7 @@ impl FuzzTestManager {
510529
// There are two Cairo0 contracts and two Cairo1 contracts that can be called.
511530
// When calling from a Cairo1 context, the caller can unwrap the call result or not.
512531
let current_context_is_cairo1 = self.is_current_context_cairo1();
513-
self.context
514-
.deployed_contracts
515-
.keys()
532+
self.deployed_contracts()
516533
.flat_map(|address| {
517534
if current_context_is_cairo1 {
518535
[true, false]
@@ -571,7 +588,7 @@ impl FuzzTestManager {
571588
.collect(),
572589
FuzzOperation::ReplaceClass => {
573590
// If class was already replaced, no more replacements are allowed.
574-
if self.context.class_replaced {
591+
if self.context.replaced_address.is_some() {
575592
return vec![];
576593
}
577594
vec![FuzzOperationData::ReplaceClass(*CAIRO1_REPLACEMENT_CLASS_HASH)]
@@ -669,8 +686,8 @@ impl FuzzTestManager {
669686
} else {
670687
BTreeSet::new()
671688
},
672-
class_replaced_and_original_class_hash: if root_call.class_replaced_here {
673-
Some((root_call.address, root_call.class_hash))
689+
class_replaced: if root_call.class_replaced_here {
690+
Some(root_call.class_hash)
674691
} else {
675692
None
676693
},
@@ -682,14 +699,17 @@ impl FuzzTestManager {
682699
pub fn apply_revert_info(&mut self, revert_info: RevertInfo) {
683700
// Revert class replacement. Do this before "undeploying" deployed contracts so we don't
684701
// "redeploy" anything when we only intend to revert the class hash change.
685-
if let Some((address, class_hash)) = revert_info.class_replaced_and_original_class_hash {
686-
self.context.deployed_contracts.insert(address, class_hash);
702+
if let Some(original_class_hash) = revert_info.class_replaced {
703+
let replaced_address = self.context.replaced_address.take().unwrap();
704+
if self.context.newly_deployed_contracts.contains_key(&replaced_address) {
705+
self.context.newly_deployed_contracts.insert(replaced_address, original_class_hash);
706+
}
687707
}
688708
// "Undeploy" all deployed contracts.
689709
for address in revert_info.deployed_addresses.iter() {
690710
// Remove without asserting that the address was actually deployed - the
691711
// constructor may have reverted before being finalized.
692-
self.context.deployed_contracts.remove(address);
712+
self.context.newly_deployed_contracts.remove(address);
693713
}
694714
}
695715

@@ -714,7 +734,7 @@ impl FuzzTestManager {
714734
}
715735
FuzzOperationData::Call(call_operation_data) => {
716736
let address = *call_operation_data.address();
717-
let class_hash = *self.context.deployed_contracts.get(&address).unwrap();
737+
let class_hash = self.class_hash_of(&address);
718738
self.enter_call(address, class_hash, call_operation_data.parent_failure_behavior());
719739
}
720740
FuzzOperationData::LibraryCall(library_call_operation_data) => {
@@ -729,12 +749,10 @@ impl FuzzTestManager {
729749
self.context.next_storage_write_value.0 += Felt::ONE;
730750
}
731751
FuzzOperationData::ReplaceClass(class_hash) => {
732-
assert!(!self.context.class_replaced);
752+
assert!(self.context.replaced_address.is_none());
733753
assert_eq!(class_hash, *CAIRO1_REPLACEMENT_CLASS_HASH);
734-
self.context.class_replaced = true;
735-
// Update the mapping from address to class hash, so subsequent calls to this
736-
// address will correctly use the new class hash.
737-
self.context.deployed_contracts.insert(self.current_address(), class_hash);
754+
let current_address = self.current_address();
755+
self.context.replaced_address = Some(current_address);
738756
// Update the current call to mark that it was replaced at this point, to make it
739757
// easy to track if the change must be reverted mid-test.
740758
self.current_fuzz_call_info_mut().class_replaced_here = true;
@@ -747,7 +765,7 @@ impl FuzzTestManager {
747765
// Increment the salt for the next deploy operation.
748766
self.context.next_salt.0 += Felt::ONE;
749767
// Update the mapping from address to class hash.
750-
self.context.deployed_contracts.insert(deployed_address, class_hash);
768+
self.context.newly_deployed_contracts.insert(deployed_address, class_hash);
751769
// Enter constructor context.
752770
self.enter_deploy(deployed_address, class_hash);
753771
}
@@ -854,10 +872,10 @@ impl FuzzTestManager {
854872
FuzzOperationData::Call(call_operation_data) => {
855873
// It's possible that the address is no longer deployed (post-revert).
856874
let class_info_string =
857-
match self.context.deployed_contracts.get(call_operation_data.address()) {
875+
match self.try_class_hash_of(call_operation_data.address()) {
858876
Some(class_hash) => format!(
859877
"Cairo{} address, class hash: {}",
860-
if self.is_cairo1_class(class_hash) { "1" } else { "0" },
878+
if self.is_cairo1_class(&class_hash) { "1" } else { "0" },
861879
class_hash.0.to_hex_string()
862880
),
863881
None => "unknown class hash, deployment reverted".to_string(),

0 commit comments

Comments
 (0)