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#[cfg(not(feature = "std"))]
7use once_cell::race::OnceBox as OnceLock;
8#[cfg(feature = "std")]
9use std::sync::OnceLock;
10
11/// This struct is used to store `account_changes` in a block.
12pub type BlockAccessList = Vec<AccountChanges>;
13
14/// Computes the hash of the given block access list.
15#[cfg(feature = "rlp")]
16pub fn compute_block_access_list_hash(bal: &[AccountChanges]) -> alloy_primitives::B256 {
17    let mut buf = Vec::new();
18    alloy_rlp::encode_list(bal, &mut buf);
19    alloy_primitives::keccak256(&buf)
20}
21
22/// Computes the total number of items in the block access list, counting each account and unique
23/// storage slot.
24pub fn total_bal_items(bal: &[AccountChanges]) -> u64 {
25    let mut bal_items: u64 = 0;
26
27    for account in bal {
28        // Count address
29        bal_items += 1;
30
31        // Collect unique storage slots across reads + writes
32        let mut unique_slots = alloy_primitives::map::HashSet::new();
33
34        for change in account.storage_changes() {
35            unique_slots.insert(change.slot);
36        }
37
38        for slot in account.storage_reads() {
39            unique_slots.insert(*slot);
40        }
41
42        // Count unique storage keys
43        bal_items += unique_slots.len() as u64;
44    }
45    bal_items
46}
47
48/// Block-Level Access List wrapper type with helper methods for metrics and validation.
49pub mod bal {
50    use super::OnceLock;
51    use crate::{
52        BlockAccessListGasError, BlockAccessListHashMismatch, account_changes::AccountChanges,
53        diff::BalDiff,
54    };
55    use alloc::vec::{IntoIter, Vec};
56    use alloy_primitives::{B256, Bytes};
57    use core::{
58        ops::{Deref, Index},
59        slice::Iter,
60    };
61
62    /// A wrapper around [`Vec<AccountChanges>`] that provides helper methods for
63    /// computing metrics and statistics about the block access list.
64    ///
65    /// This type implements `Deref` to `[AccountChanges]` for easy access to the
66    /// underlying data while providing additional utility methods for BAL analysis.
67    #[derive(Clone, Debug, Default, PartialEq, Eq)]
68    #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
69    #[cfg_attr(
70        feature = "rlp",
71        derive(alloy_rlp::RlpEncodableWrapper, alloy_rlp::RlpDecodableWrapper)
72    )]
73    #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
74    #[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
75    pub struct Bal(Vec<AccountChanges>);
76
77    impl From<Bal> for Vec<AccountChanges> {
78        #[inline]
79        fn from(this: Bal) -> Self {
80            this.0
81        }
82    }
83
84    impl From<Vec<AccountChanges>> for Bal {
85        #[inline]
86        fn from(list: Vec<AccountChanges>) -> Self {
87            Self(list)
88        }
89    }
90
91    #[cfg(feature = "rlp")]
92    impl alloy_primitives::Sealable for Bal {
93        fn hash_slow(&self) -> alloy_primitives::B256 {
94            self.compute_hash()
95        }
96    }
97
98    impl Deref for Bal {
99        type Target = [AccountChanges];
100
101        #[inline]
102        fn deref(&self) -> &Self::Target {
103            self.as_slice()
104        }
105    }
106
107    impl IntoIterator for Bal {
108        type Item = AccountChanges;
109        type IntoIter = IntoIter<AccountChanges>;
110
111        #[inline]
112        fn into_iter(self) -> Self::IntoIter {
113            self.0.into_iter()
114        }
115    }
116
117    impl<'a> IntoIterator for &'a Bal {
118        type Item = &'a AccountChanges;
119        type IntoIter = Iter<'a, AccountChanges>;
120
121        #[inline]
122        fn into_iter(self) -> Self::IntoIter {
123            self.iter()
124        }
125    }
126
127    impl FromIterator<AccountChanges> for Bal {
128        fn from_iter<I: IntoIterator<Item = AccountChanges>>(iter: I) -> Self {
129            Self(iter.into_iter().collect())
130        }
131    }
132
133    impl<I> Index<I> for Bal
134    where
135        I: core::slice::SliceIndex<[AccountChanges]>,
136    {
137        type Output = I::Output;
138
139        #[inline]
140        fn index(&self, index: I) -> &Self::Output {
141            &self.0[index]
142        }
143    }
144
145    impl Bal {
146        /// Creates a new [`Bal`] from the provided account changes.
147        #[inline]
148        pub const fn new(account_changes: Vec<AccountChanges>) -> Self {
149            Self(account_changes)
150        }
151
152        /// Adds a new [`AccountChanges`] entry to the list.
153        #[inline]
154        pub fn push(&mut self, account_changes: AccountChanges) {
155            self.0.push(account_changes)
156        }
157
158        /// Returns `true` if the list contains no elements.
159        #[inline]
160        pub const fn is_empty(&self) -> bool {
161            self.0.is_empty()
162        }
163
164        /// Returns the number of account change entries contained in the list.
165        #[inline]
166        pub const fn len(&self) -> usize {
167            self.0.len()
168        }
169
170        /// Returns an iterator over the [`AccountChanges`] entries.
171        #[inline]
172        pub fn iter(&self) -> Iter<'_, AccountChanges> {
173            self.0.iter()
174        }
175
176        /// Returns a slice of the contained [`AccountChanges`].
177        #[inline]
178        pub const fn as_slice(&self) -> &[AccountChanges] {
179            self.0.as_slice()
180        }
181
182        /// Returns a compact diff describing where this BAL first diverges from `other`.
183        pub fn diff(&self, other: &[AccountChanges]) -> BalDiff {
184            BalDiff::between(self.as_slice(), other)
185        }
186
187        /// Returns a vector of [`AccountChanges`].
188        #[inline]
189        pub fn into_inner(self) -> Vec<AccountChanges> {
190            self.0
191        }
192
193        /// Sorts this block access list in-place according to the canonical EIP-7928 ordering
194        /// rules.
195        ///
196        /// This applies the ordering required by the "Ordering, Uniqueness and Determinism"
197        /// section of EIP-7928:
198        ///
199        /// - accounts are sorted lexicographically by address
200        /// - `storage_changes` are sorted lexicographically by storage key
201        /// - each per-slot `StorageChange` list is sorted by block access index in ascending order
202        /// - `storage_reads` are sorted lexicographically by storage key
203        /// - `balance_changes`, `nonce_changes`, and `code_changes` are sorted by block access
204        ///   index in ascending order
205        ///
206        /// The account-local ordering is delegated to [`AccountChanges::sort`], so callers may
207        /// sort account internals independently when a parallel sort strategy is useful.
208        ///
209        /// This method only canonicalizes ordering. It does not enforce the EIP-7928 uniqueness
210        /// constraints for accounts, storage keys, or block access indexes.
211        pub fn sort(&mut self) {
212            self.0.sort_unstable_by_key(|account| account.address);
213
214            for account in &mut self.0 {
215                account.sort();
216            }
217        }
218
219        /// Returns the total number of accounts with changes in this BAL.
220        #[inline]
221        pub const fn account_count(&self) -> usize {
222            self.0.len()
223        }
224
225        /// Returns the total number of storage changes across all accounts.
226        pub fn total_storage_changes(&self) -> usize {
227            self.0.iter().map(|a| a.storage_changes.len()).sum()
228        }
229
230        /// Returns the total number of storage reads across all accounts.
231        pub fn total_storage_reads(&self) -> usize {
232            self.0.iter().map(|a| a.storage_reads.len()).sum()
233        }
234
235        /// Returns the total number of storage slots (both changes and reads) across all accounts.
236        pub fn total_slots(&self) -> usize {
237            self.0.iter().map(|a| a.storage_changes.len() + a.storage_reads.len()).sum()
238        }
239
240        /// Returns the total number of balance changes across all accounts.
241        pub fn total_balance_changes(&self) -> usize {
242            self.0.iter().map(|a| a.balance_changes.len()).sum()
243        }
244
245        /// Returns the total number of nonce changes across all accounts.
246        pub fn total_nonce_changes(&self) -> usize {
247            self.0.iter().map(|a| a.nonce_changes.len()).sum()
248        }
249
250        /// Returns the total number of code changes across all accounts.
251        pub fn total_code_changes(&self) -> usize {
252            self.0.iter().map(|a| a.code_changes.len()).sum()
253        }
254
255        /// Returns a summary of all change counts for metrics reporting.
256        pub fn change_counts(&self) -> BalChangeCounts {
257            let mut counts = BalChangeCounts::default();
258            for account in &self.0 {
259                counts.accounts += 1;
260                counts.storage += account.storage_changes.len();
261                counts.balance += account.balance_changes.len();
262                counts.nonce += account.nonce_changes.len();
263                counts.code += account.code_changes.len();
264            }
265            counts
266        }
267
268        /// Computes the total number of items in this block access list, counting each account and
269        /// unique storage slot.
270        pub fn total_bal_items(&self) -> u64 {
271            super::total_bal_items(&self.0)
272        }
273
274        /// Validates this block access list against the block gas limit.
275        ///
276        /// EIP-7928 specifies that the total cost of the block access list items must not exceed
277        /// the gas limit. Each item costs [`crate::constants::ITEM_COST`] gas.
278        pub fn validate_gas_limit(&self, gas_limit: u64) -> Result<(), BlockAccessListGasError> {
279            let items = self.total_bal_items();
280            if items > gas_limit / crate::constants::ITEM_COST as u64 {
281                return Err(BlockAccessListGasError::new(items, gas_limit));
282            }
283            Ok(())
284        }
285
286        /// Computes the hash of this block access list.
287        #[cfg(feature = "rlp")]
288        pub fn compute_hash(&self) -> alloy_primitives::B256 {
289            if self.0.is_empty() {
290                return crate::constants::EMPTY_BLOCK_ACCESS_LIST_HASH;
291            }
292            super::compute_block_access_list_hash(&self.0)
293        }
294    }
295
296    /// Summary of change counts in a BAL for metrics reporting.
297    #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
298    pub struct BalChangeCounts {
299        /// Number of accounts with changes.
300        pub accounts: usize,
301        /// Total number of storage changes.
302        pub storage: usize,
303        /// Total number of balance changes.
304        pub balance: usize,
305        /// Total number of nonce changes.
306        pub nonce: usize,
307        /// Total number of code changes.
308        pub code: usize,
309    }
310
311    /// Raw RLP bytes for a block access list with lazy hash computation.
312    #[derive(Clone, Debug)]
313    #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
314    #[cfg_attr(feature = "serde", serde(transparent))]
315    pub struct RawBal {
316        /// The original raw RLP bytes.
317        raw: Bytes,
318        /// Lazily computed hash of the block access list.
319        #[cfg_attr(feature = "serde", serde(skip, default))]
320        hash: OnceLock<B256>,
321    }
322
323    impl PartialEq for RawBal {
324        #[inline]
325        fn eq(&self, other: &Self) -> bool {
326            self.raw == other.raw
327        }
328    }
329
330    impl Eq for RawBal {}
331
332    impl From<Bytes> for RawBal {
333        #[inline]
334        fn from(raw: Bytes) -> Self {
335            Self::new(raw)
336        }
337    }
338
339    impl RawBal {
340        /// Creates a new [`RawBal`] from raw RLP bytes.
341        #[inline]
342        pub const fn new(raw: Bytes) -> Self {
343            Self { raw, hash: OnceLock::new() }
344        }
345
346        /// Creates a new [`RawBal`] from raw RLP bytes and a precomputed hash.
347        ///
348        /// The hash is not checked against the raw bytes. Callers must ensure `hash` is the
349        /// keccak256 hash of `raw`.
350        #[inline]
351        pub fn new_unchecked(raw: Bytes, hash: B256) -> Self {
352            let this = Self::new(raw);
353            #[allow(clippy::useless_conversion)]
354            let _ = this.hash.get_or_init(|| hash.into());
355            this
356        }
357
358        /// Returns the original raw RLP bytes.
359        #[inline]
360        pub const fn as_raw(&self) -> &Bytes {
361            &self.raw
362        }
363
364        /// Consumes this value and returns the raw RLP bytes.
365        #[inline]
366        pub fn into_raw(self) -> Bytes {
367            self.raw
368        }
369
370        /// Consumes this value and returns the raw RLP bytes and hash.
371        #[inline]
372        pub fn into_parts(self) -> (Bytes, B256) {
373            let hash = self.hash();
374            (self.raw, hash)
375        }
376
377        /// Ensures the raw RLP hash matches the expected block access list hash.
378        #[inline]
379        pub fn ensure_hash(&self, expected: B256) -> Result<(), BlockAccessListHashMismatch> {
380            let computed = self.hash();
381            if computed == expected {
382                Ok(())
383            } else {
384                Err(BlockAccessListHashMismatch::new(computed, expected))
385            }
386        }
387
388        /// Returns the hash of the raw block access list bytes.
389        ///
390        /// The hash is computed lazily on first call and cached for subsequent calls.
391        #[inline]
392        pub fn hash(&self) -> B256 {
393            #[allow(clippy::useless_conversion)]
394            *self.hash.get_or_init(|| alloy_primitives::keccak256(self.raw.as_ref()).into())
395        }
396    }
397
398    #[cfg(feature = "rlp")]
399    impl alloy_rlp::Encodable for RawBal {
400        #[inline]
401        fn encode(&self, out: &mut dyn alloy_rlp::BufMut) {
402            out.put_slice(&self.raw);
403        }
404
405        #[inline]
406        fn length(&self) -> usize {
407            self.raw.len()
408        }
409    }
410
411    #[cfg(feature = "rlp")]
412    impl alloy_rlp::Decodable for RawBal {
413        #[inline]
414        fn decode(buf: &mut &[u8]) -> Result<Self, alloy_rlp::Error> {
415            let original = *buf;
416            let header = alloy_rlp::Header::decode(buf)?;
417            let header_len = original.len() - buf.len();
418            let raw_len = header_len + header.payload_length;
419            let raw = Bytes::copy_from_slice(&original[..raw_len]);
420            *buf = &original[raw_len..];
421            Ok(Self::new(raw))
422        }
423    }
424
425    /// A decoded block access list with lazy hash computation.
426    ///
427    /// This type wraps a decoded block access list along with the original raw RLP bytes,
428    /// allowing efficient hash computation on demand without re-encoding.
429    #[derive(Clone, Debug)]
430    #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
431    pub struct DecodedBal<T = Bal> {
432        /// The decoded block access list.
433        decoded: T,
434        /// Raw RLP bytes and lazily computed hash of the block access list.
435        raw: RawBal,
436    }
437
438    impl<T: PartialEq> PartialEq for DecodedBal<T> {
439        #[inline]
440        fn eq(&self, other: &Self) -> bool {
441            self.decoded == other.decoded && self.raw == other.raw
442        }
443    }
444
445    impl<T: Eq> Eq for DecodedBal<T> {}
446
447    impl<T> DecodedBal<T> {
448        /// Creates a new [`DecodedBal`] from decoded data and raw bytes.
449        #[inline]
450        pub const fn new(decoded: T, raw: Bytes) -> Self {
451            Self { decoded, raw: RawBal::new(raw) }
452        }
453
454        /// Creates a new [`DecodedBal`] from decoded data, raw bytes, and a precomputed hash.
455        ///
456        /// The hash is not checked against the raw bytes. Callers must ensure `hash` is the
457        /// keccak256 hash of `raw`.
458        #[inline]
459        pub fn new_unchecked(decoded: T, raw: Bytes, hash: B256) -> Self {
460            Self { decoded, raw: RawBal::new_unchecked(raw, hash) }
461        }
462
463        /// Creates a new [`DecodedBal`] from decoded data and a [`RawBal`].
464        #[inline]
465        pub const fn with_raw_bal(decoded: T, raw: RawBal) -> Self {
466            Self { decoded, raw }
467        }
468
469        /// Returns a reference to the decoded block access list.
470        #[inline]
471        pub const fn as_bal(&self) -> &T {
472            &self.decoded
473        }
474
475        /// Returns the original raw RLP bytes.
476        #[inline]
477        pub const fn as_raw(&self) -> &Bytes {
478            self.raw.as_raw()
479        }
480
481        /// Returns the raw BAL.
482        #[inline]
483        pub const fn as_raw_bal(&self) -> &RawBal {
484            &self.raw
485        }
486
487        /// Splits this struct into the decoded BAL and raw bytes.
488        #[inline]
489        pub fn split(self) -> (T, Bytes) {
490            (self.decoded, self.raw.into_raw())
491        }
492
493        /// Splits this struct into the decoded BAL and raw BAL.
494        #[inline]
495        pub fn split_raw_bal(self) -> (T, RawBal) {
496            (self.decoded, self.raw)
497        }
498
499        /// Splits this struct into the decoded BAL, raw bytes, and hash.
500        #[inline]
501        pub fn into_parts(self) -> (T, Bytes, B256) {
502            let hash = self.hash();
503            let (decoded, raw) = self.split();
504            (decoded, raw, hash)
505        }
506
507        /// Ensures the raw RLP hash matches the expected block access list hash.
508        ///
509        /// This checks `keccak256(raw_rlp_of_received_bal) == expected` using the cached hash of
510        /// the original raw RLP bytes captured at decode time.
511        #[inline]
512        pub fn ensure_hash(&self, expected: B256) -> Result<(), BlockAccessListHashMismatch> {
513            let computed = self.hash();
514            if computed == expected {
515                Ok(())
516            } else {
517                Err(BlockAccessListHashMismatch::new(computed, expected))
518            }
519        }
520
521        /// Returns the hash of this block access list.
522        ///
523        /// The hash is computed lazily on first call and cached for subsequent calls.
524        #[inline]
525        pub fn hash(&self) -> B256 {
526            self.raw.hash()
527        }
528
529        /// Converts the decoded BAL to the given alternative that is [`From<T>`].
530        #[inline]
531        pub fn convert<U>(self) -> DecodedBal<U>
532        where
533            U: From<T>,
534        {
535            self.map(U::from)
536        }
537
538        /// Converts the decoded BAL to the given alternative that is [`TryFrom<T>`].
539        #[inline]
540        pub fn try_convert<U>(self) -> Result<DecodedBal<U>, U::Error>
541        where
542            U: TryFrom<T>,
543        {
544            self.try_map(U::try_from)
545        }
546
547        /// Applies the given closure to the decoded BAL.
548        #[inline]
549        pub fn map<U>(self, f: impl FnOnce(T) -> U) -> DecodedBal<U> {
550            let Self { decoded, raw } = self;
551            DecodedBal { decoded: f(decoded), raw }
552        }
553
554        /// Applies the given fallible closure to the decoded BAL.
555        #[inline]
556        pub fn try_map<U, E>(self, f: impl FnOnce(T) -> Result<U, E>) -> Result<DecodedBal<U>, E> {
557            let Self { decoded, raw } = self;
558            Ok(DecodedBal { decoded: f(decoded)?, raw })
559        }
560    }
561
562    #[cfg(feature = "rlp")]
563    impl DecodedBal {
564        /// Creates a new [`DecodedBal`] by decoding from raw RLP bytes.
565        #[inline]
566        pub fn from_rlp_bytes(raw: Bytes) -> Result<Self, alloy_rlp::Error> {
567            Self::from_rlp_bytes_as(raw)
568        }
569
570        /// Creates a new [`DecodedBal`] by decoding from raw RLP bytes in a [`RawBal`].
571        #[inline]
572        pub fn from_raw_bal(raw: RawBal) -> Result<Self, alloy_rlp::Error> {
573            Self::from_raw_bal_as(raw)
574        }
575
576        /// Creates a new [`DecodedBal`] by decoding from raw RLP bytes into `T`.
577        #[inline]
578        pub fn from_rlp_bytes_as<T>(raw: Bytes) -> Result<DecodedBal<T>, alloy_rlp::Error>
579        where
580            T: alloy_rlp::Decodable,
581        {
582            Self::from_raw_bal_as(RawBal::new(raw))
583        }
584
585        /// Creates a new [`DecodedBal`] by decoding from raw RLP bytes in a [`RawBal`] into `T`.
586        #[inline]
587        pub fn from_raw_bal_as<T>(raw: RawBal) -> Result<DecodedBal<T>, alloy_rlp::Error>
588        where
589            T: alloy_rlp::Decodable,
590        {
591            let mut slice = raw.as_raw().as_ref();
592            let decoded = T::decode(&mut slice)?;
593            if !slice.is_empty() {
594                return Err(alloy_rlp::Error::UnexpectedLength);
595            }
596            Ok(DecodedBal::with_raw_bal(decoded, raw))
597        }
598    }
599
600    #[cfg(feature = "rlp")]
601    impl<T> DecodedBal<T>
602    where
603        T: alloy_primitives::Sealable,
604    {
605        /// Returns the decoded BAL as a sealed borrowed value.
606        #[inline]
607        pub fn as_sealed_bal(&self) -> alloy_primitives::Sealed<&T> {
608            alloy_primitives::Sealable::seal_ref_unchecked(&self.decoded, self.hash())
609        }
610
611        /// Consumes this struct and returns the decoded BAL together with its hash.
612        #[inline]
613        pub fn into_sealed(self) -> alloy_primitives::Sealed<T> {
614            let seal = self.hash();
615            let (decoded, _) = self.split();
616            alloy_primitives::Sealable::seal_unchecked(decoded, seal)
617        }
618    }
619
620    #[cfg(feature = "rlp")]
621    impl<T> alloy_rlp::Decodable for DecodedBal<T>
622    where
623        T: alloy_rlp::Decodable,
624    {
625        #[inline]
626        fn decode(buf: &mut &[u8]) -> Result<Self, alloy_rlp::Error> {
627            let original = *buf;
628            let decoded = T::decode(buf)?;
629            let consumed = original.len() - buf.len();
630            let raw = Bytes::copy_from_slice(&original[..consumed]);
631            Ok(Self::new(decoded, raw))
632        }
633    }
634
635    #[cfg(feature = "rlp")]
636    impl<T> alloy_rlp::Encodable for DecodedBal<T> {
637        #[inline]
638        fn encode(&self, out: &mut dyn alloy_rlp::BufMut) {
639            alloy_rlp::Encodable::encode(&self.raw, out);
640        }
641
642        #[inline]
643        fn length(&self) -> usize {
644            alloy_rlp::Encodable::length(&self.raw)
645        }
646    }
647
648    /// Either raw RLP bytes or a decoded block access list.
649    ///
650    /// This type is useful when callers may receive raw BAL bytes before the BAL needs to be
651    /// decoded, while still allowing decoded values to preserve and re-use their original raw
652    /// bytes.
653    #[derive(Clone, Debug, PartialEq, Eq)]
654    #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
655    pub enum RawOrDecodedBal<T = Bal> {
656        /// Raw RLP bytes for a block access list with lazy hash computation.
657        Raw(RawBal),
658        /// A decoded block access list with its original raw RLP bytes.
659        Decoded(DecodedBal<T>),
660    }
661
662    impl<T> From<Bytes> for RawOrDecodedBal<T> {
663        #[inline]
664        fn from(raw: Bytes) -> Self {
665            Self::Raw(RawBal::new(raw))
666        }
667    }
668
669    impl<T> From<RawBal> for RawOrDecodedBal<T> {
670        #[inline]
671        fn from(raw: RawBal) -> Self {
672            Self::Raw(raw)
673        }
674    }
675
676    impl<T> From<DecodedBal<T>> for RawOrDecodedBal<T> {
677        #[inline]
678        fn from(decoded: DecodedBal<T>) -> Self {
679            Self::Decoded(decoded)
680        }
681    }
682
683    impl<T> RawOrDecodedBal<T> {
684        /// Creates a new [`RawOrDecodedBal`] from raw RLP bytes.
685        #[inline]
686        pub const fn raw(raw: Bytes) -> Self {
687            Self::Raw(RawBal::new(raw))
688        }
689
690        /// Creates a new [`RawOrDecodedBal`] from raw RLP bytes and a precomputed hash.
691        ///
692        /// The hash is not checked against the raw bytes. Callers must ensure `hash` is the
693        /// keccak256 hash of `raw`.
694        #[inline]
695        pub fn raw_unchecked(raw: Bytes, hash: B256) -> Self {
696            Self::Raw(RawBal::new_unchecked(raw, hash))
697        }
698
699        /// Creates a new [`RawOrDecodedBal`] from a [`RawBal`].
700        #[inline]
701        pub const fn raw_bal(raw: RawBal) -> Self {
702            Self::Raw(raw)
703        }
704
705        /// Creates a new [`RawOrDecodedBal`] from a decoded BAL.
706        #[inline]
707        pub const fn decoded(decoded: DecodedBal<T>) -> Self {
708            Self::Decoded(decoded)
709        }
710
711        /// Returns `true` if this contains raw RLP bytes.
712        #[inline]
713        pub const fn is_raw(&self) -> bool {
714            matches!(self, Self::Raw(_))
715        }
716
717        /// Returns `true` if this contains a decoded BAL.
718        #[inline]
719        pub const fn is_decoded(&self) -> bool {
720            matches!(self, Self::Decoded(_))
721        }
722
723        /// Returns the raw RLP bytes.
724        #[inline]
725        pub const fn as_raw(&self) -> &Bytes {
726            match self {
727                Self::Raw(raw) => raw.as_raw(),
728                Self::Decoded(decoded) => decoded.as_raw(),
729            }
730        }
731
732        /// Returns the raw BAL.
733        #[inline]
734        pub const fn as_raw_bal(&self) -> &RawBal {
735            match self {
736                Self::Raw(raw) => raw,
737                Self::Decoded(decoded) => decoded.as_raw_bal(),
738            }
739        }
740
741        /// Returns the decoded BAL if available.
742        #[inline]
743        pub const fn as_decoded(&self) -> Option<&DecodedBal<T>> {
744            match self {
745                Self::Raw(_) => None,
746                Self::Decoded(decoded) => Some(decoded),
747            }
748        }
749
750        /// Returns the decoded BAL if available.
751        #[inline]
752        pub fn into_decoded(self) -> Option<DecodedBal<T>> {
753            match self {
754                Self::Raw(_) => None,
755                Self::Decoded(decoded) => Some(decoded),
756            }
757        }
758
759        /// Returns the decoded block access list if available.
760        #[inline]
761        pub const fn as_bal(&self) -> Option<&T> {
762            match self {
763                Self::Raw(_) => None,
764                Self::Decoded(decoded) => Some(decoded.as_bal()),
765            }
766        }
767
768        /// Consumes this value and returns the raw RLP bytes.
769        #[inline]
770        pub fn into_raw(self) -> Bytes {
771            match self {
772                Self::Raw(raw) => raw.into_raw(),
773                Self::Decoded(decoded) => decoded.split().1,
774            }
775        }
776
777        /// Consumes this value and returns the raw BAL.
778        #[inline]
779        pub fn into_raw_bal(self) -> RawBal {
780            match self {
781                Self::Raw(raw) => raw,
782                Self::Decoded(decoded) => decoded.split_raw_bal().1,
783            }
784        }
785
786        /// Splits this value into its decoded BAL, if available, and raw RLP bytes.
787        #[inline]
788        pub fn split(self) -> (Option<T>, Bytes) {
789            match self {
790                Self::Raw(raw) => (None, raw.into_raw()),
791                Self::Decoded(decoded) => {
792                    let (bal, raw) = decoded.split();
793                    (Some(bal), raw)
794                }
795            }
796        }
797
798        /// Splits this value into its decoded BAL, if available, and raw BAL.
799        #[inline]
800        pub fn split_raw_bal(self) -> (Option<T>, RawBal) {
801            match self {
802                Self::Raw(raw) => (None, raw),
803                Self::Decoded(decoded) => {
804                    let (bal, raw) = decoded.split_raw_bal();
805                    (Some(bal), raw)
806                }
807            }
808        }
809
810        /// Ensures the raw RLP hash matches the expected block access list hash.
811        #[inline]
812        pub fn ensure_hash(&self, expected: B256) -> Result<(), BlockAccessListHashMismatch> {
813            let computed = self.hash();
814            if computed == expected {
815                Ok(())
816            } else {
817                Err(BlockAccessListHashMismatch::new(computed, expected))
818            }
819        }
820
821        /// Returns the hash of the raw block access list bytes.
822        #[inline]
823        pub fn hash(&self) -> B256 {
824            match self {
825                Self::Raw(raw) => raw.hash(),
826                Self::Decoded(decoded) => decoded.hash(),
827            }
828        }
829
830        /// Converts the decoded BAL to the given alternative that is [`From<T>`].
831        ///
832        /// Raw values stay raw.
833        #[inline]
834        pub fn convert<U>(self) -> RawOrDecodedBal<U>
835        where
836            U: From<T>,
837        {
838            self.map(U::from)
839        }
840
841        /// Converts the decoded BAL to the given alternative that is [`TryFrom<T>`].
842        ///
843        /// Raw values stay raw.
844        #[inline]
845        pub fn try_convert<U>(self) -> Result<RawOrDecodedBal<U>, U::Error>
846        where
847            U: TryFrom<T>,
848        {
849            self.try_map(U::try_from)
850        }
851
852        /// Applies the given closure to the decoded BAL if available.
853        #[inline]
854        pub fn map<U>(self, f: impl FnOnce(T) -> U) -> RawOrDecodedBal<U> {
855            match self {
856                Self::Raw(raw) => RawOrDecodedBal::Raw(raw),
857                Self::Decoded(decoded) => RawOrDecodedBal::Decoded(decoded.map(f)),
858            }
859        }
860
861        /// Applies the given fallible closure to the decoded BAL if available.
862        #[inline]
863        pub fn try_map<U, E>(
864            self,
865            f: impl FnOnce(T) -> Result<U, E>,
866        ) -> Result<RawOrDecodedBal<U>, E> {
867            match self {
868                Self::Raw(raw) => Ok(RawOrDecodedBal::Raw(raw)),
869                Self::Decoded(decoded) => decoded.try_map(f).map(RawOrDecodedBal::Decoded),
870            }
871        }
872    }
873
874    #[cfg(feature = "rlp")]
875    impl<T> RawOrDecodedBal<T>
876    where
877        T: alloy_rlp::Decodable,
878    {
879        /// Up-converts raw RLP bytes into a decoded BAL, or returns the existing decoded BAL.
880        #[inline]
881        pub fn try_into_decoded(self) -> Result<DecodedBal<T>, alloy_rlp::Error> {
882            match self {
883                Self::Raw(raw) => DecodedBal::from_raw_bal_as(raw),
884                Self::Decoded(decoded) => Ok(decoded),
885            }
886        }
887    }
888
889    #[cfg(feature = "rlp")]
890    impl<T> alloy_rlp::Encodable for RawOrDecodedBal<T> {
891        #[inline]
892        fn encode(&self, out: &mut dyn alloy_rlp::BufMut) {
893            out.put_slice(self.as_raw());
894        }
895
896        #[inline]
897        fn length(&self) -> usize {
898            self.as_raw().len()
899        }
900    }
901
902    #[cfg(feature = "rlp")]
903    impl<T> alloy_rlp::Decodable for RawOrDecodedBal<T> {
904        #[inline]
905        fn decode(buf: &mut &[u8]) -> Result<Self, alloy_rlp::Error> {
906            <RawBal as alloy_rlp::Decodable>::decode(buf).map(Self::Raw)
907        }
908    }
909}
910
911/// Error returned when a block access list item cost exceeds the block gas limit.
912#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, thiserror::Error)]
913#[error(
914    "block access list item cost exceeds gas limit: items={items}, max_items={max_items}, gas_limit={gas_limit}"
915)]
916pub struct BlockAccessListGasError {
917    /// Number of block access list items.
918    pub items: u64,
919    /// Maximum number of block access list items allowed by the gas limit.
920    pub max_items: u64,
921    /// Block gas limit used for validation.
922    pub gas_limit: u64,
923}
924
925impl BlockAccessListGasError {
926    /// Creates a new gas limit validation error for the provided item count and gas limit.
927    #[inline]
928    pub const fn new(items: u64, gas_limit: u64) -> Self {
929        Self { items, max_items: gas_limit / crate::constants::ITEM_COST as u64, gas_limit }
930    }
931}
932
933/// Error returned when a decoded block access list hash does not match the expected hash.
934#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, thiserror::Error)]
935#[error("block access list hash mismatch: computed={computed}, expected={expected}")]
936pub struct BlockAccessListHashMismatch {
937    /// Hash computed from the received BAL bytes.
938    pub computed: alloy_primitives::B256,
939    /// Hash expected by the caller, typically `header.block_access_list_hash`.
940    pub expected: alloy_primitives::B256,
941}
942
943impl BlockAccessListHashMismatch {
944    /// Creates a new block access list hash validation error.
945    #[inline]
946    pub const fn new(computed: alloy_primitives::B256, expected: alloy_primitives::B256) -> Self {
947        Self { computed, expected }
948    }
949}
950
951#[cfg(test)]
952mod hash_tests {
953    use super::bal::{Bal, DecodedBal, RawBal, RawOrDecodedBal};
954    use crate::{
955        AccountChanges, BalanceChange, BlockAccessIndex, CodeChange, NonceChange, SlotChanges,
956        StorageChange, constants::ITEM_COST,
957    };
958    use alloy_primitives::{Address, B256, Bytes, U256};
959
960    #[test]
961    fn decoded_bal_hash_uses_raw_bytes_without_rlp_feature() {
962        let raw = Bytes::from_static(&[0xc0]);
963        let decoded = DecodedBal::new(Bal::default(), raw.clone());
964
965        assert_eq!(decoded.hash(), alloy_primitives::keccak256(raw.as_ref()));
966
967        let (bal, split_raw, split_hash) = decoded.into_parts();
968        assert!(bal.is_empty());
969        assert_eq!(split_raw, raw);
970        assert_eq!(split_hash, alloy_primitives::keccak256(raw.as_ref()));
971    }
972
973    #[test]
974    fn decoded_bal_map_preserves_raw_and_hash() {
975        let raw = Bytes::from_static(&[0xc0]);
976        let decoded = DecodedBal::new(Bal::default(), raw.clone());
977        let hash = decoded.hash();
978
979        let mapped = decoded.map(|bal| bal.len());
980
981        assert_eq!(mapped.as_bal(), &0);
982        assert_eq!(mapped.as_raw(), &raw);
983        assert_eq!(mapped.hash(), hash);
984    }
985
986    #[test]
987    fn decoded_bal_try_map_converts_or_returns_error() {
988        let raw = Bytes::from_static(&[0xc0]);
989        let decoded = DecodedBal::new(Bal::default(), raw.clone());
990
991        let mapped = decoded.try_map(|bal| Ok::<_, core::convert::Infallible>(bal.len())).unwrap();
992
993        assert_eq!(mapped.as_bal(), &0);
994        assert_eq!(mapped.as_raw(), &raw);
995
996        let decoded = DecodedBal::new(Bal::default(), raw);
997        let err = decoded.try_map(|_| Err::<usize, _>("expected error")).unwrap_err();
998
999        assert_eq!(err, "expected error");
1000    }
1001
1002    #[derive(Debug, PartialEq, Eq)]
1003    struct BalLen(usize);
1004
1005    impl From<Bal> for BalLen {
1006        fn from(value: Bal) -> Self {
1007            Self(value.len())
1008        }
1009    }
1010
1011    #[derive(Debug, PartialEq, Eq)]
1012    struct NonEmptyBal(Bal);
1013
1014    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
1015    struct EmptyBal;
1016
1017    impl TryFrom<Bal> for NonEmptyBal {
1018        type Error = EmptyBal;
1019
1020        fn try_from(value: Bal) -> Result<Self, Self::Error> {
1021            if value.is_empty() { Err(EmptyBal) } else { Ok(Self(value)) }
1022        }
1023    }
1024
1025    #[test]
1026    fn decoded_bal_convert_and_try_convert_use_inner_conversions() {
1027        let raw = Bytes::from_static(&[0xc0]);
1028        let converted: DecodedBal<BalLen> = DecodedBal::new(Bal::default(), raw.clone()).convert();
1029
1030        assert_eq!(converted.as_bal(), &BalLen(0));
1031        assert_eq!(converted.as_raw(), &raw);
1032
1033        let err = DecodedBal::new(Bal::default(), raw.clone()).try_convert::<NonEmptyBal>();
1034        assert_eq!(err.unwrap_err(), EmptyBal);
1035
1036        let bal = Bal::new(vec![AccountChanges::new(Address::from([0x11; 20]))]);
1037        let converted = DecodedBal::new(bal, raw).try_convert::<NonEmptyBal>().unwrap();
1038
1039        assert_eq!(converted.as_bal().0.len(), 1);
1040    }
1041
1042    #[test]
1043    fn decoded_bal_ensure_hash_reports_both_hashes() {
1044        let raw = Bytes::from_static(&[0xc0]);
1045        let decoded = DecodedBal::new(Bal::default(), raw.clone());
1046        let computed = alloy_primitives::keccak256(raw.as_ref());
1047        let expected = B256::from([0x11; 32]);
1048
1049        assert_eq!(decoded.ensure_hash(computed), Ok(()));
1050        assert_eq!(
1051            decoded.ensure_hash(expected),
1052            Err(super::BlockAccessListHashMismatch::new(computed, expected))
1053        );
1054    }
1055
1056    #[test]
1057    fn raw_bal_hash_uses_raw_bytes() {
1058        let raw = Bytes::from_static(&[0xc0]);
1059        let raw_bal = RawBal::new(raw.clone());
1060        let computed = alloy_primitives::keccak256(raw.as_ref());
1061        let expected = B256::from([0x11; 32]);
1062
1063        assert_eq!(raw_bal.as_raw(), &raw);
1064        assert_eq!(raw_bal.hash(), computed);
1065        assert_eq!(raw_bal.ensure_hash(computed), Ok(()));
1066        assert_eq!(
1067            raw_bal.ensure_hash(expected),
1068            Err(super::BlockAccessListHashMismatch::new(computed, expected))
1069        );
1070
1071        let (split_raw, split_hash) = raw_bal.into_parts();
1072        assert_eq!(split_raw, raw);
1073        assert_eq!(split_hash, computed);
1074    }
1075
1076    #[test]
1077    fn raw_bal_new_unchecked_uses_supplied_hash() {
1078        let raw = Bytes::from_static(&[0xc0]);
1079        let hash = B256::from([0x11; 32]);
1080        let raw_bal = RawBal::new_unchecked(raw.clone(), hash);
1081
1082        assert_eq!(raw_bal.as_raw(), &raw);
1083        assert_eq!(raw_bal.hash(), hash);
1084        assert_eq!(raw_bal.ensure_hash(hash), Ok(()));
1085
1086        let (split_raw, split_hash) = raw_bal.into_parts();
1087        assert_eq!(split_raw, raw);
1088        assert_eq!(split_hash, hash);
1089    }
1090
1091    #[test]
1092    fn decoded_bal_exposes_raw_bal() {
1093        let raw = Bytes::from_static(&[0xc0]);
1094        let raw_bal = RawBal::new(raw.clone());
1095        let decoded = DecodedBal::with_raw_bal(Bal::default(), raw_bal.clone());
1096
1097        assert_eq!(decoded.as_raw_bal(), &raw_bal);
1098        assert_eq!(decoded.as_raw(), &raw);
1099
1100        let (bal, split_raw_bal) = decoded.split_raw_bal();
1101        assert!(bal.is_empty());
1102        assert_eq!(split_raw_bal, raw_bal);
1103    }
1104
1105    #[test]
1106    fn decoded_bal_new_unchecked_uses_supplied_hash() {
1107        let raw = Bytes::from_static(&[0xc0]);
1108        let hash = B256::from([0x11; 32]);
1109        let decoded = DecodedBal::new_unchecked(Bal::default(), raw.clone(), hash);
1110
1111        assert_eq!(decoded.as_raw(), &raw);
1112        assert_eq!(decoded.hash(), hash);
1113        assert_eq!(decoded.ensure_hash(hash), Ok(()));
1114    }
1115
1116    #[cfg(feature = "serde")]
1117    #[test]
1118    fn decoded_bal_serde_keeps_raw_bytes_field() {
1119        let raw = Bytes::from_static(&[0xc0]);
1120        let decoded = DecodedBal::new(Bal::default(), raw.clone());
1121        let value = serde_json::to_value(&decoded).unwrap();
1122
1123        assert!(value.get("decoded").is_some());
1124        assert_eq!(value.get("raw"), Some(&serde_json::to_value(&raw).unwrap()));
1125        assert!(value.get("hash").is_none());
1126
1127        let decoded = serde_json::from_value::<DecodedBal>(value).unwrap();
1128        assert_eq!(decoded.as_bal(), &Bal::default());
1129        assert_eq!(decoded.as_raw(), &raw);
1130    }
1131
1132    #[test]
1133    fn raw_or_decoded_bal_raw_helpers_use_raw_bytes() {
1134        let raw = Bytes::from_static(&[0xc0]);
1135        let bal = RawOrDecodedBal::<Bal>::raw(raw.clone());
1136        let hash = alloy_primitives::keccak256(raw.as_ref());
1137
1138        assert!(bal.is_raw());
1139        assert!(!bal.is_decoded());
1140        assert_eq!(bal.as_raw(), &raw);
1141        assert_eq!(bal.as_raw_bal().as_raw(), &raw);
1142        assert_eq!(bal.as_decoded(), None);
1143        assert_eq!(bal.as_bal(), None);
1144        assert_eq!(bal.hash(), hash);
1145        assert_eq!(bal.ensure_hash(hash), Ok(()));
1146
1147        let (decoded, split_raw) = bal.clone().split();
1148        assert_eq!(decoded, None);
1149        assert_eq!(split_raw, raw);
1150        let (decoded, split_raw_bal) = bal.clone().split_raw_bal();
1151        assert_eq!(decoded, None);
1152        assert_eq!(split_raw_bal.as_raw(), &raw);
1153        assert_eq!(bal.clone().into_raw_bal().as_raw(), &raw);
1154        assert_eq!(bal.into_raw(), raw);
1155    }
1156
1157    #[test]
1158    fn raw_or_decoded_bal_raw_unchecked_uses_supplied_hash() {
1159        let raw = Bytes::from_static(&[0xc0]);
1160        let hash = B256::from([0x11; 32]);
1161        let bal = RawOrDecodedBal::<Bal>::raw_unchecked(raw.clone(), hash);
1162
1163        assert!(bal.is_raw());
1164        assert_eq!(bal.as_raw(), &raw);
1165        assert_eq!(bal.hash(), hash);
1166        assert_eq!(bal.ensure_hash(hash), Ok(()));
1167    }
1168
1169    #[test]
1170    fn raw_or_decoded_bal_decoded_helpers_use_decoded_bal() {
1171        let raw = Bytes::from_static(&[0xc0]);
1172        let decoded = DecodedBal::new(Bal::default(), raw.clone());
1173        let hash = decoded.hash();
1174        let bal = RawOrDecodedBal::decoded(decoded.clone());
1175
1176        assert!(!bal.is_raw());
1177        assert!(bal.is_decoded());
1178        assert_eq!(bal.as_raw(), &raw);
1179        assert_eq!(bal.as_raw_bal(), decoded.as_raw_bal());
1180        assert_eq!(bal.as_decoded(), Some(&decoded));
1181        assert_eq!(bal.as_bal(), Some(decoded.as_bal()));
1182        assert_eq!(bal.hash(), hash);
1183
1184        let (split_bal, split_raw) = bal.clone().split();
1185        assert_eq!(split_bal, Some(Bal::default()));
1186        assert_eq!(split_raw, raw);
1187        let (split_bal, split_raw_bal) = bal.clone().split_raw_bal();
1188        assert_eq!(split_bal, Some(Bal::default()));
1189        assert_eq!(split_raw_bal.as_raw(), &raw);
1190        assert_eq!(bal.into_decoded(), Some(decoded));
1191    }
1192
1193    #[test]
1194    fn raw_or_decoded_bal_convert_maps_only_decoded_values() {
1195        let raw = Bytes::from_static(&[0xc0]);
1196        let raw_bal: RawOrDecodedBal<Bal> = RawOrDecodedBal::raw(raw.clone());
1197        let converted_raw: RawOrDecodedBal<BalLen> = raw_bal.convert();
1198
1199        assert!(converted_raw.is_raw());
1200        assert_eq!(converted_raw.as_raw(), &raw);
1201        assert_eq!(converted_raw.as_bal(), None);
1202
1203        let decoded = DecodedBal::new(Bal::default(), raw.clone());
1204        let converted_decoded: RawOrDecodedBal<BalLen> =
1205            RawOrDecodedBal::decoded(decoded).convert();
1206
1207        assert!(converted_decoded.is_decoded());
1208        assert_eq!(converted_decoded.as_bal(), Some(&BalLen(0)));
1209        assert_eq!(converted_decoded.as_raw(), &raw);
1210
1211        let err = RawOrDecodedBal::decoded(DecodedBal::new(Bal::default(), raw.clone()))
1212            .try_convert::<NonEmptyBal>();
1213        assert_eq!(err.unwrap_err(), EmptyBal);
1214
1215        let raw_result: Result<RawOrDecodedBal<NonEmptyBal>, EmptyBal> =
1216            RawOrDecodedBal::<Bal>::raw(raw.clone()).try_convert();
1217        let raw_result = raw_result.unwrap();
1218        assert!(raw_result.is_raw());
1219        assert_eq!(raw_result.as_raw(), &raw);
1220    }
1221
1222    #[test]
1223    fn bal_sort_orders_all_eip7928_lists() {
1224        let address_1 = Address::from([0x11; 20]);
1225        let address_2 = Address::from([0x22; 20]);
1226        let mut bal = Bal::new(vec![
1227            AccountChanges {
1228                address: address_2,
1229                storage_changes: vec![
1230                    SlotChanges::new(
1231                        U256::from(3),
1232                        vec![
1233                            StorageChange::new(BlockAccessIndex::new(8), U256::from(0x80)),
1234                            StorageChange::new(BlockAccessIndex::new(2), U256::from(0x20)),
1235                        ],
1236                    ),
1237                    SlotChanges::new(
1238                        U256::from(1),
1239                        vec![
1240                            StorageChange::new(BlockAccessIndex::new(5), U256::from(0x50)),
1241                            StorageChange::new(BlockAccessIndex::new(1), U256::from(0x10)),
1242                        ],
1243                    ),
1244                ],
1245                storage_reads: vec![U256::from(4), U256::from(2)],
1246                balance_changes: vec![
1247                    BalanceChange::new(BlockAccessIndex::new(6), U256::from(600)),
1248                    BalanceChange::new(BlockAccessIndex::new(3), U256::from(300)),
1249                ],
1250                nonce_changes: vec![
1251                    NonceChange::new(BlockAccessIndex::new(7), 70),
1252                    NonceChange::new(BlockAccessIndex::new(4), 40),
1253                ],
1254                code_changes: vec![
1255                    CodeChange::new(BlockAccessIndex::new(9), Bytes::from_static(&[0x60, 0x09])),
1256                    CodeChange::new(BlockAccessIndex::new(5), Bytes::from_static(&[0x60, 0x05])),
1257                ],
1258            },
1259            AccountChanges {
1260                address: address_1,
1261                storage_changes: vec![
1262                    SlotChanges::new(
1263                        U256::from(2),
1264                        vec![
1265                            StorageChange::new(BlockAccessIndex::new(4), U256::from(0x40)),
1266                            StorageChange::new(BlockAccessIndex::new(0), U256::from(0x00)),
1267                        ],
1268                    ),
1269                    SlotChanges::new(
1270                        U256::from(1),
1271                        vec![
1272                            StorageChange::new(BlockAccessIndex::new(3), U256::from(0x30)),
1273                            StorageChange::new(BlockAccessIndex::new(1), U256::from(0x10)),
1274                        ],
1275                    ),
1276                ],
1277                storage_reads: vec![U256::from(5), U256::from(3)],
1278                balance_changes: vec![
1279                    BalanceChange::new(BlockAccessIndex::new(5), U256::from(500)),
1280                    BalanceChange::new(BlockAccessIndex::new(2), U256::from(200)),
1281                ],
1282                nonce_changes: vec![
1283                    NonceChange::new(BlockAccessIndex::new(8), 80),
1284                    NonceChange::new(BlockAccessIndex::new(1), 10),
1285                ],
1286                code_changes: vec![
1287                    CodeChange::new(BlockAccessIndex::new(4), Bytes::from_static(&[0x60, 0x04])),
1288                    CodeChange::new(BlockAccessIndex::new(2), Bytes::from_static(&[0x60, 0x02])),
1289                ],
1290            },
1291        ]);
1292
1293        bal.sort();
1294
1295        assert_eq!(bal[0].address, address_1);
1296        assert_eq!(bal[1].address, address_2);
1297
1298        for account in bal.iter() {
1299            assert!(account.storage_changes.windows(2).all(|slots| slots[0].slot <= slots[1].slot));
1300            for slot_changes in &account.storage_changes {
1301                assert!(
1302                    slot_changes
1303                        .changes
1304                        .windows(2)
1305                        .all(|changes| changes[0].block_access_index
1306                            <= changes[1].block_access_index)
1307                );
1308            }
1309            assert!(account.storage_reads.windows(2).all(|slots| slots[0] <= slots[1]));
1310            assert!(
1311                account
1312                    .balance_changes
1313                    .windows(2)
1314                    .all(|changes| changes[0].block_access_index <= changes[1].block_access_index)
1315            );
1316            assert!(
1317                account
1318                    .nonce_changes
1319                    .windows(2)
1320                    .all(|changes| changes[0].block_access_index <= changes[1].block_access_index)
1321            );
1322            assert!(
1323                account
1324                    .code_changes
1325                    .windows(2)
1326                    .all(|changes| changes[0].block_access_index <= changes[1].block_access_index)
1327            );
1328        }
1329    }
1330
1331    #[test]
1332    fn bal_validate_gas_limit_accepts_exact_item_cost() {
1333        let bal = Bal::new(vec![
1334            AccountChanges::new(Address::from([0x11; 20]))
1335                .with_storage_read(U256::from(1))
1336                .with_storage_change(SlotChanges::new(
1337                    U256::from(1),
1338                    vec![StorageChange::new(BlockAccessIndex::new(0), U256::from(0xaa))],
1339                )),
1340        ]);
1341
1342        assert_eq!(bal.total_bal_items(), 2);
1343        assert_eq!(bal.validate_gas_limit(2 * ITEM_COST as u64), Ok(()));
1344    }
1345
1346    #[test]
1347    fn bal_validate_gas_limit_rejects_item_cost_above_limit() {
1348        let bal = Bal::new(vec![
1349            AccountChanges::new(Address::from([0x11; 20]))
1350                .with_storage_read(U256::from(1))
1351                .with_storage_read(U256::from(2)),
1352        ]);
1353        let gas_limit = 3 * ITEM_COST as u64 - 1;
1354
1355        assert_eq!(bal.total_bal_items(), 3);
1356        assert_eq!(
1357            bal.validate_gas_limit(gas_limit),
1358            Err(super::BlockAccessListGasError::new(3, gas_limit))
1359        );
1360    }
1361}
1362
1363#[cfg(all(test, feature = "rlp"))]
1364mod tests {
1365    use super::bal::{Bal, DecodedBal, RawBal, RawOrDecodedBal};
1366    use crate::{
1367        AccountChanges, BalanceChange, BlockAccessIndex, CodeChange, NonceChange, SlotChanges,
1368        StorageChange, constants::EMPTY_BLOCK_ACCESS_LIST_HASH,
1369    };
1370    use alloy_primitives::{Address, Bytes, U256};
1371
1372    fn sample_bal() -> Bal {
1373        Bal::new(vec![
1374            AccountChanges::new(Address::from([0x11; 20]))
1375                .with_storage_read(U256::from(0x10))
1376                .with_storage_change(SlotChanges::new(
1377                    U256::from(0x01),
1378                    vec![StorageChange::new(BlockAccessIndex::new(0), U256::from(0xaa))],
1379                ))
1380                .with_balance_change(BalanceChange::new(
1381                    BlockAccessIndex::new(1),
1382                    U256::from(1_000),
1383                ))
1384                .with_nonce_change(NonceChange::new(BlockAccessIndex::new(2), 7))
1385                .with_code_change(CodeChange::new(
1386                    BlockAccessIndex::new(3),
1387                    Bytes::from(vec![0x60, 0x00]),
1388                )),
1389            AccountChanges::new(Address::from([0x22; 20]))
1390                .with_storage_read(U256::from(0x20))
1391                .with_storage_change(SlotChanges::new(
1392                    U256::from(0x02),
1393                    vec![StorageChange::new(BlockAccessIndex::new(4), U256::from(0xbb))],
1394                )),
1395        ])
1396    }
1397
1398    #[test]
1399    fn bal_compute_hash_returns_empty_hash_for_empty_bal() {
1400        let bal = Bal::default();
1401
1402        assert_eq!(bal.compute_hash(), EMPTY_BLOCK_ACCESS_LIST_HASH);
1403    }
1404
1405    #[test]
1406    fn bal_compute_hash_matches_free_function_for_non_empty_bal() {
1407        let bal = sample_bal();
1408
1409        assert_eq!(bal.compute_hash(), super::compute_block_access_list_hash(bal.as_slice()));
1410        assert_ne!(bal.compute_hash(), EMPTY_BLOCK_ACCESS_LIST_HASH);
1411    }
1412
1413    #[test]
1414    fn decoded_bal_from_rlp_bytes_preserves_raw_and_hash() {
1415        let bal = sample_bal();
1416        let raw = Bytes::from(alloy_rlp::encode(&bal));
1417        let decoded = DecodedBal::from_rlp_bytes(raw.clone()).unwrap();
1418
1419        assert_eq!(decoded.as_bal(), &bal);
1420        assert_eq!(decoded.as_raw(), &raw);
1421        assert_eq!(decoded.hash(), bal.compute_hash());
1422        assert_eq!(decoded.hash(), alloy_primitives::keccak256(raw.as_ref()));
1423        assert_eq!(decoded.as_sealed_bal().hash(), bal.compute_hash());
1424        assert_eq!(decoded.as_sealed_bal().inner(), &decoded.as_bal());
1425
1426        let (split_bal, split_raw) = decoded.clone().split();
1427        assert_eq!(split_bal, bal);
1428        assert_eq!(split_raw, raw);
1429
1430        let (split_bal, split_raw, split_hash) = decoded.clone().into_parts();
1431        assert_eq!(split_bal, bal);
1432        assert_eq!(split_raw, raw);
1433        assert_eq!(split_hash, bal.compute_hash());
1434
1435        let sealed = decoded.into_sealed();
1436        assert_eq!(sealed.hash(), bal.compute_hash());
1437        assert_eq!(sealed.inner(), &bal);
1438    }
1439
1440    #[test]
1441    fn decoded_bal_from_rlp_bytes_decodes_generic_inner_type() {
1442        let bal = sample_bal();
1443        let raw = Bytes::from(alloy_rlp::encode(&bal));
1444        let decoded = DecodedBal::from_rlp_bytes_as::<Vec<AccountChanges>>(raw.clone()).unwrap();
1445
1446        assert_eq!(decoded.as_bal().as_slice(), bal.as_slice());
1447        assert_eq!(decoded.as_raw(), &raw);
1448        assert_eq!(decoded.hash(), bal.compute_hash());
1449    }
1450
1451    #[test]
1452    fn decoded_bal_decode_consumes_exact_raw_rlp_item() {
1453        let bal = sample_bal();
1454        let raw = alloy_rlp::encode(&bal);
1455        let mut buf = raw.as_ref();
1456        let decoded = <DecodedBal as alloy_rlp::Decodable>::decode(&mut buf).unwrap();
1457
1458        assert!(buf.is_empty());
1459        assert_eq!(decoded.as_bal(), &bal);
1460        assert_eq!(decoded.as_raw().as_ref(), raw.as_slice());
1461        assert_eq!(alloy_rlp::encode(&decoded), raw);
1462    }
1463
1464    #[test]
1465    fn raw_bal_rlp_roundtrip_preserves_raw_item() {
1466        let bal = sample_bal();
1467        let raw = alloy_rlp::encode(&bal);
1468        let mut buf = raw.as_ref();
1469        let raw_bal = <RawBal as alloy_rlp::Decodable>::decode(&mut buf).unwrap();
1470
1471        assert!(buf.is_empty());
1472        assert_eq!(raw_bal.as_raw().as_ref(), raw.as_slice());
1473        assert_eq!(alloy_rlp::encode(&raw_bal), raw);
1474        assert_eq!(raw_bal.hash(), bal.compute_hash());
1475    }
1476
1477    #[test]
1478    fn raw_or_decoded_bal_try_into_decoded_decodes_raw() {
1479        let bal = sample_bal();
1480        let raw = Bytes::from(alloy_rlp::encode(&bal));
1481        let decoded = RawOrDecodedBal::<Bal>::raw(raw.clone()).try_into_decoded().unwrap();
1482
1483        assert_eq!(decoded.as_bal(), &bal);
1484        assert_eq!(decoded.as_raw(), &raw);
1485        assert_eq!(decoded.hash(), bal.compute_hash());
1486    }
1487
1488    #[test]
1489    fn raw_or_decoded_bal_try_into_decoded_reuses_decoded() {
1490        let bal = sample_bal();
1491        let raw = Bytes::from(alloy_rlp::encode(&bal));
1492        let decoded = DecodedBal::new(bal.clone(), raw.clone());
1493        let decoded = RawOrDecodedBal::decoded(decoded).try_into_decoded().unwrap();
1494
1495        assert_eq!(decoded.as_bal(), &bal);
1496        assert_eq!(decoded.as_raw(), &raw);
1497        assert_eq!(decoded.hash(), bal.compute_hash());
1498    }
1499
1500    #[test]
1501    fn raw_or_decoded_bal_rlp_encodes_raw_bytes() {
1502        let bal = sample_bal();
1503        let raw = alloy_rlp::encode(&bal);
1504        let raw_bal = RawOrDecodedBal::<Bal>::raw(Bytes::from(raw.clone()));
1505        let decoded_bal = RawOrDecodedBal::decoded(DecodedBal::new(bal, Bytes::from(raw.clone())));
1506
1507        assert_eq!(alloy_rlp::encode(&raw_bal), raw);
1508        assert_eq!(alloy_rlp::encode(&decoded_bal), raw);
1509    }
1510
1511    #[test]
1512    fn raw_or_decoded_bal_decode_preserves_raw_rlp_item() {
1513        let bal = sample_bal();
1514        let raw = alloy_rlp::encode(&bal);
1515        let mut buf = raw.as_ref();
1516        let decoded = <RawOrDecodedBal as alloy_rlp::Decodable>::decode(&mut buf).unwrap();
1517
1518        assert!(buf.is_empty());
1519        assert!(decoded.is_raw());
1520        assert_eq!(decoded.as_raw().as_ref(), raw.as_slice());
1521        assert_eq!(alloy_rlp::encode(&decoded), raw);
1522
1523        let decoded = decoded.try_into_decoded().unwrap();
1524        assert_eq!(decoded.as_bal(), &bal);
1525    }
1526}