mirror of
https://github.com/logos-blockchain/logos-execution-zone.git
synced 2026-08-25 19:31:11 +00:00
feat(lee): anchor privacy-circuit image ids and migrate all programs to Deploy
Privacy-preserving circuit: - Add ProgramImageClaim, letting a Deploy-created program's real image_id be anchored to its account for privacy-circuit env::verify, with the sequencer independently re-deriving the real image_id via get_program to authenticate the claim (journal reconstruction, same pattern as public_actions pre-states). - Fix compute_public_authorized_pdas and the private circuit's resolve_authorization_and_record_bindings to use the caller's real recovered image_id instead of bijection-guessing it from the caller's account_id, which was wrong for Deploy-created callers. Program storage: - V03State::insert_program (used by both genesis's with_programs and live ProgramDeploymentTransaction execution) now always writes the Deploy two-account shape (ProgramData header + segment), not the legacy PROGRAM_STORAGE_OWNER raw-elf shape. The header stays at the existing bijection address so no dispatch-address reference needed to change; only the segment (never a caller-facing address) moves to its PDA. - Drop the now-redundant ProgramAlreadyExists pre-check in ProgramDeploymentTransaction validation; PDA claiming already prevents redeploying an account. - Migrate remaining tests off ProgramDeploymentTransaction onto native Deploy (sequencer_core, integration_tests' auth_transfer and block_size_limit), adding a shared deploy_targets/deploy_transaction/encoded_tx_size helper. Rebuild artifacts and the prebuilt test fixture for the circuit and program storage changes.
This commit is contained in:
@@ -1,54 +1,71 @@
|
||||
pub use lee_core::program::PdaSeed;
|
||||
pub use lee_core::program::{PdaSeed, ProgramData};
|
||||
use lee_core::{
|
||||
account::{Account, AccountId, AccountWithMetadata, Data},
|
||||
program::{AccountPostState, Claim, ProgramId},
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
const DEPLOY_SEED_DOMAIN_SEPARATOR: AccountId =
|
||||
AccountId::new(*b"/LEZ/v0.3/LoaderDeploySeed/00000");
|
||||
const DEPLOY_HEADER_SEED_DOMAIN_SEPARATOR: AccountId =
|
||||
AccountId::new(*b"/LEZ/v0.3/LoaderDeployHeaderSeed");
|
||||
const DEPLOY_SEGMENT_SEED_DOMAIN_SEPARATOR: AccountId =
|
||||
AccountId::new(*b"/LEZ/v0.3/LoaderDeploySegmentSee");
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub enum Instruction {
|
||||
/// Deploys a new program, claiming its `ProgramData` account as a PDA of the loader.
|
||||
/// Deploys a new program: writes its `ProgramData` header and one bytecode segment, each
|
||||
/// claimed as a PDA of the loader.
|
||||
///
|
||||
/// Required accounts (1):
|
||||
/// - The target `ProgramData` PDA account (must be `Account::default()`)
|
||||
/// Required accounts (2), in order:
|
||||
/// - The target `ProgramData` header PDA account (must be `Account::default()`)
|
||||
/// - The target segment PDA account holding the raw bytecode (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.
|
||||
/// Derives the PDA seed for a deployed program's `ProgramData` header 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.
|
||||
/// Combines the program's content-derived identity (`image_id`), its position in a (currently
|
||||
/// always single-segment) split (`segment_number`), and who may redeploy it (`update_auth`).
|
||||
///
|
||||
/// Domain-separated from other PDA-seed derivations in the codebase, including
|
||||
/// [`deploy_segment_pda_seed`], so a header seed can never collide with a segment seed (or
|
||||
/// anything else) even when the input triple coincides.
|
||||
#[must_use]
|
||||
pub fn deploy_pda_seed(
|
||||
pub fn deploy_header_pda_seed(
|
||||
image_id: ProgramId,
|
||||
segment_number: u32,
|
||||
update_auth: AccountId,
|
||||
) -> PdaSeed {
|
||||
deploy_seed(
|
||||
DEPLOY_HEADER_SEED_DOMAIN_SEPARATOR,
|
||||
image_id,
|
||||
segment_number,
|
||||
update_auth,
|
||||
)
|
||||
}
|
||||
|
||||
/// Derives the PDA seed for a deployed program's bytecode segment account.
|
||||
///
|
||||
/// Same inputs as [`deploy_header_pda_seed`], domain-separated so the two never collide. Kept as
|
||||
/// a distinct account from the header specifically so that authenticating a program's identity
|
||||
/// (e.g. for privacy-preserving proof verification) never has to touch its bytecode: the only
|
||||
/// account-authentication primitive available is whole-account equality, so what's bundled into
|
||||
/// one account sets the floor for how cheap that authentication can be.
|
||||
#[must_use]
|
||||
pub fn deploy_segment_pda_seed(
|
||||
image_id: ProgramId,
|
||||
segment_number: u32,
|
||||
update_auth: AccountId,
|
||||
) -> PdaSeed {
|
||||
deploy_seed(
|
||||
DEPLOY_SEGMENT_SEED_DOMAIN_SEPARATOR,
|
||||
image_id,
|
||||
segment_number,
|
||||
update_auth,
|
||||
)
|
||||
}
|
||||
|
||||
fn deploy_seed(
|
||||
domain_separator: AccountId,
|
||||
image_id: ProgramId,
|
||||
segment_number: u32,
|
||||
update_auth: AccountId,
|
||||
@@ -56,7 +73,7 @@ pub fn deploy_pda_seed(
|
||||
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.as_ref());
|
||||
bytes[0..32].copy_from_slice(domain_separator.as_ref());
|
||||
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);
|
||||
@@ -72,7 +89,7 @@ pub fn deploy_pda_seed(
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn deploy_account_id(
|
||||
pub fn deploy_header_account_id(
|
||||
loader_program_id: ProgramId,
|
||||
image_id: ProgramId,
|
||||
segment_number: u32,
|
||||
@@ -80,12 +97,27 @@ pub fn deploy_account_id(
|
||||
) -> AccountId {
|
||||
AccountId::for_public_pda(
|
||||
&loader_program_id,
|
||||
&deploy_pda_seed(image_id, segment_number, update_auth),
|
||||
&deploy_header_pda_seed(image_id, segment_number, update_auth),
|
||||
)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn deploy_segment_account_id(
|
||||
loader_program_id: ProgramId,
|
||||
image_id: ProgramId,
|
||||
segment_number: u32,
|
||||
update_auth: AccountId,
|
||||
) -> AccountId {
|
||||
AccountId::for_public_pda(
|
||||
&loader_program_id,
|
||||
&deploy_segment_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.
|
||||
/// binary, derives its header and segment PDAs, and claims both. Called natively from
|
||||
/// dispatch's `RESERVED_DEPLOYMENT_PROGRAM_ACCOUNT_ID` shortcut (see that constant's doc
|
||||
/// comment in `lee_core::program`).
|
||||
#[must_use]
|
||||
pub fn execute_deploy(
|
||||
self_program_id: ProgramId,
|
||||
@@ -97,32 +129,54 @@ pub fn execute_deploy(
|
||||
.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 header_seed = deploy_header_pda_seed(image_id, segment_number, update_auth);
|
||||
let segment_seed = deploy_segment_pda_seed(image_id, segment_number, update_auth);
|
||||
let header_pda = AccountId::for_public_pda(&self_program_id, &header_seed);
|
||||
let segment_pda = AccountId::for_public_pda(&self_program_id, &segment_seed);
|
||||
|
||||
let [target] = pre_states
|
||||
let [header_target, segment_target] = pre_states
|
||||
.try_into()
|
||||
.expect("Deploy requires exactly 1 account");
|
||||
.expect("Deploy requires exactly 2 accounts");
|
||||
|
||||
assert_eq!(target.account_id, pda, "wrong deployment target account");
|
||||
assert_eq!(
|
||||
target.account,
|
||||
header_target.account_id, header_pda,
|
||||
"wrong deployment header target account"
|
||||
);
|
||||
assert_eq!(
|
||||
header_target.account,
|
||||
Account::default(),
|
||||
"program already deployed"
|
||||
"program header already deployed"
|
||||
);
|
||||
assert_eq!(
|
||||
segment_target.account_id, segment_pda,
|
||||
"wrong deployment segment target account"
|
||||
);
|
||||
assert_eq!(
|
||||
segment_target.account,
|
||||
Account::default(),
|
||||
"program segment 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),
|
||||
)]
|
||||
vec![
|
||||
AccountPostState::new_claimed(
|
||||
Account {
|
||||
data: Data::from(&program_data),
|
||||
..Account::default()
|
||||
},
|
||||
Claim::Pda(header_seed),
|
||||
),
|
||||
AccountPostState::new_claimed(
|
||||
Account {
|
||||
data: Data::try_from(bytecode).expect("elf must fit under DATA_MAX_LENGTH"),
|
||||
..Account::default()
|
||||
},
|
||||
Claim::Pda(segment_seed),
|
||||
),
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user