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]
40 pub const fn from_tx_index(tx_index: u64) -> Self {
41 Self(tx_index + 1)
42 }
43
44 #[inline]
46 pub const fn get(self) -> u64 {
47 self.0
48 }
49
50 #[inline]
52 pub const fn increment(&mut self) {
53 self.0 += 1;
54 }
55
56 #[inline]
67 pub const fn phase(self, tx_len: usize) -> Option<BlockAccessPhase> {
68 let tx_len_u64 = tx_len as u64;
71 if self.0 == 0 {
72 Some(BlockAccessPhase::PreExecution)
73 } else if self.0 <= tx_len_u64 {
74 Some(BlockAccessPhase::Transaction((self.0 - 1) as usize))
77 } else if self.0 == tx_len_u64 + 1 {
78 Some(BlockAccessPhase::PostExecution)
79 } else {
80 None
81 }
82 }
83}
84
85impl fmt::Display for BlockAccessIndex {
86 #[inline]
87 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
88 fmt::Display::fmt(&self.0, f)
89 }
90}
91
92impl fmt::LowerHex for BlockAccessIndex {
93 #[inline]
94 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
95 fmt::LowerHex::fmt(&self.0, f)
96 }
97}
98
99#[cfg(feature = "serde")]
100impl serde::Serialize for BlockAccessIndex {
101 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
102 alloy_primitives::U64::from(self.0).serialize(serializer)
103 }
104}
105
106#[cfg(feature = "serde")]
107impl<'de> serde::Deserialize<'de> for BlockAccessIndex {
108 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
109 alloy_primitives::U64::deserialize(deserializer).map(|value| Self(value.to()))
110 }
111}
112
113#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
117#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
118pub enum BlockAccessPhase {
119 PreExecution,
121 Transaction(usize),
124 PostExecution,
126}
127
128#[cfg(test)]
129mod tests {
130 use super::*;
131
132 #[test]
133 fn pre_execution_is_index_zero() {
134 assert_eq!(BlockAccessIndex::new(0).phase(0), Some(BlockAccessPhase::PreExecution));
135 assert_eq!(BlockAccessIndex::new(0).phase(5), Some(BlockAccessPhase::PreExecution));
136 assert_eq!(BlockAccessIndex::PRE_EXECUTION.phase(5), Some(BlockAccessPhase::PreExecution));
137 }
138
139 #[test]
140 fn transaction_indices_are_one_based() {
141 assert_eq!(BlockAccessIndex::new(1).phase(3), Some(BlockAccessPhase::Transaction(0)));
142 assert_eq!(BlockAccessIndex::new(2).phase(3), Some(BlockAccessPhase::Transaction(1)));
143 assert_eq!(BlockAccessIndex::new(3).phase(3), Some(BlockAccessPhase::Transaction(2)));
144 }
145
146 #[test]
147 fn from_tx_index_offsets_by_one() {
148 assert_eq!(BlockAccessIndex::from_tx_index(0), BlockAccessIndex::new(1));
149 assert_eq!(BlockAccessIndex::from_tx_index(1), BlockAccessIndex::new(2));
150 }
151
152 #[test]
153 fn post_execution_is_tx_len_plus_one() {
154 assert_eq!(BlockAccessIndex::new(4).phase(3), Some(BlockAccessPhase::PostExecution));
155 assert_eq!(BlockAccessIndex::new(1).phase(0), Some(BlockAccessPhase::PostExecution));
156 }
157
158 #[test]
159 fn out_of_range_returns_none() {
160 assert_eq!(BlockAccessIndex::new(5).phase(3), None);
161 assert_eq!(BlockAccessIndex::new(u64::MAX).phase(3), None);
162 }
163
164 #[test]
165 fn empty_block_has_only_pre_and_post() {
166 assert_eq!(BlockAccessIndex::new(0).phase(0), Some(BlockAccessPhase::PreExecution));
167 assert_eq!(BlockAccessIndex::new(1).phase(0), Some(BlockAccessPhase::PostExecution));
168 assert_eq!(BlockAccessIndex::new(2).phase(0), None);
169 }
170
171 #[test]
172 fn increment_bumps_by_one() {
173 let mut idx = BlockAccessIndex::new(3);
174 idx.increment();
175 assert_eq!(idx, BlockAccessIndex::new(4));
176 }
177
178 #[test]
179 fn new_and_get_roundtrip() {
180 let idx = BlockAccessIndex::new(42);
181 assert_eq!(idx.get(), 42);
182 }
183
184 #[test]
185 fn display_matches_inner() {
186 extern crate alloc;
187 assert_eq!(alloc::format!("{}", BlockAccessIndex::new(7)), "7");
188 assert_eq!(alloc::format!("{:x}", BlockAccessIndex::new(255)), "ff");
189 }
190
191 #[cfg(feature = "serde")]
192 #[test]
193 fn serde_hex_quantity_roundtrip() {
194 let idx = BlockAccessIndex::new(26);
195 let json = serde_json::to_string(&idx).unwrap();
196 assert_eq!(json, "\"0x1a\"");
197 let back: BlockAccessIndex = serde_json::from_str(&json).unwrap();
198 assert_eq!(back, idx);
199 }
200
201 #[cfg(feature = "rlp")]
202 #[test]
203 fn rlp_matches_raw_u64() {
204 use alloy_rlp::Decodable;
205 let idx = BlockAccessIndex::new(300);
206 let encoded = alloy_rlp::encode(idx);
207 assert_eq!(encoded, alloy_rlp::encode(300u64));
208 let decoded = BlockAccessIndex::decode(&mut encoded.as_slice()).unwrap();
209 assert_eq!(decoded, idx);
210 }
211}