1use alloc::collections::BTreeMap;
2use alloc::collections::btree_map::Entry;
3use alloc::vec::Vec;
4
5use super::{
6 AccountDeltaError,
7 ByteReader,
8 ByteWriter,
9 Deserializable,
10 DeserializationError,
11 Serializable,
12 Word,
13};
14use crate::account::{
15 StorageMap,
16 StorageMapKey,
17 StorageSlotContent,
18 StorageSlotName,
19 StorageSlotType,
20};
21use crate::{EMPTY_WORD, Felt, LexicographicWord, ZERO};
22
23#[derive(Clone, Debug, PartialEq, Eq)]
30pub struct AccountStorageDelta {
31 deltas: BTreeMap<StorageSlotName, StorageSlotDelta>,
33}
34
35impl AccountStorageDelta {
36 pub fn new() -> Self {
38 Self { deltas: BTreeMap::new() }
39 }
40
41 pub fn from_raw(deltas: BTreeMap<StorageSlotName, StorageSlotDelta>) -> Self {
43 Self { deltas }
44 }
45
46 pub fn get(&self, slot_name: &StorageSlotName) -> Option<&StorageSlotDelta> {
48 self.deltas.get(slot_name)
49 }
50
51 pub(crate) fn slots(&self) -> impl Iterator<Item = (&StorageSlotName, &StorageSlotDelta)> {
53 self.deltas.iter()
54 }
55
56 pub fn values(&self) -> impl Iterator<Item = (&StorageSlotName, &Word)> {
58 self.deltas.iter().filter_map(|(slot_name, slot_delta)| match slot_delta {
59 StorageSlotDelta::Value(word) => Some((slot_name, word)),
60 StorageSlotDelta::Map(_) => None,
61 })
62 }
63
64 pub fn maps(&self) -> impl Iterator<Item = (&StorageSlotName, &StorageMapDelta)> {
66 self.deltas.iter().filter_map(|(slot_name, slot_delta)| match slot_delta {
67 StorageSlotDelta::Value(_) => None,
68 StorageSlotDelta::Map(map_delta) => Some((slot_name, map_delta)),
69 })
70 }
71
72 pub fn is_empty(&self) -> bool {
74 self.deltas.is_empty()
75 }
76
77 pub fn set_item(
87 &mut self,
88 slot_name: StorageSlotName,
89 new_slot_value: Word,
90 ) -> Result<(), AccountDeltaError> {
91 if !self.deltas.get(&slot_name).map(StorageSlotDelta::is_value).unwrap_or(true) {
92 return Err(AccountDeltaError::StorageSlotUsedAsDifferentTypes(slot_name));
93 }
94
95 self.deltas.insert(slot_name, StorageSlotDelta::Value(new_slot_value));
96
97 Ok(())
98 }
99
100 pub fn set_map_item(
110 &mut self,
111 slot_name: StorageSlotName,
112 key: StorageMapKey,
113 new_value: Word,
114 ) -> Result<(), AccountDeltaError> {
115 match self
116 .deltas
117 .entry(slot_name.clone())
118 .or_insert(StorageSlotDelta::Map(StorageMapDelta::default()))
119 {
120 StorageSlotDelta::Value(_) => {
121 return Err(AccountDeltaError::StorageSlotUsedAsDifferentTypes(slot_name));
122 },
123 StorageSlotDelta::Map(storage_map_delta) => {
124 storage_map_delta.insert(key, new_value);
125 },
126 };
127
128 Ok(())
129 }
130
131 pub fn insert_empty_map_delta(&mut self, slot_name: StorageSlotName) {
137 self.deltas.insert(slot_name, StorageSlotDelta::with_empty_map());
138 }
139
140 pub fn merge(&mut self, other: Self) -> Result<(), AccountDeltaError> {
142 for (slot_name, slot_delta) in other.deltas {
143 match self.deltas.entry(slot_name.clone()) {
144 Entry::Vacant(vacant_entry) => {
145 vacant_entry.insert(slot_delta);
146 },
147 Entry::Occupied(mut occupied_entry) => {
148 occupied_entry.get_mut().merge(slot_delta).ok_or_else(|| {
149 AccountDeltaError::StorageSlotUsedAsDifferentTypes(slot_name)
150 })?;
151 },
152 }
153 }
154
155 Ok(())
156 }
157
158 fn cleared_values(&self) -> impl Iterator<Item = &StorageSlotName> {
160 self.values().filter_map(
161 |(slot_name, slot_value)| {
162 if slot_value.is_empty() { Some(slot_name) } else { None }
163 },
164 )
165 }
166
167 fn updated_values(&self) -> impl Iterator<Item = (&StorageSlotName, &Word)> {
169 self.values().filter_map(|(slot_name, slot_value)| {
170 if !slot_value.is_empty() {
171 Some((slot_name, slot_value))
172 } else {
173 None
174 }
175 })
176 }
177
178 pub(super) fn append_delta_elements(&self, elements: &mut Vec<Felt>) {
181 const DOMAIN_VALUE: Felt = Felt::new(2);
182 const DOMAIN_MAP: Felt = Felt::new(3);
183
184 for (slot_name, slot_delta) in self.deltas.iter() {
185 let slot_id = slot_name.id();
186
187 match slot_delta {
188 StorageSlotDelta::Value(new_value) => {
189 elements.extend_from_slice(&[
190 DOMAIN_VALUE,
191 ZERO,
192 slot_id.suffix(),
193 slot_id.prefix(),
194 ]);
195 elements.extend_from_slice(new_value.as_elements());
196 },
197 StorageSlotDelta::Map(map_delta) => {
198 for (key, value) in map_delta.entries() {
199 elements.extend_from_slice(key.inner().as_elements());
200 elements.extend_from_slice(value.as_elements());
201 }
202
203 let num_changed_entries = Felt::try_from(map_delta.num_entries()).expect(
204 "number of changed entries should not exceed max representable felt",
205 );
206
207 elements.extend_from_slice(&[
208 DOMAIN_MAP,
209 num_changed_entries,
210 slot_id.suffix(),
211 slot_id.prefix(),
212 ]);
213 elements.extend_from_slice(EMPTY_WORD.as_elements());
214 },
215 }
216 }
217 }
218
219 pub fn into_map(self) -> BTreeMap<StorageSlotName, StorageSlotDelta> {
221 self.deltas
222 }
223}
224
225impl Default for AccountStorageDelta {
226 fn default() -> Self {
227 Self::new()
228 }
229}
230
231impl Serializable for AccountStorageDelta {
232 fn write_into<W: ByteWriter>(&self, target: &mut W) {
233 let num_cleared_values = self.cleared_values().count();
234 let num_cleared_values =
235 u8::try_from(num_cleared_values).expect("number of slots should fit in u8");
236 let cleared_values = self.cleared_values();
237
238 let num_updated_values = self.updated_values().count();
239 let num_updated_values =
240 u8::try_from(num_updated_values).expect("number of slots should fit in u8");
241 let updated_values = self.updated_values();
242
243 let num_maps = self.maps().count();
244 let num_maps = u8::try_from(num_maps).expect("number of slots should fit in u8");
245 let maps = self.maps();
246
247 target.write_u8(num_cleared_values);
248 target.write_many(cleared_values);
249
250 target.write_u8(num_updated_values);
251 target.write_many(updated_values);
252
253 target.write_u8(num_maps);
254 target.write_many(maps);
255 }
256
257 fn get_size_hint(&self) -> usize {
258 let u8_size = 0u8.get_size_hint();
259
260 let mut storage_map_delta_size = 0;
261 for (slot_name, storage_map_delta) in self.maps() {
262 storage_map_delta_size += slot_name.get_size_hint() + storage_map_delta.get_size_hint();
265 }
266
267 u8_size * 3 +
269 self.cleared_values().fold(0, |acc, slot_name| acc + slot_name.get_size_hint()) +
271 self.updated_values().fold(0, |acc, (slot_name, slot_value)| {
273 acc + slot_name.get_size_hint() + slot_value.get_size_hint()
274 }) +
275 storage_map_delta_size
277 }
278}
279
280impl Deserializable for AccountStorageDelta {
281 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
282 let mut deltas = BTreeMap::new();
283
284 let num_cleared_values = source.read_u8()?;
285 for _ in 0..num_cleared_values {
286 let cleared_value: StorageSlotName = source.read()?;
287 deltas.insert(cleared_value, StorageSlotDelta::with_empty_value());
288 }
289
290 let num_updated_values = source.read_u8()?;
291 for _ in 0..num_updated_values {
292 let (updated_slot, updated_value) = source.read()?;
293 deltas.insert(updated_slot, StorageSlotDelta::Value(updated_value));
294 }
295
296 let num_maps = source.read_u8()? as usize;
297 deltas.extend(
298 source
299 .read_many::<(StorageSlotName, StorageMapDelta)>(num_maps)?
300 .into_iter()
301 .map(|(slot_name, map_delta)| (slot_name, StorageSlotDelta::Map(map_delta))),
302 );
303
304 Ok(Self::from_raw(deltas))
305 }
306}
307
308#[derive(Debug, Clone, PartialEq, Eq)]
317pub enum StorageSlotDelta {
318 Value(Word),
319 Map(StorageMapDelta),
320}
321
322impl StorageSlotDelta {
323 const VALUE: u8 = 0;
328
329 const MAP: u8 = 1;
331
332 pub fn with_empty_value() -> Self {
337 Self::Value(Word::empty())
338 }
339
340 pub fn with_empty_map() -> Self {
342 Self::Map(StorageMapDelta::default())
343 }
344
345 pub fn slot_type(&self) -> StorageSlotType {
350 match self {
351 StorageSlotDelta::Value(_) => StorageSlotType::Value,
352 StorageSlotDelta::Map(_) => StorageSlotType::Map,
353 }
354 }
355
356 pub fn is_value(&self) -> bool {
358 matches!(self, Self::Value(_))
359 }
360
361 pub fn is_map(&self) -> bool {
363 matches!(self, Self::Map(_))
364 }
365
366 pub fn unwrap_value(self) -> Word {
376 match self {
377 StorageSlotDelta::Value(value) => value,
378 StorageSlotDelta::Map(_) => panic!("called unwrap_value on a map slot delta"),
379 }
380 }
381
382 pub fn unwrap_map(self) -> StorageMapDelta {
389 match self {
390 StorageSlotDelta::Value(_) => panic!("called unwrap_map on a value slot delta"),
391 StorageSlotDelta::Map(map_delta) => map_delta,
392 }
393 }
394
395 #[must_use]
402 fn merge(&mut self, other: Self) -> Option<()> {
403 match (self, other) {
404 (StorageSlotDelta::Value(current_value), StorageSlotDelta::Value(new_value)) => {
405 *current_value = new_value;
406 },
407 (StorageSlotDelta::Map(current_map_delta), StorageSlotDelta::Map(new_map_delta)) => {
408 current_map_delta.merge(new_map_delta);
409 },
410 (..) => {
411 return None;
412 },
413 }
414
415 Some(())
416 }
417}
418
419impl From<StorageSlotContent> for StorageSlotDelta {
420 fn from(content: StorageSlotContent) -> Self {
421 match content {
422 StorageSlotContent::Value(word) => StorageSlotDelta::Value(word),
423 StorageSlotContent::Map(storage_map) => {
424 StorageSlotDelta::Map(StorageMapDelta::from(storage_map))
425 },
426 }
427 }
428}
429
430impl Serializable for StorageSlotDelta {
431 fn write_into<W: ByteWriter>(&self, target: &mut W) {
432 match self {
433 StorageSlotDelta::Value(value) => {
434 target.write_u8(Self::VALUE);
435 target.write(value);
436 },
437 StorageSlotDelta::Map(storage_map_delta) => {
438 target.write_u8(Self::MAP);
439 target.write(storage_map_delta);
440 },
441 }
442 }
443}
444
445impl Deserializable for StorageSlotDelta {
446 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
447 match source.read_u8()? {
448 Self::VALUE => {
449 let value = source.read()?;
450 Ok(Self::Value(value))
451 },
452 Self::MAP => {
453 let map_delta = source.read()?;
454 Ok(Self::Map(map_delta))
455 },
456 other => Err(DeserializationError::InvalidValue(format!(
457 "unknown storage slot delta variant {other}"
458 ))),
459 }
460 }
461}
462
463#[derive(Clone, Debug, Default, PartialEq, Eq)]
474pub struct StorageMapDelta(BTreeMap<LexicographicWord<StorageMapKey>, Word>);
475
476impl StorageMapDelta {
477 pub fn new(map: BTreeMap<LexicographicWord<StorageMapKey>, Word>) -> Self {
479 Self(map)
480 }
481
482 pub fn num_entries(&self) -> usize {
484 self.0.len()
485 }
486
487 pub fn entries(&self) -> &BTreeMap<LexicographicWord<StorageMapKey>, Word> {
491 &self.0
492 }
493
494 pub fn insert(&mut self, key: StorageMapKey, value: Word) {
496 self.0.insert(LexicographicWord::new(key), value);
497 }
498
499 pub fn is_empty(&self) -> bool {
501 self.0.is_empty()
502 }
503
504 pub fn merge(&mut self, other: Self) {
506 self.0.extend(other.0);
508 }
509
510 pub fn as_map_mut(&mut self) -> &mut BTreeMap<LexicographicWord<StorageMapKey>, Word> {
512 &mut self.0
513 }
514
515 fn cleared_keys(&self) -> impl Iterator<Item = &StorageMapKey> + '_ {
517 self.0.iter().filter(|&(_, value)| value.is_empty()).map(|(key, _)| key.inner())
518 }
519
520 fn updated_entries(&self) -> impl Iterator<Item = (&StorageMapKey, &Word)> + '_ {
522 self.0.iter().filter_map(|(key, value)| {
523 if !value.is_empty() {
524 Some((key.inner(), value))
525 } else {
526 None
527 }
528 })
529 }
530}
531
532#[cfg(any(feature = "testing", test))]
533impl StorageMapDelta {
534 pub fn from_iters(
536 cleared_leaves: impl IntoIterator<Item = StorageMapKey>,
537 updated_leaves: impl IntoIterator<Item = (StorageMapKey, Word)>,
538 ) -> Self {
539 Self(BTreeMap::from_iter(
540 cleared_leaves
541 .into_iter()
542 .map(|key| (LexicographicWord::new(key), EMPTY_WORD))
543 .chain(
544 updated_leaves
545 .into_iter()
546 .map(|(key, value)| (LexicographicWord::new(key), value)),
547 ),
548 ))
549 }
550
551 pub fn into_map(self) -> BTreeMap<LexicographicWord<StorageMapKey>, Word> {
553 self.0
554 }
555}
556
557impl From<StorageMap> for StorageMapDelta {
559 fn from(map: StorageMap) -> Self {
560 StorageMapDelta::new(
561 map.into_entries()
562 .into_iter()
563 .map(|(key, value)| (LexicographicWord::new(key), value))
564 .collect(),
565 )
566 }
567}
568
569impl Serializable for StorageMapDelta {
570 fn write_into<W: ByteWriter>(&self, target: &mut W) {
571 let cleared: Vec<&StorageMapKey> = self.cleared_keys().collect();
572 let updated: Vec<(&StorageMapKey, &Word)> = self.updated_entries().collect();
573
574 target.write_usize(cleared.len());
575 target.write_many(cleared.iter());
576
577 target.write_usize(updated.len());
578 target.write_many(updated.iter());
579 }
580
581 fn get_size_hint(&self) -> usize {
582 let cleared_keys_count = self.cleared_keys().count();
583 let updated_entries_count = self.updated_entries().count();
584
585 cleared_keys_count.get_size_hint() +
587 cleared_keys_count * StorageMapKey::SERIALIZED_SIZE +
588
589 updated_entries_count.get_size_hint() +
591 updated_entries_count * (StorageMapKey::SERIALIZED_SIZE + Word::SERIALIZED_SIZE)
592 }
593}
594
595impl Deserializable for StorageMapDelta {
596 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
597 let mut map = BTreeMap::new();
598
599 let cleared_count = source.read_usize()?;
600 for _ in 0..cleared_count {
601 let cleared_key = source.read()?;
602 map.insert(LexicographicWord::new(cleared_key), EMPTY_WORD);
603 }
604
605 let updated_count = source.read_usize()?;
606 for _ in 0..updated_count {
607 let (updated_key, updated_value) = source.read()?;
608 map.insert(LexicographicWord::new(updated_key), updated_value);
609 }
610
611 Ok(Self::new(map))
612 }
613}
614
615#[cfg(test)]
619mod tests {
620 use anyhow::Context;
621 use assert_matches::assert_matches;
622
623 use super::{AccountStorageDelta, Deserializable, Serializable};
624 use crate::account::{StorageMapDelta, StorageMapKey, StorageSlotDelta, StorageSlotName};
625 use crate::errors::AccountDeltaError;
626 use crate::{ONE, Word};
627
628 #[test]
629 fn account_storage_delta_returns_err_on_slot_type_mismatch() {
630 let value_slot_name = StorageSlotName::mock(1);
631 let map_slot_name = StorageSlotName::mock(2);
632
633 let mut delta = AccountStorageDelta::from_iters(
634 [value_slot_name.clone()],
635 [],
636 [(map_slot_name.clone(), StorageMapDelta::default())],
637 );
638
639 let err = delta
640 .set_map_item(value_slot_name.clone(), StorageMapKey::empty(), Word::empty())
641 .unwrap_err();
642 assert_matches!(err, AccountDeltaError::StorageSlotUsedAsDifferentTypes(slot_name) => {
643 assert_eq!(value_slot_name, slot_name)
644 });
645
646 let err = delta.set_item(map_slot_name.clone(), Word::empty()).unwrap_err();
647 assert_matches!(err, AccountDeltaError::StorageSlotUsedAsDifferentTypes(slot_name) => {
648 assert_eq!(map_slot_name, slot_name)
649 });
650 }
651
652 #[test]
653 fn test_is_empty() {
654 let storage_delta = AccountStorageDelta::new();
655 assert!(storage_delta.is_empty());
656
657 let storage_delta = AccountStorageDelta::from_iters([StorageSlotName::mock(1)], [], []);
658 assert!(!storage_delta.is_empty());
659
660 let storage_delta = AccountStorageDelta::from_iters(
661 [],
662 [(StorageSlotName::mock(2), Word::from([ONE, ONE, ONE, ONE]))],
663 [],
664 );
665 assert!(!storage_delta.is_empty());
666
667 let storage_delta = AccountStorageDelta::from_iters(
668 [],
669 [],
670 [(StorageSlotName::mock(3), StorageMapDelta::default())],
671 );
672 assert!(!storage_delta.is_empty());
673 }
674
675 #[test]
676 fn test_serde_account_storage_delta() {
677 let storage_delta = AccountStorageDelta::new();
678 let serialized = storage_delta.to_bytes();
679 let deserialized = AccountStorageDelta::read_from_bytes(&serialized).unwrap();
680 assert_eq!(deserialized, storage_delta);
681 assert_eq!(storage_delta.get_size_hint(), serialized.len());
682
683 let storage_delta = AccountStorageDelta::from_iters([StorageSlotName::mock(1)], [], []);
684 let serialized = storage_delta.to_bytes();
685 let deserialized = AccountStorageDelta::read_from_bytes(&serialized).unwrap();
686 assert_eq!(deserialized, storage_delta);
687 assert_eq!(storage_delta.get_size_hint(), serialized.len());
688
689 let storage_delta = AccountStorageDelta::from_iters(
690 [],
691 [(StorageSlotName::mock(2), Word::from([ONE, ONE, ONE, ONE]))],
692 [],
693 );
694 let serialized = storage_delta.to_bytes();
695 let deserialized = AccountStorageDelta::read_from_bytes(&serialized).unwrap();
696 assert_eq!(deserialized, storage_delta);
697 assert_eq!(storage_delta.get_size_hint(), serialized.len());
698
699 let storage_delta = AccountStorageDelta::from_iters(
700 [],
701 [],
702 [(StorageSlotName::mock(3), StorageMapDelta::default())],
703 );
704 let serialized = storage_delta.to_bytes();
705 let deserialized = AccountStorageDelta::read_from_bytes(&serialized).unwrap();
706 assert_eq!(deserialized, storage_delta);
707 assert_eq!(storage_delta.get_size_hint(), serialized.len());
708 }
709
710 #[test]
711 fn test_serde_storage_map_delta() {
712 let storage_map_delta = StorageMapDelta::default();
713 let serialized = storage_map_delta.to_bytes();
714 let deserialized = StorageMapDelta::read_from_bytes(&serialized).unwrap();
715 assert_eq!(deserialized, storage_map_delta);
716
717 let storage_map_delta =
718 StorageMapDelta::from_iters([StorageMapKey::from_array([1, 1, 1, 1])], []);
719 let serialized = storage_map_delta.to_bytes();
720 let deserialized = StorageMapDelta::read_from_bytes(&serialized).unwrap();
721 assert_eq!(deserialized, storage_map_delta);
722
723 let storage_map_delta = StorageMapDelta::from_iters(
724 [],
725 [(StorageMapKey::empty(), Word::from([ONE, ONE, ONE, ONE]))],
726 );
727 let serialized = storage_map_delta.to_bytes();
728 let deserialized = StorageMapDelta::read_from_bytes(&serialized).unwrap();
729 assert_eq!(deserialized, storage_map_delta);
730 }
731
732 #[test]
733 fn test_serde_storage_slot_value_delta() {
734 let slot_delta = StorageSlotDelta::with_empty_value();
735 let serialized = slot_delta.to_bytes();
736 let deserialized = StorageSlotDelta::read_from_bytes(&serialized).unwrap();
737 assert_eq!(deserialized, slot_delta);
738
739 let slot_delta = StorageSlotDelta::Value(Word::from([1, 2, 3, 4u32]));
740 let serialized = slot_delta.to_bytes();
741 let deserialized = StorageSlotDelta::read_from_bytes(&serialized).unwrap();
742 assert_eq!(deserialized, slot_delta);
743 }
744
745 #[test]
746 fn test_serde_storage_slot_map_delta() {
747 let slot_delta = StorageSlotDelta::with_empty_map();
748 let serialized = slot_delta.to_bytes();
749 let deserialized = StorageSlotDelta::read_from_bytes(&serialized).unwrap();
750 assert_eq!(deserialized, slot_delta);
751
752 let map_delta = StorageMapDelta::from_iters(
753 [StorageMapKey::from_array([1, 2, 3, 4])],
754 [(StorageMapKey::from_array([5, 6, 7, 8]), Word::from([3, 4, 5, 6u32]))],
755 );
756 let slot_delta = StorageSlotDelta::Map(map_delta);
757 let serialized = slot_delta.to_bytes();
758 let deserialized = StorageSlotDelta::read_from_bytes(&serialized).unwrap();
759 assert_eq!(deserialized, slot_delta);
760 }
761
762 #[rstest::rstest]
763 #[case::some_some(Some(1), Some(2), Some(2))]
764 #[case::none_some(None, Some(2), Some(2))]
765 #[case::some_none(Some(1), None, None)]
766 #[test]
767 fn merge_items(
768 #[case] x: Option<u32>,
769 #[case] y: Option<u32>,
770 #[case] expected: Option<u32>,
771 ) -> anyhow::Result<()> {
772 fn create_delta(item: Option<u32>) -> AccountStorageDelta {
774 let slot_name = StorageSlotName::mock(123);
775 let item = item.map(|x| (slot_name.clone(), Word::from([x, 0, 0, 0])));
776
777 AccountStorageDelta::new()
778 .add_cleared_items(item.is_none().then_some(slot_name.clone()))
779 .add_updated_values(item)
780 }
781
782 let mut delta_x = create_delta(x);
783 let delta_y = create_delta(y);
784 let expected = create_delta(expected);
785
786 delta_x.merge(delta_y).context("failed to merge deltas")?;
787
788 assert_eq!(delta_x, expected);
789
790 Ok(())
791 }
792
793 #[rstest::rstest]
794 #[case::some_some(Some(1), Some(2), Some(2))]
795 #[case::none_some(None, Some(2), Some(2))]
796 #[case::some_none(Some(1), None, None)]
797 #[test]
798 fn merge_maps(#[case] x: Option<u32>, #[case] y: Option<u32>, #[case] expected: Option<u32>) {
799 fn create_delta(value: Option<u32>) -> StorageMapDelta {
800 let key = StorageMapKey::from_array([10, 0, 0, 0]);
801 match value {
802 Some(value) => {
803 StorageMapDelta::from_iters([], [(key, Word::from([value, 0, 0, 0]))])
804 },
805 None => StorageMapDelta::from_iters([key], []),
806 }
807 }
808
809 let mut delta_x = create_delta(x);
810 let delta_y = create_delta(y);
811 let expected = create_delta(expected);
812
813 delta_x.merge(delta_y);
814
815 assert_eq!(delta_x, expected);
816 }
817}