Marvin Jones a39d545775 refactor(lee): split large modules into directories and extract tests
Split state.rs, program.rs, circuit.rs, validated_state_diff.rs,
merkle_tree, and core/program.rs into module directories with separate
test files. State tests are further split into themed files (genesis,
authenticated_transfer, circuit, claiming, etc.). Extract
authenticate_public_transaction_signers helper in validated_state_diff
to remove duplicated authentication logic.
2026-07-01 10:52:47 -04:00

115 lines
3.5 KiB
Rust

use std::borrow::Cow;
use borsh::{BorshDeserialize, BorshSerialize};
use lee_core::{
account::AccountWithMetadata,
program::{InstructionData, ProgramId, ProgramOutput},
};
use risc0_zkvm::{ExecutorEnv, ExecutorEnvBuilder, default_executor, serde::to_vec};
use serde::Serialize;
use crate::error::LeeError;
/// Maximum number of cycles for a public execution.
/// TODO: Make this variable when fees are implemented.
const MAX_NUM_CYCLES_PUBLIC_EXECUTION: u64 = 1024 * 1024 * 32; // 32M cycles
#[derive(Clone, Debug, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
pub struct Program {
id: ProgramId,
elf: Cow<'static, [u8]>,
}
impl Program {
pub fn new(elf: Cow<'static, [u8]>) -> Result<Self, LeeError> {
let binary = risc0_binfmt::ProgramBinary::decode(elf.as_ref())
.map_err(LeeError::InvalidProgramBytecode)?;
let id = binary
.compute_image_id()
.map_err(LeeError::InvalidProgramBytecode)?
.into();
Ok(Self { id, elf })
}
#[must_use]
pub const fn new_unchecked(id: ProgramId, elf: Cow<'static, [u8]>) -> Self {
Self { id, elf }
}
#[must_use]
pub const fn id(&self) -> ProgramId {
self.id
}
#[must_use]
pub fn elf(&self) -> &[u8] {
&self.elf
}
pub fn serialize_instruction<T: Serialize>(
instruction: T,
) -> Result<InstructionData, LeeError> {
to_vec(&instruction).map_err(|e| LeeError::InstructionSerializationError(e.to_string()))
}
pub(crate) fn execute(
&self,
caller_program_id: Option<ProgramId>,
pre_states: &[AccountWithMetadata],
instruction_data: &InstructionData,
) -> Result<ProgramOutput, LeeError> {
// Write inputs to the program
let mut env_builder = ExecutorEnv::builder();
env_builder.session_limit(Some(MAX_NUM_CYCLES_PUBLIC_EXECUTION));
Self::write_inputs(
self.id,
caller_program_id,
pre_states,
instruction_data,
&mut env_builder,
)?;
let env = env_builder.build().unwrap();
// Execute the program (without proving)
let executor = default_executor();
let session_info = executor
.execute(env, self.elf())
.map_err(|e| LeeError::ProgramExecutionFailed(e.to_string()))?;
// Get outputs
let program_output = session_info
.journal
.decode()
.map_err(|e| LeeError::ProgramExecutionFailed(e.to_string()))?;
Ok(program_output)
}
/// Writes inputs to `env_builder` in the order expected by the programs.
pub(crate) fn write_inputs(
program_id: ProgramId,
caller_program_id: Option<ProgramId>,
pre_states: &[AccountWithMetadata],
instruction_data: &[u32],
env_builder: &mut ExecutorEnvBuilder,
) -> Result<(), LeeError> {
env_builder
.write(&program_id)
.map_err(|e| LeeError::ProgramWriteInputFailed(e.to_string()))?;
env_builder
.write(&caller_program_id)
.map_err(|e| LeeError::ProgramWriteInputFailed(e.to_string()))?;
let pre_states = pre_states.to_vec();
env_builder
.write(&pre_states)
.map_err(|e| LeeError::ProgramWriteInputFailed(e.to_string()))?;
env_builder
.write(&instruction_data)
.map_err(|e| LeeError::ProgramWriteInputFailed(e.to_string()))?;
Ok(())
}
}
#[cfg(test)]
mod tests;