Skip to main content

alloy_eip7928/
block_access_list.rs

1//! Contains the [`BlockAccessList`] type, which represents a simple list of account changes.
2
3use crate::account_changes::AccountChanges;
4use alloc::vec::Vec;
5
6/// This struct is used to store `account_changes` in a block.
7pub type BlockAccessList = Vec<AccountChanges>;
8
9/// Computes the hash of the given block access list.
10#[cfg(feature = "rlp")]
11pub fn compute_block_access_list_hash(bal: &[AccountChanges]) -> alloy_primitives::B256 {
12    let mut buf = Vec::new();
13    alloy_rlp::encode_list(bal, &mut buf);
14    alloy_primitives::keccak256(&buf)
15}
16
17/// Computes the total number of items in the block access list, counting each account and unique
18/// storage slot.
19pub fn total_bal_items(bal: &[AccountChanges]) -> u64 {
20    let mut bal_items: u64 = 0;
21
22    for account in bal {
23        // Count address
24        bal_items += 1;
25
26        // Collect unique storage slots across reads + writes
27        let mut unique_slots = alloy_primitives::map::HashSet::new();
28
29        for change in account.storage_changes() {
30            unique_slots.insert(change.slot);
31        }
32
33        for slot in account.storage_reads() {
34            unique_slots.insert(*slot);
35        }
36
37        // Count unique storage keys
38        bal_items += unique_slots.len() as u64;
39    }
40    bal_items
41}
42
43/// Block-Level Access List wrapper type with helper methods for metrics and validation.
44pub mod bal {
45    use crate::account_changes::AccountChanges;
46    use alloc::vec::{IntoIter, Vec};
47    use core::{
48        ops::{Deref, Index},
49        slice::Iter,
50    };
51
52    /// A wrapper around [`Vec<AccountChanges>`] that provides helper methods for
53    /// computing metrics and statistics about the block access list.
54    ///
55    /// This type implements `Deref` to `[AccountChanges]` for easy access to the
56    /// underlying data while providing additional utility methods for BAL analysis.
57    #[derive(Clone, Debug, Default, PartialEq, Eq)]
58    #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
59    #[cfg_attr(feature = "rlp", derive(alloy_rlp::RlpEncodable, alloy_rlp::RlpDecodable))]
60    pub struct Bal(Vec<AccountChanges>);
61
62    impl From<Bal> for Vec<AccountChanges> {
63        fn from(this: Bal) -> Self {
64            this.0
65        }
66    }
67
68    impl From<Vec<AccountChanges>> for Bal {
69        fn from(list: Vec<AccountChanges>) -> Self {
70            Self(list)
71        }
72    }
73
74    impl Deref for Bal {
75        type Target = [AccountChanges];
76
77        fn deref(&self) -> &Self::Target {
78            self.as_slice()
79        }
80    }
81
82    impl IntoIterator for Bal {
83        type Item = AccountChanges;
84        type IntoIter = IntoIter<AccountChanges>;
85
86        fn into_iter(self) -> Self::IntoIter {
87            self.0.into_iter()
88        }
89    }
90
91    impl<'a> IntoIterator for &'a Bal {
92        type Item = &'a AccountChanges;
93        type IntoIter = Iter<'a, AccountChanges>;
94
95        fn into_iter(self) -> Self::IntoIter {
96            self.iter()
97        }
98    }
99
100    impl FromIterator<AccountChanges> for Bal {
101        fn from_iter<I: IntoIterator<Item = AccountChanges>>(iter: I) -> Self {
102            Self(iter.into_iter().collect())
103        }
104    }
105
106    impl<I> Index<I> for Bal
107    where
108        I: core::slice::SliceIndex<[AccountChanges]>,
109    {
110        type Output = I::Output;
111
112        #[inline]
113        fn index(&self, index: I) -> &Self::Output {
114            &self.0[index]
115        }
116    }
117
118    impl Bal {
119        /// Creates a new [`Bal`] from the provided account changes.
120        pub const fn new(account_changes: Vec<AccountChanges>) -> Self {
121            Self(account_changes)
122        }
123
124        /// Adds a new [`AccountChanges`] entry to the list.
125        pub fn push(&mut self, account_changes: AccountChanges) {
126            self.0.push(account_changes)
127        }
128
129        /// Returns `true` if the list contains no elements.
130        #[inline]
131        pub const fn is_empty(&self) -> bool {
132            self.0.is_empty()
133        }
134
135        /// Returns the number of account change entries contained in the list.
136        #[inline]
137        pub const fn len(&self) -> usize {
138            self.0.len()
139        }
140
141        /// Returns an iterator over the [`AccountChanges`] entries.
142        #[inline]
143        pub fn iter(&self) -> Iter<'_, AccountChanges> {
144            self.0.iter()
145        }
146
147        /// Returns a slice of the contained [`AccountChanges`].
148        #[inline]
149        pub const fn as_slice(&self) -> &[AccountChanges] {
150            self.0.as_slice()
151        }
152
153        /// Returns a vector of [`AccountChanges`].
154        pub fn into_inner(self) -> Vec<AccountChanges> {
155            self.0
156        }
157
158        /// Returns the total number of accounts with changes in this BAL.
159        #[inline]
160        pub const fn account_count(&self) -> usize {
161            self.0.len()
162        }
163
164        /// Returns the total number of storage changes across all accounts.
165        pub fn total_storage_changes(&self) -> usize {
166            self.0.iter().map(|a| a.storage_changes.len()).sum()
167        }
168
169        /// Returns the total number of storage reads across all accounts.
170        pub fn total_storage_reads(&self) -> usize {
171            self.0.iter().map(|a| a.storage_reads.len()).sum()
172        }
173
174        /// Returns the total number of storage slots (both changes and reads) across all accounts.
175        pub fn total_slots(&self) -> usize {
176            self.0.iter().map(|a| a.storage_changes.len() + a.storage_reads.len()).sum()
177        }
178
179        /// Returns the total number of balance changes across all accounts.
180        pub fn total_balance_changes(&self) -> usize {
181            self.0.iter().map(|a| a.balance_changes.len()).sum()
182        }
183
184        /// Returns the total number of nonce changes across all accounts.
185        pub fn total_nonce_changes(&self) -> usize {
186            self.0.iter().map(|a| a.nonce_changes.len()).sum()
187        }
188
189        /// Returns the total number of code changes across all accounts.
190        pub fn total_code_changes(&self) -> usize {
191            self.0.iter().map(|a| a.code_changes.len()).sum()
192        }
193
194        /// Returns a summary of all change counts for metrics reporting.
195        ///
196        /// Returns a tuple of (account_changes, storage_changes, balance_changes, nonce_changes,
197        /// code_changes).
198        pub fn change_counts(&self) -> BalChangeCounts {
199            let mut counts = BalChangeCounts::default();
200            for account in &self.0 {
201                counts.accounts += 1;
202                counts.storage += account.storage_changes.len();
203                counts.balance += account.balance_changes.len();
204                counts.nonce += account.nonce_changes.len();
205                counts.code += account.code_changes.len();
206            }
207            counts
208        }
209
210        /// Computes the total number of items in this block access list, counting each account and
211        /// unique storage slot.
212        pub fn total_bal_items(&self) -> u64 {
213            super::total_bal_items(&self.0)
214        }
215
216        /// Computes the hash of this block access list.
217        #[cfg(feature = "rlp")]
218        pub fn compute_hash(&self) -> alloy_primitives::B256 {
219            super::compute_block_access_list_hash(&self.0)
220        }
221    }
222
223    /// Summary of change counts in a BAL for metrics reporting.
224    #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
225    pub struct BalChangeCounts {
226        /// Number of accounts with changes.
227        pub accounts: usize,
228        /// Total number of storage changes.
229        pub storage: usize,
230        /// Total number of balance changes.
231        pub balance: usize,
232        /// Total number of nonce changes.
233        pub nonce: usize,
234        /// Total number of code changes.
235        pub code: usize,
236    }
237}