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