1use alloy::primitives::U256;
16
17use crate::{
18 error::Result,
19 storage::{Layout, Storable},
20};
21
22#[derive(Debug, Clone, Copy)]
24pub struct FieldLocation {
25 pub offset_slots: usize,
27 pub offset_bytes: usize,
29 pub size: usize,
31}
32
33impl FieldLocation {
34 #[inline]
36 pub const fn new(offset_slots: usize, offset_bytes: usize, size: usize) -> Self {
37 Self {
38 offset_slots,
39 offset_bytes,
40 size,
41 }
42 }
43}
44
45#[inline]
47pub fn is_packable(bytes: usize) -> bool {
48 bytes < 32 && 32 % bytes == 0
49}
50
51#[inline]
56pub fn create_element_mask(byte_count: usize) -> U256 {
57 if byte_count >= 32 {
58 U256::MAX
59 } else {
60 (U256::ONE << (byte_count * 8)) - U256::ONE
61 }
62}
63
64#[inline]
66pub fn extract_packed_value<const SLOTS: usize, T: Storable<SLOTS>>(
67 slot_value: U256,
68 offset: usize,
69 bytes: usize,
70) -> Result<T> {
71 debug_assert!(
72 matches!(T::LAYOUT, Layout::Bytes(..)),
73 "Packing is only supported by primitive types"
74 );
75
76 if offset + bytes > 32 {
78 return Err(crate::error::TempoPrecompileError::Fatal(format!(
79 "Value of {} bytes at offset {} would span slot boundary (max offset: {})",
80 bytes,
81 offset,
82 32 - bytes
83 )));
84 }
85
86 let shift_bits = offset * 8;
88
89 let mask = if bytes == 32 {
91 U256::MAX
92 } else {
93 (U256::ONE << (bytes * 8)) - U256::ONE
94 };
95
96 T::from_evm_words(std::array::from_fn(|_| (slot_value >> shift_bits) & mask))
98}
99
100#[inline]
102pub fn insert_packed_value<const SLOTS: usize, T: Storable<SLOTS>>(
103 current: U256,
104 value: &T,
105 offset: usize,
106 bytes: usize,
107) -> Result<U256> {
108 debug_assert!(
109 matches!(T::LAYOUT, Layout::Bytes(..)),
110 "Packing is only supported by primitive types"
111 );
112
113 if offset + bytes > 32 {
115 return Err(crate::error::TempoPrecompileError::Fatal(format!(
116 "Value of {} bytes at offset {} would span slot boundary (max offset: {})",
117 bytes,
118 offset,
119 32 - bytes
120 )));
121 }
122
123 let field_value = value.to_evm_words()?[0];
125
126 let shift_bits = offset * 8;
128 let mask = if bytes == 32 {
129 U256::MAX
130 } else {
131 (U256::ONE << (bytes * 8)) - U256::ONE
132 };
133
134 let clear_mask = !(mask << shift_bits);
136 let cleared = current & clear_mask;
137
138 let positioned = (field_value & mask) << shift_bits;
140 Ok(cleared | positioned)
141}
142
143#[inline]
148pub fn zero_packed_value(current: U256, offset: usize, bytes: usize) -> Result<U256> {
149 if offset + bytes > 32 {
151 return Err(crate::error::TempoPrecompileError::Fatal(format!(
152 "Value of {} bytes at offset {} would span slot boundary (max offset: {})",
153 bytes,
154 offset,
155 32 - bytes
156 )));
157 }
158
159 let mask = create_element_mask(bytes);
160 let shifted_mask = mask << (offset * 8);
161 Ok(current & !shifted_mask)
162}
163
164#[inline]
166pub const fn calc_element_slot(idx: usize, elem_bytes: usize) -> usize {
167 (idx * elem_bytes) / 32
168}
169
170#[inline]
172pub const fn calc_element_offset(idx: usize, elem_bytes: usize) -> usize {
173 (idx * elem_bytes) % 32
174}
175
176#[inline]
178pub const fn calc_packed_slot_count(n: usize, elem_bytes: usize) -> usize {
179 (n * elem_bytes).div_ceil(32)
180}
181
182pub fn extract_field<T: Storable<1>>(slot_value: U256, offset: usize, bytes: usize) -> Result<T> {
187 extract_packed_value(slot_value, offset, bytes)
188}
189
190pub fn gen_slot_from(values: &[&str]) -> U256 {
205 let mut bytes = Vec::new();
206
207 for value in values {
208 let hex_str = value.strip_prefix("0x").unwrap_or(value);
209
210 assert!(
212 hex_str.len() % 2 == 0,
213 "Hex string '{value}' has odd length"
214 );
215
216 for i in (0..hex_str.len()).step_by(2) {
217 let byte_str = &hex_str[i..i + 2];
218 let byte = u8::from_str_radix(byte_str, 16)
219 .unwrap_or_else(|e| panic!("Invalid hex in '{value}': {e}"));
220 bytes.push(byte);
221 }
222 }
223
224 assert!(
225 bytes.len() <= 32,
226 "Total bytes ({}) exceed 32-byte slot limit",
227 bytes.len()
228 );
229
230 let mut slot_bytes = [0u8; 32];
232 let start_idx = 32 - bytes.len();
233 slot_bytes[start_idx..].copy_from_slice(&bytes);
234
235 U256::from_be_bytes(slot_bytes)
236}
237
238#[cfg(test)]
239mod tests {
240 use super::*;
241 use alloy::primitives::Address;
242
243 #[test]
246 fn test_calc_element_slot() {
247 assert_eq!(calc_element_slot(0, 1), 0);
249 assert_eq!(calc_element_slot(31, 1), 0);
250 assert_eq!(calc_element_slot(32, 1), 1);
251 assert_eq!(calc_element_slot(63, 1), 1);
252 assert_eq!(calc_element_slot(64, 1), 2);
253
254 assert_eq!(calc_element_slot(0, 2), 0);
256 assert_eq!(calc_element_slot(15, 2), 0);
257 assert_eq!(calc_element_slot(16, 2), 1);
258
259 assert_eq!(calc_element_slot(0, 20), 0);
261 assert_eq!(calc_element_slot(1, 20), 0);
262 assert_eq!(calc_element_slot(2, 20), 1); }
264
265 #[test]
266 fn test_calc_element_offset() {
267 assert_eq!(calc_element_offset(0, 1), 0);
269 assert_eq!(calc_element_offset(1, 1), 1);
270 assert_eq!(calc_element_offset(31, 1), 31);
271 assert_eq!(calc_element_offset(32, 1), 0);
272
273 assert_eq!(calc_element_offset(0, 2), 0);
275 assert_eq!(calc_element_offset(1, 2), 2);
276 assert_eq!(calc_element_offset(15, 2), 30);
277 assert_eq!(calc_element_offset(16, 2), 0);
278
279 assert_eq!(calc_element_offset(0, 20), 0);
281 assert_eq!(calc_element_offset(1, 20), 20);
282 assert_eq!(calc_element_offset(2, 20), 8);
283 }
284
285 #[test]
286 fn test_calc_packed_slot_count() {
287 assert_eq!(calc_packed_slot_count(10, 1), 1); assert_eq!(calc_packed_slot_count(32, 1), 1); assert_eq!(calc_packed_slot_count(33, 1), 2); assert_eq!(calc_packed_slot_count(100, 1), 4); assert_eq!(calc_packed_slot_count(16, 2), 1); assert_eq!(calc_packed_slot_count(17, 2), 2); assert_eq!(calc_packed_slot_count(1, 20), 1); assert_eq!(calc_packed_slot_count(2, 20), 2); assert_eq!(calc_packed_slot_count(3, 20), 2); }
302
303 #[test]
304 fn test_create_element_mask() {
305 assert_eq!(create_element_mask(1), U256::from(0xff));
307
308 assert_eq!(create_element_mask(2), U256::from(0xffff));
310
311 assert_eq!(create_element_mask(4), U256::from(0xffffffffu32));
313
314 assert_eq!(create_element_mask(8), U256::from(u64::MAX));
316
317 assert_eq!(create_element_mask(16), U256::from(u128::MAX));
319
320 assert_eq!(create_element_mask(32), U256::MAX);
322
323 assert_eq!(create_element_mask(64), U256::MAX);
325 }
326
327 #[test]
328 fn test_zero_packed_value() {
329 let slot = gen_slot_from(&[
331 "0xff", "0x56", "0x34", "0x12", ]);
336
337 let cleared = zero_packed_value(slot, 1, 1).unwrap();
339 let expected = gen_slot_from(&[
340 "0xff", "0x56", "0x00", "0x12", ]);
345 assert_eq!(cleared, expected, "Should zero offset 1");
346
347 let slot = gen_slot_from(&["0x5678", "0x1234"]);
349 let cleared = zero_packed_value(slot, 0, 2).unwrap();
350 let expected = gen_slot_from(&["0x5678", "0x0000"]);
351 assert_eq!(cleared, expected, "Should zero u16 at offset 0");
352
353 let slot = gen_slot_from(&["0xff"]);
355 let cleared = zero_packed_value(slot, 0, 1).unwrap();
356 assert_eq!(cleared, U256::ZERO, "Should zero entire slot");
357 }
358
359 #[test]
360 fn test_extract_field_wrapper() {
361 let address = Address::random();
362 let mut slot = U256::ZERO;
363 slot = insert_packed_value(slot, &address, 0, 20).unwrap();
364
365 let extracted: Address = extract_field(slot, 0, 20).unwrap();
367 assert_eq!(extracted, address);
368 }
369
370 #[test]
373 fn test_boundary_validation_rejects_spanning() {
374 let addr = Address::random();
376 let result = insert_packed_value(U256::ZERO, &addr, 13, 20);
377 assert!(
378 result.is_err(),
379 "Should reject address at offset 13 (would span slot)"
380 );
381
382 let val: u16 = 42;
384 let result = insert_packed_value(U256::ZERO, &val, 31, 2);
385 assert!(
386 result.is_err(),
387 "Should reject u16 at offset 31 (would span slot)"
388 );
389
390 let val: u32 = 42;
392 let result = insert_packed_value(U256::ZERO, &val, 29, 4);
393 assert!(
394 result.is_err(),
395 "Should reject u32 at offset 29 (would span slot)"
396 );
397
398 let result = extract_packed_value::<1, Address>(U256::ZERO, 13, 20);
400 assert!(
401 result.is_err(),
402 "Should reject extracting address from offset 13"
403 );
404 }
405
406 #[test]
407 fn test_boundary_validation_accepts_valid() {
408 let addr = Address::random();
410 let result = insert_packed_value(U256::ZERO, &addr, 12, 20);
411 assert!(result.is_ok(), "Should accept address at offset 12");
412
413 let val: u16 = 42;
415 let result = insert_packed_value(U256::ZERO, &val, 30, 2);
416 assert!(result.is_ok(), "Should accept u16 at offset 30");
417
418 let val: u8 = 42;
420 let result = insert_packed_value(U256::ZERO, &val, 31, 1);
421 assert!(result.is_ok(), "Should accept u8 at offset 31");
422
423 let val = U256::from(42);
425 let result = insert_packed_value(U256::ZERO, &val, 0, 32);
426 assert!(result.is_ok(), "Should accept U256 at offset 0");
427 }
428
429 #[test]
432 fn test_bool() {
433 let expected = gen_slot_from(&[
435 "0x01", ]);
437
438 let slot = insert_packed_value(U256::ZERO, &true, 0, 1).unwrap();
439 assert_eq!(
440 slot, expected,
441 "Single bool [true] should match Solidity layout"
442 );
443 assert!(extract_packed_value::<1, bool>(slot, 0, 1).unwrap());
444
445 let expected = gen_slot_from(&[
447 "0x01", "0x01", ]);
450
451 let mut slot = U256::ZERO;
452 slot = insert_packed_value(slot, &true, 0, 1).unwrap();
453 slot = insert_packed_value(slot, &true, 1, 1).unwrap();
454 assert_eq!(slot, expected, "[true, true] should match Solidity layout");
455 assert!(extract_packed_value::<1, bool>(slot, 0, 1).unwrap());
456 assert!(extract_packed_value::<1, bool>(slot, 1, 1).unwrap());
457 }
458
459 #[test]
460 fn test_u8_packing() {
461 let v1: u8 = 0x12;
463 let v2: u8 = 0x34;
464 let v3: u8 = 0x56;
465 let v4: u8 = u8::MAX;
466
467 let expected = gen_slot_from(&[
468 "0xff", "0x56", "0x34", "0x12", ]);
473
474 let mut slot = U256::ZERO;
475 slot = insert_packed_value(slot, &v1, 0, 1).unwrap();
476 slot = insert_packed_value(slot, &v2, 1, 1).unwrap();
477 slot = insert_packed_value(slot, &v3, 2, 1).unwrap();
478 slot = insert_packed_value(slot, &v4, 3, 1).unwrap();
479
480 assert_eq!(slot, expected, "u8 packing should match Solidity layout");
481 assert_eq!(extract_packed_value::<1, u8>(slot, 0, 1).unwrap(), v1);
482 assert_eq!(extract_packed_value::<1, u8>(slot, 1, 1).unwrap(), v2);
483 assert_eq!(extract_packed_value::<1, u8>(slot, 2, 1).unwrap(), v3);
484 assert_eq!(extract_packed_value::<1, u8>(slot, 3, 1).unwrap(), v4);
485 }
486
487 #[test]
488 fn test_u16_packing() {
489 let v1: u16 = 0x1234;
491 let v2: u16 = 0x5678;
492 let v3: u16 = u16::MAX;
493
494 let expected = gen_slot_from(&[
495 "0xffff", "0x5678", "0x1234", ]);
499
500 let mut slot = U256::ZERO;
501 slot = insert_packed_value(slot, &v1, 0, 2).unwrap();
502 slot = insert_packed_value(slot, &v2, 2, 2).unwrap();
503 slot = insert_packed_value(slot, &v3, 4, 2).unwrap();
504
505 assert_eq!(slot, expected, "u16 packing should match Solidity layout");
506 assert_eq!(extract_packed_value::<1, u16>(slot, 0, 2).unwrap(), v1);
507 assert_eq!(extract_packed_value::<1, u16>(slot, 2, 2).unwrap(), v2);
508 assert_eq!(extract_packed_value::<1, u16>(slot, 4, 2).unwrap(), v3);
509 }
510
511 #[test]
512 fn test_u32_packing() {
513 let v1: u32 = 0x12345678;
515 let v2: u32 = u32::MAX;
516
517 let expected = gen_slot_from(&[
518 "0xffffffff", "0x12345678", ]);
521
522 let mut slot = U256::ZERO;
523 slot = insert_packed_value(slot, &v1, 0, 4).unwrap();
524 slot = insert_packed_value(slot, &v2, 4, 4).unwrap();
525
526 assert_eq!(slot, expected, "u32 packing should match Solidity layout");
527 assert_eq!(extract_packed_value::<1, u32>(slot, 0, 4).unwrap(), v1);
528 assert_eq!(extract_packed_value::<1, u32>(slot, 4, 4).unwrap(), v2);
529 }
530
531 #[test]
532 fn test_u64_packing() {
533 let v1: u64 = 0x123456789abcdef0;
535 let v2: u64 = u64::MAX;
536
537 let expected = gen_slot_from(&[
538 "0xffffffffffffffff", "0x123456789abcdef0", ]);
541
542 let mut slot = U256::ZERO;
543 slot = insert_packed_value(slot, &v1, 0, 8).unwrap();
544 slot = insert_packed_value(slot, &v2, 8, 8).unwrap();
545
546 assert_eq!(slot, expected, "u64 packing should match Solidity layout");
547 assert_eq!(extract_packed_value::<1, u64>(slot, 0, 8).unwrap(), v1);
548 assert_eq!(extract_packed_value::<1, u64>(slot, 8, 8).unwrap(), v2);
549 }
550
551 #[test]
552 fn test_u128_packing() {
553 let v1: u128 = 0x123456789abcdef0fedcba9876543210;
555 let v2: u128 = u128::MAX;
556
557 let expected = gen_slot_from(&[
558 "0xffffffffffffffffffffffffffffffff", "0x123456789abcdef0fedcba9876543210", ]);
561
562 let mut slot = U256::ZERO;
563 slot = insert_packed_value(slot, &v1, 0, 16).unwrap();
564 slot = insert_packed_value(slot, &v2, 16, 16).unwrap();
565
566 assert_eq!(slot, expected, "u128 packing should match Solidity layout");
567 assert_eq!(extract_packed_value::<1, u128>(slot, 0, 16).unwrap(), v1);
568 assert_eq!(extract_packed_value::<1, u128>(slot, 16, 16).unwrap(), v2);
569 }
570
571 #[test]
572 fn test_u256_packing() {
573 let value = U256::from_be_bytes([
575 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54,
576 0x32, 0x10, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc,
577 0xdd, 0xee, 0xff, 0x00,
578 ]);
579
580 let expected =
581 gen_slot_from(&["0x123456789abcdef0fedcba9876543210112233445566778899aabbccddeeff00"]);
582
583 let slot = insert_packed_value(U256::ZERO, &value, 0, 32).unwrap();
584 assert_eq!(slot, expected, "u256 packing should match Solidity layout");
585 assert_eq!(extract_packed_value::<1, U256>(slot, 0, 32).unwrap(), value);
586
587 let slot = insert_packed_value(U256::ZERO, &U256::MAX, 0, 32).unwrap();
589 assert_eq!(
590 extract_packed_value::<1, U256>(slot, 0, 32).unwrap(),
591 U256::MAX
592 );
593 }
594
595 #[test]
596 fn test_i8_packing() {
597 let v1: i8 = -128; let v2: i8 = 0;
600 let v3: i8 = 127; let v4: i8 = -1;
602
603 let expected = gen_slot_from(&[
604 "0xff", "0x7f", "0x00", "0x80", ]);
609
610 let mut slot = U256::ZERO;
611 slot = insert_packed_value(slot, &v1, 0, 1).unwrap();
612 slot = insert_packed_value(slot, &v2, 1, 1).unwrap();
613 slot = insert_packed_value(slot, &v3, 2, 1).unwrap();
614 slot = insert_packed_value(slot, &v4, 3, 1).unwrap();
615
616 assert_eq!(slot, expected, "i8 packing should match Solidity layout");
617 assert_eq!(extract_packed_value::<1, i8>(slot, 0, 1).unwrap(), v1);
618 assert_eq!(extract_packed_value::<1, i8>(slot, 1, 1).unwrap(), v2);
619 assert_eq!(extract_packed_value::<1, i8>(slot, 2, 1).unwrap(), v3);
620 assert_eq!(extract_packed_value::<1, i8>(slot, 3, 1).unwrap(), v4);
621 }
622
623 #[test]
624 fn test_i16_packing() {
625 let v1: i16 = -32768; let v2: i16 = 32767; let v3: i16 = -1;
629
630 let expected = gen_slot_from(&[
631 "0xffff", "0x7fff", "0x8000", ]);
635
636 let mut slot = U256::ZERO;
637 slot = insert_packed_value(slot, &v1, 0, 2).unwrap();
638 slot = insert_packed_value(slot, &v2, 2, 2).unwrap();
639 slot = insert_packed_value(slot, &v3, 4, 2).unwrap();
640
641 assert_eq!(slot, expected, "i16 packing should match Solidity layout");
642 assert_eq!(extract_packed_value::<1, i16>(slot, 0, 2).unwrap(), v1);
643 assert_eq!(extract_packed_value::<1, i16>(slot, 2, 2).unwrap(), v2);
644 assert_eq!(extract_packed_value::<1, i16>(slot, 4, 2).unwrap(), v3);
645 }
646
647 #[test]
648 fn test_i32_packing() {
649 let v1: i32 = -2147483648; let v2: i32 = 2147483647; let expected = gen_slot_from(&[
654 "0x7fffffff", "0x80000000", ]);
657
658 let mut slot = U256::ZERO;
659 slot = insert_packed_value(slot, &v1, 0, 4).unwrap();
660 slot = insert_packed_value(slot, &v2, 4, 4).unwrap();
661
662 assert_eq!(slot, expected, "i32 packing should match Solidity layout");
663 assert_eq!(extract_packed_value::<1, i32>(slot, 0, 4).unwrap(), v1);
664 assert_eq!(extract_packed_value::<1, i32>(slot, 4, 4).unwrap(), v2);
665 }
666
667 #[test]
668 fn test_i64_packing() {
669 let v1: i64 = -9223372036854775808; let v2: i64 = 9223372036854775807; let expected = gen_slot_from(&[
674 "0x7fffffffffffffff", "0x8000000000000000", ]);
677
678 let mut slot = U256::ZERO;
679 slot = insert_packed_value(slot, &v1, 0, 8).unwrap();
680 slot = insert_packed_value(slot, &v2, 8, 8).unwrap();
681
682 assert_eq!(slot, expected, "i64 packing should match Solidity layout");
683 assert_eq!(extract_packed_value::<1, i64>(slot, 0, 8).unwrap(), v1);
684 assert_eq!(extract_packed_value::<1, i64>(slot, 8, 8).unwrap(), v2);
685 }
686
687 #[test]
688 fn test_i128_packing() {
689 let v1: i128 = -170141183460469231731687303715884105728; let v2: i128 = 170141183460469231731687303715884105727; let expected = gen_slot_from(&[
694 "0x7fffffffffffffffffffffffffffffff", "0x80000000000000000000000000000000", ]);
697
698 let mut slot = U256::ZERO;
699 slot = insert_packed_value(slot, &v1, 0, 16).unwrap();
700 slot = insert_packed_value(slot, &v2, 16, 16).unwrap();
701
702 assert_eq!(slot, expected, "i128 packing should match Solidity layout");
703 assert_eq!(extract_packed_value::<1, i128>(slot, 0, 16).unwrap(), v1);
704 assert_eq!(extract_packed_value::<1, i128>(slot, 16, 16).unwrap(), v2);
705 }
706
707 #[test]
708 fn test_mixed_uint_packing() {
709 let v1: u8 = 0xaa;
711 let v2: u16 = 0xbbcc;
712 let v3: u32 = 0xddeeff00;
713 let v4: u64 = 0x1122334455667788;
714
715 let expected = gen_slot_from(&[
716 "0x1122334455667788", "0xddeeff00", "0xbbcc", "0xaa", ]);
721
722 let mut slot = U256::ZERO;
723 slot = insert_packed_value(slot, &v1, 0, 1).unwrap();
724 slot = insert_packed_value(slot, &v2, 1, 2).unwrap();
725 slot = insert_packed_value(slot, &v3, 3, 4).unwrap();
726 slot = insert_packed_value(slot, &v4, 7, 8).unwrap();
727
728 assert_eq!(
729 slot, expected,
730 "Mixed types packing should match Solidity layout"
731 );
732 assert_eq!(extract_packed_value::<1, u8>(slot, 0, 1).unwrap(), v1);
733 assert_eq!(extract_packed_value::<1, u16>(slot, 1, 2).unwrap(), v2);
734 assert_eq!(extract_packed_value::<1, u32>(slot, 3, 4).unwrap(), v3);
735 assert_eq!(extract_packed_value::<1, u64>(slot, 7, 8).unwrap(), v4);
736 }
737
738 #[test]
739 fn test_mixed_type_packing() {
740 let addr = Address::from([0x11; 20]);
741 let number: u8 = 0x2a;
742
743 let expected = gen_slot_from(&[
744 "0x2a", "0x1111111111111111111111111111111111111111", "0x01", ]);
748
749 let mut slot = U256::ZERO;
750 slot = insert_packed_value(slot, &true, 0, 1).unwrap();
751 slot = insert_packed_value(slot, &addr, 1, 20).unwrap();
752 slot = insert_packed_value(slot, &number, 21, 1).unwrap();
753 assert_eq!(
754 slot, expected,
755 "[bool, address, u8] should match Solidity layout"
756 );
757 assert!(extract_packed_value::<1, bool>(slot, 0, 1).unwrap());
758 assert_eq!(
759 extract_packed_value::<1, Address>(slot, 1, 20).unwrap(),
760 addr
761 );
762 assert_eq!(extract_packed_value::<1, u8>(slot, 21, 1).unwrap(), number);
763 }
764
765 #[test]
766 fn test_zero_values() {
767 let v1: u8 = 0;
769 let v2: u16 = 0;
770 let v3: u32 = 0;
771
772 let expected = U256::ZERO;
773
774 let mut slot = U256::ZERO;
775 slot = insert_packed_value(slot, &v1, 0, 1).unwrap();
776 slot = insert_packed_value(slot, &v2, 1, 2).unwrap();
777 slot = insert_packed_value(slot, &v3, 3, 4).unwrap();
778
779 assert_eq!(slot, expected, "Zero values should produce zero slot");
780 assert_eq!(extract_packed_value::<1, u8>(slot, 0, 1).unwrap(), 0);
781 assert_eq!(extract_packed_value::<1, u16>(slot, 1, 2).unwrap(), 0);
782 assert_eq!(extract_packed_value::<1, u32>(slot, 3, 4).unwrap(), 0);
783
784 let v4: u8 = 0xff;
786 slot = insert_packed_value(slot, &v4, 10, 1).unwrap();
787 assert_eq!(extract_packed_value::<1, u8>(slot, 0, 1).unwrap(), 0);
788 assert_eq!(extract_packed_value::<1, u8>(slot, 10, 1).unwrap(), 0xff);
789 }
790
791 use crate::storage::{
794 PrecompileStorageProvider, StorageOps, hashmap::HashMapStorageProvider, types::Slot,
795 };
796
797 struct TestContract<'a, S> {
799 address: Address,
800 storage: &'a mut S,
801 }
802
803 impl<'a, S: PrecompileStorageProvider> StorageOps for TestContract<'a, S> {
804 fn sstore(&mut self, slot: U256, value: U256) -> Result<()> {
805 self.storage.sstore(self.address, slot, value)
806 }
807
808 fn sload(&mut self, slot: U256) -> Result<U256> {
809 self.storage.sload(self.address, slot)
810 }
811 }
812
813 fn setup_test_contract<'a>(
815 storage: &'a mut HashMapStorageProvider,
816 ) -> TestContract<'a, HashMapStorageProvider> {
817 TestContract {
818 address: Address::random(),
819 storage,
820 }
821 }
822
823 #[test]
824 fn test_packed_at_multiple_types() -> Result<()> {
825 let mut storage = HashMapStorageProvider::new(1);
826 let mut contract = setup_test_contract(&mut storage);
827 let struct_base = U256::from(0x2000);
828
829 let flag = true;
831 let timestamp: u64 = 1234567890;
832 let amount: u128 = 999888777666;
833
834 Slot::<bool>::new_at_loc(struct_base, FieldLocation::new(0, 0, 1))
835 .write(&mut contract, flag)?;
836 Slot::<u64>::new_at_loc(struct_base, FieldLocation::new(0, 1, 8))
837 .write(&mut contract, timestamp)?;
838 Slot::<u128>::new_at_loc(struct_base, FieldLocation::new(0, 9, 16))
839 .write(&mut contract, amount)?;
840
841 let read_flag = Slot::<bool>::new_at_loc(struct_base, FieldLocation::new(0, 0, 1))
843 .read(&mut contract)?;
844 let read_time = Slot::<u64>::new_at_loc(struct_base, FieldLocation::new(0, 1, 8))
845 .read(&mut contract)?;
846 let read_amount = Slot::<u128>::new_at_loc(struct_base, FieldLocation::new(0, 9, 16))
847 .read(&mut contract)?;
848
849 assert_eq!(read_flag, flag);
850 assert_eq!(read_time, timestamp);
851 assert_eq!(read_amount, amount);
852
853 Slot::<u64>::new_at_loc(struct_base, FieldLocation::new(0, 1, 8)).delete(&mut contract)?;
855
856 let read_flag = Slot::<bool>::new_at_loc(struct_base, FieldLocation::new(0, 0, 1))
858 .read(&mut contract)?;
859 let read_time = Slot::<u64>::new_at_loc(struct_base, FieldLocation::new(0, 1, 8))
860 .read(&mut contract)?;
861 let read_amount = Slot::<u128>::new_at_loc(struct_base, FieldLocation::new(0, 9, 16))
862 .read(&mut contract)?;
863
864 assert_eq!(read_flag, flag);
865 assert_eq!(read_time, 0);
866 assert_eq!(read_amount, amount);
867
868 Ok(())
869 }
870
871 #[test]
872 fn test_packed_at_different_slots() -> Result<()> {
873 let mut storage = HashMapStorageProvider::new(1);
874 let mut contract = setup_test_contract(&mut storage);
875 let struct_base = U256::from(0x4000);
876
877 let flag = false;
879 Slot::<bool>::new_at_loc(struct_base, FieldLocation::new(0, 0, 1))
880 .write(&mut contract, flag)?;
881
882 let amount: u128 = 0xdeadbeef;
884 Slot::<u128>::new_at_loc(struct_base, FieldLocation::new(1, 0, 16))
885 .write(&mut contract, amount)?;
886
887 let value: u64 = 123456789;
889 Slot::<u64>::new_at_loc(struct_base, FieldLocation::new(2, 0, 8))
890 .write(&mut contract, value)?;
891
892 let read_flag = Slot::<bool>::new_at_loc(struct_base, FieldLocation::new(0, 0, 1))
894 .read(&mut contract)?;
895 let read_amount = Slot::<u128>::new_at_loc(struct_base, FieldLocation::new(1, 0, 16))
896 .read(&mut contract)?;
897 let read_val = Slot::<u64>::new_at_loc(struct_base, FieldLocation::new(2, 0, 8))
898 .read(&mut contract)?;
899
900 assert_eq!(read_flag, flag);
901 assert_eq!(read_amount, amount);
902 assert_eq!(read_val, value);
903
904 Ok(())
905 }
906 use proptest::prelude::*;
909
910 fn arb_address() -> impl Strategy<Value = Address> {
912 any::<[u8; 20]>().prop_map(Address::from)
913 }
914
915 fn arb_u256() -> impl Strategy<Value = U256> {
917 any::<[u64; 4]>().prop_map(U256::from_limbs)
918 }
919
920 fn arb_offset(bytes: usize) -> impl Strategy<Value = usize> {
922 0..=(32 - bytes)
923 }
924
925 proptest! {
926 #![proptest_config(ProptestConfig::with_cases(500))]
927
928 #[test]
929 fn proptest_roundtrip_u8(value: u8, offset in arb_offset(1)) {
930 let slot = insert_packed_value(U256::ZERO, &value, offset, 1)?;
931 let extracted: u8 = extract_packed_value(slot, offset, 1)?;
932 prop_assert_eq!(extracted, value);
933 }
934
935 #[test]
936 fn proptest_roundtrip_u16(value: u16, offset in arb_offset(2)) {
937 let slot = insert_packed_value(U256::ZERO, &value, offset, 2)?;
938 let extracted: u16 = extract_packed_value(slot, offset, 2)?;
939 prop_assert_eq!(extracted, value);
940 }
941
942 #[test]
943 fn proptest_roundtrip_u32(value: u32, offset in arb_offset(4)) {
944 let slot = insert_packed_value(U256::ZERO, &value, offset, 4)?;
945 let extracted: u32 = extract_packed_value(slot, offset, 4)?;
946 prop_assert_eq!(extracted, value);
947 }
948
949 #[test]
950 fn proptest_roundtrip_u64(value: u64, offset in arb_offset(8)) {
951 let slot = insert_packed_value(U256::ZERO, &value, offset, 8)?;
952 let extracted: u64 = extract_packed_value(slot, offset, 8)?;
953 prop_assert_eq!(extracted, value);
954 }
955
956 #[test]
957 fn proptest_roundtrip_u128(value: u128, offset in arb_offset(16)) {
958 let slot = insert_packed_value(U256::ZERO, &value, offset, 16)?;
959 let extracted: u128 = extract_packed_value(slot, offset, 16)?;
960 prop_assert_eq!(extracted, value);
961 }
962
963 #[test]
964 fn proptest_roundtrip_address(addr in arb_address(), offset in arb_offset(20)) {
965 let slot = insert_packed_value(U256::ZERO, &addr, offset, 20)?;
966 let extracted: Address = extract_packed_value(slot, offset, 20)?;
967 prop_assert_eq!(extracted, addr);
968 }
969
970 #[test]
971 fn proptest_roundtrip_u256(value in arb_u256()) {
972 let slot = insert_packed_value(U256::ZERO, &value, 0, 32)?;
974 let extracted: U256 = extract_packed_value(slot, 0, 32)?;
975 prop_assert_eq!(extracted, value);
976 }
977
978 #[test]
979 fn proptest_roundtrip_bool(value: bool, offset in arb_offset(1)) {
980 let slot = insert_packed_value(U256::ZERO, &value, offset, 1)?;
981 let extracted: bool = extract_packed_value(slot, offset, 1)?;
982 prop_assert_eq!(extracted, value);
983 }
984
985 #[test]
986 fn proptest_roundtrip_i8(value: i8, offset in arb_offset(1)) {
987 let slot = insert_packed_value(U256::ZERO, &value, offset, 1)?;
988 let extracted: i8 = extract_packed_value(slot, offset, 1)?;
989 prop_assert_eq!(extracted, value);
990 }
991
992 #[test]
993 fn proptest_roundtrip_i16(value: i16, offset in arb_offset(2)) {
994 let slot = insert_packed_value(U256::ZERO, &value, offset, 2)?;
995 let extracted: i16 = extract_packed_value(slot, offset, 2)?;
996 prop_assert_eq!(extracted, value);
997 }
998
999 #[test]
1000 fn proptest_roundtrip_i32(value: i32, offset in arb_offset(4)) {
1001 let slot = insert_packed_value(U256::ZERO, &value, offset, 4)?;
1002 let extracted: i32 = extract_packed_value(slot, offset, 4)?;
1003 prop_assert_eq!(extracted, value);
1004 }
1005
1006 #[test]
1007 fn proptest_roundtrip_i64(value: i64, offset in arb_offset(8)) {
1008 let slot = insert_packed_value(U256::ZERO, &value, offset, 8)?;
1009 let extracted: i64 = extract_packed_value(slot, offset, 8)?;
1010 prop_assert_eq!(extracted, value);
1011 }
1012
1013 #[test]
1014 fn proptest_roundtrip_i128(value: i128, offset in arb_offset(16)) {
1015 let slot = insert_packed_value(U256::ZERO, &value, offset, 16)?;
1016 let extracted: i128 = extract_packed_value(slot, offset, 16)?;
1017 prop_assert_eq!(extracted, value);
1018 }
1019 }
1020
1021 proptest! {
1022 #![proptest_config(ProptestConfig::with_cases(500))]
1023
1024 #[test]
1025 fn proptest_multiple_values_no_interference(
1026 v1: u8,
1027 v2: u16,
1028 v3: u32,
1029 ) {
1030 let mut slot = U256::ZERO;
1035 slot = insert_packed_value(slot, &v1, 0, 1)?;
1036 slot = insert_packed_value(slot, &v2, 1, 2)?;
1037 slot = insert_packed_value(slot, &v3, 3, 4)?;
1038
1039 let e1: u8 = extract_packed_value(slot, 0, 1)?;
1041 let e2: u16 = extract_packed_value(slot, 1, 2)?;
1042 let e3: u32 = extract_packed_value(slot, 3, 4)?;
1043
1044 prop_assert_eq!(e1, v1);
1045 prop_assert_eq!(e2, v2);
1046 prop_assert_eq!(e3, v3);
1047 }
1048
1049 #[test]
1050 fn proptest_overwrite_preserves_others(
1051 v1: u8,
1052 v2: u16,
1053 v1_new: u8,
1054 ) {
1055 let mut slot = U256::ZERO;
1057 slot = insert_packed_value(slot, &v1, 0, 1)?;
1058 slot = insert_packed_value(slot, &v2, 1, 2)?;
1059
1060 slot = insert_packed_value(slot, &v1_new, 0, 1)?;
1062
1063 let e1: u8 = extract_packed_value(slot, 0, 1)?;
1065 let e2: u16 = extract_packed_value(slot, 1, 2)?;
1066
1067 prop_assert_eq!(e1, v1_new);
1068 prop_assert_eq!(e2, v2); }
1070
1071 #[test]
1072 fn proptest_bool_with_mixed_types(
1073 flag1: bool,
1074 u16_val: u16,
1075 flag2: bool,
1076 u32_val: u32,
1077 ) {
1078 let mut slot = U256::ZERO;
1080 slot = insert_packed_value(slot, &flag1, 0, 1)?;
1081 slot = insert_packed_value(slot, &u16_val, 1, 2)?;
1082 slot = insert_packed_value(slot, &flag2, 3, 1)?;
1083 slot = insert_packed_value(slot, &u32_val, 4, 4)?;
1084
1085 let e_flag1: bool = extract_packed_value(slot, 0, 1)?;
1087 let e_u16: u16 = extract_packed_value(slot, 1, 2)?;
1088 let e_flag2: bool = extract_packed_value(slot, 3, 1)?;
1089 let e_u32: u32 = extract_packed_value(slot, 4, 4)?;
1090
1091 prop_assert_eq!(e_flag1, flag1);
1092 prop_assert_eq!(e_u16, u16_val);
1093 prop_assert_eq!(e_flag2, flag2);
1094 prop_assert_eq!(e_u32, u32_val);
1095 }
1096
1097 #[test]
1098 fn proptest_multiple_bools_no_interference(
1099 flags in proptest::collection::vec(any::<bool>(), 1..=20)
1100 ) {
1101 let mut slot = U256::ZERO;
1103 for (i, &flag) in flags.iter().enumerate() {
1104 slot = insert_packed_value(slot, &flag, i, 1)?;
1105 }
1106
1107 for (i, &expected_flag) in flags.iter().enumerate() {
1109 let extracted: bool = extract_packed_value(slot, i, 1)?;
1110 prop_assert_eq!(extracted, expected_flag, "Flag at offset {} mismatch", i);
1111 }
1112 }
1113
1114 #[test]
1115 fn proptest_element_slot_offset_consistency_u8(
1116 idx in 0usize..1000,
1117 ) {
1118 let slot = calc_element_slot(idx, 1);
1120 let offset = calc_element_offset(idx, 1);
1121
1122 prop_assert_eq!(slot * 32 + offset, idx);
1124
1125 prop_assert!(offset < 32);
1127 }
1128
1129 #[test]
1130 fn proptest_element_slot_offset_consistency_u16(
1131 idx in 0usize..1000,
1132 ) {
1133 let slot = calc_element_slot(idx, 2);
1135 let offset = calc_element_offset(idx, 2);
1136
1137 prop_assert_eq!(slot * 32 + offset, idx * 2);
1138 prop_assert!(offset < 32);
1139 }
1140
1141 #[test]
1142 fn proptest_element_slot_offset_consistency_address(
1143 idx in 0usize..100,
1144 ) {
1145 let slot = calc_element_slot(idx, 20);
1147 let offset = calc_element_offset(idx, 20);
1148
1149 prop_assert_eq!(slot * 32 + offset, idx * 20);
1150 prop_assert!(offset < 32);
1151 }
1152
1153 #[test]
1154 fn proptest_packed_slot_count_sufficient(
1155 n in 1usize..100,
1156 elem_bytes in 1usize..=32,
1157 ) {
1158 let slot_count = calc_packed_slot_count(n, elem_bytes);
1159 let total_bytes = n * elem_bytes;
1160 let min_slots = total_bytes.div_ceil(32);
1161
1162 prop_assert_eq!(slot_count, min_slots);
1164
1165 prop_assert!(slot_count * 32 >= total_bytes);
1167
1168 if slot_count > 0 {
1170 prop_assert!(slot_count * 32 - total_bytes < 32);
1171 }
1172 }
1173 }
1174}