ab_contract_file/
lib.rs

1//! Utilities for working with contract files
2
3#![feature(maybe_uninit_as_bytes, maybe_uninit_fill, trusted_len)]
4#![no_std]
5
6use ab_contracts_common::metadata::decode::{
7    MetadataDecoder, MetadataDecodingError, MetadataItem, MethodMetadataItem,
8    MethodsMetadataDecoder,
9};
10use ab_io_type::trivial_type::TrivialType;
11use ab_riscv_primitives::instruction::GenericBaseInstruction;
12use ab_riscv_primitives::instruction::b_64_ext::BZbc64ExtInstruction;
13use ab_riscv_primitives::instruction::m_64_ext::M64ExtInstruction;
14use ab_riscv_primitives::instruction::rv64::Rv64Instruction;
15use ab_riscv_primitives::instruction::tuples::Tuple3Instruction;
16use ab_riscv_primitives::registers::EReg;
17use core::iter;
18use core::iter::TrustedLen;
19use core::mem::MaybeUninit;
20use replace_with::replace_with_or_abort;
21use tracing::{debug, trace};
22
23/// Magic bytes at the beginning of the file
24pub const CONTRACT_FILE_MAGIC: [u8; 4] = *b"ABC0";
25/// A register type used by contracts
26pub type Register = EReg<u64>;
27/// An instruction type used by contracts
28pub type Instruction = Tuple3Instruction<
29    M64ExtInstruction<Register>,
30    BZbc64ExtInstruction<Register>,
31    Rv64Instruction<Register>,
32>;
33
34/// Header of the contract file
35#[derive(Debug, Clone, Copy, PartialEq, Eq, TrivialType)]
36#[repr(C)]
37pub struct ContractFileHeader {
38    /// Always [`CONTRACT_FILE_MAGIC`]
39    pub magic: [u8; 4],
40    /// Size of the read-only section in bytes as stored in the file
41    pub read_only_section_file_size: u32,
42    /// Size of the read-only section in bytes as will be written to memory during execution.
43    ///
44    /// If larger than `read_only_section_file_size`, then zeroed padding needs to be added.
45    pub read_only_section_memory_size: u32,
46    /// Offset of the metadata section in bytes relative to the start of the file
47    pub metadata_offset: u32,
48    /// Size of the metadata section in bytes
49    pub metadata_size: u16,
50    /// Number of methods in the contract
51    pub num_methods: u16,
52    /// Host call function offset in bytes relative to the start of the file.
53    ///
54    /// `0` means no host call.
55    pub host_call_fn_offset: u32,
56}
57
58/// Metadata about each method of the contract that can be called from the outside
59#[derive(Debug, Clone, Copy, PartialEq, Eq, TrivialType)]
60#[repr(C)]
61pub struct ContractFileMethodMetadata {
62    /// Offset of the method code in bytes relative to the start of the file
63    pub offset: u32,
64    /// Size of the method code in bytes
65    pub size: u32,
66}
67
68#[derive(Debug, Copy, Clone)]
69pub struct ContractFileMethod<'a> {
70    /// Address of the method in the contract memory
71    pub address: u32,
72    /// Method metadata item
73    pub method_metadata_item: MethodMetadataItem<'a>,
74    /// Method metadata bytes.
75    ///
76    /// Can be used to compute [`MethodFingerprint`].
77    ///
78    /// [`MethodFingerprint`]: ab_contracts_common::method::MethodFingerprint
79    pub method_metadata_bytes: &'a [u8],
80}
81
82/// Error for [`ContractFile::parse()`]
83#[derive(Debug, thiserror::Error)]
84pub enum ContractFileParseError {
85    /// The file is too large, must fit into `u32`
86    #[error("The file is too large, must fit into `u32`: {file_size} bytes")]
87    FileTooLarge {
88        /// Actual file size
89        file_size: usize,
90    },
91    /// The file does not have a header (not enough bytes)
92    #[error("The file does not have a header (not enough bytes)")]
93    NoHeader,
94    /// The magic bytes in the header are incorrect
95    #[error("The magic bytes in the header are incorrect")]
96    WrongMagicBytes,
97    /// The metadata section is out of bounds of the file
98    #[error(
99        "The metadata section is out of bounds of the file: offset {offset}, size {size}, file \
100        size {file_size}"
101    )]
102    MetadataOutOfRange {
103        /// Offset of the metadata section in bytes relative to the start of the file
104        offset: u32,
105        /// Size of the metadata section in bytes
106        size: u16,
107        /// Size of the file in bytes
108        file_size: u32,
109    },
110    /// Failed to decode metadata item
111    #[error("Failed to decode metadata item")]
112    MetadataDecoding,
113    /// The file is too small
114    #[error(
115        "The file is too small: num_methods {num_methods}, read_only_section_size \
116        {read_only_section_size}, file_size {file_size}"
117    )]
118    FileTooSmall {
119        /// Number of methods in the contract
120        num_methods: u16,
121        /// Size of the read-only section in bytes as stored in the file
122        read_only_section_size: u32,
123        /// Size of the file in bytes
124        file_size: u32,
125    },
126    /// Method offset is out of bounds of the file
127    #[error(
128        "Method offset is out of bounds of the file: offset {offset}, code section \
129        offset {code_section_offset} file_size {file_size}"
130    )]
131    MethodOutOfRange {
132        /// Offset of the method in bytes relative to the start of the file
133        offset: u32,
134        /// Offset of the code section in bytes relative to the start of the file
135        code_section_offset: u32,
136        /// Size of the file in bytes
137        file_size: u32,
138    },
139    /// The host call function offset is out of bounds of the file
140    #[error(
141        "The host call function offset is out of bounds of the file: offset {offset}, code section \
142        offset {code_section_offset} file_size {file_size}"
143    )]
144    HostCallFnOutOfRange {
145        /// Offset of the host call function in bytes relative to the start of the file
146        offset: u32,
147        /// Offset of the code section in bytes relative to the start of the file
148        code_section_offset: u32,
149        /// Size of the file in bytes
150        file_size: u32,
151    },
152    /// Host call function doesn't have auipc + jalr tailcall pattern
153    #[error("The host call function doesn't have auipc + jalr tailcall pattern: {first} {second}")]
154    InvalidHostCallFnPattern {
155        /// First instruction of the host call function
156        first: Instruction,
157        /// Second instruction of the host call function
158        second: Instruction,
159    },
160    /// The read-only section file size is larger than the memory size
161    #[error(
162        "The read-only section file size is larger than the memory size: file_size {file_size}, \
163        memory_size {memory_size}"
164    )]
165    InvalidReadOnlySizes {
166        /// Size of the read-only section in bytes as stored in the file
167        file_size: u32,
168        /// Size of the read-only section in bytes as will be written to memory during execution
169        memory_size: u32,
170    },
171    /// There are not enough methods in the header to match the number of methods in the actual
172    /// metadata
173    #[error(
174        "There are not enough methods in the header to match the number of methods in the actual \
175        metadata: header_num_methods {header_num_methods}, metadata_method_index \
176        {metadata_method_index}"
177    )]
178    InsufficientHeaderMethods {
179        /// Number of methods in the header
180        header_num_methods: u16,
181        /// Index of the method in the actual metadata that is missing from the header
182        metadata_method_index: u16,
183    },
184    /// The number of methods in the header does not match the number of methods in the actual
185    /// metadata
186    #[error(
187        "The number of methods in the header {header_num_methods} does not match the number of \
188        methods in the actual metadata {metadata_num_methods}"
189    )]
190    MetadataNumMethodsMismatch {
191        /// Number of methods in the header
192        header_num_methods: u16,
193        /// Number of methods in the actual metadata
194        metadata_num_methods: u16,
195    },
196    /// Unexpected instruction encountered while parsing the code section
197    #[error("Unexpected instruction encountered while parsing the code section: {instruction}")]
198    UnexpectedInstruction {
199        /// Instruction
200        instruction: Instruction,
201    },
202    /// Unexpected trailing code bytes encountered while parsing the code section
203    #[error(
204        "Unexpected trailing code bytes encountered while parsing the code section: {num_bytes} \
205        trailing bytes"
206    )]
207    UnexpectedTrailingCodeBytes {
208        /// Number of trailing bytes encountered
209        num_bytes: usize,
210    },
211}
212
213impl From<MetadataDecodingError<'_>> for ContractFileParseError {
214    fn from(error: MetadataDecodingError<'_>) -> Self {
215        debug!(?error, "Failed to decode metadata item");
216        Self::MetadataDecoding
217    }
218}
219
220#[derive(Debug)]
221pub struct ContractFile<'a> {
222    read_only_section_file_size: u32,
223    read_only_section_memory_size: u32,
224    num_methods: u16,
225    bytes: &'a [u8],
226}
227
228impl<'a> ContractFile<'a> {
229    /// Parse file bytes and verify that internal invariants are valid.
230    ///
231    /// `contract_method` argument is an optional callback called for each method in the contract
232    /// file with its method address in the contract memory, metadata item, and corresponding
233    /// metadata bytes. This can be used to collect available methods during parsing and avoid extra
234    /// iteration later using [`Self::iterate_methods()`] to compute [`MethodFingerprint`], etc.
235    ///
236    /// [`MethodFingerprint`]: ab_contracts_common::method::MethodFingerprint
237    pub fn parse<CM>(
238        file_bytes: &'a [u8],
239        mut contract_method: CM,
240    ) -> Result<Self, ContractFileParseError>
241    where
242        CM: FnMut(ContractFileMethod<'a>) -> Result<(), ContractFileParseError>,
243    {
244        let file_size = u32::try_from(file_bytes.len()).map_err(|_error| {
245            ContractFileParseError::FileTooLarge {
246                file_size: file_bytes.len(),
247            }
248        })?;
249        let (header_bytes, after_header_bytes) = file_bytes
250            .split_at_checked(size_of::<ContractFileHeader>())
251            .ok_or(ContractFileParseError::NoHeader)?;
252        // SAFETY: Size is correct, content is checked below
253        let header = unsafe { ContractFileHeader::read_unaligned_unchecked(header_bytes) };
254
255        if header.magic != CONTRACT_FILE_MAGIC {
256            return Err(ContractFileParseError::WrongMagicBytes);
257        }
258
259        if header.read_only_section_file_size > header.read_only_section_memory_size {
260            return Err(ContractFileParseError::InvalidReadOnlySizes {
261                file_size: header.read_only_section_file_size,
262                memory_size: header.read_only_section_memory_size,
263            });
264        }
265
266        let metadata_bytes = file_bytes
267            .get(header.metadata_offset as usize..)
268            .ok_or(ContractFileParseError::MetadataOutOfRange {
269                offset: header.metadata_offset,
270                size: header.metadata_size,
271                file_size,
272            })?
273            .get(..header.metadata_size as usize)
274            .ok_or(ContractFileParseError::MetadataOutOfRange {
275                offset: header.metadata_offset,
276                size: header.metadata_size,
277                file_size,
278            })?;
279
280        let read_only_padding_size =
281            header.read_only_section_memory_size - header.read_only_section_file_size;
282        let read_only_section_offset = ContractFileHeader::SIZE
283            + u32::from(header.num_methods) * ContractFileMethodMetadata::SIZE;
284        let code_section_offset =
285            read_only_section_offset.saturating_add(header.read_only_section_file_size);
286
287        {
288            let mut contract_file_methods_metadata_iter = {
289                let mut file_contract_metadata_bytes = after_header_bytes;
290
291                (0..header.num_methods).map(move |_| {
292                    let contract_file_method_metadata_bytes = file_contract_metadata_bytes
293                        .split_off(..size_of::<ContractFileMethodMetadata>())
294                        .ok_or(ContractFileParseError::FileTooSmall {
295                            num_methods: header.num_methods,
296                            read_only_section_size: header.read_only_section_file_size,
297                            file_size,
298                        })?;
299                    // SAFETY: The number of bytes is correct, content is checked below
300                    let contract_file_method_metadata = unsafe {
301                        ContractFileMethodMetadata::read_unaligned_unchecked(
302                            contract_file_method_metadata_bytes,
303                        )
304                    };
305
306                    if (contract_file_method_metadata.offset + contract_file_method_metadata.size)
307                        > file_size
308                    {
309                        return Err(ContractFileParseError::FileTooSmall {
310                            num_methods: header.num_methods,
311                            read_only_section_size: header.read_only_section_file_size,
312                            file_size,
313                        });
314                    }
315
316                    if contract_file_method_metadata.offset < code_section_offset {
317                        return Err(ContractFileParseError::MethodOutOfRange {
318                            offset: contract_file_method_metadata.offset,
319                            code_section_offset,
320                            file_size,
321                        });
322                    }
323
324                    Ok(contract_file_method_metadata)
325                })
326            };
327
328            let mut metadata_num_methods = 0;
329            let mut remaining_metadata_bytes = metadata_bytes;
330            let mut metadata_decoder = MetadataDecoder::new(metadata_bytes);
331
332            while let Some(maybe_metadata_item) = metadata_decoder.decode_next() {
333                let metadata_item = maybe_metadata_item?;
334                trace!(?metadata_item, "Decoded metadata item");
335
336                let mut methods_metadata_decoder = metadata_item.into_decoder();
337                loop {
338                    // This is used instead of `while let Some(method_metadata_decoder)` because the
339                    // compiler is not smart enough to understand where `method_metadata_decoder` is
340                    // dropped
341                    let Some(method_metadata_decoder) = methods_metadata_decoder.decode_next()
342                    else {
343                        break;
344                    };
345
346                    let before_remaining_bytes = method_metadata_decoder.remaining_metadata_bytes();
347                    let (_, method_metadata_item) = method_metadata_decoder.decode_next()?;
348
349                    trace!(?method_metadata_item, "Decoded method metadata item");
350                    metadata_num_methods += 1;
351
352                    let method_metadata_bytes = remaining_metadata_bytes
353                        .split_off(
354                            ..before_remaining_bytes
355                                - methods_metadata_decoder.remaining_metadata_bytes(),
356                        )
357                        .ok_or(MetadataDecodingError::NotEnoughMetadata)?;
358
359                    let contract_file_method_metadata = contract_file_methods_metadata_iter
360                        .next()
361                        .ok_or(ContractFileParseError::InsufficientHeaderMethods {
362                            header_num_methods: header.num_methods,
363                            metadata_method_index: metadata_num_methods - 1,
364                        })??;
365                    contract_method(ContractFileMethod {
366                        address: contract_file_method_metadata.offset - read_only_section_offset
367                            + read_only_padding_size,
368                        method_metadata_item,
369                        method_metadata_bytes,
370                    })?;
371                }
372            }
373
374            if metadata_num_methods != header.num_methods {
375                return Err(ContractFileParseError::MetadataNumMethodsMismatch {
376                    header_num_methods: header.num_methods,
377                    metadata_num_methods,
378                });
379            }
380        }
381
382        if code_section_offset >= file_size {
383            return Err(ContractFileParseError::FileTooSmall {
384                num_methods: header.num_methods,
385                read_only_section_size: header.read_only_section_file_size,
386                file_size,
387            });
388        }
389
390        if header.host_call_fn_offset != 0
391            && (header.host_call_fn_offset >= file_size
392                || header.host_call_fn_offset < code_section_offset)
393        {
394            return Err(ContractFileParseError::HostCallFnOutOfRange {
395                offset: header.host_call_fn_offset,
396                code_section_offset,
397                file_size,
398            });
399        }
400
401        if header.host_call_fn_offset != 0 {
402            let instructions_bytes = file_bytes
403                .get(header.host_call_fn_offset as usize..)
404                .ok_or(ContractFileParseError::HostCallFnOutOfRange {
405                    offset: header.host_call_fn_offset,
406                    code_section_offset,
407                    file_size,
408                })?
409                .get(..size_of::<[u32; 2]>())
410                .ok_or(ContractFileParseError::HostCallFnOutOfRange {
411                    offset: header.host_call_fn_offset,
412                    code_section_offset,
413                    file_size,
414                })?;
415
416            let first_instruction = u32::from_le_bytes([
417                instructions_bytes[0],
418                instructions_bytes[1],
419                instructions_bytes[2],
420                instructions_bytes[3],
421            ]);
422            let second_instruction = u32::from_le_bytes([
423                instructions_bytes[4],
424                instructions_bytes[5],
425                instructions_bytes[6],
426                instructions_bytes[7],
427            ]);
428
429            let first = Instruction::decode(first_instruction);
430            let second = Instruction::decode(second_instruction);
431
432            // TODO: Should it be canonicalized to a fixed immediate and temporary after conversion
433            //  from ELF?
434            // Checks if two consecutive instructions are:
435            //   auipc x?, 0x?
436            //   jalr  x0, offset(x?)
437            let matches_expected_pattern = if let (
438                Instruction::Base(Rv64Instruction::Auipc {
439                    rd: auipc_rd,
440                    imm: _,
441                }),
442                Instruction::Base(Rv64Instruction::Jalr {
443                    rd: jalr_rd,
444                    rs1: jalr_rs1,
445                    imm: _,
446                }),
447            ) = (first, second)
448            {
449                auipc_rd == jalr_rs1 && jalr_rd == Register::Zero
450            } else {
451                false
452            };
453
454            if !matches_expected_pattern {
455                return Err(ContractFileParseError::InvalidHostCallFnPattern { first, second });
456            }
457        }
458
459        // Ensure code only consists of expected instructions
460        {
461            let mut remaining_code_file_bytes = &file_bytes[code_section_offset as usize..];
462            while let Some(instruction_bytes) =
463                remaining_code_file_bytes.split_off(..size_of::<u32>())
464            {
465                let instruction = u32::from_le_bytes([
466                    instruction_bytes[0],
467                    instruction_bytes[1],
468                    instruction_bytes[2],
469                    instruction_bytes[3],
470                ]);
471                let instruction = Instruction::decode(instruction);
472                match instruction {
473                    Instruction::A(_)
474                    | Instruction::B(_)
475                    | Instruction::Base(
476                        Rv64Instruction::Add { .. }
477                        | Rv64Instruction::Sub { .. }
478                        | Rv64Instruction::Sll { .. }
479                        | Rv64Instruction::Slt { .. }
480                        | Rv64Instruction::Sltu { .. }
481                        | Rv64Instruction::Xor { .. }
482                        | Rv64Instruction::Srl { .. }
483                        | Rv64Instruction::Sra { .. }
484                        | Rv64Instruction::Or { .. }
485                        | Rv64Instruction::And { .. }
486                        | Rv64Instruction::Addw { .. }
487                        | Rv64Instruction::Subw { .. }
488                        | Rv64Instruction::Sllw { .. }
489                        | Rv64Instruction::Srlw { .. }
490                        | Rv64Instruction::Sraw { .. }
491                        | Rv64Instruction::Addi { .. }
492                        | Rv64Instruction::Slti { .. }
493                        | Rv64Instruction::Sltiu { .. }
494                        | Rv64Instruction::Xori { .. }
495                        | Rv64Instruction::Ori { .. }
496                        | Rv64Instruction::Andi { .. }
497                        | Rv64Instruction::Slli { .. }
498                        | Rv64Instruction::Srli { .. }
499                        | Rv64Instruction::Srai { .. }
500                        | Rv64Instruction::Addiw { .. }
501                        | Rv64Instruction::Slliw { .. }
502                        | Rv64Instruction::Srliw { .. }
503                        | Rv64Instruction::Sraiw { .. }
504                        | Rv64Instruction::Lb { .. }
505                        | Rv64Instruction::Lh { .. }
506                        | Rv64Instruction::Lw { .. }
507                        | Rv64Instruction::Ld { .. }
508                        | Rv64Instruction::Lbu { .. }
509                        | Rv64Instruction::Lhu { .. }
510                        | Rv64Instruction::Lwu { .. }
511                        | Rv64Instruction::Jalr { .. }
512                        | Rv64Instruction::Sb { .. }
513                        | Rv64Instruction::Sh { .. }
514                        | Rv64Instruction::Sw { .. }
515                        | Rv64Instruction::Sd { .. }
516                        | Rv64Instruction::Beq { .. }
517                        | Rv64Instruction::Bne { .. }
518                        | Rv64Instruction::Blt { .. }
519                        | Rv64Instruction::Bge { .. }
520                        | Rv64Instruction::Bltu { .. }
521                        | Rv64Instruction::Bgeu { .. }
522                        | Rv64Instruction::Lui { .. }
523                        | Rv64Instruction::Auipc { .. }
524                        | Rv64Instruction::Jal { .. }
525                        | Rv64Instruction::Ebreak
526                        | Rv64Instruction::Unimp,
527                    ) => {
528                        // Expected instruction
529                    }
530                    Instruction::Base(
531                        Rv64Instruction::Fence { .. }
532                        | Rv64Instruction::Ecall
533                        | Rv64Instruction::Invalid(_),
534                    ) => {
535                        return Err(ContractFileParseError::UnexpectedInstruction { instruction });
536                    }
537                }
538            }
539
540            if !remaining_code_file_bytes.is_empty() {
541                return Err(ContractFileParseError::UnexpectedTrailingCodeBytes {
542                    num_bytes: remaining_code_file_bytes.len(),
543                });
544            }
545        }
546
547        Ok(Self {
548            read_only_section_file_size: header.read_only_section_file_size,
549            read_only_section_memory_size: header.read_only_section_memory_size,
550            num_methods: header.num_methods,
551            bytes: file_bytes,
552        })
553    }
554
555    /// Similar to [`ContractFile::parse()`] but does not verify internal invariants and assumes the
556    /// input is valid.
557    ///
558    /// This method is more efficient and does no checks that [`ContractFile::parse()`] does.
559    ///
560    /// # Safety
561    /// Must be a valid input, for example, previously verified using [`ContractFile::parse()`].
562    pub unsafe fn parse_unchecked(file_bytes: &'a [u8]) -> Self {
563        // SAFETY: Unchecked method assumed input is correct
564        let header = unsafe { ContractFileHeader::read_unaligned_unchecked(file_bytes) };
565
566        Self {
567            read_only_section_file_size: header.read_only_section_file_size,
568            read_only_section_memory_size: header.read_only_section_memory_size,
569            num_methods: header.num_methods,
570            bytes: file_bytes,
571        }
572    }
573
574    /// Get file header
575    #[inline(always)]
576    pub fn header(&self) -> ContractFileHeader {
577        // SAFETY: Protected internal invariant checked in constructor
578        unsafe { ContractFileHeader::read_unaligned_unchecked(self.bytes) }
579    }
580
581    /// Metadata stored in the file
582    #[inline]
583    pub fn metadata_bytes(&self) -> &[u8] {
584        let header = self.header();
585        // SAFETY: Protected internal invariant checked in constructor
586        unsafe {
587            self.bytes
588                .get_unchecked(header.metadata_offset as usize..)
589                .get_unchecked(..header.metadata_size as usize)
590        }
591    }
592
593    /// Memory allocation required for the contract
594    #[inline]
595    pub fn contract_memory_size(&self) -> u32 {
596        let read_only_section_offset = ContractFileHeader::SIZE
597            + u32::from(self.num_methods) * ContractFileMethodMetadata::SIZE;
598        let read_only_padding_size =
599            self.read_only_section_memory_size - self.read_only_section_file_size;
600        self.bytes.len() as u32 - read_only_section_offset + read_only_padding_size
601    }
602
603    /// Initialize contract memory with file contents.
604    ///
605    /// Use [`Self::contract_memory_size()`] to identify the exact necessary amount of memory.
606    #[must_use = "Must check that contract memory was large enough"]
607    pub fn initialize_contract_memory(&self, mut contract_memory: &mut [MaybeUninit<u8>]) -> bool {
608        let contract_memory_input_size = contract_memory.len();
609        let read_only_section_offset = ContractFileHeader::SIZE
610            + u32::from(self.num_methods) * ContractFileMethodMetadata::SIZE;
611        let read_only_padding_size =
612            self.read_only_section_memory_size - self.read_only_section_file_size;
613
614        // SAFETY: Protected internal invariant checked in constructor
615        let source_bytes = unsafe {
616            self.bytes
617                .get_unchecked(read_only_section_offset as usize..)
618        };
619
620        // Simple case: memory exactly matches the file-backed sections
621        if contract_memory.len() == source_bytes.len() {
622            contract_memory.write_copy_of_slice(source_bytes);
623            return true;
624        }
625
626        let Some(read_only_file_target_bytes) =
627            contract_memory.split_off_mut(..self.read_only_section_file_size as usize)
628        else {
629            trace!(
630                %contract_memory_input_size,
631                contract_memory_size = %self.contract_memory_size(),
632                read_only_section_file_size = self.read_only_section_file_size,
633                "Not enough bytes to write read-only section from the file"
634            );
635
636            return false;
637        };
638
639        // SAFETY: Protected internal invariant checked in constructor
640        let (read_only_file_source_bytes, code_source_bytes) =
641            unsafe { source_bytes.split_at_unchecked(self.read_only_section_file_size as usize) };
642        // Write read-only data
643        read_only_file_target_bytes.write_copy_of_slice(read_only_file_source_bytes);
644
645        let Some(read_only_padding_bytes) =
646            contract_memory.split_off_mut(..read_only_padding_size as usize)
647        else {
648            trace!(
649                %contract_memory_input_size,
650                contract_memory_size = %self.contract_memory_size(),
651                read_only_section_file_size = self.read_only_section_file_size,
652                read_only_section_memory_size = self.read_only_section_memory_size,
653                %read_only_padding_size,
654                "Not enough bytes to write read-only padding section"
655            );
656
657            return false;
658        };
659
660        // Write read-only padding
661        read_only_padding_bytes.write_filled(0);
662
663        if code_source_bytes.len() != contract_memory.len() {
664            trace!(
665                %contract_memory_input_size,
666                contract_memory_size = %self.contract_memory_size(),
667                read_only_section_file_size = self.read_only_section_file_size,
668                read_only_section_memory_size = self.read_only_section_memory_size,
669                %read_only_padding_size,
670                code_size = %code_source_bytes.len(),
671                "Not enough bytes to write code section from the file"
672            );
673
674            return false;
675        }
676
677        contract_memory.write_copy_of_slice(code_source_bytes);
678
679        true
680    }
681
682    /// Get the complete code section with instructions
683    pub fn get_code(&self) -> &[u8] {
684        let read_only_section_offset = ContractFileHeader::SIZE
685            + u32::from(self.num_methods) * ContractFileMethodMetadata::SIZE;
686
687        // SAFETY: Protected internal invariant checked in constructor
688        let source_bytes = unsafe {
689            self.bytes
690                .get_unchecked(read_only_section_offset as usize..)
691        };
692
693        // SAFETY: Protected internal invariant checked in constructor
694        let (_read_only_file_source_bytes, code_source_bytes) =
695            unsafe { source_bytes.split_at_unchecked(self.read_only_section_file_size as usize) };
696
697        code_source_bytes
698    }
699
700    /// Iterate over all methods in the contract
701    pub fn iterate_methods(
702        &self,
703    ) -> impl ExactSizeIterator<Item = ContractFileMethod<'_>> + TrustedLen {
704        let metadata_bytes = self.metadata_bytes();
705
706        #[ouroboros::self_referencing]
707        struct MethodsMetadataIterState<'metadata> {
708            metadata_decoder: MetadataDecoder<'metadata>,
709            #[borrows(mut metadata_decoder)]
710            #[covariant]
711            methods_metadata_decoder: Option<MethodsMetadataDecoder<'this, 'metadata>>,
712        }
713
714        let metadata_decoder = MetadataDecoder::new(metadata_bytes);
715
716        let mut methods_metadata_state =
717            MethodsMetadataIterState::new(metadata_decoder, |metadata_decoder| {
718                metadata_decoder
719                    .decode_next()
720                    .and_then(Result::ok)
721                    .map(MetadataItem::into_decoder)
722            });
723
724        let mut metadata_methods_iter = iter::from_fn(move || {
725            loop {
726                let maybe_next_item = methods_metadata_state.with_methods_metadata_decoder_mut(
727                    |maybe_methods_metadata_decoder| {
728                        let methods_metadata_decoder = maybe_methods_metadata_decoder.as_mut()?;
729                        let method_metadata_decoder = methods_metadata_decoder.decode_next()?;
730
731                        let before_remaining_bytes =
732                            method_metadata_decoder.remaining_metadata_bytes();
733
734                        let (_, method_metadata_item) = method_metadata_decoder
735                            .decode_next()
736                            .expect("Input is valid according to function contract; qed");
737
738                        // SAFETY: Protected internal invariant checked in constructor
739                        let method_metadata_bytes = unsafe {
740                            metadata_bytes
741                                .get_unchecked(metadata_bytes.len() - before_remaining_bytes..)
742                                .get_unchecked(
743                                    ..before_remaining_bytes
744                                        - methods_metadata_decoder.remaining_metadata_bytes(),
745                                )
746                        };
747
748                        Some((method_metadata_item, method_metadata_bytes))
749                    },
750                );
751
752                if let Some(next_item) = maybe_next_item {
753                    return Some(next_item);
754                }
755
756                // Process methods of the next contract/trait
757                replace_with_or_abort(&mut methods_metadata_state, |methods_metadata_state| {
758                    let metadata_decoder = methods_metadata_state.into_heads().metadata_decoder;
759                    MethodsMetadataIterState::new(metadata_decoder, |metadata_decoder| {
760                        metadata_decoder
761                            .decode_next()
762                            .and_then(Result::ok)
763                            .map(MetadataItem::into_decoder)
764                    })
765                });
766
767                if methods_metadata_state
768                    .borrow_methods_metadata_decoder()
769                    .is_none()
770                {
771                    return None;
772                }
773            }
774        });
775
776        let read_only_padding_size =
777            self.read_only_section_memory_size - self.read_only_section_file_size;
778        // SAFETY: Protected internal invariant checked in constructor
779        let contract_file_methods_metadata_bytes =
780            unsafe { self.bytes.get_unchecked(size_of::<ContractFileHeader>()..) };
781
782        (0..self.num_methods).map(move |method_index| {
783            // SAFETY: Protected internal invariant checked in constructor
784            let contract_file_method_metadata_bytes = unsafe {
785                contract_file_methods_metadata_bytes
786                    .get_unchecked(
787                        method_index as usize * size_of::<ContractFileMethodMetadata>()..,
788                    )
789                    .get_unchecked(..size_of::<ContractFileMethodMetadata>())
790            };
791            // SAFETY: Protected internal invariant checked in constructor
792            let contract_file_method_metadata = unsafe {
793                ContractFileMethodMetadata::read_unaligned_unchecked(
794                    contract_file_method_metadata_bytes,
795                )
796            };
797
798            let (method_metadata_item, method_metadata_bytes) = metadata_methods_iter
799                .next()
800                .expect("Protected internal invariant checked in constructor; qed");
801
802            ContractFileMethod {
803                address: contract_file_method_metadata.offset + read_only_padding_size,
804                method_metadata_item,
805                method_metadata_bytes,
806            }
807        })
808    }
809}