153 lines
4.8 KiB
Rust
Raw Normal View History

use std::borrow::Cow;
2026-01-27 01:20:17 -03:00
use borsh::{BorshDeserialize, BorshSerialize};
use lee_core::{
2025-10-29 15:34:11 -03:00
account::AccountWithMetadata,
2025-08-27 16:24:20 -03:00
program::{InstructionData, ProgramId, ProgramOutput},
2025-08-09 20:25:58 -03:00
};
2025-08-27 18:23:56 -03:00
use risc0_zkvm::{ExecutorEnv, ExecutorEnvBuilder, default_executor, serde::to_vec};
2025-08-10 18:59:29 -03:00
use serde::Serialize;
2025-08-09 20:25:58 -03:00
use crate::error::LeeError;
2025-08-09 20:25:58 -03:00
/// Maximum number of cycles for a public execution.
2026-03-10 00:17:43 +03:00
/// TODO: Make this variable when fees are implemented.
const MAX_NUM_CYCLES_PUBLIC_EXECUTION: u64 = 1024 * 1024 * 32; // 32M cycles
2026-01-27 01:20:17 -03:00
#[derive(Clone, Debug, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
2025-08-09 20:25:58 -03:00
pub struct Program {
2025-08-13 01:33:11 -03:00
id: ProgramId,
elf: Cow<'static, [u8]>,
2025-08-09 20:25:58 -03:00
}
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 }
}
2026-03-03 23:21:08 +03:00
#[must_use]
2026-03-09 18:27:56 +03:00
pub const fn id(&self) -> ProgramId {
2025-08-09 20:25:58 -03:00
self.id
}
2025-08-09 20:35:44 -03:00
2026-03-03 23:21:08 +03:00
#[must_use]
pub fn elf(&self) -> &[u8] {
&self.elf
2025-08-18 19:57:21 -03:00
}
2025-08-10 19:53:05 -03:00
pub fn serialize_instruction<T: Serialize>(
2025-08-11 12:07:30 -03:00
instruction: T,
) -> Result<InstructionData, LeeError> {
to_vec(&instruction).map_err(|e| LeeError::InstructionSerializationError(e.to_string()))
2025-08-10 18:51:55 -03:00
}
2025-08-09 20:25:58 -03:00
pub(crate) fn execute(
&self,
caller_program_id: Option<ProgramId>,
2025-08-09 20:25:58 -03:00
pre_states: &[AccountWithMetadata],
2025-08-10 18:51:55 -03:00
instruction_data: &InstructionData,
) -> Result<ProgramOutput, LeeError> {
2025-08-09 20:25:58 -03:00
// 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,
)?;
2025-08-09 20:25:58 -03:00
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()))?;
2025-08-09 20:25:58 -03:00
// Get outputs
let program_output = session_info
2025-08-09 20:25:58 -03:00
.journal
.decode()
.map_err(|e| LeeError::ProgramExecutionFailed(e.to_string()))?;
2025-08-09 20:25:58 -03:00
Ok(program_output)
2025-08-14 13:28:23 -03:00
}
2026-03-10 00:17:43 +03:00
/// Writes inputs to `env_builder` in the order expected by the programs.
2025-08-18 19:57:21 -03:00
pub(crate) fn write_inputs(
program_id: ProgramId,
caller_program_id: Option<ProgramId>,
2025-08-09 20:25:58 -03:00
pre_states: &[AccountWithMetadata],
2025-08-10 18:51:55 -03:00
instruction_data: &[u32],
2025-08-09 20:25:58 -03:00
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()))?;
2025-08-09 20:25:58 -03:00
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()))?;
2025-08-09 20:25:58 -03:00
Ok(())
}
2025-08-10 00:53:53 -03:00
}
2025-08-13 01:33:11 -03:00
#[cfg(test)]
mod tests {
use lee_core::account::{Account, AccountId, AccountWithMetadata};
2025-08-13 01:33:11 -03:00
use crate::program::Program;
2025-08-13 01:33:11 -03:00
#[test]
2026-03-04 18:42:33 +03:00
fn program_execution() {
let program = crate::test_methods::simple_balance_transfer();
2026-03-03 23:21:08 +03:00
let balance_to_move: u128 = 11_223_344_556_677;
2025-08-13 01:33:11 -03:00
let instruction_data = Program::serialize_instruction(balance_to_move).unwrap();
2025-09-11 16:37:28 -03:00
let sender = AccountWithMetadata::new(
Account {
2026-03-03 23:21:08 +03:00
balance: 77_665_544_332_211,
2025-08-13 01:33:11 -03:00
..Account::default()
},
2025-09-11 16:37:28 -03:00
true,
2025-09-12 09:18:40 -03:00
AccountId::new([0; 32]),
2025-09-11 16:37:28 -03:00
);
let recipient =
2025-09-12 09:18:40 -03:00
AccountWithMetadata::new(Account::default(), false, AccountId::new([1; 32]));
2025-08-13 01:33:11 -03:00
let expected_sender_post = Account {
2026-03-03 23:21:08 +03:00
balance: 77_665_544_332_211 - balance_to_move,
2025-08-13 01:33:11 -03:00
..Account::default()
};
let expected_recipient_post = Account {
balance: balance_to_move,
..Account::default()
};
let program_output = program
.execute(None, &[sender, recipient], &instruction_data)
2025-08-13 01:33:11 -03:00
.unwrap();
let [sender_post, recipient_post] = program_output.post_states.try_into().unwrap();
2025-12-04 16:26:40 -03:00
assert_eq!(sender_post.account(), &expected_sender_post);
assert_eq!(recipient_post.account(), &expected_recipient_post);
2025-08-13 01:33:11 -03:00
}
}