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