1#![allow(clippy::must_use_candidate)]
2#![allow(clippy::missing_const_for_fn)]
3#![allow(clippy::cast_possible_wrap)]
4#![allow(clippy::missing_errors_doc)]
5#![allow(clippy::too_many_lines)]
6#![allow(clippy::missing_panics_doc)]
7#![allow(clippy::cast_possible_truncation)]
8#![allow(clippy::cast_sign_loss)]
9use std::collections::HashMap;
10
11use alloy::primitives::Bytes;
12use num_bigint::BigUint;
13use rayon::prelude::*;
14
15use crate::errors::CompressorError;
16
17pub type Bytes32 = [u8; 32];
18
19#[derive(Debug, Clone)]
21pub struct CompressDataDescription {
22 pub start_byte: usize, pub amount_bytes: usize, pub method: u8, }
26
27impl CompressDataDescription {
28 pub fn new(start_byte: usize, amount_bytes: usize, method: u8) -> Self {
29 Self {
30 start_byte,
31 amount_bytes,
32 method,
33 }
34 }
35}
36
37#[derive(Debug, Clone, Default)]
39pub struct CompressDataPower {
40 pub decompressed_size: usize, pub compressed_size: usize, }
43
44impl CompressDataPower {
45 pub fn new(decompressed_size: usize, compressed_size: usize) -> Self {
46 Self {
47 decompressed_size,
48 compressed_size,
49 }
50 }
51
52 pub fn range(&self) -> i64 {
54 self.decompressed_size as i64 - self.compressed_size as i64
55 }
56
57 pub fn add(&mut self, other: &Self) {
59 self.decompressed_size += other.decompressed_size;
60 self.compressed_size += other.compressed_size;
61 }
62}
63
64#[derive(Default, Debug, Clone)]
66pub struct CompressData {
67 pub power: CompressDataPower, pub descriptions: Vec<CompressDataDescription>, }
70
71impl CompressData {
72 pub fn new(power: CompressDataPower, descriptions: Vec<CompressDataDescription>) -> Self {
73 Self {
74 power,
75 descriptions,
76 }
77 }
78}
79
80#[derive(Default, Debug, Clone)]
81pub struct ByteInfo {
82 pub index: usize,
83 pub zero_compress: CompressDataPower,
84 pub copy_compress: CompressDataPower,
85 pub storage_compress: Vec<CompressDataPower>,
86}
87
88impl ByteInfo {
89 pub fn new(
90 index: usize,
91 zero_compress: CompressDataPower,
92 copy_compress: CompressDataPower,
93 storage_compress: Vec<CompressDataPower>,
94 ) -> Self {
95 Self {
96 index,
97 zero_compress,
98 copy_compress,
99 storage_compress,
100 }
101 }
102}
103
104#[derive(Debug, Clone)]
107pub struct Calldata {
108 pub data: Bytes,
109 pub wallet_addr: Bytes32,
110 pub contract_addr: Bytes32,
111 pub bytes_info: Vec<ByteInfo>,
112 pub dict: Vec<Bytes32>, pub lookup: HashMap<Vec<u8>, usize>, }
115
116impl Calldata {
117 pub fn new(
118 data: Bytes,
119 wallet_addr: Bytes32,
120 contract_addr: Bytes32,
121 ) -> Result<Self, CompressorError> {
122 let len = data.len();
123 Ok(Self {
124 data,
125 wallet_addr,
126 contract_addr,
127 bytes_info: vec![ByteInfo::default(); len],
128 dict: Vec::new(),
129 lookup: HashMap::new(),
130 })
131 }
132
133 pub fn analyse(&mut self) {
134 for i in 0..self.data.len() {
135 self.bytes_info[i] = ByteInfo {
136 index: i,
137 zero_compress: self.check_zeros_case(i),
138 copy_compress: self.check_copy_case_with_zeros(i),
139 storage_compress: self.check_storage_case(i).unwrap_or_default(),
140 };
141 }
142 }
143
144 pub fn create_desc(
145 &self,
146 from_byte: usize,
147 array_desc: &[CompressDataDescription],
148 amount_bytes: usize,
149 method: u8,
150 ) -> CompressDataDescription {
151 let start_byte: usize = if array_desc.is_empty() {
152 from_byte
153 } else {
154 let prev_desc_index = array_desc.len() - 1;
155 array_desc[prev_desc_index].start_byte + array_desc[prev_desc_index].amount_bytes
156 };
157 CompressDataDescription {
158 start_byte,
159 amount_bytes,
160 method,
161 }
162 }
163
164 pub fn add_just_copy_compress(
165 &self,
166 from_byte: usize,
167 mut result_compress: CompressData,
168 amount: usize,
169 ) -> CompressData {
170 if amount != 0 {
171 result_compress.power.add(&CompressDataPower {
172 decompressed_size: amount,
173 compressed_size: 1 + amount,
174 });
175 result_compress.descriptions.push(self.create_desc(
176 from_byte,
177 &result_compress.descriptions,
178 amount,
179 0x01,
180 ));
181 }
182 result_compress
183 }
184
185 pub fn compress_part(&self, from_byte: usize, to_byte: usize) -> CompressData {
186 let mut part_compress = CompressData {
187 power: CompressDataPower {
188 decompressed_size: 0,
189 compressed_size: 0,
190 },
191 descriptions: vec![],
192 };
193 let mut just_copy_amount: usize = 0;
194
195 let mut i = from_byte;
196 while i <= to_byte {
197 if self.bytes_info[i].zero_compress.decompressed_size > to_byte - i {
198 part_compress =
199 self.add_just_copy_compress(from_byte, part_compress, just_copy_amount);
200 part_compress.power.add(&CompressDataPower {
201 decompressed_size: to_byte - from_byte + 1,
202 compressed_size: 1,
203 });
204 part_compress.descriptions.push(CompressDataDescription {
205 start_byte: i,
206 amount_bytes: to_byte - i + 1,
207 method: 0x00,
208 });
209 return part_compress;
210 }
211
212 let mut zero_bytes_amount = 0;
213 let mut is_padding_with_copy = false;
214 let mut need_just_copy_amount = true;
215
216 if self.bytes_info[i].zero_compress.decompressed_size != 0 {
217 if self.bytes_info[i].copy_compress.decompressed_size > to_byte - i
218 || self.bytes_info[i].zero_compress.range()
219 > self.bytes_info[i].copy_compress.range()
220 {
221 zero_bytes_amount = self.bytes_info[i].zero_compress.decompressed_size;
222 } else {
223 is_padding_with_copy = true;
224 }
225 }
226 let mut is_storage_compress_used: bool = false;
227 let is_zero_compress: bool = zero_bytes_amount > 0;
228 for j in 0..self.bytes_info[i].storage_compress.len() {
229 if self.bytes_info[i].storage_compress[j].decompressed_size <= to_byte - i + 1 {
230 let is_storage_range_more_than_copy_compress =
231 self.bytes_info[i].storage_compress[j].range()
232 > self.bytes_info[i].copy_compress.range();
233
234 if !is_zero_compress
235 && !is_storage_range_more_than_copy_compress
236 && !is_padding_with_copy
237 {
238 continue;
239 }
240
241 part_compress =
242 self.add_just_copy_compress(from_byte, part_compress, just_copy_amount);
243
244 if is_zero_compress {
245 if self.bytes_info[i].storage_compress[j].range()
246 > self.bytes_info[i].zero_compress.range()
247 {
248 part_compress
249 .power
250 .add(&self.bytes_info[i].storage_compress[j]);
251 part_compress.descriptions.push(self.create_desc(
252 from_byte,
253 &part_compress.descriptions,
254 self.bytes_info[i].storage_compress[j].decompressed_size,
255 if self.bytes_info[i].storage_compress[j].compressed_size == 2 {
256 0x10
257 } else {
258 0x11
259 },
260 ));
261 i += self.bytes_info[i].storage_compress[j].decompressed_size;
262 } else {
263 part_compress.power.add(&self.bytes_info[i].zero_compress);
264 part_compress.descriptions.push(self.create_desc(
265 from_byte,
266 &part_compress.descriptions,
267 zero_bytes_amount,
268 0x00,
269 ));
270 i += zero_bytes_amount;
271 }
272 } else if is_storage_range_more_than_copy_compress {
273 part_compress
274 .power
275 .add(&self.bytes_info[i].storage_compress[j]);
276 part_compress.descriptions.push(self.create_desc(
277 from_byte,
278 &part_compress.descriptions,
279 self.bytes_info[i].storage_compress[j].decompressed_size,
280 if self.bytes_info[i].storage_compress[j].compressed_size == 2 {
281 0x10
282 } else {
283 0x11
284 },
285 ));
286 i += self.bytes_info[i].storage_compress[j].decompressed_size;
287 } else if is_padding_with_copy {
288 part_compress.power.add(&self.bytes_info[i].copy_compress);
289 part_compress.descriptions.push(self.create_desc(
290 from_byte,
291 &part_compress.descriptions,
292 self.bytes_info[i].copy_compress.decompressed_size,
293 0x01,
294 ));
295 i += self.bytes_info[i].copy_compress.decompressed_size;
296 }
297
298 just_copy_amount = 0;
299 need_just_copy_amount = false;
300 is_storage_compress_used = true;
301 break;
302 }
303 }
304
305 if !is_storage_compress_used {
306 if is_zero_compress || is_padding_with_copy {
307 part_compress =
308 self.add_just_copy_compress(from_byte, part_compress, just_copy_amount);
309 }
310
311 if is_zero_compress {
312 part_compress.power.add(&self.bytes_info[i].zero_compress);
313 part_compress.descriptions.push(self.create_desc(
314 from_byte,
315 &part_compress.descriptions,
316 zero_bytes_amount,
317 0x00,
318 ));
319 i += zero_bytes_amount;
320 } else if is_padding_with_copy {
321 part_compress.power.add(&self.bytes_info[i].copy_compress);
322 part_compress.descriptions.push(self.create_desc(
323 from_byte,
324 &part_compress.descriptions,
325 self.bytes_info[i].copy_compress.decompressed_size,
326 0x01,
327 ));
328 i += self.bytes_info[i].copy_compress.decompressed_size;
329 }
330
331 if is_zero_compress || is_padding_with_copy {
332 just_copy_amount = 0;
333 need_just_copy_amount = false;
334 }
335 }
336 if need_just_copy_amount {
337 let new_just_copy_amount = std::cmp::min(
338 self.bytes_info[i].copy_compress.decompressed_size,
339 to_byte - i + 1,
340 );
341 just_copy_amount += new_just_copy_amount;
342 if just_copy_amount > 32 {
343 part_compress = self.add_just_copy_compress(from_byte, part_compress, 32);
344 just_copy_amount -= 32;
345 }
346 i += new_just_copy_amount;
347 }
348 }
349
350 part_compress = self.add_just_copy_compress(from_byte, part_compress, just_copy_amount);
351
352 part_compress
353 }
354
355 pub fn zip(
356 &self,
357 descriptions: &[CompressDataDescription],
358 ) -> Result<Vec<u8>, CompressorError> {
359 let mut result: Vec<u8> = Vec::new();
360 let bb = [32, 20, 4, 31];
361 for description in descriptions {
362 match description.method {
363 0x00 => {
364 result.push((description.amount_bytes - 1) as u8);
366 }
367 0x01 => {
368 let copy_bytes =
370 self.get_bytes(description.start_byte, description.amount_bytes)?;
371 let mut non_zero_byte_index = 0;
372 for (j, _) in copy_bytes.iter().enumerate().take(description.amount_bytes) {
373 if copy_bytes[j] != 0x00 {
374 non_zero_byte_index = j;
375 break;
376 }
377 }
378 result.push(
379 ((description.amount_bytes - non_zero_byte_index - 1)
380 + 64
381 + if non_zero_byte_index == 0 { 0 } else { 32 })
382 as u8,
383 );
384 let copy_bytes = self.get_bytes(
385 description.start_byte + non_zero_byte_index,
386 description.amount_bytes - non_zero_byte_index,
387 )?;
388 result.extend(copy_bytes);
389 }
390 0x10 => {
391 let index = *self
393 .lookup
394 .get(self.get_bytes(description.start_byte, description.amount_bytes)?)
395 .ok_or(CompressorError::LookupNotFound)?;
396 result.extend(
397 BigUint::from(
398 index
399 + 2_u64.pow(15) as usize
400 + (bb
401 .par_iter()
402 .position_first(|&r| r == description.amount_bytes)
403 .unwrap()
404 * 2_u64.pow(12) as usize),
405 )
406 .to_bytes_be(),
407 );
408 }
409 0x11 => {
410 let index = *self
412 .lookup
413 .get(self.get_bytes(description.start_byte, description.amount_bytes)?)
414 .ok_or(CompressorError::LookupNotFound)?;
415 result.extend(
416 BigUint::from(
417 index
418 + 3 * 2_u64.pow(22) as usize
419 + (bb
420 .par_iter()
421 .position_first(|&r| r == description.amount_bytes)
422 .unwrap()
423 * 2_u64.pow(20) as usize),
424 )
425 .to_bytes_be(),
426 );
427 }
428 _ => {
429 return Err(CompressorError::UnsupportedMethod(description.method));
430 }
431 }
432 }
433 Ok(result)
434 }
435
436 pub fn compress(&mut self) -> Result<CompressResult, CompressorError> {
437 self.analyse();
438 let mut best_compress_for_first_n_bytes: Vec<CompressData> =
439 vec![CompressData::default(); self.bytes_info.len()];
440
441 if self.bytes_info[0].zero_compress.decompressed_size != 0 {
442 best_compress_for_first_n_bytes[0] = CompressData {
443 power: CompressDataPower {
444 decompressed_size: 1,
445 compressed_size: 1,
446 },
447 descriptions: vec![CompressDataDescription {
448 start_byte: 0,
449 amount_bytes: 1,
450 method: 0x00,
451 }],
452 };
453 } else {
454 best_compress_for_first_n_bytes[0] = CompressData {
455 power: CompressDataPower {
456 decompressed_size: 1,
457 compressed_size: 2,
458 },
459 descriptions: vec![CompressDataDescription {
460 start_byte: 0,
461 amount_bytes: 1,
462 method: 0x01,
463 }],
464 };
465 }
466
467 for i in 1..self.bytes_info.len() {
468 best_compress_for_first_n_bytes[i] = CompressData {
469 power: CompressDataPower {
470 decompressed_size: best_compress_for_first_n_bytes[i - 1]
471 .power
472 .decompressed_size
473 + 1,
474 compressed_size: best_compress_for_first_n_bytes[i - 1].power.compressed_size
475 + 2,
476 },
477 descriptions: [
478 best_compress_for_first_n_bytes[i - 1].descriptions.clone(),
479 vec![CompressDataDescription {
480 start_byte: i,
481 amount_bytes: 1,
482 method: 0x01,
483 }],
484 ]
485 .concat(),
486 };
487
488 for j in (std::cmp::max(0, i as isize - 63) as usize..=i).rev() {
489 let part_compress = self.compress_part(j, i);
490
491 let mut prefix_compress = CompressData {
492 power: CompressDataPower::default(),
493 descriptions: Vec::new(),
494 };
495
496 if part_compress.descriptions[0].start_byte != 0 {
497 let prev_desc_index = part_compress.descriptions[0].start_byte - 1;
498 prefix_compress.power = best_compress_for_first_n_bytes[prev_desc_index]
499 .power
500 .clone();
501 prefix_compress.descriptions = best_compress_for_first_n_bytes[prev_desc_index]
502 .descriptions
503 .clone();
504 }
505
506 if prefix_compress.power.range() + part_compress.power.range()
507 > best_compress_for_first_n_bytes[i].power.range()
508 {
509 best_compress_for_first_n_bytes[i] = CompressData {
510 power: CompressDataPower {
511 decompressed_size: prefix_compress.power.decompressed_size
512 + part_compress.power.decompressed_size,
513 compressed_size: prefix_compress.power.compressed_size
514 + part_compress.power.compressed_size,
515 },
516 descriptions: [prefix_compress.descriptions, part_compress.descriptions]
517 .concat(),
518 };
519 }
520 }
521
522 }
524
525 Ok(CompressResult {
526 uncompressed_data: self.data.clone(),
527 compressed_data: Bytes::from(
528 self.zip(
529 &best_compress_for_first_n_bytes
530 .last()
531 .unwrap()
532 .descriptions
533 .clone(),
534 )?,
535 ),
536 power: best_compress_for_first_n_bytes
537 .last()
538 .unwrap()
539 .power
540 .clone(),
541 description: best_compress_for_first_n_bytes
542 .last()
543 .unwrap()
544 .descriptions
545 .clone(),
546 })
547 }
548
549 pub fn get_byte(&self, n: usize) -> Result<&u8, CompressorError> {
550 self.data.get(n).ok_or(CompressorError::InvalidRange)
551 }
552
553 pub fn get_bytes(&self, start: usize, n: usize) -> Result<&[u8], CompressorError> {
554 let end = std::cmp::min(start + n, self.data.len());
555 if start >= end {
556 return Err(CompressorError::InvalidRange);
557 }
558 self.data
559 .get(start..end)
560 .ok_or(CompressorError::InvalidRange)
561 }
562
563 pub fn init_dict(&mut self, dict: &[Bytes32]) {
564 let mut dict_data = vec![self.wallet_addr, self.contract_addr];
565 dict_data.extend(dict);
566 self.dict = dict_data;
567
568 self.dict.iter().enumerate().for_each(|(i, data)| {
569 let value: Vec<u8> = data.to_vec();
570 self.lookup.insert(value.clone(), i);
571 self.lookup.insert(value[value.len() - 4..].to_vec(), i);
572 self.lookup.insert(value[value.len() - 20..].to_vec(), i);
573 self.lookup.insert(value[value.len() - 31..].to_vec(), i);
574 });
575 }
576
577 pub fn check_zeros_case(&self, n: usize) -> CompressDataPower {
579 let mut current_byte_index = n;
580 let byte = self.get_byte(current_byte_index);
581 if !byte.is_ok_and(|x| *x == 0x00) {
582 return CompressDataPower {
583 decompressed_size: 0,
584 compressed_size: 0,
585 };
586 }
587 current_byte_index += 1;
588 while self.get_byte(current_byte_index).is_ok_and(|x| *x == 0x00)
590 && current_byte_index < self.data.len()
591 && current_byte_index - n <= 63
592 {
593 current_byte_index += 1;
594 }
595 CompressDataPower {
596 decompressed_size: current_byte_index - n,
597 compressed_size: 1,
598 }
599 }
600
601 pub fn check_copy_case_with_zeros(&self, n: usize) -> CompressDataPower {
603 let mut current_byte_index = n;
604 let byte = self.get_byte(current_byte_index);
605 if !byte.is_ok_and(|x| *x == 0x00) {
606 return CompressDataPower {
609 decompressed_size: 1,
610 compressed_size: 2,
611 };
612 }
613 current_byte_index += 1;
614 while self.get_byte(current_byte_index).is_ok_and(|x| *x == 0x00)
616 && current_byte_index < self.data.len()
617 {
618 if current_byte_index - n == 32 {
619 return CompressDataPower {
620 decompressed_size: 31,
621 compressed_size: 32,
622 };
623 }
624 current_byte_index += 1;
625 }
626 let decompressed_bytes_amount = std::cmp::min(self.data.len() - n, 32);
627 CompressDataPower {
628 decompressed_size: decompressed_bytes_amount,
629 compressed_size: if decompressed_bytes_amount == 32 {
630 1 + 32 - (current_byte_index - n + 1)
631 } else {
632 1 + decompressed_bytes_amount
633 },
634 }
635 }
636
637 pub fn check_storage_case(&self, n: usize) -> Result<Vec<CompressDataPower>, CompressorError> {
639 if self.dict.is_empty() || self.lookup.is_empty() {
640 return Err(CompressorError::DictNotInit);
641 }
642
643 let mut best = Vec::<CompressDataPower>::new();
644 for len in &[32, 31, 20, 4] {
645 let tail = self.get_bytes(n, *len).unwrap();
646 let index = self.lookup.get(tail);
647 if let Some(index) = index {
648 if tail.len() >= *len {
649 best.push(CompressDataPower {
650 decompressed_size: *len,
651 compressed_size: if *index > 4096 { 3 } else { 2 }, });
653 }
654 }
655 }
656 Ok(best)
657 }
658}
659
660pub struct CompressResult {
661 pub uncompressed_data: Bytes,
662 pub compressed_data: Bytes,
663 pub power: CompressDataPower,
664 pub description: Vec<CompressDataDescription>,
665}
666
667pub fn compress(
668 calldata: Bytes,
669 wallet_addr: Bytes32,
670 contract_addr: Bytes32,
671 dict: &[Bytes32],
672) -> Result<CompressResult, CompressorError> {
673 let mut calldata = Calldata::new(calldata, wallet_addr, contract_addr).unwrap();
674 calldata.init_dict(dict);
675 calldata.compress()
676}
677
678#[cfg(test)]
679mod tests {
680 use std::{fs::File, io::Read, str::FromStr};
681
682 use serde::Deserialize;
683
684 use super::*;
685 use crate::assert_json_eq;
686 #[allow(dead_code)]
687 #[derive(Debug, Deserialize)]
688 struct TestData {
689 pub compress: String,
690 pub uncompress: String,
691 }
692
693 fn read_calldata_file(file_path: &str) -> Result<TestData, Box<dyn std::error::Error>> {
694 let mut file = File::open(file_path)?;
695
696 let mut contents = String::new();
697 file.read_to_string(&mut contents)?;
698
699 let data: TestData = serde_json::from_str(&contents)?;
700
701 Ok(data)
702 }
703
704 fn read_json_file(file_path: &str) -> Result<String, Box<dyn std::error::Error>> {
705 let mut file = File::open(file_path)?;
706
707 let mut contents = String::new();
708 file.read_to_string(&mut contents)?;
709
710 Ok(contents)
711 }
712
713 #[test]
714 fn test_compress_big() {
715 let empty_dict = vec![Bytes32::default(); 1];
717 let test_data = read_calldata_file("test-data/calldata.json").unwrap();
718 let calldata = test_data.uncompress.strip_prefix("0x").unwrap();
719 let expected_compress = test_data.compress.strip_prefix("0x").unwrap();
720 let calldata = Bytes::from(hex::decode(calldata).unwrap());
721 let wallet_addr = Bytes32::default();
722 let contract_addr = Bytes32::default();
723 let result = compress(calldata, wallet_addr, contract_addr, &empty_dict);
724 assert!(result.is_ok());
725 assert_eq!(
726 hex::encode(result.unwrap().compressed_data.to_vec()),
727 expected_compress
728 );
729 }
730
731 #[test]
732 fn test_compress_small() {
733 let empty_dict = vec![Bytes32::default(); 1];
734 let calldata = "0xf433d35e04bf7fea9df9ef9f80d4a91a3c3dec84540583b7103c7a69f7bbd4b7585ef752847ebec11584e73282b6dec46dd8ea6464d69f4003581960f39d8492000000000000000000000000000000000000000000000000000000000000001b0000000000000000000000000000000000000000000000000000000000003a13000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000258220761ade872c94f85e62f1b24a74eec792aaa3677b6201071fd05c1698e89000000000000000000000000000000000000000000000000000000000000000f00000000000000000000000000000000000000000000000000000033390598000000000000000000000000000000000000000000000000000000000010f7df4000000000000000000000000000000000000000000000000000000000001cab680000000000000000000000000000000000000000000000000000dcc54f790800000000000000000000000000000000000000000000000000000000000001ba1700000000000000000000000000000000000000000000000017ac92ba438492fe0000000000000000000000000000000000000000000000000000018d2f8b7e8858220761ade872c94f85e62f1b24a74eec792aaa3677b6201071fd05c1698e89000000000000000000000000000000000000000000000000000000000000000fffffffffffffffffffffffffffffffffffffffffffffffffffffffccc6fa68000000000000000000000000000000000000000000000000000000000010f7df4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dcc54f790800000000000000000000000000000000000000000000000000000000000001ba1800000000000000000000000000000000000000000000000017ac92ba438492fe0000000000000000000000000000000000000000000000000000018d2f8b7e88".strip_prefix("0x").unwrap();
735 let calldata = Bytes::from(hex::decode(calldata).unwrap());
736 let wallet_addr = Bytes32::default();
737 let contract_addr = Bytes32::default();
738
739 let mut cb = Calldata::new(calldata.clone(), wallet_addr, contract_addr).unwrap();
740 cb.init_dict(&empty_dict);
741 cb.analyse();
742
743 let mut zero_compresses: Vec<[usize; 2]> = vec![];
744 let mut copy_compress: Vec<[usize; 2]> = vec![];
745 for info in cb.bytes_info {
746 zero_compresses.push([
747 info.zero_compress.decompressed_size,
748 info.zero_compress.compressed_size,
749 ]);
750 copy_compress.push([
751 info.copy_compress.decompressed_size,
752 info.copy_compress.compressed_size,
753 ]);
754 }
755 let zero_compresses_json = serde_json::to_string(&zero_compresses).unwrap();
757 let copy_compress_json = serde_json::to_string(©_compress).unwrap();
758 let expected_zero_compress = read_json_file("test-data/zero_compress.json").unwrap();
759 assert_json_eq!(&zero_compresses_json, &expected_zero_compress);
760 let expected_copy_compress = read_json_file("test-data/copy_compress.json").unwrap();
761 assert_json_eq!(©_compress_json, &expected_copy_compress);
762
763 let result = compress(calldata, wallet_addr, contract_addr, &empty_dict);
764 assert!(result.is_ok());
765 assert_eq!(
766 hex::encode(result.unwrap().compressed_data.to_vec()),
767 "40f45f33d35e04bf7fea9df9ef9f80d4a91a3c3dec84540583b7103c7a69f7bbd4b7585e5ef752847ebec11584e73282b6dec46dd8ea6464d69f4003581960f39d849200611b001c413a13006102001d40c0006102585e220761ade872c94f85e62f1b24a74eec792aaa3677b6201071fd05c1698e8900610f00194333390598006310f7df4000631cab68001844dcc54f7908006201ba17006817ac92ba438492fe000067018d2f8b7e8858225d0761ade872c94f85e62f1b24a74eec792aaa3677b6201071fd05c1698e8900610fff5dffffffffffffffffffffffffffffffffffffffffffffffffffffccc6fa68006310f7df40003844dcc54f7908006201ba18006817ac92ba438492fe001845018d2f8b7e88"
768 );
769 }
770}