fix(lee): address PR #733 review comments

- RESERVED_DEPLOYMENT_PROGRAM_ACCOUNT_ID: restore the SHA256 derivation
  docstring that explains the constant instead of leaving it as an opaque
  byte array, and switch the array itself to a hex literal (hex-literal,
  already a workspace dependency) for readability.
- Rename the loader/loader_core crate to program_loader/program_loader_core
  to disambiguate it, across the directory, package name, workspace
  members/dependency alias, both dependent crates, and every call site.
This commit is contained in:
Marvin Jones
2026-08-22 17:39:23 -04:00
parent f9fe3f0d5c
commit 6b1f016aee
10 changed files with 43 additions and 34 deletions
@@ -0,0 +1,16 @@
[package]
name = "program_loader_core"
version = "0.1.0"
edition = "2024"
license = { workspace = true }
[lints]
workspace = true
[dependencies]
lee_core.workspace = true
serde = { workspace = true, default-features = false }
borsh.workspace = true
bytemuck.workspace = true
risc0-zkvm.workspace = true
risc0-binfmt.workspace = true
+127
View File
@@ -0,0 +1,127 @@
pub use lee_core::program::PdaSeed;
use lee_core::{
account::{Account, AccountId, AccountWithMetadata, Data},
program::{AccountPostState, Claim, ProgramId},
};
use serde::{Deserialize, Serialize};
const DEPLOY_SEED_DOMAIN_SEPARATOR: [u8; 32] = *b"/LEZ/v0.3/LoaderDeploySeed/00000";
#[derive(Serialize, Deserialize)]
pub enum Instruction {
/// Deploys a new program, claiming its `ProgramData` account as a PDA of the loader.
///
/// Required accounts (1):
/// - The target `ProgramData` PDA account (must be `Account::default()`)
Deploy { bytecode: Vec<u8> },
}
#[derive(Debug, PartialEq, Eq, borsh::BorshSerialize, borsh::BorshDeserialize)]
pub struct ProgramData {
pub image_id: ProgramId,
pub segment_number: u32,
pub update_auth: AccountId,
pub elf_segment: Vec<u8>,
}
impl TryFrom<&Data> for ProgramData {
type Error = std::io::Error;
fn try_from(data: &Data) -> Result<Self, Self::Error> {
borsh::BorshDeserialize::try_from_slice(data.as_ref())
}
}
impl From<&ProgramData> for Data {
fn from(program_data: &ProgramData) -> Self {
let mut data = Vec::with_capacity(std::mem::size_of_val(program_data));
borsh::BorshSerialize::serialize(program_data, &mut data)
.expect("borsh serialization should not fail");
Self::try_from(data).expect("elf must fit under DATA_MAX_LENGTH")
}
}
/// Derives the PDA seed for a deployed program's `ProgramData` account.
///
/// Domain-separated from other PDA-seed derivations in the codebase so that a `deploy_pda_seed`
/// output can never collide with a seed meant for a different purpose, even if the input triple
/// happened to coincide.
#[must_use]
pub fn deploy_pda_seed(
image_id: ProgramId,
segment_number: u32,
update_auth: AccountId,
) -> PdaSeed {
use risc0_zkvm::sha::{Impl, Sha256 as _};
let mut bytes = [0_u8; 32 + 32 + 4 + 32];
bytes[0..32].copy_from_slice(&DEPLOY_SEED_DOMAIN_SEPARATOR);
let image_id_bytes: &[u8] =
bytemuck::try_cast_slice(&image_id).expect("ProgramId should be castable to &[u8]");
bytes[32..64].copy_from_slice(image_id_bytes);
bytes[64..68].copy_from_slice(&segment_number.to_le_bytes());
bytes[68..].copy_from_slice(update_auth.as_ref());
PdaSeed::new(
Impl::hash_bytes(&bytes)
.as_bytes()
.try_into()
.expect("Hash output must be exactly 32 bytes long"),
)
}
#[must_use]
pub fn deploy_account_id(
loader_program_id: ProgramId,
image_id: ProgramId,
segment_number: u32,
update_auth: AccountId,
) -> AccountId {
AccountId::for_public_pda(
&loader_program_id,
&deploy_pda_seed(image_id, segment_number, update_auth),
)
}
/// Executes the `Deploy` instruction: verifies `bytecode` decodes as a valid RISC0 program
/// binary, derives its `ProgramData` PDA, and claims it.
#[must_use]
pub fn execute_deploy(
self_program_id: ProgramId,
pre_states: Vec<AccountWithMetadata>,
bytecode: Vec<u8>,
) -> Vec<AccountPostState> {
let image_id: ProgramId = risc0_binfmt::compute_image_id(&bytecode)
.expect("bytecode must decode as a valid RISC0 program binary")
.into();
let segment_number = 0_u32;
let update_auth = AccountId::default();
let seed = deploy_pda_seed(image_id, segment_number, update_auth);
let pda = AccountId::for_public_pda(&self_program_id, &seed);
let [target] = pre_states
.try_into()
.expect("Deploy requires exactly 1 account");
assert_eq!(target.account_id, pda, "wrong deployment target account");
assert_eq!(
target.account,
Account::default(),
"program already deployed"
);
let program_data = ProgramData {
image_id,
segment_number,
update_auth,
elf_segment: bytecode,
};
vec![AccountPostState::new_claimed(
Account {
data: Data::from(&program_data),
..Account::default()
},
Claim::Pda(seed),
)]
}