Skip to main content

ethrex_common/types/
account.rs

1use std::collections::BTreeMap;
2use std::sync::{Arc, LazyLock};
3
4use bytes::{BufMut, Bytes, BytesMut};
5use ethereum_types::{H256, U256};
6use ethrex_crypto::{Crypto, NativeCrypto};
7use ethrex_trie::Trie;
8use rustc_hash::FxHashMap;
9use serde::{Deserialize, Serialize};
10
11use ethrex_rlp::{
12    decode::RLPDecode,
13    encode::RLPEncode,
14    error::RLPDecodeError,
15    structs::{Decoder, Encoder},
16};
17
18use super::GenesisAccount;
19use crate::constants::{EMPTY_KECCAK_HASH, EMPTY_TRIE_HASH};
20
21/// Shared empty jumpdest bitmap. `Code::default()` and any bytecode without a
22/// `JUMPDEST` clone this (a refcount bump) instead of allocating a fresh empty
23/// `Arc` header each time. This matters because the per-tx `Code::default()`
24/// placeholder and every EOA / empty-code load would otherwise each allocate.
25static EMPTY_JUMPDESTS: LazyLock<Arc<[u8]>> = LazyLock::new(|| Arc::from(Vec::new()));
26
27/// The allocations an initcode [`Code`] is built in: its bytecode buffer and its
28/// jump-destination bitmap. A finished initcode hands them back through
29/// [`Code::into_initcode_buffer`], so the VM can keep them for the next CREATE instead of
30/// allocating new ones each time, which a zkVM guest's bump allocator never reclaims.
31#[derive(Debug, Default)]
32pub struct InitcodeBuffer {
33    bytecode: BytesMut,
34    jumpdests: Option<Arc<[u8]>>,
35}
36
37impl InitcodeBuffer {
38    /// Replaces the contents with `initcode`, with room for the padding
39    /// [`Code::from_initcode_buffer`] appends, so that appending it does not reallocate.
40    pub fn set(&mut self, initcode: &[u8]) {
41        let needed = initcode.len().saturating_add(BYTECODE_PADDING);
42        let capacity = self.bytecode.capacity();
43        if capacity < needed {
44            // A new buffer rather than this one grown, which would copy what it held. At
45            // least twice as large, so initcode that grows a little each CREATE allocates a
46            // few times rather than every time.
47            self.bytecode = BytesMut::with_capacity(needed.max(capacity.saturating_mul(2)));
48        } else {
49            self.bytecode.clear();
50        }
51        self.bytecode.extend_from_slice(initcode);
52    }
53
54    /// Empties the buffer, keeping its allocations.
55    pub fn clear(&mut self) {
56        self.bytecode.clear();
57    }
58
59    /// The initcode the buffer was last [set](InitcodeBuffer::set) to.
60    pub fn initcode(&self) -> &[u8] {
61        &self.bytecode
62    }
63
64    /// How many bytes of initcode and padding the buffer holds without reallocating.
65    pub fn capacity(&self) -> usize {
66        self.bytecode.capacity()
67    }
68
69    /// Whether the buffer holds any allocation worth keeping.
70    pub fn is_allocated(&self) -> bool {
71        self.bytecode.capacity() > 0 || self.jumpdests.is_some()
72    }
73}
74
75/// A zeroed jump-destination bitmap of `len` bytes, in one allocation: one built in a `Vec`
76/// is copied into a second allocation to become an `Arc`.
77fn zeroed_bitmap(len: usize) -> Arc<[u8]> {
78    std::iter::repeat_n(0, len).collect()
79}
80
81/// A zeroed jump-destination bitmap at least `len` bytes long: `scratch`, whole, when it is
82/// long enough and nothing else references it, which holds for the ones
83/// [`Code::into_initcode_buffer`] hands back, and a new one otherwise. A new one replacing a
84/// `scratch` too short is at least twice as long, as [`InitcodeBuffer::set`] does.
85fn zeroed_bitmap_in(scratch: Option<Arc<[u8]>>, len: usize) -> Arc<[u8]> {
86    let outgrown = match scratch {
87        Some(mut bitmap) if bitmap.len() >= len => {
88            if let Some(bits) = Arc::get_mut(&mut bitmap) {
89                // All of it, not just the first `len` bytes. JUMP trusts the bitmap alone, so
90                // a bit left past the end of the code would make a jump there valid; and the
91                // per-opcode scan only writes the bytes that hold a jump destination.
92                bits.fill(0);
93                return bitmap;
94            }
95            0
96        }
97        Some(bitmap) => bitmap.len(),
98        None => 0,
99    };
100    zeroed_bitmap(len.max(outgrown.saturating_mul(2)))
101}
102
103/// Sets the bit of every `JUMPDEST` in `code` that is not a `PUSH` immediate in `bitmap`,
104/// which is zeroed and `ceil(code.len() / 8)` bytes long. Returns whether it set any.
105/// See [`Code::compute_jumpdests`] for why the scan depends on the target.
106fn mark_jumpdests(code: &[u8], bitmap: &mut [u8]) -> bool {
107    #[cfg(any(
108        target_arch = "aarch64",
109        all(target_arch = "x86_64", target_feature = "ssse3")
110    ))]
111    let any = by_block::jumpdests(code, bitmap);
112    #[cfg(not(any(
113        target_arch = "aarch64",
114        all(target_arch = "x86_64", target_feature = "ssse3")
115    )))]
116    let any = jumpdests_by_opcode(code, bitmap);
117    any
118}
119
120/// Trailing STOP bytes appended to every bytecode so the dispatch loop can read
121/// the next opcode without a bounds check. 33 is the widest single-opcode advance
122/// (PUSH32: 1 opcode byte + 32 immediate bytes), so `pc` can never step past the
123/// padding regardless of which opcode sits at the last real byte.
124pub const BYTECODE_PADDING: usize = 33;
125
126#[derive(Clone, Debug, PartialEq, Eq, Hash)]
127pub struct Code {
128    // hash is only used for bytecodes stored in the DB, either for reading it from the DB
129    // or with the CODEHASH opcode, which needs an account address as argument and
130    // thus only accessed persisted bytecodes.
131    // We use a bogus H256::zero() value for initcodes as there is no way for the VM or
132    // endpoints to access that hash, saving one expensive Keccak hash.
133    pub hash: H256,
134    /// bytecode padded with 33 zeroes (STOP opcodes, due to PUSH32) to avoid checks on the hot path.
135    bytecode: Bytes,
136    /// The real bytecode length, needed for some opcodes, `bytecode` is padded with 33 STOPs to avoid checked adds on hot loop.
137    bytecode_len: usize,
138    /// One bit per bytecode byte, set when that offset holds a `JUMPDEST` that is not
139    /// part of a `PUSH` immediate. Costs `ceil(len / 8)` bytes regardless of how dense
140    /// the jump destinations are, and validating a jump is a bit test rather than a
141    /// search.
142    ///
143    /// Bytecode with no jump destination stores a zero-length bitmap ([`EMPTY_JUMPDESTS`])
144    /// rather than an all-zero one, so this is not always `ceil(len / 8)` bytes long;
145    /// [`Code::is_valid_jumpdest`] reads a missing byte as "no jump destination". Initcode
146    /// built by [`Code::from_initcode_buffer`] is the exception: it keeps the bitmap it was
147    /// built in, which can be all zero, or longer than `ceil(len / 8)` and zero past it.
148    //
149    // `Arc<[u8]>` so cloning `Code` (hot: every message-call resolves and clones
150    // the callee's code) is a refcount bump instead of deep-copying the bitmap.
151    // Serializes via serde's `rc` feature (enabled workspace-wide).
152    jumpdests: Arc<[u8]>,
153}
154
155impl Code {
156    // SAFETY: hash will be stored as-is, so it either needs to match
157    // the real code hash (i.e. it was precomputed and we're reusing)
158    // or never be read (e.g. for initcode).
159    //
160    // `code` is the logical, unpadded bytecode; `BYTECODE_PADDING` STOP bytes are
161    // appended internally by `from_parts_unchecked`.
162    pub fn from_bytecode_unchecked(code: Bytes, hash: H256) -> Self {
163        let jumpdests = Self::compute_jumpdests(&code);
164        Self::from_parts_unchecked(hash, &code, jumpdests)
165    }
166
167    /// `code` is the logical, unpadded bytecode; `BYTECODE_PADDING` STOP bytes are
168    /// appended internally by `from_parts_unchecked`.
169    pub fn from_bytecode(code: Bytes, crypto: &dyn Crypto) -> Self {
170        let jumpdests = Self::compute_jumpdests(&code);
171        let hash = H256(crypto.keccak256(code.as_ref()));
172        Self::from_parts_unchecked(hash, &code, jumpdests)
173    }
174
175    /// Builds a `Code` from precomputed parts. The caller must guarantee `hash`
176    /// and `jumpdests` correspond to `code`; neither is recomputed or validated.
177    ///
178    /// `code` is the logical, unpadded bytecode: this function appends
179    /// `BYTECODE_PADDING` STOP bytes and records the original length in
180    /// `bytecode_len`. Never pass a pre-padded buffer, or the logical length and
181    /// every `JUMPDEST`/`PUSH` offset derived from it would be wrong.
182    pub fn from_parts_unchecked(hash: H256, code: &[u8], jumpdests: Arc<[u8]>) -> Self {
183        let bytecode_len = code.len();
184        let mut padded_code = Vec::with_capacity(bytecode_len + BYTECODE_PADDING);
185        padded_code.extend_from_slice(code);
186        padded_code.extend_from_slice(&[0u8; BYTECODE_PADDING]);
187        Self {
188            hash,
189            bytecode: Bytes::from_owner(padded_code),
190            bytecode_len,
191            jumpdests,
192        }
193    }
194
195    /// Builds initcode from the initcode `buffer` was [set](InitcodeBuffer::set) to, in the
196    /// buffer's allocations: the padding is appended in place and the buffer becomes the
197    /// bytecode, and the jump-destination bitmap reuses the buffer's when that is the right
198    /// size. [`Code::into_initcode_buffer`] hands them back for the next initcode. The hash is
199    /// the same placeholder any initcode gets.
200    ///
201    /// Unlike other code, initcode keeps the bitmap it was built in, so it can be handed back
202    /// with the bytecode: even when it marks nothing, instead of the shared empty one, and
203    /// even when it is longer than the code needs, zeroed past its end.
204    pub fn from_initcode_buffer(buffer: InitcodeBuffer) -> Self {
205        let InitcodeBuffer {
206            mut bytecode,
207            jumpdests,
208        } = buffer;
209        let bytecode_len = bytecode.len();
210        let bitmap_len = bytecode_len.div_ceil(8);
211        let mut jumpdests = zeroed_bitmap_in(jumpdests, bitmap_len);
212        // The bitmap can be longer than this code needs; the scan writes its first
213        // `bitmap_len` bytes, and the rest stay zero.
214        mark_jumpdests(&bytecode, &mut Arc::make_mut(&mut jumpdests)[..bitmap_len]);
215        bytecode.extend_from_slice(&[0u8; BYTECODE_PADDING]);
216        Self {
217            hash: H256::zero(),
218            bytecode: bytecode.freeze(),
219            bytecode_len,
220            jumpdests,
221        }
222    }
223
224    /// Hands back the allocations of initcode built by [`Code::from_initcode_buffer`], each
225    /// only when nothing else references it. The bytecode buffer comes back empty, so handing
226    /// back any other `Code`'s is harmless too: no stale bytecode can be built from again.
227    pub fn into_initcode_buffer(self) -> InitcodeBuffer {
228        let mut bytecode = self.bytecode.try_into_mut().unwrap_or_default();
229        bytecode.clear();
230        let mut jumpdests = self.jumpdests;
231        let unique = Arc::get_mut(&mut jumpdests).is_some();
232        InitcodeBuffer {
233            bytecode,
234            jumpdests: unique.then_some(jumpdests),
235        }
236    }
237
238    /// Builds the [`Code::jumpdests`] bitmap: the bit of every `JUMPDEST` that is not a
239    /// `PUSH` immediate.
240    ///
241    /// On aarch64, and on x86_64 with SSSE3, this runs without a data-dependent branch
242    /// (see `by_block`), because a scan that branches on each opcode stalls on every
243    /// misprediction, and random bytecode mispredicts on most bytes. execution-specs#3631
244    /// builds such initcode on purpose. Other targets are mostly the zkVM guests, which
245    /// have no branch predictor and would only pay for the extra instructions, so they
246    /// keep the plain scan (`jumpdests_by_opcode`).
247    pub fn compute_jumpdests(code: &[u8]) -> Arc<[u8]> {
248        // Even an empty `Arc` allocates its reference counts.
249        if code.is_empty() {
250            return EMPTY_JUMPDESTS.clone();
251        }
252        let mut bitmap = zeroed_bitmap(code.len().div_ceil(8));
253        let any = mark_jumpdests(code, Arc::make_mut(&mut bitmap));
254        // Share the single empty bitmap for jumpless bytecode (very common: EOAs,
255        // tiny contracts) so we don't keep an all-zero map; `is_valid_jumpdest`
256        // reads a missing byte as "no jump destination".
257        if any { bitmap } else { EMPTY_JUMPDESTS.clone() }
258    }
259
260    /// Whether `offset` is a valid jump destination, i.e. it holds a `JUMPDEST` that is
261    /// not part of a `PUSH` immediate. Offsets past the bytecode are not valid.
262    #[inline]
263    pub fn is_valid_jumpdest(&self, offset: usize) -> bool {
264        self.jumpdests
265            .get(offset / 8)
266            .is_some_and(|byte| byte & (1 << (offset % 8)) != 0)
267    }
268
269    /// The raw [`Code::jumpdests`] bitmap, for persisting it alongside the bytecode.
270    #[inline]
271    pub fn jumpdests(&self) -> &[u8] {
272        &self.jumpdests
273    }
274
275    #[inline]
276    pub fn code(&self) -> &[u8] {
277        self.bytecode.get(..self.bytecode_len).unwrap_or_default()
278    }
279
280    #[inline]
281    pub fn code_bytes(&self) -> Bytes {
282        self.bytecode.slice(..self.bytecode_len)
283    }
284
285    #[inline]
286    pub fn len(&self) -> usize {
287        self.bytecode_len
288    }
289
290    #[inline]
291    pub fn is_empty(&self) -> bool {
292        self.bytecode_len == 0
293    }
294
295    /// Returns the padded bytecode buffer (real code + [`BYTECODE_PADDING`] trailing
296    /// STOPs) used by the opcode dispatch loop to read opcodes without bounds checks.
297    /// Use [`Code::code`] for the real, unpadded bytecode.
298    #[inline]
299    pub fn dispatch_buf(&self) -> &[u8] {
300        &self.bytecode
301    }
302
303    /// Estimates the size of the Code struct in bytes
304    /// (including stack size and heap allocation).
305    ///
306    /// Note: an estimate. It ignores allocator overhead and the `Arc`/`Bytes` control
307    /// blocks, so it slightly under-counts, and a shared allocation is attributed in
308    /// full to every entry holding it.
309    ///
310    /// # Returns
311    ///
312    /// usize - Estimated size in bytes
313    pub fn size(&self) -> usize {
314        let hash_size = size_of::<H256>();
315        let bytes_size = size_of::<Bytes>() + self.bytecode.len();
316        let bitmap_size = size_of::<Arc<[u8]>>() + self.jumpdests.len();
317        hash_size + bytes_size + bitmap_size
318    }
319}
320
321// Opcodes the jump destination analysis needs. The vm module defines them too, but
322// depending on it here would be circular.
323const OP_JUMPDEST: u8 = 0x5B;
324const OP_PUSH1: u8 = 0x60;
325const OP_PUSH32: u8 = 0x7F;
326
327/// Fills `bitmap` for `code` one opcode at a time and returns whether any bit is set.
328///
329/// The bits of a byte are accumulated in a register and written once the scan leaves
330/// that byte, which the monotonic `i` makes safe. Reading the bitmap back inside the
331/// loop instead would turn each `JUMPDEST` into a read-modify-write, and indexing it
332/// would put a bounds-check panic path in the loop body, which inhibits optimization
333/// of every iteration rather than only the ones that find a destination.
334#[cfg_attr(
335    any(
336        target_arch = "aarch64",
337        all(target_arch = "x86_64", target_feature = "ssse3")
338    ),
339    allow(dead_code)
340)]
341fn jumpdests_by_opcode(code: &[u8], bitmap: &mut [u8]) -> bool {
342    let mut any = false;
343    let mut current_byte = usize::MAX;
344    let mut bits = 0u8;
345    let mut i = 0;
346    while i < code.len() {
347        match code[i] {
348            OP_JUMPDEST => {
349                if i / 8 != current_byte {
350                    if let Some(byte) = bitmap.get_mut(current_byte) {
351                        *byte = bits;
352                    }
353                    current_byte = i / 8;
354                    bits = 0;
355                }
356                bits |= 1 << (i % 8);
357                any = true;
358            }
359            // PUSH1..PUSH32 are followed by 1..32 immediate bytes.
360            c @ OP_PUSH1..=OP_PUSH32 => i += usize::from(c - OP_PUSH1) + 1,
361            _ => (),
362        }
363        i += 1;
364    }
365    if let Some(byte) = bitmap.get_mut(current_byte) {
366        *byte = bits;
367    }
368    any
369}
370
371/// The jump destination analysis without a data-dependent branch, 64 bytes at a time.
372///
373/// Whether a byte is an opcode depends on every `PUSH` before it, so the natural scan
374/// branches on each opcode, and on random bytecode that branch mispredicts on most bytes.
375/// Here the work per block is the same whatever its bytes are:
376///
377/// 1. In each 16-byte group, the walks from all 16 bytes, each as if its byte were an
378///    opcode, advance at once until they leave the group (see [`group_walks`]).
379/// 2. The real walk enters the block where the previous block's walk left it. Where it
380///    leaves a group is where it enters the next one, so four dependent loads pick the
381///    walk of each group, and give where the walk enters the next block.
382#[cfg(any(
383    target_arch = "aarch64",
384    all(target_arch = "x86_64", target_feature = "ssse3")
385))]
386mod by_block {
387    use super::{OP_JUMPDEST, OP_PUSH1};
388    use lanes::Lanes;
389
390    /// Bytes per block, one `u64` of bitmap.
391    const BLOCK: usize = 64;
392    /// Bytes per group, one SIMD register.
393    const GROUP: usize = 16;
394
395    /// Fills `bitmap` for `code` and returns whether any bit is set.
396    pub(super) fn jumpdests(code: &[u8], bitmap: &mut [u8]) -> bool {
397        let mut walks = Walks::new();
398        let (blocks, tail) = code.as_chunks::<BLOCK>();
399        let mut carry = 0;
400        let mut any = 0;
401        // A full block fills exactly one 8-byte word of the bitmap.
402        for (block, word) in blocks.iter().zip(bitmap.as_chunks_mut::<8>().0) {
403            let valid = block_jumpdests(block, &mut carry, &mut walks);
404            *word = valid.to_le_bytes();
405            any |= valid;
406        }
407        if !tail.is_empty() {
408            // Zeros are STOPs, which are neither destinations nor PUSHes.
409            let mut padded = [0; BLOCK];
410            padded[..tail.len()].copy_from_slice(tail);
411            let valid = block_jumpdests(&padded, &mut carry, &mut walks);
412            let rest = &mut bitmap[blocks.len() * 8..];
413            rest.copy_from_slice(&valid.to_le_bytes()[..rest.len()]);
414            any |= valid;
415        }
416        any != 0
417    }
418
419    /// The walks of one block's bytes (see [`group_walks`]), by position in the block:
420    /// where each leaves its group, and which bytes of the group it lands on, the lower
421    /// 8 in `low_stops` and the upper 8 in `high_stops`.
422    ///
423    /// Positions from 64 on stand for the next block: they lead to themselves and land on
424    /// nothing, so a walk that has left the block stays where it is.
425    struct Walks {
426        exits: [u8; 2 * BLOCK],
427        low_stops: [u8; 2 * BLOCK],
428        high_stops: [u8; 2 * BLOCK],
429    }
430
431    impl Walks {
432        fn new() -> Self {
433            Self {
434                exits: core::array::from_fn(|i| i as u8),
435                low_stops: [0; 2 * BLOCK],
436                high_stops: [0; 2 * BLOCK],
437            }
438        }
439    }
440
441    /// The valid jump destinations of one block, bit `i` for byte `i`.
442    ///
443    /// `carry` is how many leading bytes of the block are immediates of a `PUSH` in the
444    /// previous block, and is updated for the next one.
445    #[inline(always)]
446    fn block_jumpdests(block: &[u8; BLOCK], carry: &mut usize, walks: &mut Walks) -> u64 {
447        let groups = block.as_chunks::<GROUP>().0;
448        let groups: [Lanes; BLOCK / GROUP] = core::array::from_fn(|g| Lanes::load(&groups[g]));
449        let jumpdests = Lanes::bits(groups.map(|bytes| bytes.eq(Lanes::splat(OP_JUMPDEST))));
450        for (g, bytes) in groups.into_iter().enumerate() {
451            let (exits, low_stops, high_stops) = group_walks(bytes);
452            // From here on, positions are relative to the block instead of the group.
453            let exits = exits.add(Lanes::splat((g * GROUP) as u8));
454            exits.store(&mut walks.exits.as_chunks_mut::<GROUP>().0[g]);
455            low_stops.store(&mut walks.low_stops.as_chunks_mut::<GROUP>().0[g]);
456            high_stops.store(&mut walks.high_stops.as_chunks_mut::<GROUP>().0[g]);
457        }
458        let mut at = *carry;
459        let mut opcodes = 0;
460        for _ in 0..BLOCK / GROUP {
461            // At most 96, 32 immediates past the block; the modulo drops the bounds checks.
462            let i = at % (2 * BLOCK);
463            let stops = u64::from(walks.low_stops[i]) | (u64::from(walks.high_stops[i]) << 8);
464            // Past the block there are no stops, so the shift only matters inside it.
465            let group_start = i & (BLOCK - GROUP);
466            opcodes |= stops << group_start;
467            at = usize::from(walks.exits[i]);
468        }
469        // Each load moves the walk past at least one more group, so it is now past the
470        // block, by as many bytes as the next block starts with immediates.
471        *carry = at - BLOCK;
472        jumpdests & opcodes
473    }
474
475    /// For each byte of a 16-byte group, the walk that starts if that byte is an opcode,
476    /// up to where it leaves the group: the position it leaves to, relative to the group
477    /// (16 to 48), and the bytes it lands on, bits 0 to 7 in the first mask and bits 8 to
478    /// 15 in the second.
479    ///
480    /// All 16 walks advance together by pointer doubling: each round, a walk that is still
481    /// in the group takes as many steps again by continuing with the walk of the byte it
482    /// is at, which a table lookup of every lane (TBL on aarch64, PSHUFB on x86_64) reads.
483    /// A step moves at least one byte, so four rounds (16 steps) take every walk out of
484    /// the group.
485    #[inline(always)]
486    fn group_walks(bytes: Lanes) -> (Lanes, Lanes, Lanes) {
487        const POSITIONS_PLUS_ONE: [u8; GROUP] =
488            [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
489        const LOW_POSITION_BITS: [u8; GROUP] =
490            [1, 2, 4, 8, 16, 32, 64, 128, 0, 0, 0, 0, 0, 0, 0, 0];
491        const HIGH_POSITION_BITS: [u8; GROUP] =
492            [0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 4, 8, 16, 32, 64, 128];
493        // PUSH1..PUSH32 are exactly the bytes 0b011x_xxxx, with `byte - PUSH1 + 1`
494        // immediates.
495        let is_push = bytes.and(Lanes::splat(0xE0)).eq(Lanes::splat(OP_PUSH1));
496        let immediates = is_push.and(bytes.sub(Lanes::splat(OP_PUSH1 - 1)));
497        // One step, past the opcode and its immediates.
498        let mut exits = Lanes::load(&POSITIONS_PLUS_ONE).add(immediates);
499        let mut low_stops = Lanes::load(&LOW_POSITION_BITS);
500        let mut high_stops = Lanes::load(&HIGH_POSITION_BITS);
501        // The lookups read zero for a walk that has left the group, so its stops stay as
502        // they are, and `max` keeps its exit, while a walk still inside moves ahead.
503        // Stops in the lower 8 bytes are a walk's first 8 at most, so the last round can
504        // skip them.
505        for round in 0..4 {
506            if round < 3 {
507                low_stops = low_stops.or(low_stops.lookup(exits));
508            }
509            high_stops = high_stops.or(high_stops.lookup(exits));
510            exits = exits.max(exits.lookup(exits));
511        }
512        (exits, low_stops, high_stops)
513    }
514
515    #[cfg(target_arch = "aarch64")]
516    mod lanes {
517        use core::arch::aarch64::*;
518
519        /// 16 bytes in a NEON register.
520        #[derive(Clone, Copy)]
521        pub(super) struct Lanes(uint8x16_t);
522
523        // SAFETY (every `unsafe` below): NEON is part of the aarch64 baseline, and the
524        // loads and stores go through a reference to exactly 16 bytes, with no alignment
525        // requirement.
526        impl Lanes {
527            #[inline(always)]
528            pub(super) fn load(bytes: &[u8; 16]) -> Self {
529                unsafe { Self(vld1q_u8(bytes.as_ptr())) }
530            }
531
532            #[inline(always)]
533            pub(super) fn store(self, out: &mut [u8; 16]) {
534                unsafe { vst1q_u8(out.as_mut_ptr(), self.0) }
535            }
536
537            #[inline(always)]
538            pub(super) fn splat(byte: u8) -> Self {
539                unsafe { Self(vdupq_n_u8(byte)) }
540            }
541
542            #[inline(always)]
543            pub(super) fn and(self, other: Self) -> Self {
544                unsafe { Self(vandq_u8(self.0, other.0)) }
545            }
546
547            #[inline(always)]
548            pub(super) fn or(self, other: Self) -> Self {
549                unsafe { Self(vorrq_u8(self.0, other.0)) }
550            }
551
552            #[inline(always)]
553            pub(super) fn add(self, other: Self) -> Self {
554                unsafe { Self(vaddq_u8(self.0, other.0)) }
555            }
556
557            #[inline(always)]
558            pub(super) fn sub(self, other: Self) -> Self {
559                unsafe { Self(vsubq_u8(self.0, other.0)) }
560            }
561
562            /// All ones in the lanes that are equal, zero in the others.
563            #[inline(always)]
564            pub(super) fn eq(self, other: Self) -> Self {
565                unsafe { Self(vceqq_u8(self.0, other.0)) }
566            }
567
568            #[inline(always)]
569            pub(super) fn max(self, other: Self) -> Self {
570                unsafe { Self(vmaxq_u8(self.0, other.0)) }
571            }
572
573            /// Lane `i` of `self` for each lane `i` of `indices` below 16, zero for the rest.
574            #[inline(always)]
575            pub(super) fn lookup(self, indices: Self) -> Self {
576                unsafe { Self(vqtbl1q_u8(self.0, indices.0)) }
577            }
578
579            /// The top bit of every lane of four registers of all-ones or zero lanes.
580            ///
581            /// NEON has no instruction that gathers one bit per byte, so each lane keeps
582            /// the bit of its position within 8 bytes, and three rounds of pairwise adds
583            /// pack them into one `u64`.
584            #[inline(always)]
585            pub(super) fn bits(masks: [Self; 4]) -> u64 {
586                unsafe {
587                    let position_bits = vreinterpretq_u8_u64(vdupq_n_u64(0x8040_2010_0804_0201));
588                    let [a, b, c, d] = masks.map(|m| vandq_u8(m.0, position_bits));
589                    let quarters = vpaddq_u8(vpaddq_u8(a, b), vpaddq_u8(c, d));
590                    vgetq_lane_u64::<0>(vreinterpretq_u64_u8(vpaddq_u8(quarters, quarters)))
591                }
592            }
593        }
594    }
595
596    #[cfg(target_arch = "x86_64")]
597    mod lanes {
598        use core::arch::x86_64::*;
599
600        /// 16 bytes in an SSE register.
601        #[derive(Clone, Copy)]
602        pub(super) struct Lanes(__m128i);
603
604        // SAFETY (every `unsafe` below): SSE2 is part of the x86_64 baseline, SSSE3 is
605        // enabled for this build (see the `cfg` on `by_block`), and the loads and stores
606        // go through a reference to exactly 16 bytes, with no alignment requirement.
607        impl Lanes {
608            #[inline(always)]
609            pub(super) fn load(bytes: &[u8; 16]) -> Self {
610                unsafe { Self(_mm_loadu_si128(bytes.as_ptr().cast())) }
611            }
612
613            #[inline(always)]
614            pub(super) fn store(self, out: &mut [u8; 16]) {
615                unsafe { _mm_storeu_si128(out.as_mut_ptr().cast(), self.0) }
616            }
617
618            #[inline(always)]
619            pub(super) fn splat(byte: u8) -> Self {
620                unsafe { Self(_mm_set1_epi8(byte as i8)) }
621            }
622
623            #[inline(always)]
624            pub(super) fn and(self, other: Self) -> Self {
625                unsafe { Self(_mm_and_si128(self.0, other.0)) }
626            }
627
628            #[inline(always)]
629            pub(super) fn or(self, other: Self) -> Self {
630                unsafe { Self(_mm_or_si128(self.0, other.0)) }
631            }
632
633            #[inline(always)]
634            pub(super) fn add(self, other: Self) -> Self {
635                unsafe { Self(_mm_add_epi8(self.0, other.0)) }
636            }
637
638            #[inline(always)]
639            pub(super) fn sub(self, other: Self) -> Self {
640                unsafe { Self(_mm_sub_epi8(self.0, other.0)) }
641            }
642
643            /// All ones in the lanes that are equal, zero in the others.
644            #[inline(always)]
645            pub(super) fn eq(self, other: Self) -> Self {
646                unsafe { Self(_mm_cmpeq_epi8(self.0, other.0)) }
647            }
648
649            #[inline(always)]
650            pub(super) fn max(self, other: Self) -> Self {
651                unsafe { Self(_mm_max_epu8(self.0, other.0)) }
652            }
653
654            /// Lane `i` of `self` for each lane `i` of `indices` below 16, zero for the rest
655            /// up to 143.
656            #[inline(always)]
657            pub(super) fn lookup(self, indices: Self) -> Self {
658                // PSHUFB zeroes the lanes whose index has its top bit set and otherwise
659                // uses the index's low 4 bits. Adding 0x70 sets the top bit of 16..=143
660                // and keeps the low 4 bits of 0..=15.
661                unsafe {
662                    let indices = _mm_add_epi8(indices.0, _mm_set1_epi8(0x70));
663                    Self(_mm_shuffle_epi8(self.0, indices))
664                }
665            }
666
667            /// The top bit of every lane of four registers of all-ones or zero lanes.
668            #[inline(always)]
669            pub(super) fn bits(masks: [Self; 4]) -> u64 {
670                masks.iter().enumerate().fold(0, |bits, (i, m)| {
671                    let lanes = unsafe { _mm_movemask_epi8(m.0) } as u16;
672                    bits | (u64::from(lanes) << (16 * i))
673                })
674            }
675        }
676    }
677}
678
679/// Serde shadow for [`Code`]. Stores the *logical* (unpadded) bytecode so the
680/// padding is never part of the serialized form. Deserialization re-pads through
681/// [`Code::from_parts_unchecked`], which keeps the dispatch-loop invariant (every
682/// `Code` is padded with [`BYTECODE_PADDING`] trailing STOPs) sound regardless of
683/// where the bytes came from. Deserializing the padded buffer directly would
684/// otherwise let unpadded input through and cause OOB reads during execution.
685/// The jump destinations are deliberately absent: they are a pure function of the
686/// bytecode, so carrying them would put a derived value in the wire format of everything
687/// that embeds a `Code` (notably `AccountUpdate`, which the L2 rollup store persists with
688/// bincode) and couple that format to how they happen to be represented.
689/// Wire form of [`Code`]. Jump destinations are recomputed from `code` on
690/// deserialize, never read from here — but the `jump_targets` field is kept so
691/// the layout stays byte-identical to the pre-bitmap `Code` serialization.
692/// `Code` is embedded in `AccountUpdate`, which is persisted with bincode (a
693/// non-self-describing, positional codec) in the L2 rollup store; dropping the
694/// field would shift every following field and make existing rows undecodable.
695/// It is always serialized empty and ignored on read.
696#[derive(Serialize, Deserialize)]
697struct CodeSerde {
698    hash: H256,
699    code: Bytes,
700    jump_targets: Arc<[u32]>,
701}
702
703impl Serialize for Code {
704    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
705        CodeSerde {
706            hash: self.hash,
707            code: self.code_bytes(),
708            jump_targets: Arc::from([]),
709        }
710        .serialize(serializer)
711    }
712}
713
714impl<'de> Deserialize<'de> for Code {
715    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
716        // `jump_targets` is read only to consume its bytes (layout compatibility);
717        // the bitmap is always recomputed from `code`.
718        let CodeSerde {
719            hash,
720            code,
721            jump_targets: _,
722        } = CodeSerde::deserialize(deserializer)?;
723        Ok(Self::from_parts_unchecked(
724            hash,
725            &code,
726            Self::compute_jumpdests(&code),
727        ))
728    }
729}
730
731#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
732pub struct CodeMetadata {
733    pub length: u64,
734}
735
736#[derive(Clone, Default, Debug, PartialEq, Eq, Serialize, Deserialize)]
737pub struct Account {
738    pub info: AccountInfo,
739    pub code: Code,
740    pub storage: FxHashMap<H256, U256>,
741}
742
743#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)]
744pub struct AccountInfo {
745    pub code_hash: H256,
746    pub balance: U256,
747    pub nonce: u64,
748}
749
750#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
751pub struct AccountState {
752    pub nonce: u64,
753    pub balance: U256,
754    pub storage_root: H256,
755    pub code_hash: H256,
756}
757
758/// A slim codec for an [`AccountState`].
759///
760/// The slim codec will optimize both the [storage root](AccountState::storage_root) and the
761/// [code hash](AccountState::code_hash)'s encoding so that it does not take space when empty.
762///
763/// The correct way to use it is to wrap the [`AccountState`] and encode it using this codec, and
764/// not to store the codec as a field in a struct.
765#[derive(Clone, Copy, Debug, Default, PartialEq)]
766pub struct AccountStateSlimCodec(pub AccountState);
767
768impl Default for AccountInfo {
769    fn default() -> Self {
770        Self {
771            code_hash: *EMPTY_KECCAK_HASH,
772            balance: Default::default(),
773            nonce: Default::default(),
774        }
775    }
776}
777
778impl Default for AccountState {
779    fn default() -> Self {
780        Self {
781            nonce: Default::default(),
782            balance: Default::default(),
783            storage_root: *EMPTY_TRIE_HASH,
784            code_hash: *EMPTY_KECCAK_HASH,
785        }
786    }
787}
788
789impl Default for Code {
790    fn default() -> Self {
791        Self {
792            bytecode: Bytes::from_static(&[0u8; BYTECODE_PADDING]),
793            bytecode_len: 0,
794            hash: *EMPTY_KECCAK_HASH,
795            jumpdests: EMPTY_JUMPDESTS.clone(),
796        }
797    }
798}
799
800impl From<GenesisAccount> for Account {
801    fn from(genesis: GenesisAccount) -> Self {
802        let code = Code::from_bytecode(genesis.code, &NativeCrypto);
803        Self {
804            info: AccountInfo {
805                code_hash: code.hash,
806                balance: genesis.balance,
807                nonce: genesis.nonce,
808            },
809            code,
810            storage: genesis
811                .storage
812                .iter()
813                .map(|(k, v)| (H256(k.to_big_endian()), *v))
814                .collect(),
815        }
816    }
817}
818
819pub fn code_hash(code: &Bytes, crypto: &dyn Crypto) -> H256 {
820    H256(crypto.keccak256(code.as_ref()))
821}
822
823/// EIP-7702 delegation designation: an EOA whose code is `0xef0100 || address`.
824/// See <https://eips.ethereum.org/EIPS/eip-7702>.
825pub const EIP7702_DELEGATION_PREFIX: [u8; 3] = [0xef, 0x01, 0x00];
826/// Total byte length of an EIP-7702 delegation designation: 3-byte prefix
827/// plus the 20-byte target address.
828pub const EIP7702_DELEGATED_CODE_LEN: usize = 23;
829
830/// Returns true iff `code` is a valid EIP-7702 delegation designation
831/// (exactly 23 bytes, prefixed with `0xef0100`).
832pub fn is_eip7702_delegation(code: &[u8]) -> bool {
833    code.len() == EIP7702_DELEGATED_CODE_LEN && code.starts_with(&EIP7702_DELEGATION_PREFIX)
834}
835
836impl RLPEncode for AccountInfo {
837    fn encode(&self, buf: &mut dyn bytes::BufMut) {
838        Encoder::new(buf)
839            .encode_field(&self.code_hash)
840            .encode_field(&self.balance)
841            .encode_field(&self.nonce)
842            .finish();
843    }
844}
845
846impl RLPDecode for AccountInfo {
847    fn decode_unfinished(rlp: &[u8]) -> Result<(AccountInfo, &[u8]), RLPDecodeError> {
848        let decoder = Decoder::new(rlp)?;
849        let (code_hash, decoder) = decoder.decode_field("code_hash")?;
850        let (balance, decoder) = decoder.decode_field("balance")?;
851        let (nonce, decoder) = decoder.decode_field("nonce")?;
852        let account_info = AccountInfo {
853            code_hash,
854            balance,
855            nonce,
856        };
857        Ok((account_info, decoder.finish()?))
858    }
859}
860
861impl RLPEncode for AccountState {
862    fn encode(&self, buf: &mut dyn bytes::BufMut) {
863        Encoder::new(buf)
864            .encode_field(&self.nonce)
865            .encode_field(&self.balance)
866            .encode_field(&self.storage_root)
867            .encode_field(&self.code_hash)
868            .finish();
869    }
870}
871
872impl RLPDecode for AccountState {
873    fn decode_unfinished(rlp: &[u8]) -> Result<(AccountState, &[u8]), RLPDecodeError> {
874        let decoder = Decoder::new(rlp)?;
875        let (nonce, decoder) = decoder.decode_field("nonce")?;
876        let (balance, decoder) = decoder.decode_field("balance")?;
877        let (storage_root, decoder) = decoder.decode_field("storage_root")?;
878        let (code_hash, decoder) = decoder.decode_field("code_hash")?;
879        let state = AccountState {
880            nonce,
881            balance,
882            storage_root,
883            code_hash,
884        };
885        Ok((state, decoder.finish()?))
886    }
887}
888
889impl RLPEncode for AccountStateSlimCodec {
890    fn encode(&self, buf: &mut dyn BufMut) {
891        struct StorageRootCodec<'a>(&'a H256);
892        impl RLPEncode for StorageRootCodec<'_> {
893            fn encode(&self, buf: &mut dyn BufMut) {
894                let data = if *self.0 != *EMPTY_TRIE_HASH {
895                    self.0.as_bytes()
896                } else {
897                    &[]
898                };
899
900                data.encode(buf);
901            }
902        }
903
904        struct CodeHashCodec<'a>(&'a H256);
905        impl RLPEncode for CodeHashCodec<'_> {
906            fn encode(&self, buf: &mut dyn BufMut) {
907                let data = if *self.0 != *EMPTY_KECCAK_HASH {
908                    self.0.as_bytes()
909                } else {
910                    &[]
911                };
912
913                data.encode(buf);
914            }
915        }
916
917        Encoder::new(buf)
918            .encode_field(&self.0.nonce)
919            .encode_field(&self.0.balance)
920            .encode_field(&StorageRootCodec(&self.0.storage_root))
921            .encode_field(&CodeHashCodec(&self.0.code_hash))
922            .finish();
923    }
924}
925
926impl RLPDecode for AccountStateSlimCodec {
927    fn decode_unfinished(rlp: &[u8]) -> Result<(Self, &[u8]), RLPDecodeError> {
928        struct StorageRootCodec(H256);
929        impl RLPDecode for StorageRootCodec {
930            fn decode_unfinished(mut rlp: &[u8]) -> Result<(Self, &[u8]), RLPDecodeError> {
931                let value = match rlp.split_off_first() {
932                    Some(0x80) => *EMPTY_TRIE_HASH,
933                    Some(0xA0) => {
934                        let data;
935                        (data, rlp) = rlp
936                            .split_first_chunk::<32>()
937                            .ok_or(RLPDecodeError::InvalidLength)?;
938                        H256(*data)
939                    }
940                    _ => return Err(RLPDecodeError::InvalidLength),
941                };
942
943                Ok((Self(value), rlp))
944            }
945        }
946
947        struct CodeHashCodec(H256);
948        impl RLPDecode for CodeHashCodec {
949            fn decode_unfinished(mut rlp: &[u8]) -> Result<(Self, &[u8]), RLPDecodeError> {
950                let value = match rlp.split_off_first() {
951                    Some(0x80) => *EMPTY_KECCAK_HASH,
952                    Some(0xA0) => {
953                        let data;
954                        (data, rlp) = rlp
955                            .split_first_chunk::<32>()
956                            .ok_or(RLPDecodeError::InvalidLength)?;
957                        H256(*data)
958                    }
959                    _ => return Err(RLPDecodeError::InvalidLength),
960                };
961
962                Ok((Self(value), rlp))
963            }
964        }
965
966        let decoder = Decoder::new(rlp)?;
967        let (nonce, decoder) = decoder.decode_field("nonce")?;
968        let (balance, decoder) = decoder.decode_field("balance")?;
969        let (StorageRootCodec(storage_root), decoder) = decoder.decode_field("storage_root")?;
970        let (CodeHashCodec(code_hash), decoder) = decoder.decode_field("code_hash")?;
971
972        Ok((
973            Self(AccountState {
974                nonce,
975                balance,
976                storage_root,
977                code_hash,
978            }),
979            decoder.finish()?,
980        ))
981    }
982}
983
984pub fn compute_storage_root(storage: &BTreeMap<U256, U256>, crypto: &dyn Crypto) -> H256 {
985    let iter = storage.iter().filter_map(|(k, v)| {
986        (!v.is_zero()).then_some((
987            crypto.keccak256(&k.to_big_endian()).to_vec(),
988            v.encode_to_vec(),
989        ))
990    });
991    Trie::compute_hash_from_unsorted_iter(iter, crypto)
992}
993
994impl From<&GenesisAccount> for AccountState {
995    fn from(value: &GenesisAccount) -> Self {
996        AccountState {
997            nonce: value.nonce,
998            balance: value.balance,
999            storage_root: compute_storage_root(&value.storage, &NativeCrypto),
1000            code_hash: code_hash(&value.code, &NativeCrypto),
1001        }
1002    }
1003}
1004
1005impl Account {
1006    pub fn new(balance: U256, code: Code, nonce: u64, storage: FxHashMap<H256, U256>) -> Self {
1007        Self {
1008            info: AccountInfo {
1009                balance,
1010                code_hash: code.hash,
1011                nonce,
1012            },
1013            code,
1014            storage,
1015        }
1016    }
1017}
1018
1019impl AccountInfo {
1020    pub fn is_empty(&self) -> bool {
1021        self.balance.is_zero() && self.nonce == 0 && self.code_hash == *EMPTY_KECCAK_HASH
1022    }
1023}
1024
1025#[cfg(test)]
1026mod test {
1027    use std::str::FromStr;
1028
1029    use super::*;
1030
1031    // Pre-bitmap on-wire layout of `Code`: `{hash, code, jump_targets}`. `AccountUpdate`
1032    // embeds `Code` and is persisted with bincode (positional) in the L2 rollup store,
1033    // so a row written before the bitmap change carries a non-empty `jump_targets`.
1034    #[derive(serde::Serialize)]
1035    struct OldCodeSerde {
1036        hash: H256,
1037        code: Bytes,
1038        jump_targets: Vec<u32>,
1039    }
1040
1041    // `Code` is never persisted alone — it sits inside `AccountUpdate`, which has fields
1042    // AFTER `code`. These wrappers mirror that: a field following the code. A standalone
1043    // `Code` would tolerate trailing bytes, hiding the bug; only an embedded field
1044    // exposes the positional shift.
1045    #[derive(serde::Serialize)]
1046    struct OldWrap {
1047        code: OldCodeSerde,
1048        tail: u64,
1049    }
1050    #[derive(serde::Deserialize)]
1051    struct NewWrap {
1052        code: Code,
1053        tail: u64,
1054    }
1055
1056    // A row serialized under the old three-field layout must still decode with the field
1057    // following `code` intact: the current `Code` deserializer reads and discards
1058    // `jump_targets`, keeping the layout aligned. Without the retained field, bincode
1059    // reads `tail` from the `jump_targets` bytes and every following field is corrupt —
1060    // exactly the `store_account_updates_by_block_number` break.
1061    #[test]
1062    fn embedded_code_decodes_an_old_layout_bincode_row() {
1063        // PUSH1 0x00 JUMPDEST STOP — offset 2 is a real JUMPDEST, offset 1 a PUSH immediate.
1064        let bytecode = Bytes::from(vec![0x60, 0x00, 0x5b, 0x00]);
1065        let hash = H256::from_low_u64_be(0xabc);
1066        const SENTINEL: u64 = 0x0123_4567_89ab_cdef;
1067        let old = OldWrap {
1068            code: OldCodeSerde {
1069                hash,
1070                code: bytecode.clone(),
1071                jump_targets: vec![2], // non-empty, as an old row would hold
1072            },
1073            tail: SENTINEL,
1074        };
1075        let bytes = bincode::serialize(&old).expect("serialize old layout");
1076
1077        let decoded: NewWrap = bincode::deserialize(&bytes).expect("decode old row under new Code");
1078        assert_eq!(decoded.code.hash, hash);
1079        assert_eq!(decoded.code.code_bytes(), bytecode);
1080        // The field after `code` must survive — this is what breaks if the layout shifts.
1081        assert_eq!(
1082            decoded.tail, SENTINEL,
1083            "field after Code corrupted by layout shift"
1084        );
1085        // Jump destinations recomputed from code, not read from the row.
1086        assert!(decoded.code.is_valid_jumpdest(2));
1087        assert!(!decoded.code.is_valid_jumpdest(1));
1088    }
1089
1090    #[test]
1091    fn test_code_hash() {
1092        let empty_code = Bytes::new();
1093        let hash = code_hash(&empty_code, &NativeCrypto);
1094        assert_eq!(
1095            hash,
1096            H256::from_str("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470")
1097                .unwrap()
1098        )
1099    }
1100
1101    #[test]
1102    fn test_is_eip7702_delegation_valid() {
1103        // 0xef0100 || 20-byte address
1104        let mut code = Vec::with_capacity(23);
1105        code.extend_from_slice(&EIP7702_DELEGATION_PREFIX);
1106        code.extend_from_slice(&[0x42; 20]);
1107        assert!(is_eip7702_delegation(&code));
1108    }
1109
1110    #[test]
1111    fn test_is_eip7702_delegation_rejects_empty() {
1112        assert!(!is_eip7702_delegation(&[]));
1113    }
1114
1115    #[test]
1116    fn test_is_eip7702_delegation_rejects_short() {
1117        // Prefix only, no address.
1118        assert!(!is_eip7702_delegation(&EIP7702_DELEGATION_PREFIX));
1119    }
1120
1121    #[test]
1122    fn test_is_eip7702_delegation_rejects_long() {
1123        // Correct prefix but 24 bytes total.
1124        let mut code = Vec::with_capacity(24);
1125        code.extend_from_slice(&EIP7702_DELEGATION_PREFIX);
1126        code.extend_from_slice(&[0x42; 21]);
1127        assert!(!is_eip7702_delegation(&code));
1128    }
1129
1130    #[test]
1131    fn test_is_eip7702_delegation_rejects_wrong_prefix() {
1132        // Right length, wrong magic.
1133        let mut code = Vec::with_capacity(23);
1134        code.extend_from_slice(&[0xef, 0x01, 0x01]); // off by one in the last prefix byte
1135        code.extend_from_slice(&[0x42; 20]);
1136        assert!(!is_eip7702_delegation(&code));
1137    }
1138
1139    #[test]
1140    fn test_is_eip7702_delegation_rejects_arbitrary_contract_code() {
1141        // Real contract code starting with anything else.
1142        let code = vec![0x60, 0x60, 0x60, 0x40, 0x52 /* ... */];
1143        assert!(!is_eip7702_delegation(&code));
1144    }
1145
1146    // On these targets `compute_jumpdests` runs `by_block`, so the scan it replaces only
1147    // runs here, as the reference: both must give the same bitmap.
1148    #[cfg(any(
1149        target_arch = "aarch64",
1150        all(target_arch = "x86_64", target_feature = "ssse3")
1151    ))]
1152    mod by_block_against_the_scan {
1153        use super::*;
1154
1155        fn assert_same_jumpdests(code: &[u8]) {
1156            let mut want = vec![0; code.len().div_ceil(8)];
1157            let mut got = want.clone();
1158            let want_any = jumpdests_by_opcode(code, &mut want);
1159            let got_any = by_block::jumpdests(code, &mut got);
1160            assert_eq!((got_any, got), (want_any, want), "code {code:02x?}");
1161        }
1162
1163        /// One or two bytes at every offset of codes whose lengths straddle the 16-byte
1164        /// groups and 64-byte blocks, so every PUSH below crosses each boundary, and is cut
1165        /// short by the end of the code.
1166        #[test]
1167        fn matches_around_group_and_block_boundaries() {
1168            // STOP, JUMPDEST, the shortest PUSHes, the ones that fill a group with their
1169            // immediates or come a byte short or over, and the longest ones.
1170            const BYTES: [u8; 9] = [
1171                0x00,
1172                OP_JUMPDEST,
1173                OP_PUSH1,
1174                OP_PUSH1 + 1,
1175                OP_PUSH1 + 14,
1176                OP_PUSH1 + 15,
1177                OP_PUSH1 + 16,
1178                OP_PUSH32 - 1,
1179                OP_PUSH32,
1180            ];
1181            let lengths = [
1182                1, 15, 16, 17, 31, 32, 33, 63, 64, 65, 96, 127, 128, 129, 193,
1183            ];
1184            // A JUMPDEST filler exposes any immediate taken for an opcode or the other way
1185            // round. A PUSH1 filler makes where each block's walk starts depend on every
1186            // byte before it.
1187            for filler in [OP_JUMPDEST, OP_PUSH1] {
1188                for len in lengths {
1189                    for at in 0..len {
1190                        for first in BYTES {
1191                            for second in [None].into_iter().chain(BYTES.map(Some)) {
1192                                let mut code = vec![filler; len];
1193                                code[at] = first;
1194                                if let (Some(second), Some(byte)) = (second, code.get_mut(at + 1)) {
1195                                    *byte = second;
1196                                }
1197                                assert_same_jumpdests(&code);
1198                            }
1199                        }
1200                    }
1201                }
1202            }
1203        }
1204
1205        /// Random code up to 4 KiB long, over byte mixes that make the scan's branches
1206        /// unpredictable.
1207        #[test]
1208        fn matches_on_random_code() {
1209            // xorshift64, so every run checks the same inputs.
1210            let mut state = 0x9E37_79B9_7F4A_7C15_u64;
1211            let mut next = move || {
1212                state ^= state << 13;
1213                state ^= state >> 7;
1214                state ^= state << 17;
1215                state
1216            };
1217            let jumpdest_and_pushes: Vec<u8> = std::iter::once(OP_JUMPDEST)
1218                .chain(OP_PUSH1..=OP_PUSH32)
1219                .collect();
1220            let all_bytes: Vec<u8> = (0..=u8::MAX).collect();
1221            let mixes: [&[u8]; 4] = [
1222                &[0x00, OP_JUMPDEST, OP_PUSH1],
1223                &[OP_JUMPDEST, OP_PUSH1, OP_PUSH1 + 1],
1224                &jumpdest_and_pushes,
1225                &all_bytes,
1226            ];
1227            for mix in mixes {
1228                for _ in 0..200 {
1229                    let len = (next() % 4096) as usize;
1230                    let code: Vec<u8> = (0..len)
1231                        .map(|_| mix[(next() % mix.len() as u64) as usize])
1232                        .collect();
1233                    assert_same_jumpdests(&code);
1234                }
1235            }
1236        }
1237    }
1238
1239    #[test]
1240    fn a_reused_jump_destination_bitmap_keeps_nothing_of_the_initcode_before() {
1241        // Initcode of the same length, so the second reuses the first one's bitmap: a
1242        // JUMPDEST at offset 8, in the bitmap's second byte, then one only at offset 0, in
1243        // its first. The per-opcode scan the zkVM guests use writes only the first byte, so a
1244        // bitmap reused without zeroing keeps offset 8 valid there. The block scan on this
1245        // host rewrites every byte; the tests below catch a missing zeroing here too.
1246        let mut first_initcode = [0u8; 16];
1247        first_initcode[8] = 0x5b;
1248        let mut second_initcode = [0u8; 16];
1249        second_initcode[0] = 0x5b;
1250        let mut buffer = InitcodeBuffer::default();
1251        buffer.set(&first_initcode);
1252        let first = Code::from_initcode_buffer(buffer);
1253
1254        let mut buffer = first.into_initcode_buffer();
1255        buffer.set(&second_initcode);
1256        let second = Code::from_initcode_buffer(buffer);
1257
1258        assert!(second.is_valid_jumpdest(0));
1259        assert!(!second.is_valid_jumpdest(8));
1260    }
1261
1262    #[test]
1263    fn shorter_initcode_has_no_jump_destination_past_its_end_in_a_reused_bitmap() {
1264        // A JUMPDEST at every offset of the first initcode fills its 8-byte bitmap. The
1265        // second, two bytes long, reuses that bitmap but needs only its first byte, and
1266        // JUMP trusts the bitmap alone: a bit left past the end of the code would make a
1267        // jump there valid.
1268        let mut buffer = InitcodeBuffer::default();
1269        buffer.set(&[0x5b; 64]);
1270        let first = Code::from_initcode_buffer(buffer);
1271
1272        let mut buffer = first.into_initcode_buffer();
1273        buffer.set(&[0x00, 0x5b]); // STOP, JUMPDEST
1274        let second = Code::from_initcode_buffer(buffer);
1275
1276        assert!(second.is_valid_jumpdest(1));
1277        assert!(
1278            (0..64)
1279                .filter(|&offset| offset != 1)
1280                .all(|offset| !second.is_valid_jumpdest(offset))
1281        );
1282    }
1283
1284    #[test]
1285    fn a_longer_bitmap_is_reused_whole_and_zeroed() {
1286        let scratch: Arc<[u8]> = Arc::from(vec![0xff; 8]);
1287
1288        let bitmap = zeroed_bitmap_in(Some(scratch), 4);
1289
1290        // Kept at its length rather than replaced by a 4-byte one, and zeroed past the 4
1291        // bytes the code needs too.
1292        assert_eq!(&*bitmap, &[0; 8]);
1293    }
1294
1295    #[test]
1296    fn a_buffer_outgrown_by_initcode_at_least_doubles() {
1297        let mut buffer = InitcodeBuffer::default();
1298        buffer.set(&[0; 1000]);
1299
1300        buffer.set(&[0; 1001]);
1301
1302        // Replaced by one exactly the new size, initcode growing a byte each CREATE would
1303        // allocate a new buffer every time.
1304        assert!(buffer.capacity() >= 2 * (1000 + BYTECODE_PADDING));
1305    }
1306
1307    #[test]
1308    fn an_outgrown_bitmap_at_least_doubles() {
1309        let scratch: Arc<[u8]> = Arc::from(vec![0; 4]);
1310
1311        let bitmap = zeroed_bitmap_in(Some(scratch), 5);
1312
1313        assert!(bitmap.len() >= 8);
1314        assert!(bitmap.iter().all(|&byte| byte == 0));
1315    }
1316
1317    #[test]
1318    fn a_handed_back_initcode_buffer_holds_no_bytecode() {
1319        // A buffer is meant to be set before code is built from it again. Built from as it
1320        // comes back, it must give empty code, not the previous bytecode and its padding.
1321        let mut buffer = InitcodeBuffer::default();
1322        buffer.set(&[0x60, 0x01]); // PUSH1 1
1323        let code = Code::from_initcode_buffer(buffer);
1324
1325        let rebuilt = Code::from_initcode_buffer(code.into_initcode_buffer());
1326
1327        assert!(rebuilt.is_empty());
1328    }
1329}