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    compute_block_access_list_hash_with_buf(bal, &mut Vec::new())
18}
19
20/// Computes the hash of the given block access list, encoding into the given buffer.
21///
22/// The buffer is cleared before use, so a buffer with existing capacity can be reused across
23/// calls to avoid repeated allocations.
24#[cfg(feature = "rlp")]
25pub fn compute_block_access_list_hash_with_buf(
26    bal: &[AccountChanges],
27    buf: &mut Vec<u8>,
28) -> alloy_primitives::B256 {
29    buf.clear();
30    alloy_rlp::encode_list(bal, buf);
31    alloy_primitives::keccak256(buf)
32}
33
34/// Computes the total number of items in the block access list, counting each account and storage
35/// entry.
36pub fn total_bal_items(bal: &[AccountChanges]) -> u64 {
37    bal.iter()
38        .map(|account| 1 + account.storage_changes().len() + account.storage_reads().len())
39        .sum::<usize>() as u64
40}
41
42/// Block-Level Access List wrapper type with helper methods for metrics and validation.
43pub mod bal {
44    use super::OnceLock;
45    use crate::{
46        BlockAccessIndex, BlockAccessListGasError, BlockAccessListHashMismatch,
47        account_changes::AccountChanges, diff::BalDiff,
48    };
49    use alloc::vec::{IntoIter, Vec};
50    use alloy_primitives::{B256, Bytes, map::HashMap};
51    use core::{
52        ops::{Deref, Index},
53        slice::Iter,
54    };
55
56    /// A wrapper around [`Vec<AccountChanges>`] that provides helper methods for
57    /// computing metrics and statistics about the block access list.
58    ///
59    /// This type implements `Deref` to `[AccountChanges]` for easy access to the
60    /// underlying data while providing additional utility methods for BAL analysis.
61    #[derive(Clone, Debug, Default, PartialEq, Eq)]
62    #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
63    #[cfg_attr(
64        feature = "rlp",
65        derive(alloy_rlp::RlpEncodableWrapper, alloy_rlp::RlpDecodableWrapper)
66    )]
67    #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
68    #[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
69    pub struct Bal(Vec<AccountChanges>);
70
71    impl From<Bal> for Vec<AccountChanges> {
72        #[inline]
73        fn from(this: Bal) -> Self {
74            this.0
75        }
76    }
77
78    impl From<Vec<AccountChanges>> for Bal {
79        #[inline]
80        fn from(list: Vec<AccountChanges>) -> Self {
81            Self(list)
82        }
83    }
84
85    #[cfg(feature = "rlp")]
86    impl alloy_primitives::Sealable for Bal {
87        fn hash_slow(&self) -> alloy_primitives::B256 {
88            self.compute_hash()
89        }
90    }
91
92    impl Deref for Bal {
93        type Target = [AccountChanges];
94
95        #[inline]
96        fn deref(&self) -> &Self::Target {
97            self.as_slice()
98        }
99    }
100
101    impl IntoIterator for Bal {
102        type Item = AccountChanges;
103        type IntoIter = IntoIter<AccountChanges>;
104
105        #[inline]
106        fn into_iter(self) -> Self::IntoIter {
107            self.0.into_iter()
108        }
109    }
110
111    impl<'a> IntoIterator for &'a Bal {
112        type Item = &'a AccountChanges;
113        type IntoIter = Iter<'a, AccountChanges>;
114
115        #[inline]
116        fn into_iter(self) -> Self::IntoIter {
117            self.iter()
118        }
119    }
120
121    impl FromIterator<AccountChanges> for Bal {
122        fn from_iter<I: IntoIterator<Item = AccountChanges>>(iter: I) -> Self {
123            Self(iter.into_iter().collect())
124        }
125    }
126
127    impl<I> Index<I> for Bal
128    where
129        I: core::slice::SliceIndex<[AccountChanges]>,
130    {
131        type Output = I::Output;
132
133        #[inline]
134        fn index(&self, index: I) -> &Self::Output {
135            &self.0[index]
136        }
137    }
138
139    impl Bal {
140        /// Creates a new [`Bal`] from the provided account changes.
141        #[inline]
142        pub const fn new(account_changes: Vec<AccountChanges>) -> Self {
143            Self(account_changes)
144        }
145
146        /// Adds a new [`AccountChanges`] entry to the list.
147        #[inline]
148        pub fn push(&mut self, account_changes: AccountChanges) {
149            self.0.push(account_changes)
150        }
151
152        /// Merges the provided account changes into this block access list.
153        ///
154        /// Duplicate account entries already present in this BAL or provided by `incoming` are
155        /// folded into the first entry for that account using [`AccountChanges::merge`]. Changes
156        /// for accounts not already present are appended as new entries.
157        ///
158        /// This preserves relative ordering by keeping existing account entries in place and
159        /// appending newly seen accounts. Call [`Self::sort`] after merging if canonical EIP-7928
160        /// ordering is required.
161        pub fn merge<I>(&mut self, incoming: I)
162        where
163            I: IntoIterator<Item = AccountChanges>,
164        {
165            let existing_accounts = core::mem::take(&mut self.0);
166            let mut merged_accounts = Vec::<AccountChanges>::with_capacity(existing_accounts.len());
167            let mut account_positions = HashMap::<_, usize>::with_capacity_and_hasher(
168                existing_accounts.len(),
169                Default::default(),
170            );
171
172            for account_changes in existing_accounts.into_iter().chain(incoming) {
173                if let Some(&idx) = account_positions.get(&account_changes.address) {
174                    merged_accounts[idx].merge(account_changes);
175                } else {
176                    account_positions.insert(account_changes.address, merged_accounts.len());
177                    merged_accounts.push(account_changes);
178                }
179            }
180
181            self.0 = merged_accounts;
182        }
183
184        /// Inserts a synthetic change layer at `block_access_index`.
185        ///
186        /// The supplied change sets are normalized first: duplicate accounts and duplicate slot
187        /// entries are folded, empty slot entries are dropped, storage reads are deduplicated and
188        /// pruned against written slots, and duplicate writes within the inserted layer collapse
189        /// to the last supplied value. Accounts that carry no data after normalization are
190        /// ignored.
191        ///
192        /// Existing changes at or after the insertion point are shifted forward by one
193        /// (saturating at `u64::MAX`), the collapsed changes are assigned to the insertion point,
194        /// and the BAL is renormalized into canonical EIP-7928 order, which also folds any
195        /// duplicate account entries that were already present. Inserting a layer grows the
196        /// block's index domain by one, so classifying shifted indices with
197        /// [`BlockAccessIndex::phase`] requires the grown transaction count.
198        ///
199        /// Returns the index immediately after the inserted layer. Callers positioned at
200        /// `block_access_index` should use the returned index to observe the inserted changes while
201        /// preserving the original BAL state at that position.
202        ///
203        /// BAL reads observe changes strictly before the positioned index, so inserting a layer
204        /// moves the read position forward with the original suffix:
205        ///
206        /// ```text
207        /// before: [change 0] [change 1] | read @ 2 | [change 2] [change 3]
208        /// after:  [change 0] [change 1] [override @ 2] | read @ 3 | [change 3] [change 4]
209        /// ```
210        ///
211        /// This effectively allows RPC-style state overrides to be applied directly to a positioned
212        /// BAL: convert the overridden balance, nonce, code, and storage values into
213        /// [`AccountChanges`], insert them at the current position, then continue reading at the
214        /// returned index. No parallel override field or cache is required.
215        ///
216        /// A full storage replacement cannot be represented completely by a BAL alone. Known BAL
217        /// slots can be zeroed in the inserted layer, but fallback reads for slots absent from the
218        /// BAL still require replacement-aware backing state.
219        ///
220        /// Storage reads carry no block access index: supplied reads are merged into the affected
221        /// accounts without reserving a layer, so input that normalizes to reads only leaves all
222        /// indices (and the returned index) unchanged. If `incoming` contains no account data, the
223        /// BAL and returned index are unchanged.
224        #[must_use = "the returned index replaces the caller's position after the inserted layer"]
225        pub fn insert_changes_at<I>(
226            &mut self,
227            block_access_index: BlockAccessIndex,
228            incoming: I,
229        ) -> BlockAccessIndex
230        where
231            I: IntoIterator<Item = AccountChanges>,
232        {
233            let mut inserted = Self::default();
234            // Empty slot entries must be dropped before merging so they cannot mask a supplied
235            // read of the same slot as written.
236            inserted.merge(incoming.into_iter().map(|mut account| {
237                account.storage_changes.retain(|slot_changes| !slot_changes.is_empty());
238                account
239            }));
240            for account in &mut inserted.0 {
241                account.normalize();
242                account.collapse_changes_at(block_access_index);
243            }
244            inserted.0.retain(|account| !account.is_empty());
245            if inserted.is_empty() {
246                return block_access_index;
247            }
248
249            // Reads carry no index, so a layer (and the associated suffix shift) is only needed
250            // when at least one indexed change is inserted.
251            let inserts_layer = inserted.0.iter().any(has_indexed_changes);
252            if inserts_layer {
253                for account in &mut self.0 {
254                    account.shift_indices_from(block_access_index);
255                }
256            }
257
258            self.merge(inserted);
259            self.sort();
260
261            if !inserts_layer {
262                return block_access_index;
263            }
264            let mut positioned_index = block_access_index;
265            positioned_index.saturating_increment();
266            positioned_index
267        }
268
269        /// Returns `true` if the list contains no elements.
270        #[inline]
271        pub const fn is_empty(&self) -> bool {
272            self.0.is_empty()
273        }
274
275        /// Returns the number of account change entries contained in the list.
276        #[inline]
277        pub const fn len(&self) -> usize {
278            self.0.len()
279        }
280
281        /// Returns an iterator over the [`AccountChanges`] entries.
282        #[inline]
283        pub fn iter(&self) -> Iter<'_, AccountChanges> {
284            self.0.iter()
285        }
286
287        /// Returns a slice of the contained [`AccountChanges`].
288        #[inline]
289        pub const fn as_slice(&self) -> &[AccountChanges] {
290            self.0.as_slice()
291        }
292
293        /// Returns the contained [`Vec<AccountChanges>`].
294        #[inline]
295        pub const fn as_vec(&self) -> &Vec<AccountChanges> {
296            &self.0
297        }
298
299        /// Returns a compact diff describing where this BAL first diverges from `other`.
300        pub fn diff(&self, other: &[AccountChanges]) -> BalDiff {
301            BalDiff::between(self.as_slice(), other)
302        }
303
304        /// Returns a vector of [`AccountChanges`].
305        #[inline]
306        pub fn into_inner(self) -> Vec<AccountChanges> {
307            self.0
308        }
309
310        /// Sorts this block access list in-place according to the canonical EIP-7928 ordering
311        /// rules.
312        ///
313        /// This applies the ordering required by the "Ordering, Uniqueness and Determinism"
314        /// section of EIP-7928:
315        ///
316        /// - accounts are sorted lexicographically by address
317        /// - `storage_changes` are sorted lexicographically by storage key
318        /// - each per-slot `StorageChange` list is sorted by block access index in ascending order
319        /// - `storage_reads` are sorted lexicographically by storage key
320        /// - `balance_changes`, `nonce_changes`, and `code_changes` are sorted by block access
321        ///   index in ascending order
322        ///
323        /// The account-local ordering is delegated to [`AccountChanges::sort`], so callers may
324        /// sort account internals independently when a parallel sort strategy is useful.
325        ///
326        /// This method only canonicalizes ordering. It does not enforce the EIP-7928 uniqueness
327        /// constraints for accounts, storage keys, or block access indexes.
328        pub fn sort(&mut self) {
329            self.0.sort_unstable_by_key(|account| account.address);
330
331            for account in &mut self.0 {
332                account.sort();
333            }
334        }
335
336        /// Returns the total number of accounts with changes in this BAL.
337        #[inline]
338        pub const fn account_count(&self) -> usize {
339            self.0.len()
340        }
341
342        /// Returns the total number of storage changes across all accounts.
343        pub fn total_storage_changes(&self) -> usize {
344            self.0.iter().map(|a| a.storage_changes.len()).sum()
345        }
346
347        /// Returns the total number of storage reads across all accounts.
348        pub fn total_storage_reads(&self) -> usize {
349            self.0.iter().map(|a| a.storage_reads.len()).sum()
350        }
351
352        /// Returns the total number of storage slots (both changes and reads) across all accounts.
353        pub fn total_slots(&self) -> usize {
354            self.0.iter().map(|a| a.storage_changes.len() + a.storage_reads.len()).sum()
355        }
356
357        /// Returns the total number of balance changes across all accounts.
358        pub fn total_balance_changes(&self) -> usize {
359            self.0.iter().map(|a| a.balance_changes.len()).sum()
360        }
361
362        /// Returns the total number of nonce changes across all accounts.
363        pub fn total_nonce_changes(&self) -> usize {
364            self.0.iter().map(|a| a.nonce_changes.len()).sum()
365        }
366
367        /// Returns the total number of code changes across all accounts.
368        pub fn total_code_changes(&self) -> usize {
369            self.0.iter().map(|a| a.code_changes.len()).sum()
370        }
371
372        /// Returns a summary of all change counts for metrics reporting.
373        pub fn change_counts(&self) -> BalChangeCounts {
374            let mut counts = BalChangeCounts::default();
375            for account in &self.0 {
376                counts.accounts += 1;
377                counts.storage += account.storage_changes.len();
378                counts.balance += account.balance_changes.len();
379                counts.nonce += account.nonce_changes.len();
380                counts.code += account.code_changes.len();
381            }
382            counts
383        }
384
385        /// Computes the total number of items in this block access list, counting each account and
386        /// unique storage slot.
387        pub fn total_bal_items(&self) -> u64 {
388            super::total_bal_items(&self.0)
389        }
390
391        /// Validates this block access list's structure and block access indices.
392        ///
393        /// This is a convenience wrapper around [`crate::validate_block_access_list`]. It checks
394        /// canonical ordering, uniqueness, non-empty storage change lists, and bounds every block
395        /// access index by this block's `transaction_count`.
396        pub fn validate_structure(
397            &self,
398            transaction_count: usize,
399        ) -> Result<(), crate::BlockAccessListValidationError> {
400            crate::validate_block_access_list(self.as_slice(), transaction_count)
401        }
402
403        /// Validates this block access list against the block gas limit.
404        ///
405        /// EIP-7928 specifies that the total cost of the block access list items must not exceed
406        /// the gas limit. Each item costs [`crate::constants::ITEM_COST`] gas.
407        pub fn validate_gas_limit(&self, gas_limit: u64) -> Result<(), BlockAccessListGasError> {
408            let items = self.total_bal_items();
409            if items > gas_limit / crate::constants::ITEM_COST as u64 {
410                return Err(BlockAccessListGasError::new(items, gas_limit));
411            }
412            Ok(())
413        }
414
415        /// Computes the hash of this block access list.
416        #[cfg(feature = "rlp")]
417        pub fn compute_hash(&self) -> alloy_primitives::B256 {
418            self.compute_hash_with_buf(&mut Vec::new())
419        }
420
421        /// Computes the hash of this block access list, encoding into the given buffer if the
422        /// list is non-empty.
423        ///
424        /// For a non-empty list the buffer is cleared before use, so a buffer with existing
425        /// capacity can be reused across calls to avoid repeated allocations. An empty list
426        /// returns [`crate::constants::EMPTY_BLOCK_ACCESS_LIST_HASH`] without touching the
427        /// buffer.
428        #[cfg(feature = "rlp")]
429        pub fn compute_hash_with_buf(&self, buf: &mut Vec<u8>) -> alloy_primitives::B256 {
430            if self.0.is_empty() {
431                return crate::constants::EMPTY_BLOCK_ACCESS_LIST_HASH;
432            }
433            super::compute_block_access_list_hash_with_buf(&self.0, buf)
434        }
435    }
436
437    /// Returns `true` if the account carries changes that occupy a block access index.
438    const fn has_indexed_changes(account: &AccountChanges) -> bool {
439        let AccountChanges {
440            address: _,
441            storage_changes,
442            storage_reads: _,
443            balance_changes,
444            nonce_changes,
445            code_changes,
446        } = account;
447        !storage_changes.is_empty()
448            || !balance_changes.is_empty()
449            || !nonce_changes.is_empty()
450            || !code_changes.is_empty()
451    }
452
453    /// Summary of change counts in a BAL for metrics reporting.
454    #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
455    pub struct BalChangeCounts {
456        /// Number of accounts with changes.
457        pub accounts: usize,
458        /// Total number of storage changes.
459        pub storage: usize,
460        /// Total number of balance changes.
461        pub balance: usize,
462        /// Total number of nonce changes.
463        pub nonce: usize,
464        /// Total number of code changes.
465        pub code: usize,
466    }
467
468    /// Raw RLP bytes for a block access list with lazy hash computation.
469    #[derive(Clone, Debug)]
470    #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
471    #[cfg_attr(feature = "serde", serde(transparent))]
472    pub struct RawBal {
473        /// The original raw RLP bytes.
474        raw: Bytes,
475        /// Lazily computed hash of the block access list.
476        #[cfg_attr(feature = "serde", serde(skip, default))]
477        hash: OnceLock<B256>,
478    }
479
480    impl PartialEq for RawBal {
481        #[inline]
482        fn eq(&self, other: &Self) -> bool {
483            self.raw == other.raw
484        }
485    }
486
487    impl Eq for RawBal {}
488
489    impl From<Bytes> for RawBal {
490        #[inline]
491        fn from(raw: Bytes) -> Self {
492            Self::new(raw)
493        }
494    }
495
496    impl RawBal {
497        /// Creates a new [`RawBal`] from raw RLP bytes.
498        #[inline]
499        pub const fn new(raw: Bytes) -> Self {
500            Self { raw, hash: OnceLock::new() }
501        }
502
503        /// Creates a new [`RawBal`] from raw RLP bytes and a precomputed hash.
504        ///
505        /// The hash is not checked against the raw bytes. Callers must ensure `hash` is the
506        /// keccak256 hash of `raw`.
507        #[inline]
508        pub fn new_unchecked(raw: Bytes, hash: B256) -> Self {
509            let this = Self::new(raw);
510            #[allow(clippy::useless_conversion)]
511            let _ = this.hash.get_or_init(|| hash.into());
512            this
513        }
514
515        /// Returns the original raw RLP bytes.
516        #[inline]
517        pub const fn as_raw(&self) -> &Bytes {
518            &self.raw
519        }
520
521        /// Consumes this value and returns the raw RLP bytes.
522        #[inline]
523        pub fn into_raw(self) -> Bytes {
524            self.raw
525        }
526
527        /// Consumes this value and returns the raw RLP bytes and hash.
528        #[inline]
529        pub fn into_parts(self) -> (Bytes, B256) {
530            let hash = self.hash();
531            (self.raw, hash)
532        }
533
534        /// Ensures the raw RLP hash matches the expected block access list hash.
535        #[inline]
536        pub fn ensure_hash(&self, expected: B256) -> Result<(), BlockAccessListHashMismatch> {
537            let computed = self.hash();
538            if computed == expected {
539                Ok(())
540            } else {
541                Err(BlockAccessListHashMismatch::new(computed, expected))
542            }
543        }
544
545        /// Returns the hash of the raw block access list bytes.
546        ///
547        /// The hash is computed lazily on first call and cached for subsequent calls.
548        #[inline]
549        pub fn hash(&self) -> B256 {
550            #[allow(clippy::useless_conversion)]
551            *self.hash.get_or_init(|| alloy_primitives::keccak256(self.raw.as_ref()).into())
552        }
553    }
554
555    #[cfg(feature = "rlp")]
556    impl alloy_rlp::Encodable for RawBal {
557        #[inline]
558        fn encode(&self, out: &mut dyn alloy_rlp::BufMut) {
559            out.put_slice(&self.raw);
560        }
561
562        #[inline]
563        fn length(&self) -> usize {
564            self.raw.len()
565        }
566    }
567
568    #[cfg(feature = "rlp")]
569    impl alloy_rlp::Decodable for RawBal {
570        #[inline]
571        fn decode(buf: &mut &[u8]) -> Result<Self, alloy_rlp::Error> {
572            let original = *buf;
573            let header = alloy_rlp::Header::decode(buf)?;
574            let header_len = original.len() - buf.len();
575            let raw_len = header_len + header.payload_length;
576            let raw = Bytes::copy_from_slice(&original[..raw_len]);
577            *buf = &original[raw_len..];
578            Ok(Self::new(raw))
579        }
580    }
581
582    /// A decoded block access list with lazy hash computation.
583    ///
584    /// This type wraps a decoded block access list along with the original raw RLP bytes,
585    /// allowing efficient hash computation on demand without re-encoding.
586    #[derive(Clone, Debug)]
587    #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
588    pub struct DecodedBal<T = Bal> {
589        /// The decoded block access list.
590        decoded: T,
591        /// Raw RLP bytes and lazily computed hash of the block access list.
592        raw: RawBal,
593    }
594
595    impl<T: PartialEq> PartialEq for DecodedBal<T> {
596        #[inline]
597        fn eq(&self, other: &Self) -> bool {
598            self.decoded == other.decoded && self.raw == other.raw
599        }
600    }
601
602    impl<T: Eq> Eq for DecodedBal<T> {}
603
604    impl<T> DecodedBal<T> {
605        /// Creates a new [`DecodedBal`] from decoded data and raw bytes.
606        #[inline]
607        pub const fn new(decoded: T, raw: Bytes) -> Self {
608            Self { decoded, raw: RawBal::new(raw) }
609        }
610
611        /// Creates a new [`DecodedBal`] from decoded data, raw bytes, and a precomputed hash.
612        ///
613        /// The hash is not checked against the raw bytes. Callers must ensure `hash` is the
614        /// keccak256 hash of `raw`.
615        #[inline]
616        pub fn new_unchecked(decoded: T, raw: Bytes, hash: B256) -> Self {
617            Self { decoded, raw: RawBal::new_unchecked(raw, hash) }
618        }
619
620        /// Creates a new [`DecodedBal`] from decoded data and a [`RawBal`].
621        #[inline]
622        pub const fn with_raw_bal(decoded: T, raw: RawBal) -> Self {
623            Self { decoded, raw }
624        }
625
626        /// Returns a reference to the decoded block access list.
627        #[inline]
628        pub const fn as_bal(&self) -> &T {
629            &self.decoded
630        }
631
632        /// Returns the original raw RLP bytes.
633        #[inline]
634        pub const fn as_raw(&self) -> &Bytes {
635            self.raw.as_raw()
636        }
637
638        /// Returns the raw BAL.
639        #[inline]
640        pub const fn as_raw_bal(&self) -> &RawBal {
641            &self.raw
642        }
643
644        /// Splits this struct into the decoded BAL and raw bytes.
645        #[inline]
646        pub fn split(self) -> (T, Bytes) {
647            (self.decoded, self.raw.into_raw())
648        }
649
650        /// Splits this struct into the decoded BAL and raw BAL.
651        #[inline]
652        pub fn split_raw_bal(self) -> (T, RawBal) {
653            (self.decoded, self.raw)
654        }
655
656        /// Splits this struct into the decoded BAL, raw bytes, and hash.
657        #[inline]
658        pub fn into_parts(self) -> (T, Bytes, B256) {
659            let hash = self.hash();
660            let (decoded, raw) = self.split();
661            (decoded, raw, hash)
662        }
663
664        /// Ensures the raw RLP hash matches the expected block access list hash.
665        ///
666        /// This checks `keccak256(raw_rlp_of_received_bal) == expected` using the cached hash of
667        /// the original raw RLP bytes captured at decode time.
668        #[inline]
669        pub fn ensure_hash(&self, expected: B256) -> Result<(), BlockAccessListHashMismatch> {
670            let computed = self.hash();
671            if computed == expected {
672                Ok(())
673            } else {
674                Err(BlockAccessListHashMismatch::new(computed, expected))
675            }
676        }
677
678        /// Returns the hash of this block access list.
679        ///
680        /// The hash is computed lazily on first call and cached for subsequent calls.
681        #[inline]
682        pub fn hash(&self) -> B256 {
683            self.raw.hash()
684        }
685
686        /// Converts the decoded BAL to the given alternative that is [`From<T>`].
687        #[inline]
688        pub fn convert<U>(self) -> DecodedBal<U>
689        where
690            U: From<T>,
691        {
692            self.map(U::from)
693        }
694
695        /// Converts the decoded BAL to the given alternative that is [`TryFrom<T>`].
696        #[inline]
697        pub fn try_convert<U>(self) -> Result<DecodedBal<U>, U::Error>
698        where
699            U: TryFrom<T>,
700        {
701            self.try_map(U::try_from)
702        }
703
704        /// Applies the given closure to the decoded BAL.
705        #[inline]
706        pub fn map<U>(self, f: impl FnOnce(T) -> U) -> DecodedBal<U> {
707            let Self { decoded, raw } = self;
708            DecodedBal { decoded: f(decoded), raw }
709        }
710
711        /// Applies the given fallible closure to the decoded BAL.
712        #[inline]
713        pub fn try_map<U, E>(self, f: impl FnOnce(T) -> Result<U, E>) -> Result<DecodedBal<U>, E> {
714            let Self { decoded, raw } = self;
715            Ok(DecodedBal { decoded: f(decoded)?, raw })
716        }
717    }
718
719    #[cfg(feature = "rlp")]
720    impl DecodedBal {
721        /// Creates a new [`DecodedBal`] by decoding from raw RLP bytes.
722        #[inline]
723        pub fn from_rlp_bytes(raw: Bytes) -> Result<Self, alloy_rlp::Error> {
724            Self::from_rlp_bytes_as(raw)
725        }
726
727        /// Creates a new [`DecodedBal`] by decoding from raw RLP bytes in a [`RawBal`].
728        #[inline]
729        pub fn from_raw_bal(raw: RawBal) -> Result<Self, alloy_rlp::Error> {
730            Self::from_raw_bal_as(raw)
731        }
732
733        /// Creates a new [`DecodedBal`] by decoding from raw RLP bytes into `T`.
734        #[inline]
735        pub fn from_rlp_bytes_as<T>(raw: Bytes) -> Result<DecodedBal<T>, alloy_rlp::Error>
736        where
737            T: alloy_rlp::Decodable,
738        {
739            Self::from_raw_bal_as(RawBal::new(raw))
740        }
741
742        /// Creates a new [`DecodedBal`] by decoding from raw RLP bytes in a [`RawBal`] into `T`.
743        #[inline]
744        pub fn from_raw_bal_as<T>(raw: RawBal) -> Result<DecodedBal<T>, alloy_rlp::Error>
745        where
746            T: alloy_rlp::Decodable,
747        {
748            let mut slice = raw.as_raw().as_ref();
749            let decoded = T::decode(&mut slice)?;
750            if !slice.is_empty() {
751                return Err(alloy_rlp::Error::UnexpectedLength);
752            }
753            Ok(DecodedBal::with_raw_bal(decoded, raw))
754        }
755    }
756
757    #[cfg(feature = "rlp")]
758    impl<T> DecodedBal<T>
759    where
760        T: alloy_primitives::Sealable,
761    {
762        /// Returns the decoded BAL as a sealed borrowed value.
763        #[inline]
764        pub fn as_sealed_bal(&self) -> alloy_primitives::Sealed<&T> {
765            alloy_primitives::Sealable::seal_ref_unchecked(&self.decoded, self.hash())
766        }
767
768        /// Consumes this struct and returns the decoded BAL together with its hash.
769        #[inline]
770        pub fn into_sealed(self) -> alloy_primitives::Sealed<T> {
771            let seal = self.hash();
772            let (decoded, _) = self.split();
773            alloy_primitives::Sealable::seal_unchecked(decoded, seal)
774        }
775    }
776
777    #[cfg(feature = "rlp")]
778    impl<T> alloy_rlp::Decodable for DecodedBal<T>
779    where
780        T: alloy_rlp::Decodable,
781    {
782        #[inline]
783        fn decode(buf: &mut &[u8]) -> Result<Self, alloy_rlp::Error> {
784            let original = *buf;
785            let decoded = T::decode(buf)?;
786            let consumed = original.len() - buf.len();
787            let raw = Bytes::copy_from_slice(&original[..consumed]);
788            Ok(Self::new(decoded, raw))
789        }
790    }
791
792    #[cfg(feature = "rlp")]
793    impl<T> alloy_rlp::Encodable for DecodedBal<T> {
794        #[inline]
795        fn encode(&self, out: &mut dyn alloy_rlp::BufMut) {
796            alloy_rlp::Encodable::encode(&self.raw, out);
797        }
798
799        #[inline]
800        fn length(&self) -> usize {
801            alloy_rlp::Encodable::length(&self.raw)
802        }
803    }
804
805    /// Either raw RLP bytes or a decoded block access list.
806    ///
807    /// This type is useful when callers may receive raw BAL bytes before the BAL needs to be
808    /// decoded, while still allowing decoded values to preserve and re-use their original raw
809    /// bytes.
810    #[derive(Clone, Debug, PartialEq, Eq)]
811    #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
812    pub enum RawOrDecodedBal<T = Bal> {
813        /// Raw RLP bytes for a block access list with lazy hash computation.
814        Raw(RawBal),
815        /// A decoded block access list with its original raw RLP bytes.
816        Decoded(DecodedBal<T>),
817    }
818
819    impl<T> From<Bytes> for RawOrDecodedBal<T> {
820        #[inline]
821        fn from(raw: Bytes) -> Self {
822            Self::Raw(RawBal::new(raw))
823        }
824    }
825
826    impl<T> From<RawBal> for RawOrDecodedBal<T> {
827        #[inline]
828        fn from(raw: RawBal) -> Self {
829            Self::Raw(raw)
830        }
831    }
832
833    impl<T> From<DecodedBal<T>> for RawOrDecodedBal<T> {
834        #[inline]
835        fn from(decoded: DecodedBal<T>) -> Self {
836            Self::Decoded(decoded)
837        }
838    }
839
840    impl<T> RawOrDecodedBal<T> {
841        /// Creates a new [`RawOrDecodedBal`] from raw RLP bytes.
842        #[inline]
843        pub const fn raw(raw: Bytes) -> Self {
844            Self::Raw(RawBal::new(raw))
845        }
846
847        /// Creates a new [`RawOrDecodedBal`] from raw RLP bytes and a precomputed hash.
848        ///
849        /// The hash is not checked against the raw bytes. Callers must ensure `hash` is the
850        /// keccak256 hash of `raw`.
851        #[inline]
852        pub fn raw_unchecked(raw: Bytes, hash: B256) -> Self {
853            Self::Raw(RawBal::new_unchecked(raw, hash))
854        }
855
856        /// Creates a new [`RawOrDecodedBal`] from a [`RawBal`].
857        #[inline]
858        pub const fn raw_bal(raw: RawBal) -> Self {
859            Self::Raw(raw)
860        }
861
862        /// Creates a new [`RawOrDecodedBal`] from a decoded BAL.
863        #[inline]
864        pub const fn decoded(decoded: DecodedBal<T>) -> Self {
865            Self::Decoded(decoded)
866        }
867
868        /// Returns `true` if this contains raw RLP bytes.
869        #[inline]
870        pub const fn is_raw(&self) -> bool {
871            matches!(self, Self::Raw(_))
872        }
873
874        /// Returns `true` if this contains a decoded BAL.
875        #[inline]
876        pub const fn is_decoded(&self) -> bool {
877            matches!(self, Self::Decoded(_))
878        }
879
880        /// Returns the raw RLP bytes.
881        #[inline]
882        pub const fn as_raw(&self) -> &Bytes {
883            match self {
884                Self::Raw(raw) => raw.as_raw(),
885                Self::Decoded(decoded) => decoded.as_raw(),
886            }
887        }
888
889        /// Returns the raw BAL.
890        #[inline]
891        pub const fn as_raw_bal(&self) -> &RawBal {
892            match self {
893                Self::Raw(raw) => raw,
894                Self::Decoded(decoded) => decoded.as_raw_bal(),
895            }
896        }
897
898        /// Returns the decoded BAL if available.
899        #[inline]
900        pub const fn as_decoded(&self) -> Option<&DecodedBal<T>> {
901            match self {
902                Self::Raw(_) => None,
903                Self::Decoded(decoded) => Some(decoded),
904            }
905        }
906
907        /// Returns the decoded BAL if available.
908        #[inline]
909        pub fn into_decoded(self) -> Option<DecodedBal<T>> {
910            match self {
911                Self::Raw(_) => None,
912                Self::Decoded(decoded) => Some(decoded),
913            }
914        }
915
916        /// Returns the decoded block access list if available.
917        #[inline]
918        pub const fn as_bal(&self) -> Option<&T> {
919            match self {
920                Self::Raw(_) => None,
921                Self::Decoded(decoded) => Some(decoded.as_bal()),
922            }
923        }
924
925        /// Consumes this value and returns the raw RLP bytes.
926        #[inline]
927        pub fn into_raw(self) -> Bytes {
928            match self {
929                Self::Raw(raw) => raw.into_raw(),
930                Self::Decoded(decoded) => decoded.split().1,
931            }
932        }
933
934        /// Consumes this value and returns the raw BAL.
935        #[inline]
936        pub fn into_raw_bal(self) -> RawBal {
937            match self {
938                Self::Raw(raw) => raw,
939                Self::Decoded(decoded) => decoded.split_raw_bal().1,
940            }
941        }
942
943        /// Splits this value into its decoded BAL, if available, and raw RLP bytes.
944        #[inline]
945        pub fn split(self) -> (Option<T>, Bytes) {
946            match self {
947                Self::Raw(raw) => (None, raw.into_raw()),
948                Self::Decoded(decoded) => {
949                    let (bal, raw) = decoded.split();
950                    (Some(bal), raw)
951                }
952            }
953        }
954
955        /// Splits this value into its decoded BAL, if available, and raw BAL.
956        #[inline]
957        pub fn split_raw_bal(self) -> (Option<T>, RawBal) {
958            match self {
959                Self::Raw(raw) => (None, raw),
960                Self::Decoded(decoded) => {
961                    let (bal, raw) = decoded.split_raw_bal();
962                    (Some(bal), raw)
963                }
964            }
965        }
966
967        /// Ensures the raw RLP hash matches the expected block access list hash.
968        #[inline]
969        pub fn ensure_hash(&self, expected: B256) -> Result<(), BlockAccessListHashMismatch> {
970            let computed = self.hash();
971            if computed == expected {
972                Ok(())
973            } else {
974                Err(BlockAccessListHashMismatch::new(computed, expected))
975            }
976        }
977
978        /// Returns the hash of the raw block access list bytes.
979        #[inline]
980        pub fn hash(&self) -> B256 {
981            match self {
982                Self::Raw(raw) => raw.hash(),
983                Self::Decoded(decoded) => decoded.hash(),
984            }
985        }
986
987        /// Converts the decoded BAL to the given alternative that is [`From<T>`].
988        ///
989        /// Raw values stay raw.
990        #[inline]
991        pub fn convert<U>(self) -> RawOrDecodedBal<U>
992        where
993            U: From<T>,
994        {
995            self.map(U::from)
996        }
997
998        /// Converts the decoded BAL to the given alternative that is [`TryFrom<T>`].
999        ///
1000        /// Raw values stay raw.
1001        #[inline]
1002        pub fn try_convert<U>(self) -> Result<RawOrDecodedBal<U>, U::Error>
1003        where
1004            U: TryFrom<T>,
1005        {
1006            self.try_map(U::try_from)
1007        }
1008
1009        /// Applies the given closure to the decoded BAL if available.
1010        #[inline]
1011        pub fn map<U>(self, f: impl FnOnce(T) -> U) -> RawOrDecodedBal<U> {
1012            match self {
1013                Self::Raw(raw) => RawOrDecodedBal::Raw(raw),
1014                Self::Decoded(decoded) => RawOrDecodedBal::Decoded(decoded.map(f)),
1015            }
1016        }
1017
1018        /// Applies the given fallible closure to the decoded BAL if available.
1019        #[inline]
1020        pub fn try_map<U, E>(
1021            self,
1022            f: impl FnOnce(T) -> Result<U, E>,
1023        ) -> Result<RawOrDecodedBal<U>, E> {
1024            match self {
1025                Self::Raw(raw) => Ok(RawOrDecodedBal::Raw(raw)),
1026                Self::Decoded(decoded) => decoded.try_map(f).map(RawOrDecodedBal::Decoded),
1027            }
1028        }
1029    }
1030
1031    #[cfg(feature = "rlp")]
1032    impl<T> RawOrDecodedBal<T>
1033    where
1034        T: alloy_rlp::Decodable,
1035    {
1036        /// Up-converts raw RLP bytes into a decoded BAL, or returns the existing decoded BAL.
1037        #[inline]
1038        pub fn try_into_decoded(self) -> Result<DecodedBal<T>, alloy_rlp::Error> {
1039            match self {
1040                Self::Raw(raw) => DecodedBal::from_raw_bal_as(raw),
1041                Self::Decoded(decoded) => Ok(decoded),
1042            }
1043        }
1044    }
1045
1046    #[cfg(feature = "rlp")]
1047    impl<T> alloy_rlp::Encodable for RawOrDecodedBal<T> {
1048        #[inline]
1049        fn encode(&self, out: &mut dyn alloy_rlp::BufMut) {
1050            out.put_slice(self.as_raw());
1051        }
1052
1053        #[inline]
1054        fn length(&self) -> usize {
1055            self.as_raw().len()
1056        }
1057    }
1058
1059    #[cfg(feature = "rlp")]
1060    impl<T> alloy_rlp::Decodable for RawOrDecodedBal<T> {
1061        #[inline]
1062        fn decode(buf: &mut &[u8]) -> Result<Self, alloy_rlp::Error> {
1063            <RawBal as alloy_rlp::Decodable>::decode(buf).map(Self::Raw)
1064        }
1065    }
1066}
1067
1068/// Error returned when a block access list item cost exceeds the block gas limit.
1069#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, thiserror::Error)]
1070#[error(
1071    "block access list item cost exceeds gas limit: items={items}, max_items={max_items}, gas_limit={gas_limit}"
1072)]
1073pub struct BlockAccessListGasError {
1074    /// Number of block access list items.
1075    pub items: u64,
1076    /// Maximum number of block access list items allowed by the gas limit.
1077    pub max_items: u64,
1078    /// Block gas limit used for validation.
1079    pub gas_limit: u64,
1080}
1081
1082impl BlockAccessListGasError {
1083    /// Creates a new gas limit validation error for the provided item count and gas limit.
1084    #[inline]
1085    pub const fn new(items: u64, gas_limit: u64) -> Self {
1086        Self { items, max_items: gas_limit / crate::constants::ITEM_COST as u64, gas_limit }
1087    }
1088}
1089
1090/// Error returned when a decoded block access list hash does not match the expected hash.
1091#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, thiserror::Error)]
1092#[error("block access list hash mismatch: computed={computed}, expected={expected}")]
1093pub struct BlockAccessListHashMismatch {
1094    /// Hash computed from the received BAL bytes.
1095    pub computed: alloy_primitives::B256,
1096    /// Hash expected by the caller, typically `header.block_access_list_hash`.
1097    pub expected: alloy_primitives::B256,
1098}
1099
1100impl BlockAccessListHashMismatch {
1101    /// Creates a new block access list hash validation error.
1102    #[inline]
1103    pub const fn new(computed: alloy_primitives::B256, expected: alloy_primitives::B256) -> Self {
1104        Self { computed, expected }
1105    }
1106}
1107
1108#[cfg(test)]
1109mod hash_tests {
1110    use super::bal::{Bal, DecodedBal, RawBal, RawOrDecodedBal};
1111    use crate::{
1112        AccountChanges, BalanceChange, BlockAccessIndex, CodeChange, NonceChange, SlotChanges,
1113        StorageChange, constants::ITEM_COST,
1114    };
1115    use alloc::vec::Vec;
1116    use alloy_primitives::{Address, B256, Bytes, U256};
1117
1118    #[test]
1119    fn decoded_bal_hash_uses_raw_bytes_without_rlp_feature() {
1120        let raw = Bytes::from_static(&[0xc0]);
1121        let decoded = DecodedBal::new(Bal::default(), raw.clone());
1122
1123        assert_eq!(decoded.hash(), alloy_primitives::keccak256(raw.as_ref()));
1124
1125        let (bal, split_raw, split_hash) = decoded.into_parts();
1126        assert!(bal.is_empty());
1127        assert_eq!(split_raw, raw);
1128        assert_eq!(split_hash, alloy_primitives::keccak256(raw.as_ref()));
1129    }
1130
1131    #[test]
1132    fn decoded_bal_map_preserves_raw_and_hash() {
1133        let raw = Bytes::from_static(&[0xc0]);
1134        let decoded = DecodedBal::new(Bal::default(), raw.clone());
1135        let hash = decoded.hash();
1136
1137        let mapped = decoded.map(|bal| bal.len());
1138
1139        assert_eq!(mapped.as_bal(), &0);
1140        assert_eq!(mapped.as_raw(), &raw);
1141        assert_eq!(mapped.hash(), hash);
1142    }
1143
1144    #[test]
1145    fn decoded_bal_try_map_converts_or_returns_error() {
1146        let raw = Bytes::from_static(&[0xc0]);
1147        let decoded = DecodedBal::new(Bal::default(), raw.clone());
1148
1149        let mapped = decoded.try_map(|bal| Ok::<_, core::convert::Infallible>(bal.len())).unwrap();
1150
1151        assert_eq!(mapped.as_bal(), &0);
1152        assert_eq!(mapped.as_raw(), &raw);
1153
1154        let decoded = DecodedBal::new(Bal::default(), raw);
1155        let err = decoded.try_map(|_| Err::<usize, _>("expected error")).unwrap_err();
1156
1157        assert_eq!(err, "expected error");
1158    }
1159
1160    #[test]
1161    fn bal_as_vec_returns_inner_vector_ref() {
1162        let bal = Bal::new(vec![AccountChanges::new(Address::from([0x11; 20]))]);
1163
1164        assert_eq!(bal.as_vec().len(), 1);
1165        assert_eq!(bal.as_vec().as_slice(), bal.as_slice());
1166    }
1167
1168    #[derive(Debug, PartialEq, Eq)]
1169    struct BalLen(usize);
1170
1171    impl From<Bal> for BalLen {
1172        fn from(value: Bal) -> Self {
1173            Self(value.len())
1174        }
1175    }
1176
1177    #[derive(Debug, PartialEq, Eq)]
1178    struct NonEmptyBal(Bal);
1179
1180    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
1181    struct EmptyBal;
1182
1183    impl TryFrom<Bal> for NonEmptyBal {
1184        type Error = EmptyBal;
1185
1186        fn try_from(value: Bal) -> Result<Self, Self::Error> {
1187            if value.is_empty() { Err(EmptyBal) } else { Ok(Self(value)) }
1188        }
1189    }
1190
1191    #[test]
1192    fn decoded_bal_convert_and_try_convert_use_inner_conversions() {
1193        let raw = Bytes::from_static(&[0xc0]);
1194        let converted: DecodedBal<BalLen> = DecodedBal::new(Bal::default(), raw.clone()).convert();
1195
1196        assert_eq!(converted.as_bal(), &BalLen(0));
1197        assert_eq!(converted.as_raw(), &raw);
1198
1199        let err = DecodedBal::new(Bal::default(), raw.clone()).try_convert::<NonEmptyBal>();
1200        assert_eq!(err.unwrap_err(), EmptyBal);
1201
1202        let bal = Bal::new(vec![AccountChanges::new(Address::from([0x11; 20]))]);
1203        let converted = DecodedBal::new(bal, raw).try_convert::<NonEmptyBal>().unwrap();
1204
1205        assert_eq!(converted.as_bal().0.len(), 1);
1206    }
1207
1208    #[test]
1209    fn decoded_bal_ensure_hash_reports_both_hashes() {
1210        let raw = Bytes::from_static(&[0xc0]);
1211        let decoded = DecodedBal::new(Bal::default(), raw.clone());
1212        let computed = alloy_primitives::keccak256(raw.as_ref());
1213        let expected = B256::from([0x11; 32]);
1214
1215        assert_eq!(decoded.ensure_hash(computed), Ok(()));
1216        assert_eq!(
1217            decoded.ensure_hash(expected),
1218            Err(super::BlockAccessListHashMismatch::new(computed, expected))
1219        );
1220    }
1221
1222    #[test]
1223    fn raw_bal_hash_uses_raw_bytes() {
1224        let raw = Bytes::from_static(&[0xc0]);
1225        let raw_bal = RawBal::new(raw.clone());
1226        let computed = alloy_primitives::keccak256(raw.as_ref());
1227        let expected = B256::from([0x11; 32]);
1228
1229        assert_eq!(raw_bal.as_raw(), &raw);
1230        assert_eq!(raw_bal.hash(), computed);
1231        assert_eq!(raw_bal.ensure_hash(computed), Ok(()));
1232        assert_eq!(
1233            raw_bal.ensure_hash(expected),
1234            Err(super::BlockAccessListHashMismatch::new(computed, expected))
1235        );
1236
1237        let (split_raw, split_hash) = raw_bal.into_parts();
1238        assert_eq!(split_raw, raw);
1239        assert_eq!(split_hash, computed);
1240    }
1241
1242    #[test]
1243    fn raw_bal_new_unchecked_uses_supplied_hash() {
1244        let raw = Bytes::from_static(&[0xc0]);
1245        let hash = B256::from([0x11; 32]);
1246        let raw_bal = RawBal::new_unchecked(raw.clone(), hash);
1247
1248        assert_eq!(raw_bal.as_raw(), &raw);
1249        assert_eq!(raw_bal.hash(), hash);
1250        assert_eq!(raw_bal.ensure_hash(hash), Ok(()));
1251
1252        let (split_raw, split_hash) = raw_bal.into_parts();
1253        assert_eq!(split_raw, raw);
1254        assert_eq!(split_hash, hash);
1255    }
1256
1257    #[test]
1258    fn decoded_bal_exposes_raw_bal() {
1259        let raw = Bytes::from_static(&[0xc0]);
1260        let raw_bal = RawBal::new(raw.clone());
1261        let decoded = DecodedBal::with_raw_bal(Bal::default(), raw_bal.clone());
1262
1263        assert_eq!(decoded.as_raw_bal(), &raw_bal);
1264        assert_eq!(decoded.as_raw(), &raw);
1265
1266        let (bal, split_raw_bal) = decoded.split_raw_bal();
1267        assert!(bal.is_empty());
1268        assert_eq!(split_raw_bal, raw_bal);
1269    }
1270
1271    #[test]
1272    fn decoded_bal_new_unchecked_uses_supplied_hash() {
1273        let raw = Bytes::from_static(&[0xc0]);
1274        let hash = B256::from([0x11; 32]);
1275        let decoded = DecodedBal::new_unchecked(Bal::default(), raw.clone(), hash);
1276
1277        assert_eq!(decoded.as_raw(), &raw);
1278        assert_eq!(decoded.hash(), hash);
1279        assert_eq!(decoded.ensure_hash(hash), Ok(()));
1280    }
1281
1282    #[cfg(feature = "serde")]
1283    #[test]
1284    fn decoded_bal_serde_keeps_raw_bytes_field() {
1285        let raw = Bytes::from_static(&[0xc0]);
1286        let decoded = DecodedBal::new(Bal::default(), raw.clone());
1287        let value = serde_json::to_value(&decoded).unwrap();
1288
1289        assert!(value.get("decoded").is_some());
1290        assert_eq!(value.get("raw"), Some(&serde_json::to_value(&raw).unwrap()));
1291        assert!(value.get("hash").is_none());
1292
1293        let decoded = serde_json::from_value::<DecodedBal>(value).unwrap();
1294        assert_eq!(decoded.as_bal(), &Bal::default());
1295        assert_eq!(decoded.as_raw(), &raw);
1296    }
1297
1298    #[test]
1299    fn raw_or_decoded_bal_raw_helpers_use_raw_bytes() {
1300        let raw = Bytes::from_static(&[0xc0]);
1301        let bal = RawOrDecodedBal::<Bal>::raw(raw.clone());
1302        let hash = alloy_primitives::keccak256(raw.as_ref());
1303
1304        assert!(bal.is_raw());
1305        assert!(!bal.is_decoded());
1306        assert_eq!(bal.as_raw(), &raw);
1307        assert_eq!(bal.as_raw_bal().as_raw(), &raw);
1308        assert_eq!(bal.as_decoded(), None);
1309        assert_eq!(bal.as_bal(), None);
1310        assert_eq!(bal.hash(), hash);
1311        assert_eq!(bal.ensure_hash(hash), Ok(()));
1312
1313        let (decoded, split_raw) = bal.clone().split();
1314        assert_eq!(decoded, None);
1315        assert_eq!(split_raw, raw);
1316        let (decoded, split_raw_bal) = bal.clone().split_raw_bal();
1317        assert_eq!(decoded, None);
1318        assert_eq!(split_raw_bal.as_raw(), &raw);
1319        assert_eq!(bal.clone().into_raw_bal().as_raw(), &raw);
1320        assert_eq!(bal.into_raw(), raw);
1321    }
1322
1323    #[test]
1324    fn raw_or_decoded_bal_raw_unchecked_uses_supplied_hash() {
1325        let raw = Bytes::from_static(&[0xc0]);
1326        let hash = B256::from([0x11; 32]);
1327        let bal = RawOrDecodedBal::<Bal>::raw_unchecked(raw.clone(), hash);
1328
1329        assert!(bal.is_raw());
1330        assert_eq!(bal.as_raw(), &raw);
1331        assert_eq!(bal.hash(), hash);
1332        assert_eq!(bal.ensure_hash(hash), Ok(()));
1333    }
1334
1335    #[test]
1336    fn raw_or_decoded_bal_decoded_helpers_use_decoded_bal() {
1337        let raw = Bytes::from_static(&[0xc0]);
1338        let decoded = DecodedBal::new(Bal::default(), raw.clone());
1339        let hash = decoded.hash();
1340        let bal = RawOrDecodedBal::decoded(decoded.clone());
1341
1342        assert!(!bal.is_raw());
1343        assert!(bal.is_decoded());
1344        assert_eq!(bal.as_raw(), &raw);
1345        assert_eq!(bal.as_raw_bal(), decoded.as_raw_bal());
1346        assert_eq!(bal.as_decoded(), Some(&decoded));
1347        assert_eq!(bal.as_bal(), Some(decoded.as_bal()));
1348        assert_eq!(bal.hash(), hash);
1349
1350        let (split_bal, split_raw) = bal.clone().split();
1351        assert_eq!(split_bal, Some(Bal::default()));
1352        assert_eq!(split_raw, raw);
1353        let (split_bal, split_raw_bal) = bal.clone().split_raw_bal();
1354        assert_eq!(split_bal, Some(Bal::default()));
1355        assert_eq!(split_raw_bal.as_raw(), &raw);
1356        assert_eq!(bal.into_decoded(), Some(decoded));
1357    }
1358
1359    #[test]
1360    fn raw_or_decoded_bal_convert_maps_only_decoded_values() {
1361        let raw = Bytes::from_static(&[0xc0]);
1362        let raw_bal: RawOrDecodedBal<Bal> = RawOrDecodedBal::raw(raw.clone());
1363        let converted_raw: RawOrDecodedBal<BalLen> = raw_bal.convert();
1364
1365        assert!(converted_raw.is_raw());
1366        assert_eq!(converted_raw.as_raw(), &raw);
1367        assert_eq!(converted_raw.as_bal(), None);
1368
1369        let decoded = DecodedBal::new(Bal::default(), raw.clone());
1370        let converted_decoded: RawOrDecodedBal<BalLen> =
1371            RawOrDecodedBal::decoded(decoded).convert();
1372
1373        assert!(converted_decoded.is_decoded());
1374        assert_eq!(converted_decoded.as_bal(), Some(&BalLen(0)));
1375        assert_eq!(converted_decoded.as_raw(), &raw);
1376
1377        let err = RawOrDecodedBal::decoded(DecodedBal::new(Bal::default(), raw.clone()))
1378            .try_convert::<NonEmptyBal>();
1379        assert_eq!(err.unwrap_err(), EmptyBal);
1380
1381        let raw_result: Result<RawOrDecodedBal<NonEmptyBal>, EmptyBal> =
1382            RawOrDecodedBal::<Bal>::raw(raw.clone()).try_convert();
1383        let raw_result = raw_result.unwrap();
1384        assert!(raw_result.is_raw());
1385        assert_eq!(raw_result.as_raw(), &raw);
1386    }
1387
1388    #[test]
1389    fn bal_merge_combines_duplicate_accounts_and_appends_new_accounts() {
1390        let existing_address = Address::from([0x11; 20]);
1391        let new_address = Address::from([0x22; 20]);
1392        let mut bal = Bal::new(vec![
1393            AccountChanges {
1394                address: existing_address,
1395                storage_changes: vec![SlotChanges::new(
1396                    U256::from(1),
1397                    vec![StorageChange::new(BlockAccessIndex::new(0), U256::from(10))],
1398                )],
1399                storage_reads: vec![U256::from(2)],
1400                balance_changes: vec![BalanceChange::new(
1401                    BlockAccessIndex::new(1),
1402                    U256::from(100),
1403                )],
1404                nonce_changes: vec![],
1405                code_changes: vec![],
1406            },
1407            AccountChanges::new(existing_address).with_code_change(CodeChange::new(
1408                BlockAccessIndex::new(5),
1409                Bytes::from_static(&[0xbb]),
1410            )),
1411        ]);
1412
1413        bal.merge([
1414            AccountChanges {
1415                address: existing_address,
1416                storage_changes: vec![SlotChanges::new(
1417                    U256::from(2),
1418                    vec![StorageChange::new(BlockAccessIndex::new(2), U256::from(20))],
1419                )],
1420                storage_reads: vec![U256::from(1), U256::from(3), U256::from(3)],
1421                balance_changes: vec![BalanceChange::new(
1422                    BlockAccessIndex::new(3),
1423                    U256::from(200),
1424                )],
1425                nonce_changes: vec![],
1426                code_changes: vec![],
1427            },
1428            AccountChanges::new(new_address)
1429                .with_nonce_change(NonceChange::new(BlockAccessIndex::new(4), 7)),
1430        ]);
1431
1432        assert_eq!(bal.len(), 2);
1433        assert_eq!(bal[0].address, existing_address);
1434        assert_eq!(bal[1].address, new_address);
1435        assert_eq!(
1436            bal[0].storage_changes.iter().map(|changes| changes.slot).collect::<Vec<_>>(),
1437            vec![U256::from(1), U256::from(2)]
1438        );
1439        assert_eq!(bal[0].storage_reads, vec![U256::from(3)]);
1440        assert_eq!(
1441            bal[0]
1442                .balance_changes
1443                .iter()
1444                .map(|change| change.block_access_index)
1445                .collect::<Vec<_>>(),
1446            vec![BlockAccessIndex::new(1), BlockAccessIndex::new(3)]
1447        );
1448        assert_eq!(
1449            bal[0].code_changes,
1450            vec![CodeChange::new(BlockAccessIndex::new(5), Bytes::from_static(&[0xbb]))]
1451        );
1452        assert_eq!(bal[1].nonce_changes, vec![NonceChange::new(BlockAccessIndex::new(4), 7)]);
1453    }
1454
1455    #[test]
1456    fn bal_merge_collapses_duplicate_accounts_from_incoming_iterator() {
1457        let address = Address::from([0x11; 20]);
1458        let mut bal = Bal::default();
1459
1460        bal.merge([
1461            AccountChanges::new(address)
1462                .with_balance_change(BalanceChange::new(BlockAccessIndex::new(0), U256::from(100))),
1463            AccountChanges::new(address).with_code_change(CodeChange::new(
1464                BlockAccessIndex::new(1),
1465                Bytes::from_static(&[0xaa]),
1466            )),
1467        ]);
1468
1469        assert_eq!(bal.len(), 1);
1470        assert_eq!(bal[0].balance_changes.len(), 1);
1471        assert_eq!(
1472            bal[0].code_changes,
1473            vec![CodeChange::new(BlockAccessIndex::new(1), Bytes::from_static(&[0xaa]))]
1474        );
1475    }
1476
1477    #[test]
1478    fn bal_insert_changes_shifts_suffix_and_normalizes_inserted_layer() {
1479        let existing_address = Address::from([0x22; 20]);
1480        let new_address = Address::from([0x11; 20]);
1481        let slot = U256::from(1);
1482        let mut bal = Bal::new(vec![AccountChanges {
1483            address: existing_address,
1484            storage_changes: vec![SlotChanges::new(
1485                slot,
1486                vec![StorageChange::new(BlockAccessIndex::new(2), U256::from(20))],
1487            )],
1488            storage_reads: vec![U256::from(2)],
1489            balance_changes: vec![
1490                BalanceChange::new(BlockAccessIndex::new(1), U256::from(100)),
1491                BalanceChange::new(BlockAccessIndex::new(2), U256::from(200)),
1492                BalanceChange::new(BlockAccessIndex::new(3), U256::from(300)),
1493            ],
1494            nonce_changes: vec![NonceChange::new(BlockAccessIndex::new(2), 2)],
1495            code_changes: vec![CodeChange::new(
1496                BlockAccessIndex::new(2),
1497                Bytes::from_static(&[0x60, 0x02]),
1498            )],
1499        }]);
1500
1501        let positioned_index = bal.insert_changes_at(
1502            BlockAccessIndex::new(2),
1503            [
1504                AccountChanges::new(existing_address)
1505                    .with_storage_change(SlotChanges::new(
1506                        slot,
1507                        vec![StorageChange::new(BlockAccessIndex::new(90), U256::from(900))],
1508                    ))
1509                    .with_balance_change(BalanceChange::new(
1510                        BlockAccessIndex::new(90),
1511                        U256::from(900),
1512                    )),
1513                AccountChanges::new(existing_address)
1514                    .with_storage_change(SlotChanges::new(
1515                        slot,
1516                        vec![StorageChange::new(BlockAccessIndex::new(91), U256::from(901))],
1517                    ))
1518                    .with_balance_change(BalanceChange::new(
1519                        BlockAccessIndex::new(91),
1520                        U256::from(901),
1521                    ))
1522                    .with_nonce_change(NonceChange::new(BlockAccessIndex::new(91), 9)),
1523                AccountChanges::new(new_address).with_code_change(CodeChange::new(
1524                    BlockAccessIndex::new(92),
1525                    Bytes::from_static(&[0x60, 0x09]),
1526                )),
1527            ],
1528        );
1529
1530        assert_eq!(positioned_index, BlockAccessIndex::new(3));
1531        assert_eq!(
1532            bal.iter().map(AccountChanges::address).collect::<Vec<_>>(),
1533            vec![new_address, existing_address]
1534        );
1535
1536        assert_eq!(
1537            bal[0].code_changes,
1538            vec![CodeChange::new(BlockAccessIndex::new(2), Bytes::from_static(&[0x60, 0x09]))]
1539        );
1540        assert_eq!(
1541            bal[1].balance_changes,
1542            vec![
1543                BalanceChange::new(BlockAccessIndex::new(1), U256::from(100)),
1544                BalanceChange::new(BlockAccessIndex::new(2), U256::from(901)),
1545                BalanceChange::new(BlockAccessIndex::new(3), U256::from(200)),
1546                BalanceChange::new(BlockAccessIndex::new(4), U256::from(300)),
1547            ]
1548        );
1549        assert_eq!(
1550            bal[1].storage_changes[0].changes,
1551            vec![
1552                StorageChange::new(BlockAccessIndex::new(2), U256::from(901)),
1553                StorageChange::new(BlockAccessIndex::new(3), U256::from(20)),
1554            ]
1555        );
1556        assert_eq!(
1557            bal[1].nonce_changes,
1558            vec![
1559                NonceChange::new(BlockAccessIndex::new(2), 9),
1560                NonceChange::new(BlockAccessIndex::new(3), 2),
1561            ]
1562        );
1563        assert_eq!(
1564            bal[1].code_changes,
1565            vec![CodeChange::new(BlockAccessIndex::new(3), Bytes::from_static(&[0x60, 0x02]))]
1566        );
1567        assert_eq!(bal[1].storage_reads, vec![U256::from(2)]);
1568    }
1569
1570    #[test]
1571    fn bal_insert_empty_changes_is_noop() {
1572        let original =
1573            Bal::new(vec![AccountChanges::new(Address::from([0x11; 20])).with_balance_change(
1574                BalanceChange::new(BlockAccessIndex::new(1), U256::from(100)),
1575            )]);
1576        let mut bal = original.clone();
1577
1578        let positioned_index = bal.insert_changes_at(
1579            BlockAccessIndex::new(1),
1580            [AccountChanges::new(Address::from([0x22; 20]))],
1581        );
1582
1583        assert_eq!(positioned_index, BlockAccessIndex::new(1));
1584        assert_eq!(bal, original);
1585    }
1586
1587    #[test]
1588    fn bal_insert_shifts_untouched_accounts() {
1589        let touched = Address::from([0x11; 20]);
1590        let bystander = Address::from([0x22; 20]);
1591        let mut bal = Bal::new(vec![
1592            AccountChanges::new(touched)
1593                .with_balance_change(BalanceChange::new(BlockAccessIndex::new(2), U256::from(100))),
1594            AccountChanges::new(bystander)
1595                .with_storage_change(SlotChanges::new(
1596                    U256::from(1),
1597                    vec![StorageChange::new(BlockAccessIndex::new(2), U256::from(20))],
1598                ))
1599                .with_balance_change(BalanceChange::new(BlockAccessIndex::new(1), U256::from(50)))
1600                .with_nonce_change(NonceChange::new(BlockAccessIndex::new(2), 7))
1601                .with_code_change(CodeChange::new(
1602                    BlockAccessIndex::new(3),
1603                    Bytes::from_static(&[0x60]),
1604                )),
1605        ]);
1606
1607        let positioned_index = bal.insert_changes_at(
1608            BlockAccessIndex::new(2),
1609            [AccountChanges::new(touched).with_balance_change(BalanceChange::new(
1610                BlockAccessIndex::new(0),
1611                U256::from(900),
1612            ))],
1613        );
1614
1615        assert_eq!(positioned_index, BlockAccessIndex::new(3));
1616        assert_eq!(
1617            bal[0].balance_changes,
1618            vec![
1619                BalanceChange::new(BlockAccessIndex::new(2), U256::from(900)),
1620                BalanceChange::new(BlockAccessIndex::new(3), U256::from(100)),
1621            ]
1622        );
1623        assert_eq!(bal[1].address, bystander);
1624        assert_eq!(
1625            bal[1].storage_changes,
1626            vec![SlotChanges::new(
1627                U256::from(1),
1628                vec![StorageChange::new(BlockAccessIndex::new(3), U256::from(20))],
1629            )]
1630        );
1631        assert_eq!(
1632            bal[1].balance_changes,
1633            vec![BalanceChange::new(BlockAccessIndex::new(1), U256::from(50))]
1634        );
1635        assert_eq!(bal[1].nonce_changes, vec![NonceChange::new(BlockAccessIndex::new(3), 7)]);
1636        assert_eq!(
1637            bal[1].code_changes,
1638            vec![CodeChange::new(BlockAccessIndex::new(4), Bytes::from_static(&[0x60]))]
1639        );
1640    }
1641
1642    #[test]
1643    fn bal_insert_folds_duplicate_slot_entries() {
1644        let slot = U256::from(7);
1645
1646        let new_address = Address::from([0x11; 20]);
1647        let mut bal = Bal::default();
1648        let positioned_index = bal.insert_changes_at(
1649            BlockAccessIndex::new(1),
1650            [AccountChanges::new(new_address)
1651                .with_storage_change(SlotChanges::new(
1652                    slot,
1653                    vec![StorageChange::new(BlockAccessIndex::new(90), U256::from(900))],
1654                ))
1655                .with_storage_change(SlotChanges::new(
1656                    slot,
1657                    vec![StorageChange::new(BlockAccessIndex::new(91), U256::from(901))],
1658                ))],
1659        );
1660        assert_eq!(positioned_index, BlockAccessIndex::new(2));
1661        assert_eq!(
1662            bal[0].storage_changes,
1663            vec![SlotChanges::new(
1664                slot,
1665                vec![StorageChange::new(BlockAccessIndex::new(1), U256::from(901))],
1666            )]
1667        );
1668
1669        let existing = Address::from([0x22; 20]);
1670        let mut bal =
1671            Bal::new(vec![AccountChanges::new(existing).with_storage_change(SlotChanges::new(
1672                slot,
1673                vec![StorageChange::new(BlockAccessIndex::new(1), U256::from(10))],
1674            ))]);
1675        let positioned_index = bal.insert_changes_at(
1676            BlockAccessIndex::new(1),
1677            [AccountChanges::new(existing)
1678                .with_storage_change(SlotChanges::new(
1679                    slot,
1680                    vec![StorageChange::new(BlockAccessIndex::new(90), U256::from(900))],
1681                ))
1682                .with_storage_change(SlotChanges::new(
1683                    slot,
1684                    vec![StorageChange::new(BlockAccessIndex::new(91), U256::from(901))],
1685                ))],
1686        );
1687        assert_eq!(positioned_index, BlockAccessIndex::new(2));
1688        assert_eq!(
1689            bal[0].storage_changes,
1690            vec![SlotChanges::new(
1691                slot,
1692                vec![
1693                    StorageChange::new(BlockAccessIndex::new(1), U256::from(901)),
1694                    StorageChange::new(BlockAccessIndex::new(2), U256::from(10)),
1695                ],
1696            )]
1697        );
1698    }
1699
1700    #[test]
1701    fn bal_insert_normalizes_reads_for_new_accounts() {
1702        let address = Address::from([0x11; 20]);
1703        let written = U256::from(1);
1704        let read = U256::from(2);
1705        let mut bal = Bal::default();
1706
1707        let positioned_index = bal.insert_changes_at(
1708            BlockAccessIndex::new(0),
1709            [AccountChanges::new(address)
1710                .with_storage_read(written)
1711                .with_storage_read(read)
1712                .with_storage_read(read)
1713                .with_storage_change(SlotChanges::new(
1714                    written,
1715                    vec![StorageChange::new(BlockAccessIndex::new(9), U256::from(90))],
1716                ))],
1717        );
1718
1719        assert_eq!(positioned_index, BlockAccessIndex::new(1));
1720        assert_eq!(
1721            bal[0].storage_changes,
1722            vec![SlotChanges::new(
1723                written,
1724                vec![StorageChange::new(BlockAccessIndex::new(0), U256::from(90))],
1725            )]
1726        );
1727        assert_eq!(bal[0].storage_reads, vec![read]);
1728    }
1729
1730    #[test]
1731    fn bal_insert_empty_slot_entries_are_noop() {
1732        let original =
1733            Bal::new(vec![AccountChanges::new(Address::from([0x11; 20])).with_balance_change(
1734                BalanceChange::new(BlockAccessIndex::new(1), U256::from(100)),
1735            )]);
1736        let mut bal = original.clone();
1737
1738        let positioned_index = bal.insert_changes_at(
1739            BlockAccessIndex::new(1),
1740            [AccountChanges::new(Address::from([0x22; 20]))
1741                .with_storage_change(SlotChanges::new(U256::from(1), vec![]))],
1742        );
1743
1744        assert_eq!(positioned_index, BlockAccessIndex::new(1));
1745        assert_eq!(bal, original);
1746    }
1747
1748    #[test]
1749    fn bal_insert_empty_slot_entry_does_not_swallow_read() {
1750        let address = Address::from([0x11; 20]);
1751        let slot = U256::from(42);
1752        let mut bal = Bal::default();
1753
1754        let positioned_index = bal.insert_changes_at(
1755            BlockAccessIndex::new(3),
1756            [
1757                AccountChanges::new(address).with_storage_read(slot),
1758                AccountChanges::new(address).with_storage_change(SlotChanges::new(slot, vec![])),
1759            ],
1760        );
1761
1762        assert_eq!(positioned_index, BlockAccessIndex::new(3));
1763        assert_eq!(bal[0].storage_reads, vec![slot]);
1764        assert!(bal[0].storage_changes.is_empty());
1765    }
1766
1767    #[test]
1768    fn bal_insert_reads_only_does_not_shift() {
1769        let address = Address::from([0x11; 20]);
1770        let written = U256::from(1);
1771        let mut bal =
1772            Bal::new(vec![AccountChanges::new(address).with_storage_change(SlotChanges::new(
1773                written,
1774                vec![StorageChange::new(BlockAccessIndex::new(4), U256::from(40))],
1775            ))]);
1776        let original = bal.clone();
1777
1778        // a read of an already written slot is absorbed entirely
1779        let positioned_index = bal.insert_changes_at(
1780            BlockAccessIndex::new(1),
1781            [AccountChanges::new(address).with_storage_read(written)],
1782        );
1783        assert_eq!(positioned_index, BlockAccessIndex::new(1));
1784        assert_eq!(bal, original);
1785
1786        // novel reads are merged without reserving a layer
1787        let novel = U256::from(2);
1788        let positioned_index = bal.insert_changes_at(
1789            BlockAccessIndex::new(1),
1790            [AccountChanges::new(address).with_storage_read(novel)],
1791        );
1792        assert_eq!(positioned_index, BlockAccessIndex::new(1));
1793        assert_eq!(bal[0].storage_reads, vec![novel]);
1794        assert_eq!(bal[0].storage_changes, original[0].storage_changes);
1795    }
1796
1797    #[test]
1798    fn bal_insert_saturates_index_overflow() {
1799        let address = Address::from([0x11; 20]);
1800        let mut bal = Bal::new(vec![AccountChanges::new(address).with_balance_change(
1801            BalanceChange::new(BlockAccessIndex::new(u64::MAX), U256::from(1)),
1802        )]);
1803
1804        let positioned_index = bal.insert_changes_at(
1805            BlockAccessIndex::new(u64::MAX),
1806            [AccountChanges::new(address)
1807                .with_nonce_change(NonceChange::new(BlockAccessIndex::new(0), 1))],
1808        );
1809
1810        assert_eq!(positioned_index, BlockAccessIndex::new(u64::MAX));
1811        assert_eq!(
1812            bal[0].balance_changes,
1813            vec![BalanceChange::new(BlockAccessIndex::new(u64::MAX), U256::from(1))]
1814        );
1815        assert_eq!(
1816            bal[0].nonce_changes,
1817            vec![NonceChange::new(BlockAccessIndex::new(u64::MAX), 1)]
1818        );
1819    }
1820
1821    /// Demonstrates how RPC-style state overrides — nested per-account override data built from
1822    /// plain primitives — are applied to a positioned BAL and observed at the returned index.
1823    #[test]
1824    fn bal_insert_applies_state_overrides() {
1825        let alice = Address::from([0xaa; 20]);
1826        let bob = Address::from([0xbb; 20]);
1827        let slot = U256::from(1);
1828
1829        // BAL for a 3-tx block: alice's balance and storage slot are written by txs 0..=2
1830        // (block access indices 1..=3). The caller simulates positioned at index 2.
1831        let mut bal = Bal::new(vec![
1832            AccountChanges::new(alice)
1833                .with_storage_change(SlotChanges::new(
1834                    slot,
1835                    vec![
1836                        StorageChange::new(BlockAccessIndex::new(2), U256::from(20)),
1837                        StorageChange::new(BlockAccessIndex::new(3), U256::from(30)),
1838                    ],
1839                ))
1840                .with_balance_change(BalanceChange::new(BlockAccessIndex::new(1), U256::from(100)))
1841                .with_balance_change(BalanceChange::new(BlockAccessIndex::new(2), U256::from(200)))
1842                .with_balance_change(BalanceChange::new(BlockAccessIndex::new(3), U256::from(300))),
1843        ]);
1844
1845        // Effective balance at a position: latest change strictly before it.
1846        let balance_at = |account: &AccountChanges, position: BlockAccessIndex| {
1847            account
1848                .balance_changes
1849                .iter()
1850                .rfind(|change| change.block_access_index < position)
1851                .map(|change| change.post_balance)
1852        };
1853        let position = BlockAccessIndex::new(2);
1854        assert_eq!(balance_at(&bal[0], position), Some(U256::from(100)));
1855
1856        // Override data as an RPC layer would carry it:
1857        // (address, balance, nonce, code, [(slot, value)]) — no BAL indices involved.
1858        let overrides = [
1859            (alice, Some(U256::from(999)), None, None, vec![(slot, U256::from(90))]),
1860            (
1861                bob,
1862                None,
1863                Some(7),
1864                Some(Bytes::from_static(&[0x60, 0x00])),
1865                vec![(U256::from(5), U256::from(50))],
1866            ),
1867            // a later entry for an already overridden account wins
1868            (alice, Some(U256::from(1000)), None, None, vec![]),
1869        ];
1870
1871        // Conversion uses a placeholder index; `insert_changes_at` restamps every change to the
1872        // insertion point.
1873        let placeholder = BlockAccessIndex::PRE_EXECUTION;
1874        let overlay = overrides.into_iter().map(|(address, balance, nonce, code, slots)| {
1875            let mut account = AccountChanges::new(address);
1876            if let Some(balance) = balance {
1877                account = account.with_balance_change(BalanceChange::new(placeholder, balance));
1878            }
1879            if let Some(nonce) = nonce {
1880                account = account.with_nonce_change(NonceChange::new(placeholder, nonce));
1881            }
1882            if let Some(code) = code {
1883                account = account.with_code_change(CodeChange::new(placeholder, code));
1884            }
1885            for (slot, value) in slots {
1886                account = account.with_storage_change(SlotChanges::new(
1887                    slot,
1888                    vec![StorageChange::new(placeholder, value)],
1889                ));
1890            }
1891            account
1892        });
1893
1894        let positioned_index = bal.insert_changes_at(position, overlay);
1895        assert_eq!(positioned_index, BlockAccessIndex::new(3));
1896
1897        // The override layer sits at the insertion point, the original suffix follows it.
1898        assert_eq!(
1899            bal[0].balance_changes,
1900            vec![
1901                BalanceChange::new(BlockAccessIndex::new(1), U256::from(100)),
1902                BalanceChange::new(BlockAccessIndex::new(2), U256::from(1000)),
1903                BalanceChange::new(BlockAccessIndex::new(3), U256::from(200)),
1904                BalanceChange::new(BlockAccessIndex::new(4), U256::from(300)),
1905            ]
1906        );
1907        assert_eq!(
1908            bal[0].storage_changes,
1909            vec![SlotChanges::new(
1910                slot,
1911                vec![
1912                    StorageChange::new(BlockAccessIndex::new(2), U256::from(90)),
1913                    StorageChange::new(BlockAccessIndex::new(3), U256::from(20)),
1914                    StorageChange::new(BlockAccessIndex::new(4), U256::from(30)),
1915                ],
1916            )]
1917        );
1918        assert_eq!(bal[1].address, bob);
1919        assert_eq!(bal[1].nonce_changes, vec![NonceChange::new(BlockAccessIndex::new(2), 7)]);
1920        assert_eq!(
1921            bal[1].code_changes,
1922            vec![CodeChange::new(BlockAccessIndex::new(2), Bytes::from_static(&[0x60, 0x00]))]
1923        );
1924        assert_eq!(
1925            bal[1].storage_changes,
1926            vec![SlotChanges::new(
1927                U256::from(5),
1928                vec![StorageChange::new(BlockAccessIndex::new(2), U256::from(50))],
1929            )]
1930        );
1931
1932        // The original position still observes pre-override state, the returned index observes
1933        // the override, and positions past the shifted suffix observe the original final value.
1934        assert_eq!(balance_at(&bal[0], position), Some(U256::from(100)));
1935        assert_eq!(balance_at(&bal[0], positioned_index), Some(U256::from(1000)));
1936        assert_eq!(balance_at(&bal[0], BlockAccessIndex::new(5)), Some(U256::from(300)));
1937    }
1938
1939    #[test]
1940    fn bal_sort_orders_all_eip7928_lists() {
1941        let address_1 = Address::from([0x11; 20]);
1942        let address_2 = Address::from([0x22; 20]);
1943        let mut bal = Bal::new(vec![
1944            AccountChanges {
1945                address: address_2,
1946                storage_changes: vec![
1947                    SlotChanges::new(
1948                        U256::from(3),
1949                        vec![
1950                            StorageChange::new(BlockAccessIndex::new(8), U256::from(0x80)),
1951                            StorageChange::new(BlockAccessIndex::new(2), U256::from(0x20)),
1952                        ],
1953                    ),
1954                    SlotChanges::new(
1955                        U256::from(1),
1956                        vec![
1957                            StorageChange::new(BlockAccessIndex::new(5), U256::from(0x50)),
1958                            StorageChange::new(BlockAccessIndex::new(1), U256::from(0x10)),
1959                        ],
1960                    ),
1961                ],
1962                storage_reads: vec![U256::from(4), U256::from(2)],
1963                balance_changes: vec![
1964                    BalanceChange::new(BlockAccessIndex::new(6), U256::from(600)),
1965                    BalanceChange::new(BlockAccessIndex::new(3), U256::from(300)),
1966                ],
1967                nonce_changes: vec![
1968                    NonceChange::new(BlockAccessIndex::new(7), 70),
1969                    NonceChange::new(BlockAccessIndex::new(4), 40),
1970                ],
1971                code_changes: vec![
1972                    CodeChange::new(BlockAccessIndex::new(9), Bytes::from_static(&[0x60, 0x09])),
1973                    CodeChange::new(BlockAccessIndex::new(5), Bytes::from_static(&[0x60, 0x05])),
1974                ],
1975            },
1976            AccountChanges {
1977                address: address_1,
1978                storage_changes: vec![
1979                    SlotChanges::new(
1980                        U256::from(2),
1981                        vec![
1982                            StorageChange::new(BlockAccessIndex::new(4), U256::from(0x40)),
1983                            StorageChange::new(BlockAccessIndex::new(0), U256::from(0x00)),
1984                        ],
1985                    ),
1986                    SlotChanges::new(
1987                        U256::from(1),
1988                        vec![
1989                            StorageChange::new(BlockAccessIndex::new(3), U256::from(0x30)),
1990                            StorageChange::new(BlockAccessIndex::new(1), U256::from(0x10)),
1991                        ],
1992                    ),
1993                ],
1994                storage_reads: vec![U256::from(5), U256::from(3)],
1995                balance_changes: vec![
1996                    BalanceChange::new(BlockAccessIndex::new(5), U256::from(500)),
1997                    BalanceChange::new(BlockAccessIndex::new(2), U256::from(200)),
1998                ],
1999                nonce_changes: vec![
2000                    NonceChange::new(BlockAccessIndex::new(8), 80),
2001                    NonceChange::new(BlockAccessIndex::new(1), 10),
2002                ],
2003                code_changes: vec![
2004                    CodeChange::new(BlockAccessIndex::new(4), Bytes::from_static(&[0x60, 0x04])),
2005                    CodeChange::new(BlockAccessIndex::new(2), Bytes::from_static(&[0x60, 0x02])),
2006                ],
2007            },
2008        ]);
2009
2010        bal.sort();
2011
2012        assert_eq!(bal[0].address, address_1);
2013        assert_eq!(bal[1].address, address_2);
2014
2015        for account in bal.iter() {
2016            assert!(account.storage_changes.windows(2).all(|slots| slots[0].slot <= slots[1].slot));
2017            for slot_changes in &account.storage_changes {
2018                assert!(
2019                    slot_changes
2020                        .changes
2021                        .windows(2)
2022                        .all(|changes| changes[0].block_access_index
2023                            <= changes[1].block_access_index)
2024                );
2025            }
2026            assert!(account.storage_reads.windows(2).all(|slots| slots[0] <= slots[1]));
2027            assert!(
2028                account
2029                    .balance_changes
2030                    .windows(2)
2031                    .all(|changes| changes[0].block_access_index <= changes[1].block_access_index)
2032            );
2033            assert!(
2034                account
2035                    .nonce_changes
2036                    .windows(2)
2037                    .all(|changes| changes[0].block_access_index <= changes[1].block_access_index)
2038            );
2039            assert!(
2040                account
2041                    .code_changes
2042                    .windows(2)
2043                    .all(|changes| changes[0].block_access_index <= changes[1].block_access_index)
2044            );
2045        }
2046    }
2047
2048    #[test]
2049    fn bal_validate_gas_limit_accepts_exact_item_cost() {
2050        let bal = Bal::new(vec![
2051            AccountChanges::new(Address::from([0x11; 20]))
2052                .with_storage_read(U256::from(1))
2053                .with_storage_change(SlotChanges::new(
2054                    U256::from(2),
2055                    vec![StorageChange::new(BlockAccessIndex::new(0), U256::from(0xaa))],
2056                )),
2057        ]);
2058
2059        assert_eq!(bal.total_bal_items(), 3);
2060        assert_eq!(bal.validate_gas_limit(3 * ITEM_COST as u64), Ok(()));
2061    }
2062
2063    #[test]
2064    fn bal_total_items_counts_storage_entries_without_deduplicating() {
2065        let bal = Bal::new(vec![
2066            AccountChanges::new(Address::from([0x11; 20]))
2067                .with_storage_read(U256::from(1))
2068                .with_storage_change(SlotChanges::new(
2069                    U256::from(1),
2070                    vec![StorageChange::new(BlockAccessIndex::new(0), U256::from(0xaa))],
2071                )),
2072        ]);
2073
2074        assert_eq!(bal.total_bal_items(), 3);
2075    }
2076
2077    #[test]
2078    fn bal_validate_gas_limit_rejects_item_cost_above_limit() {
2079        let bal = Bal::new(vec![
2080            AccountChanges::new(Address::from([0x11; 20]))
2081                .with_storage_read(U256::from(1))
2082                .with_storage_read(U256::from(2)),
2083        ]);
2084        let gas_limit = 3 * ITEM_COST as u64 - 1;
2085
2086        assert_eq!(bal.total_bal_items(), 3);
2087        assert_eq!(
2088            bal.validate_gas_limit(gas_limit),
2089            Err(super::BlockAccessListGasError::new(3, gas_limit))
2090        );
2091    }
2092}
2093
2094#[cfg(all(test, feature = "rlp"))]
2095mod tests {
2096    use super::bal::{Bal, DecodedBal, RawBal, RawOrDecodedBal};
2097    use crate::{
2098        AccountChanges, BalanceChange, BlockAccessIndex, CodeChange, NonceChange, SlotChanges,
2099        StorageChange, constants::EMPTY_BLOCK_ACCESS_LIST_HASH,
2100    };
2101    use alloy_primitives::{Address, Bytes, U256};
2102
2103    fn sample_bal() -> Bal {
2104        Bal::new(vec![
2105            AccountChanges::new(Address::from([0x11; 20]))
2106                .with_storage_read(U256::from(0x10))
2107                .with_storage_change(SlotChanges::new(
2108                    U256::from(0x01),
2109                    vec![StorageChange::new(BlockAccessIndex::new(0), U256::from(0xaa))],
2110                ))
2111                .with_balance_change(BalanceChange::new(
2112                    BlockAccessIndex::new(1),
2113                    U256::from(1_000),
2114                ))
2115                .with_nonce_change(NonceChange::new(BlockAccessIndex::new(2), 7))
2116                .with_code_change(CodeChange::new(
2117                    BlockAccessIndex::new(3),
2118                    Bytes::from(vec![0x60, 0x00]),
2119                )),
2120            AccountChanges::new(Address::from([0x22; 20]))
2121                .with_storage_read(U256::from(0x20))
2122                .with_storage_change(SlotChanges::new(
2123                    U256::from(0x02),
2124                    vec![StorageChange::new(BlockAccessIndex::new(4), U256::from(0xbb))],
2125                )),
2126        ])
2127    }
2128
2129    #[test]
2130    fn bal_compute_hash_returns_empty_hash_for_empty_bal() {
2131        let bal = Bal::default();
2132
2133        assert_eq!(bal.compute_hash(), EMPTY_BLOCK_ACCESS_LIST_HASH);
2134    }
2135
2136    #[test]
2137    fn bal_compute_hash_matches_free_function_for_non_empty_bal() {
2138        let bal = sample_bal();
2139
2140        assert_eq!(bal.compute_hash(), super::compute_block_access_list_hash(bal.as_slice()));
2141        assert_ne!(bal.compute_hash(), EMPTY_BLOCK_ACCESS_LIST_HASH);
2142    }
2143
2144    #[test]
2145    fn bal_compute_hash_with_buf_clears_reused_buffer() {
2146        let bal = sample_bal();
2147        let mut buf = alloc::vec![0xff; 32];
2148
2149        assert_eq!(bal.compute_hash_with_buf(&mut buf), bal.compute_hash());
2150        assert_eq!(
2151            super::compute_block_access_list_hash_with_buf(bal.as_slice(), &mut buf),
2152            super::compute_block_access_list_hash(bal.as_slice())
2153        );
2154    }
2155
2156    #[test]
2157    fn decoded_bal_from_rlp_bytes_preserves_raw_and_hash() {
2158        let bal = sample_bal();
2159        let raw = Bytes::from(alloy_rlp::encode(&bal));
2160        let decoded = DecodedBal::from_rlp_bytes(raw.clone()).unwrap();
2161
2162        assert_eq!(decoded.as_bal(), &bal);
2163        assert_eq!(decoded.as_raw(), &raw);
2164        assert_eq!(decoded.hash(), bal.compute_hash());
2165        assert_eq!(decoded.hash(), alloy_primitives::keccak256(raw.as_ref()));
2166        assert_eq!(decoded.as_sealed_bal().hash(), bal.compute_hash());
2167        assert_eq!(decoded.as_sealed_bal().inner(), &decoded.as_bal());
2168
2169        let (split_bal, split_raw) = decoded.clone().split();
2170        assert_eq!(split_bal, bal);
2171        assert_eq!(split_raw, raw);
2172
2173        let (split_bal, split_raw, split_hash) = decoded.clone().into_parts();
2174        assert_eq!(split_bal, bal);
2175        assert_eq!(split_raw, raw);
2176        assert_eq!(split_hash, bal.compute_hash());
2177
2178        let sealed = decoded.into_sealed();
2179        assert_eq!(sealed.hash(), bal.compute_hash());
2180        assert_eq!(sealed.inner(), &bal);
2181    }
2182
2183    #[test]
2184    fn decoded_bal_from_rlp_bytes_decodes_generic_inner_type() {
2185        let bal = sample_bal();
2186        let raw = Bytes::from(alloy_rlp::encode(&bal));
2187        let decoded = DecodedBal::from_rlp_bytes_as::<Vec<AccountChanges>>(raw.clone()).unwrap();
2188
2189        assert_eq!(decoded.as_bal().as_slice(), bal.as_slice());
2190        assert_eq!(decoded.as_raw(), &raw);
2191        assert_eq!(decoded.hash(), bal.compute_hash());
2192    }
2193
2194    #[test]
2195    fn decoded_bal_decode_consumes_exact_raw_rlp_item() {
2196        let bal = sample_bal();
2197        let raw = alloy_rlp::encode(&bal);
2198        let mut buf = raw.as_ref();
2199        let decoded = <DecodedBal as alloy_rlp::Decodable>::decode(&mut buf).unwrap();
2200
2201        assert!(buf.is_empty());
2202        assert_eq!(decoded.as_bal(), &bal);
2203        assert_eq!(decoded.as_raw().as_ref(), raw.as_slice());
2204        assert_eq!(alloy_rlp::encode(&decoded), raw);
2205    }
2206
2207    #[test]
2208    fn raw_bal_rlp_roundtrip_preserves_raw_item() {
2209        let bal = sample_bal();
2210        let raw = alloy_rlp::encode(&bal);
2211        let mut buf = raw.as_ref();
2212        let raw_bal = <RawBal as alloy_rlp::Decodable>::decode(&mut buf).unwrap();
2213
2214        assert!(buf.is_empty());
2215        assert_eq!(raw_bal.as_raw().as_ref(), raw.as_slice());
2216        assert_eq!(alloy_rlp::encode(&raw_bal), raw);
2217        assert_eq!(raw_bal.hash(), bal.compute_hash());
2218    }
2219
2220    #[test]
2221    fn raw_or_decoded_bal_try_into_decoded_decodes_raw() {
2222        let bal = sample_bal();
2223        let raw = Bytes::from(alloy_rlp::encode(&bal));
2224        let decoded = RawOrDecodedBal::<Bal>::raw(raw.clone()).try_into_decoded().unwrap();
2225
2226        assert_eq!(decoded.as_bal(), &bal);
2227        assert_eq!(decoded.as_raw(), &raw);
2228        assert_eq!(decoded.hash(), bal.compute_hash());
2229    }
2230
2231    #[test]
2232    fn raw_or_decoded_bal_try_into_decoded_reuses_decoded() {
2233        let bal = sample_bal();
2234        let raw = Bytes::from(alloy_rlp::encode(&bal));
2235        let decoded = DecodedBal::new(bal.clone(), raw.clone());
2236        let decoded = RawOrDecodedBal::decoded(decoded).try_into_decoded().unwrap();
2237
2238        assert_eq!(decoded.as_bal(), &bal);
2239        assert_eq!(decoded.as_raw(), &raw);
2240        assert_eq!(decoded.hash(), bal.compute_hash());
2241    }
2242
2243    #[test]
2244    fn raw_or_decoded_bal_rlp_encodes_raw_bytes() {
2245        let bal = sample_bal();
2246        let raw = alloy_rlp::encode(&bal);
2247        let raw_bal = RawOrDecodedBal::<Bal>::raw(Bytes::from(raw.clone()));
2248        let decoded_bal = RawOrDecodedBal::decoded(DecodedBal::new(bal, Bytes::from(raw.clone())));
2249
2250        assert_eq!(alloy_rlp::encode(&raw_bal), raw);
2251        assert_eq!(alloy_rlp::encode(&decoded_bal), raw);
2252    }
2253
2254    #[test]
2255    fn raw_or_decoded_bal_decode_preserves_raw_rlp_item() {
2256        let bal = sample_bal();
2257        let raw = alloy_rlp::encode(&bal);
2258        let mut buf = raw.as_ref();
2259        let decoded = <RawOrDecodedBal as alloy_rlp::Decodable>::decode(&mut buf).unwrap();
2260
2261        assert!(buf.is_empty());
2262        assert!(decoded.is_raw());
2263        assert_eq!(decoded.as_raw().as_ref(), raw.as_slice());
2264        assert_eq!(alloy_rlp::encode(&decoded), raw);
2265
2266        let decoded = decoded.try_into_decoded().unwrap();
2267        assert_eq!(decoded.as_bal(), &bal);
2268    }
2269}