alloy_eip7928/
block_access_index.rs1use core::fmt;
4
5#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
19#[cfg_attr(feature = "rlp", derive(alloy_rlp::RlpEncodableWrapper, alloy_rlp::RlpDecodableWrapper))]
20#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
21#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
22#[repr(transparent)]
23pub struct BlockAccessIndex(pub u64);
24
25impl BlockAccessIndex {
26 pub const PRE_EXECUTION: Self = Self(0);
28
29 #[inline]
31 pub const fn new(value: u64) -> Self {
32 Self(value)
33 }
34
35 #[inline]
37 pub const fn get(self) -> u64 {
38 self.0
39 }
40
41 #[inline]
43 pub const fn increment(&mut self) {
44 self.0 += 1;
45 }
46
47 #[inline]
58 pub const fn phase(self, tx_len: usize) -> Option<BlockAccessPhase> {
59 let tx_len_u64 = tx_len as u64;
62 if self.0 == 0 {
63 Some(BlockAccessPhase::PreExecution)
64 } else if self.0 <= tx_len_u64 {
65 Some(BlockAccessPhase::Transaction((self.0 - 1) as usize))
68 } else if self.0 == tx_len_u64 + 1 {
69 Some(BlockAccessPhase::PostExecution)
70 } else {
71 None
72 }
73 }
74}
75
76impl fmt::Display for BlockAccessIndex {
77 #[inline]
78 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79 fmt::Display::fmt(&self.0, f)
80 }
81}
82
83impl fmt::LowerHex for BlockAccessIndex {
84 #[inline]
85 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
86 fmt::LowerHex::fmt(&self.0, f)
87 }
88}
89
90#[cfg(feature = "serde")]
91impl serde::Serialize for BlockAccessIndex {
92 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
93 alloy_primitives::U64::from(self.0).serialize(serializer)
94 }
95}
96
97#[cfg(feature = "serde")]
98impl<'de> serde::Deserialize<'de> for BlockAccessIndex {
99 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
100 alloy_primitives::U64::deserialize(deserializer).map(|value| Self(value.to()))
101 }
102}
103
104#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
108#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
109pub enum BlockAccessPhase {
110 PreExecution,
112 Transaction(usize),
115 PostExecution,
117}
118
119#[cfg(test)]
120mod tests {
121 use super::*;
122
123 #[test]
124 fn pre_execution_is_index_zero() {
125 assert_eq!(BlockAccessIndex::new(0).phase(0), Some(BlockAccessPhase::PreExecution));
126 assert_eq!(BlockAccessIndex::new(0).phase(5), Some(BlockAccessPhase::PreExecution));
127 assert_eq!(BlockAccessIndex::PRE_EXECUTION.phase(5), Some(BlockAccessPhase::PreExecution));
128 }
129
130 #[test]
131 fn transaction_indices_are_one_based() {
132 assert_eq!(BlockAccessIndex::new(1).phase(3), Some(BlockAccessPhase::Transaction(0)));
133 assert_eq!(BlockAccessIndex::new(2).phase(3), Some(BlockAccessPhase::Transaction(1)));
134 assert_eq!(BlockAccessIndex::new(3).phase(3), Some(BlockAccessPhase::Transaction(2)));
135 }
136
137 #[test]
138 fn post_execution_is_tx_len_plus_one() {
139 assert_eq!(BlockAccessIndex::new(4).phase(3), Some(BlockAccessPhase::PostExecution));
140 assert_eq!(BlockAccessIndex::new(1).phase(0), Some(BlockAccessPhase::PostExecution));
141 }
142
143 #[test]
144 fn out_of_range_returns_none() {
145 assert_eq!(BlockAccessIndex::new(5).phase(3), None);
146 assert_eq!(BlockAccessIndex::new(u64::MAX).phase(3), None);
147 }
148
149 #[test]
150 fn empty_block_has_only_pre_and_post() {
151 assert_eq!(BlockAccessIndex::new(0).phase(0), Some(BlockAccessPhase::PreExecution));
152 assert_eq!(BlockAccessIndex::new(1).phase(0), Some(BlockAccessPhase::PostExecution));
153 assert_eq!(BlockAccessIndex::new(2).phase(0), None);
154 }
155
156 #[test]
157 fn increment_bumps_by_one() {
158 let mut idx = BlockAccessIndex::new(3);
159 idx.increment();
160 assert_eq!(idx, BlockAccessIndex::new(4));
161 }
162
163 #[test]
164 fn new_and_get_roundtrip() {
165 let idx = BlockAccessIndex::new(42);
166 assert_eq!(idx.get(), 42);
167 }
168
169 #[test]
170 fn display_matches_inner() {
171 extern crate alloc;
172 assert_eq!(alloc::format!("{}", BlockAccessIndex::new(7)), "7");
173 assert_eq!(alloc::format!("{:x}", BlockAccessIndex::new(255)), "ff");
174 }
175
176 #[cfg(feature = "serde")]
177 #[test]
178 fn serde_hex_quantity_roundtrip() {
179 let idx = BlockAccessIndex::new(26);
180 let json = serde_json::to_string(&idx).unwrap();
181 assert_eq!(json, "\"0x1a\"");
182 let back: BlockAccessIndex = serde_json::from_str(&json).unwrap();
183 assert_eq!(back, idx);
184 }
185
186 #[cfg(feature = "rlp")]
187 #[test]
188 fn rlp_matches_raw_u64() {
189 use alloy_rlp::Decodable;
190 let idx = BlockAccessIndex::new(300);
191 let encoded = alloy_rlp::encode(idx);
192 assert_eq!(encoded, alloy_rlp::encode(300u64));
193 let decoded = BlockAccessIndex::decode(&mut encoded.as_slice()).unwrap();
194 assert_eq!(decoded, idx);
195 }
196}