1use std::collections::{hash_map, HashMap, HashSet};
2use std::slice;
3
4use fake::Dummy;
5
6use crate::{
7 BlockHash,
8 CasmHash,
9 ClassHash,
10 ContractAddress,
11 ContractNonce,
12 SierraHash,
13 StateCommitment,
14 StateDiffCommitment,
15 StorageAddress,
16 StorageValue,
17};
18
19#[derive(Default, Debug, Clone, PartialEq)]
20pub struct StateUpdate {
21 pub block_hash: BlockHash,
22 pub parent_state_commitment: StateCommitment,
23 pub state_commitment: StateCommitment,
24 pub contract_updates: HashMap<ContractAddress, ContractUpdate>,
25 pub system_contract_updates: HashMap<ContractAddress, SystemContractUpdate>,
26 pub declared_cairo_classes: HashSet<ClassHash>,
27 pub declared_sierra_classes: HashMap<SierraHash, CasmHash>,
28}
29
30#[derive(Default, Debug, Clone, PartialEq, Dummy)]
31pub struct StateUpdateData {
32 pub contract_updates: HashMap<ContractAddress, ContractUpdate>,
33 pub system_contract_updates: HashMap<ContractAddress, SystemContractUpdate>,
34 pub declared_cairo_classes: HashSet<ClassHash>,
35 pub declared_sierra_classes: HashMap<SierraHash, CasmHash>,
36}
37
38#[derive(Default, Debug, Clone, PartialEq, Dummy)]
39pub struct ContractUpdate {
40 pub storage: HashMap<StorageAddress, StorageValue>,
41 pub class: Option<ContractClassUpdate>,
44 pub nonce: Option<ContractNonce>,
45}
46
47#[derive(Default, Debug, Clone, PartialEq, Dummy)]
48pub struct SystemContractUpdate {
49 pub storage: HashMap<StorageAddress, StorageValue>,
50}
51
52#[derive(Debug, Copy, Clone, PartialEq, Dummy)]
53pub enum ContractClassUpdate {
54 Deploy(ClassHash),
55 Replace(ClassHash),
56}
57
58pub struct StateUpdateRef<'a> {
59 pub contract_updates: Vec<(&'a ContractAddress, ContractUpdateRef<'a>)>,
60 pub system_contract_updates: Vec<(&'a ContractAddress, SystemContractUpdateRef<'a>)>,
61 pub declared_sierra_classes: &'a HashMap<SierraHash, CasmHash>,
62}
63
64pub struct ContractUpdateRef<'a> {
65 pub storage: StorageRef<'a>,
66 pub class: &'a Option<ContractClassUpdate>,
67 pub nonce: &'a Option<ContractNonce>,
68}
69
70pub struct SystemContractUpdateRef<'a> {
71 pub storage: StorageRef<'a>,
72}
73
74#[derive(Copy, Clone)]
75pub enum StorageRef<'a> {
76 HashMap(&'a HashMap<StorageAddress, StorageValue>),
77 Vec(&'a Vec<(StorageAddress, StorageValue)>),
78}
79
80pub enum StorageRefIter<'a> {
81 HashMap(hash_map::Iter<'a, StorageAddress, StorageValue>),
82 Vec(slice::Iter<'a, (StorageAddress, StorageValue)>),
83}
84
85impl ContractUpdate {
86 pub fn replaced_class(&self) -> Option<&ClassHash> {
87 match &self.class {
88 Some(ContractClassUpdate::Replace(hash)) => Some(hash),
89 _ => None,
90 }
91 }
92
93 pub fn deployed_class(&self) -> Option<&ClassHash> {
94 match &self.class {
95 Some(ContractClassUpdate::Deploy(hash)) => Some(hash),
96 _ => None,
97 }
98 }
99}
100
101impl ContractClassUpdate {
102 pub fn class_hash(&self) -> ClassHash {
103 match self {
104 ContractClassUpdate::Deploy(x) => *x,
105 ContractClassUpdate::Replace(x) => *x,
106 }
107 }
108
109 pub fn is_replaced(&self) -> bool {
110 matches!(self, ContractClassUpdate::Replace(_))
111 }
112}
113
114impl StateUpdate {
115 pub fn with_block_hash(mut self, block_hash: BlockHash) -> Self {
116 self.block_hash = block_hash;
117 self
118 }
119
120 pub fn with_state_commitment(mut self, state_commitment: StateCommitment) -> Self {
121 self.state_commitment = state_commitment;
122 self
123 }
124
125 pub fn with_parent_state_commitment(
126 mut self,
127 parent_state_commitment: StateCommitment,
128 ) -> Self {
129 self.parent_state_commitment = parent_state_commitment;
130 self
131 }
132
133 pub fn with_contract_nonce(mut self, contract: ContractAddress, nonce: ContractNonce) -> Self {
134 self.contract_updates.entry(contract).or_default().nonce = Some(nonce);
135 self
136 }
137
138 pub fn with_storage_update(
139 mut self,
140 contract: ContractAddress,
141 key: StorageAddress,
142 value: StorageValue,
143 ) -> Self {
144 self.contract_updates
145 .entry(contract)
146 .or_default()
147 .storage
148 .insert(key, value);
149 self
150 }
151
152 pub fn with_system_storage_update(
153 mut self,
154 contract: ContractAddress,
155 key: StorageAddress,
156 value: StorageValue,
157 ) -> Self {
158 self.system_contract_updates
159 .entry(contract)
160 .or_default()
161 .storage
162 .insert(key, value);
163 self
164 }
165
166 pub fn with_deployed_contract(mut self, contract: ContractAddress, class: ClassHash) -> Self {
167 self.contract_updates.entry(contract).or_default().class =
168 Some(ContractClassUpdate::Deploy(class));
169 self
170 }
171
172 pub fn with_replaced_class(mut self, contract: ContractAddress, class: ClassHash) -> Self {
173 self.contract_updates.entry(contract).or_default().class =
174 Some(ContractClassUpdate::Replace(class));
175 self
176 }
177
178 pub fn with_declared_sierra_class(mut self, sierra: SierraHash, casm: CasmHash) -> Self {
179 self.declared_sierra_classes.insert(sierra, casm);
180 self
181 }
182
183 pub fn with_declared_cairo_class(mut self, cairo: ClassHash) -> Self {
184 self.declared_cairo_classes.insert(cairo);
185 self
186 }
187
188 pub fn change_count(&self) -> usize {
198 self.declared_cairo_classes.len()
199 + self.declared_sierra_classes.len()
200 + self
201 .system_contract_updates
202 .iter()
203 .map(|x| x.1.storage.len())
204 .sum::<usize>()
205 + self
206 .contract_updates
207 .iter()
208 .map(|x| {
209 x.1.storage.len()
210 + x.1.class.as_ref().map(|_| 1).unwrap_or_default()
211 + x.1.nonce.as_ref().map(|_| 1).unwrap_or_default()
212 })
213 .sum::<usize>()
214 }
215
216 pub fn contract_nonce(&self, contract: ContractAddress) -> Option<ContractNonce> {
223 self.contract_updates.get(&contract).and_then(|x| {
224 x.nonce.or_else(|| {
225 x.class.as_ref().and_then(|c| match c {
226 ContractClassUpdate::Deploy(_) => {
227 Some(ContractNonce::ZERO)
230 }
231 ContractClassUpdate::Replace(_) => None,
232 })
233 })
234 })
235 }
236
237 pub fn contract_class(&self, contract: ContractAddress) -> Option<ClassHash> {
240 self.contract_updates
241 .get(&contract)
242 .and_then(|x| x.class.as_ref().map(|x| x.class_hash()))
243 }
244
245 pub fn class_is_declared(&self, class: ClassHash) -> bool {
248 if self.declared_cairo_classes.contains(&class) {
249 return true;
250 }
251
252 self.declared_sierra_classes
253 .contains_key(&SierraHash(class.0))
254 }
255
256 pub fn storage_value(
261 &self,
262 contract: ContractAddress,
263 key: StorageAddress,
264 ) -> Option<StorageValue> {
265 self.contract_updates
266 .get(&contract)
267 .and_then(|update| {
268 update
269 .storage
270 .iter()
271 .find_map(|(k, v)| (k == &key).then_some(*v))
272 .or_else(|| {
273 update.class.as_ref().and_then(|c| match c {
274 ContractClassUpdate::Deploy(_) => Some(StorageValue::ZERO),
277 ContractClassUpdate::Replace(_) => None,
278 })
279 })
280 })
281 .or_else(|| {
282 self.system_contract_updates
283 .get(&contract)
284 .and_then(|update| {
285 update
286 .storage
287 .iter()
288 .find_map(|(k, v)| (k == &key).then_some(*v))
289 })
290 })
291 }
292
293 pub fn compute_state_diff_commitment(&self) -> StateDiffCommitment {
294 state_diff_commitment::compute(
295 &self.contract_updates,
296 &self.system_contract_updates,
297 &self.declared_cairo_classes,
298 &self.declared_sierra_classes,
299 )
300 }
301
302 pub fn state_diff_length(&self) -> u64 {
303 let mut len = 0;
304 self.contract_updates.iter().for_each(|(_, update)| {
305 len += update.storage.len();
306 len += usize::from(update.nonce.is_some());
307 len += usize::from(update.class.is_some());
308 });
309 self.system_contract_updates.iter().for_each(|(_, update)| {
310 len += update.storage.len();
311 });
312 len += self.declared_cairo_classes.len() + self.declared_sierra_classes.len();
313 len.try_into().expect("ptr size is 64bits")
314 }
315}
316
317impl StateUpdateData {
318 pub fn compute_state_diff_commitment(&self) -> StateDiffCommitment {
319 state_diff_commitment::compute(
320 &self.contract_updates,
321 &self.system_contract_updates,
322 &self.declared_cairo_classes,
323 &self.declared_sierra_classes,
324 )
325 }
326
327 pub fn is_empty(&self) -> bool {
328 self.contract_updates.is_empty()
329 && self.system_contract_updates.is_empty()
330 && self.declared_cairo_classes.is_empty()
331 && self.declared_sierra_classes.is_empty()
332 }
333
334 pub fn declared_classes(&self) -> DeclaredClasses {
335 DeclaredClasses {
336 sierra: self.declared_sierra_classes.clone(),
337 cairo: self.declared_cairo_classes.clone(),
338 }
339 }
340
341 pub fn state_diff_length(&self) -> usize {
342 let mut len = 0;
343 self.contract_updates.iter().for_each(|(_, update)| {
344 len += update.storage.len();
345 len += usize::from(update.nonce.is_some());
346 len += usize::from(update.class.is_some());
347 });
348 self.system_contract_updates.iter().for_each(|(_, update)| {
349 len += update.storage.len();
350 });
351 len += self.declared_cairo_classes.len() + self.declared_sierra_classes.len();
352 len
353 }
354}
355
356impl From<StateUpdate> for StateUpdateData {
357 fn from(state_update: StateUpdate) -> Self {
358 Self {
359 contract_updates: state_update.contract_updates,
360 system_contract_updates: state_update.system_contract_updates,
361 declared_cairo_classes: state_update.declared_cairo_classes,
362 declared_sierra_classes: state_update.declared_sierra_classes,
363 }
364 }
365}
366
367impl<'a> From<&'a StateUpdate> for StateUpdateRef<'a> {
368 fn from(state_update: &'a StateUpdate) -> Self {
369 Self {
370 contract_updates: state_update
371 .contract_updates
372 .iter()
373 .map(|(k, v)| {
374 (
375 k,
376 ContractUpdateRef {
377 storage: StorageRef::HashMap(&v.storage),
378 class: &v.class,
379 nonce: &v.nonce,
380 },
381 )
382 })
383 .collect(),
384 system_contract_updates: state_update
385 .system_contract_updates
386 .iter()
387 .map(|(k, v)| {
388 (
389 k,
390 SystemContractUpdateRef {
391 storage: StorageRef::HashMap(&v.storage),
392 },
393 )
394 })
395 .collect(),
396 declared_sierra_classes: &state_update.declared_sierra_classes,
397 }
398 }
399}
400
401impl<'a> From<&'a mut StateUpdate> for StateUpdateRef<'a> {
402 fn from(state_update: &'a mut StateUpdate) -> Self {
403 Self::from(state_update as &'a StateUpdate)
404 }
405}
406
407impl<'a> From<&'a StateUpdateData> for StateUpdateRef<'a> {
408 fn from(state_update: &'a StateUpdateData) -> Self {
409 Self {
410 contract_updates: state_update
411 .contract_updates
412 .iter()
413 .map(|(k, v)| {
414 (
415 k,
416 ContractUpdateRef {
417 storage: StorageRef::HashMap(&v.storage),
418 class: &v.class,
419 nonce: &v.nonce,
420 },
421 )
422 })
423 .collect(),
424 system_contract_updates: state_update
425 .system_contract_updates
426 .iter()
427 .map(|(k, v)| {
428 (
429 k,
430 SystemContractUpdateRef {
431 storage: StorageRef::HashMap(&v.storage),
432 },
433 )
434 })
435 .collect(),
436 declared_sierra_classes: &state_update.declared_sierra_classes,
437 }
438 }
439}
440
441impl<'a> From<&'a mut StateUpdateData> for StateUpdateRef<'a> {
442 fn from(state_update: &'a mut StateUpdateData) -> Self {
443 Self::from(state_update as &'a StateUpdateData)
444 }
445}
446
447impl StorageRef<'_> {
448 pub fn iter(&self) -> StorageRefIter<'_> {
449 match self {
450 StorageRef::HashMap(map) => StorageRefIter::HashMap(map.iter()),
451 StorageRef::Vec(vec) => StorageRefIter::Vec(vec.iter()),
452 }
453 }
454
455 pub fn is_empty(&self) -> bool {
456 match self {
457 StorageRef::HashMap(map) => map.is_empty(),
458 StorageRef::Vec(vec) => vec.is_empty(),
459 }
460 }
461}
462
463impl<'a> From<&'a ContractUpdate> for ContractUpdateRef<'a> {
464 fn from(x: &'a ContractUpdate) -> Self {
465 ContractUpdateRef {
466 storage: (&x.storage).into(),
467 class: &x.class,
468 nonce: &x.nonce,
469 }
470 }
471}
472
473impl<'a> From<&'a SystemContractUpdate> for SystemContractUpdateRef<'a> {
474 fn from(x: &'a SystemContractUpdate) -> Self {
475 SystemContractUpdateRef {
476 storage: (&x.storage).into(),
477 }
478 }
479}
480
481impl<'a> From<&'a HashMap<StorageAddress, StorageValue>> for StorageRef<'a> {
482 fn from(x: &'a HashMap<StorageAddress, StorageValue>) -> Self {
483 StorageRef::HashMap(x)
484 }
485}
486
487impl<'a> From<&'a Vec<(StorageAddress, StorageValue)>> for StorageRef<'a> {
488 fn from(x: &'a Vec<(StorageAddress, StorageValue)>) -> Self {
489 StorageRef::Vec(x)
490 }
491}
492
493impl<'a> IntoIterator for &'a StorageRef<'a> {
494 type Item = (&'a StorageAddress, &'a StorageValue);
495 type IntoIter = StorageRefIter<'a>;
496
497 fn into_iter(self) -> Self::IntoIter {
498 self.iter()
499 }
500}
501
502impl<'a> Iterator for StorageRefIter<'a> {
503 type Item = (&'a StorageAddress, &'a StorageValue);
504
505 fn next(&mut self) -> Option<Self::Item> {
506 match self {
507 StorageRefIter::HashMap(iter) => iter.next(),
508 StorageRefIter::Vec(iter) => iter.next().map(|(k, v)| (k, v)),
509 }
510 }
511}
512
513mod state_diff_commitment {
514 use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
515
516 use pathfinder_crypto::hash::PoseidonHasher;
517 use pathfinder_crypto::MontFelt;
518
519 use super::{ContractUpdate, SystemContractUpdate};
520 use crate::{
521 felt_bytes,
522 CasmHash,
523 ClassHash,
524 ContractAddress,
525 SierraHash,
526 StateDiffCommitment,
527 };
528
529 pub fn compute(
533 contract_updates: &HashMap<ContractAddress, ContractUpdate>,
534 system_contract_updates: &HashMap<ContractAddress, SystemContractUpdate>,
535 declared_cairo_classes: &HashSet<ClassHash>,
536 declared_sierra_classes: &HashMap<SierraHash, CasmHash>,
537 ) -> StateDiffCommitment {
538 let mut hasher = PoseidonHasher::new();
539 hasher.write(felt_bytes!(b"STARKNET_STATE_DIFF0").into());
540 let deployed_contracts: BTreeMap<_, _> = contract_updates
542 .iter()
543 .filter_map(|(address, update)| {
544 update
545 .class
546 .as_ref()
547 .map(|update| (*address, update.class_hash()))
548 })
549 .collect();
550 hasher.write(MontFelt::from(deployed_contracts.len() as u64));
551 for (address, class_hash) in deployed_contracts {
552 hasher.write(MontFelt::from(address.0));
553 hasher.write(MontFelt::from(class_hash.0));
554 }
555 let declared_classes: BTreeSet<_> = declared_sierra_classes
557 .iter()
558 .map(|(sierra, casm)| (*sierra, *casm))
559 .collect();
560 hasher.write(MontFelt::from(declared_classes.len() as u64));
561 for (sierra, casm) in declared_classes {
562 hasher.write(MontFelt::from(sierra.0));
563 hasher.write(MontFelt::from(casm.0));
564 }
565 let deprecated_declared_classes: BTreeSet<_> =
567 declared_cairo_classes.iter().copied().collect();
568 hasher.write(MontFelt::from(deprecated_declared_classes.len() as u64));
569 for class_hash in deprecated_declared_classes {
570 hasher.write(MontFelt::from(class_hash.0));
571 }
572 hasher.write(MontFelt::ONE);
573 hasher.write(MontFelt::ZERO);
574 let storage_diffs: BTreeMap<_, _> = contract_updates
576 .iter()
577 .map(|(address, update)| (address, &update.storage))
578 .chain(
579 system_contract_updates
580 .iter()
581 .map(|(address, update)| (address, &update.storage)),
582 )
583 .filter_map(|(address, storage)| {
584 if storage.is_empty() {
585 None
586 } else {
587 let updates: BTreeMap<_, _> =
588 storage.iter().map(|(key, value)| (*key, *value)).collect();
589 Some((*address, updates))
590 }
591 })
592 .collect();
593 hasher.write(MontFelt::from(storage_diffs.len() as u64));
594 for (address, updates) in storage_diffs {
595 hasher.write(MontFelt::from(address.0));
596 hasher.write(MontFelt::from(updates.len() as u64));
597 for (key, value) in updates {
598 hasher.write(MontFelt::from(key.0));
599 hasher.write(MontFelt::from(value.0));
600 }
601 }
602 let nonces: BTreeMap<_, _> = contract_updates
604 .iter()
605 .filter_map(|(address, update)| update.nonce.map(|nonce| (*address, nonce)))
606 .collect();
607 hasher.write(MontFelt::from(nonces.len() as u64));
608 for (address, nonce) in nonces {
609 hasher.write(MontFelt::from(address.0));
610 hasher.write(MontFelt::from(nonce.0));
611 }
612 StateDiffCommitment(hasher.finish().into())
613 }
614}
615
616#[derive(Debug, PartialEq)]
617pub enum ReverseContractUpdate {
618 Deleted,
619 Updated(ContractUpdate),
620}
621
622impl ReverseContractUpdate {
623 pub fn update_mut(&mut self) -> Option<&mut ContractUpdate> {
624 match self {
625 Self::Deleted => None,
626 Self::Updated(update) => Some(update),
627 }
628 }
629}
630
631#[derive(Clone, Debug, PartialEq)]
632pub struct DeclaredClasses {
633 pub sierra: HashMap<SierraHash, CasmHash>,
634 pub cairo: HashSet<ClassHash>,
635}
636
637impl DeclaredClasses {
638 pub fn is_empty(&self) -> bool {
639 self.len() == 0
640 }
641
642 pub fn len(&self) -> usize {
643 self.sierra.len() + self.cairo.len()
644 }
645}
646
647#[derive(Debug, thiserror::Error)]
648pub enum StateUpdateError {
649 #[error("Contract class hash missing for contract {0}")]
650 ContractClassHashMissing(ContractAddress),
651 #[error(transparent)]
652 StorageError(#[from] anyhow::Error),
653}
654
655#[cfg(test)]
656mod tests {
657 use super::*;
658 use crate::macro_prelude::*;
659
660 #[test]
661 fn change_count() {
662 let state_update = StateUpdate::default()
663 .with_contract_nonce(contract_address!("0x1"), contract_nonce!("0x2"))
664 .with_contract_nonce(contract_address!("0x4"), contract_nonce!("0x5"))
665 .with_declared_cairo_class(class_hash!("0x3"))
666 .with_declared_sierra_class(sierra_hash!("0x4"), casm_hash!("0x5"))
667 .with_deployed_contract(contract_address!("0x1"), class_hash!("0x3"))
668 .with_replaced_class(contract_address!("0x33"), class_hash!("0x35"))
669 .with_system_storage_update(
670 ContractAddress::ONE,
671 storage_address!("0x10"),
672 storage_value!("0x99"),
673 )
674 .with_storage_update(
675 contract_address!("0x33"),
676 storage_address!("0x10"),
677 storage_value!("0x99"),
678 );
679
680 assert_eq!(state_update.change_count(), 8);
681 }
682
683 #[test]
684 fn contract_nonce() {
685 let state_update = StateUpdate::default()
686 .with_contract_nonce(contract_address!("0x1"), contract_nonce!("0x2"))
687 .with_deployed_contract(contract_address!("0x2"), class_hash!("0x4"))
688 .with_contract_nonce(contract_address!("0x10"), contract_nonce!("0x20"))
689 .with_deployed_contract(contract_address!("0x10"), class_hash!("0x12"))
690 .with_replaced_class(contract_address!("0x123"), class_hash!("0x1244"))
691 .with_replaced_class(contract_address!("0x1234"), class_hash!("0x12445"))
692 .with_contract_nonce(contract_address!("0x1234"), contract_nonce!("0x1111"));
693
694 assert!(state_update
695 .contract_nonce(contract_address_bytes!(b"not present"))
696 .is_none());
697
698 let result = state_update.contract_nonce(contract_address!("0x1"));
699 assert_eq!(result, Some(contract_nonce!("0x2")));
700
701 let result = state_update.contract_nonce(contract_address!("0x10"));
703 assert_eq!(result, Some(contract_nonce!("0x20")));
704
705 let result = state_update.contract_nonce(contract_address!("0x2"));
707 assert_eq!(result, Some(ContractNonce::ZERO));
708
709 let result = state_update.contract_nonce(contract_address!("0x1234"));
711 assert_eq!(result, Some(contract_nonce!("0x1111")));
712
713 assert!(state_update
715 .contract_nonce(contract_address!("0x123"))
716 .is_none());
717 }
718
719 mod storage_value {
720 use super::*;
721
722 #[test]
723 fn set() {
724 let c = contract_address!("0x1");
725 let k = storage_address!("0x2");
726 let v = storage_value!("0x3");
727 let state_update = StateUpdate::default().with_storage_update(c, k, v);
728 let result = state_update.storage_value(c, k);
729 assert_eq!(result, Some(v))
730 }
731
732 #[test]
733 fn not_set() {
734 let c = contract_address!("0x1");
735 let k = storage_address!("0x2");
736 let v = storage_value!("0x3");
737 let state_update = StateUpdate::default().with_storage_update(c, k, v);
738 let result = state_update.storage_value(contract_address!("0x4"), k);
739 assert!(result.is_none());
740
741 let result = state_update.storage_value(c, storage_address!("0x24"));
742 assert!(result.is_none());
743 }
744
745 #[test]
746 fn deployed_and_not_set() {
747 let c = contract_address!("0x1");
748 let state_update = StateUpdate::default().with_deployed_contract(c, class_hash!("0x1"));
749 let result = state_update.storage_value(c, storage_address!("0x2"));
750 assert_eq!(result, Some(StorageValue::ZERO));
751 }
752
753 #[test]
754 fn deployed_and_set() {
755 let c = contract_address!("0x1");
756 let k = storage_address!("0x2");
757 let v = storage_value!("0x3");
758 let state_update = StateUpdate::default()
759 .with_deployed_contract(c, class_hash!("0x1"))
760 .with_storage_update(c, k, v);
761 let result = state_update.storage_value(c, k);
762 assert_eq!(result, Some(v));
763 }
764
765 #[test]
766 fn replaced_and_not_set() {
767 let c = contract_address!("0x1");
768 let state_update = StateUpdate::default().with_replaced_class(c, class_hash!("0x1"));
769 let result = state_update.storage_value(c, storage_address!("0x2"));
770 assert!(result.is_none());
771 }
772
773 #[test]
774 fn replaced_and_set() {
775 let c = contract_address!("0x1");
776 let k = storage_address!("0x2");
777 let v = storage_value!("0x3");
778 let state_update = StateUpdate::default()
779 .with_replaced_class(c, class_hash!("0x1"))
780 .with_storage_update(c, k, v);
781 let result = state_update.storage_value(c, k);
782 assert_eq!(result, Some(v));
783 }
784
785 #[test]
786 fn system_contract_and_set() {
787 let c = contract_address!("0x1");
788 let k = storage_address!("0x2");
789 let v = storage_value!("0x3");
790 let state_update = StateUpdate::default().with_system_storage_update(c, k, v);
791 let result = state_update.storage_value(c, k);
792 assert_eq!(result, Some(v))
793 }
794
795 #[test]
796 fn system_contract_and_not_set() {
797 let c = contract_address!("0x1");
798 let k = storage_address!("0x2");
799 let v = storage_value!("0x3");
800 let state_update = StateUpdate::default().with_system_storage_update(c, k, v);
801 let result = state_update.storage_value(contract_address!("0x4"), k);
802 assert_eq!(result, None);
803 let result = state_update.storage_value(c, storage_address!("0x24"));
804 assert_eq!(result, None);
805 }
806 }
807
808 #[test]
809 fn class_is_declared() {
810 let cairo = class_hash_bytes!(b"cairo class");
811 let sierra = class_hash_bytes!(b"sierra class");
812
813 let state_update = StateUpdate::default()
814 .with_declared_cairo_class(cairo)
815 .with_declared_sierra_class(SierraHash(sierra.0), casm_hash_bytes!(b"anything"));
816
817 assert!(state_update.class_is_declared(cairo));
818 assert!(state_update.class_is_declared(sierra));
819 assert!(!state_update.class_is_declared(class_hash_bytes!(b"nope")));
820 }
821
822 #[test]
823 fn contract_class() {
824 let deployed = contract_address_bytes!(b"deployed");
825 let deployed_class = class_hash_bytes!(b"deployed class");
826 let replaced = contract_address_bytes!(b"replaced");
827 let replaced_class = class_hash_bytes!(b"replaced class");
828
829 let state_update = StateUpdate::default()
830 .with_deployed_contract(deployed, deployed_class)
831 .with_replaced_class(replaced, replaced_class);
832
833 let result = state_update.contract_class(deployed);
834 assert_eq!(result, Some(deployed_class));
835
836 let result = state_update.contract_class(replaced);
837 assert_eq!(result, Some(replaced_class));
838
839 assert!(state_update
840 .contract_class(contract_address_bytes!(b"bogus"))
841 .is_none());
842 }
843
844 #[test]
847 fn test_0_13_2_state_diff_commitment() {
848 let contract_updates: HashMap<_, _> = [
849 (
850 ContractAddress(0u64.into()),
851 ContractUpdate {
852 class: Some(ContractClassUpdate::Deploy(ClassHash(1u64.into()))),
853 ..Default::default()
854 },
855 ),
856 (
857 ContractAddress(2u64.into()),
858 ContractUpdate {
859 class: Some(ContractClassUpdate::Deploy(ClassHash(3u64.into()))),
860 ..Default::default()
861 },
862 ),
863 (
864 ContractAddress(4u64.into()),
865 ContractUpdate {
866 storage: [
867 (StorageAddress(5u64.into()), StorageValue(6u64.into())),
868 (StorageAddress(7u64.into()), StorageValue(8u64.into())),
869 ]
870 .iter()
871 .cloned()
872 .collect(),
873 ..Default::default()
874 },
875 ),
876 (
877 ContractAddress(9u64.into()),
878 ContractUpdate {
879 storage: [(StorageAddress(10u64.into()), StorageValue(11u64.into()))]
880 .iter()
881 .cloned()
882 .collect(),
883 ..Default::default()
884 },
885 ),
886 (
887 ContractAddress(17u64.into()),
888 ContractUpdate {
889 nonce: Some(ContractNonce(18u64.into())),
890 ..Default::default()
891 },
892 ),
893 (
894 ContractAddress(19u64.into()),
895 ContractUpdate {
896 class: Some(ContractClassUpdate::Replace(ClassHash(20u64.into()))),
897 ..Default::default()
898 },
899 ),
900 ]
901 .into_iter()
902 .collect();
903 let declared_sierra_classes: HashMap<_, _> = [
904 (SierraHash(12u64.into()), CasmHash(13u64.into())),
905 (SierraHash(14u64.into()), CasmHash(15u64.into())),
906 ]
907 .iter()
908 .cloned()
909 .collect();
910 let declared_cairo_classes: HashSet<_> =
911 [ClassHash(16u64.into())].iter().cloned().collect();
912
913 let expected_hash = StateDiffCommitment(felt!(
914 "0x0281f5966e49ad7dad9323826d53d1d27c0c4e6ebe5525e2e2fbca549bfa0a67"
915 ));
916
917 assert_eq!(
918 expected_hash,
919 state_diff_commitment::compute(
920 &contract_updates,
921 &Default::default(),
922 &declared_cairo_classes,
923 &declared_sierra_classes,
924 )
925 );
926 }
927}