sp1_core_executor/
program.rs

1//! Programs that can be executed by the SP1 zkVM.
2
3use std::{fs::File, io::Read, str::FromStr};
4
5use crate::{
6    disassembler::{transpile, Elf},
7    instruction::Instruction,
8    RiscvAirId,
9};
10use hashbrown::HashMap;
11use p3_field::{AbstractExtensionField, Field, PrimeField32};
12use p3_maybe_rayon::prelude::{IntoParallelIterator, ParallelBridge, ParallelIterator};
13use serde::{Deserialize, Serialize};
14use sp1_stark::{
15    air::{MachineAir, MachineProgram},
16    septic_curve::{SepticCurve, SepticCurveComplete},
17    septic_digest::SepticDigest,
18    septic_extension::SepticExtension,
19    shape::Shape,
20    InteractionKind,
21};
22
23/// A program that can be executed by the SP1 zkVM.
24///
25/// Contains a series of instructions along with the initial memory image. It also contains the
26/// start address and base address of the program.
27#[derive(Debug, Clone, Default, Serialize, Deserialize)]
28pub struct Program {
29    /// The instructions of the program.
30    pub instructions: Vec<Instruction>,
31    /// The start address of the program.
32    pub pc_start: u32,
33    /// The base address of the program.
34    pub pc_base: u32,
35    /// The initial memory image, useful for global constants.
36    pub memory_image: HashMap<u32, u32>,
37    /// The shape for the preprocessed tables.
38    pub preprocessed_shape: Option<Shape<RiscvAirId>>,
39}
40
41impl Program {
42    /// Create a new [Program].
43    #[must_use]
44    pub fn new(instructions: Vec<Instruction>, pc_start: u32, pc_base: u32) -> Self {
45        Self {
46            instructions,
47            pc_start,
48            pc_base,
49            memory_image: HashMap::new(),
50            preprocessed_shape: None,
51        }
52    }
53
54    /// Disassemble a RV32IM ELF to a program that be executed by the VM.
55    ///
56    /// # Errors
57    ///
58    /// This function may return an error if the ELF is not valid.
59    pub fn from(input: &[u8]) -> eyre::Result<Self> {
60        // Decode the bytes as an ELF.
61        let elf = Elf::decode(input)?;
62
63        // Transpile the RV32IM instructions.
64        let instructions = transpile(&elf.instructions);
65
66        // Return the program.
67        Ok(Program {
68            instructions,
69            pc_start: elf.pc_start,
70            pc_base: elf.pc_base,
71            memory_image: elf.memory_image,
72            preprocessed_shape: None,
73        })
74    }
75
76    /// Disassemble a RV32IM ELF to a program that be executed by the VM from a file path.
77    ///
78    /// # Errors
79    ///
80    /// This function will return an error if the file cannot be opened or read.
81    pub fn from_elf(path: &str) -> eyre::Result<Self> {
82        let mut elf_code = Vec::new();
83        File::open(path)?.read_to_end(&mut elf_code)?;
84        Program::from(&elf_code)
85    }
86
87    /// Custom logic for padding the trace to a power of two according to the proof shape.
88    pub fn fixed_log2_rows<F: Field, A: MachineAir<F>>(&self, air: &A) -> Option<usize> {
89        let id = RiscvAirId::from_str(&air.name()).unwrap();
90        self.preprocessed_shape.as_ref().map(|shape| {
91            shape
92                .log2_height(&id)
93                .unwrap_or_else(|| panic!("Chip {} not found in specified shape", air.name()))
94        })
95    }
96
97    #[must_use]
98    /// Fetch the instruction at the given program counter.
99    pub fn fetch(&self, pc: u32) -> &Instruction {
100        let idx = ((pc - self.pc_base) / 4) as usize;
101        &self.instructions[idx]
102    }
103}
104
105impl<F: PrimeField32> MachineProgram<F> for Program {
106    fn pc_start(&self) -> F {
107        F::from_canonical_u32(self.pc_start)
108    }
109
110    fn initial_global_cumulative_sum(&self) -> SepticDigest<F> {
111        let mut digests: Vec<SepticCurveComplete<F>> = self
112            .memory_image
113            .iter()
114            .par_bridge()
115            .map(|(&addr, &word)| {
116                let values = [
117                    (InteractionKind::Memory as u32) << 16,
118                    0,
119                    addr,
120                    word & 255,
121                    (word >> 8) & 255,
122                    (word >> 16) & 255,
123                    (word >> 24) & 255,
124                ];
125                let x_start =
126                    SepticExtension::<F>::from_base_fn(|i| F::from_canonical_u32(values[i]));
127                let (point, _, _, _) = SepticCurve::<F>::lift_x(x_start);
128                SepticCurveComplete::Affine(point.neg())
129            })
130            .collect();
131        digests.push(SepticCurveComplete::Affine(SepticDigest::<F>::zero().0));
132        SepticDigest(
133            digests.into_par_iter().reduce(|| SepticCurveComplete::Infinity, |a, b| a + b).point(),
134        )
135    }
136}