refactor(lee): migrate program self/caller identity from ProgramId to AccountId

ProgramInput/ProgramOutput.self_program_id/caller_program_id, and the
dispatcher's CallerData.program_id, now carry AccountId (renamed to
self_account_id/caller_account_id) instead of ProgramId. These fields
are self-reported/cross-checked dispatch bookkeeping, not RISC0 image
identity, and AccountId already crosses the guest/host boundary this
way via every pre_state.account_id.

ProgramId is now confined to what's actually image-id-keyed:
env::verify, Program.id (from compute_image_id()), and the
for_public_pda/for_private_pda derivation formulas, each recovering
the real ProgramId from AccountId via the existing bijection exactly
where needed.

Rebuilds artifacts and the prebuilt sequencer db fixture to match.
This commit is contained in:
Marvin Jones
2026-08-14 16:43:59 -04:00
parent 7ce202c64e
commit 0ef2d61fee
86 changed files with 457 additions and 427 deletions
+4 -4
View File
@@ -14,8 +14,8 @@ use lee_core::program::{ProgramInput, ProgramOutput, read_lee_inputs};
fn main() {
let (
ProgramInput {
self_program_id,
caller_program_id,
self_account_id,
caller_account_id,
pre_states,
instruction,
},
@@ -155,8 +155,8 @@ fn main() {
};
ProgramOutput::new(
self_program_id,
caller_program_id,
self_account_id,
caller_account_id,
instruction_words,
pre_states_clone,
post_states,
@@ -4,8 +4,8 @@ use lee_core::program::{ProgramInput, ProgramOutput, read_lee_inputs};
fn main() {
let (
ProgramInput {
self_program_id,
caller_program_id,
self_account_id,
caller_account_id,
pre_states,
instruction,
},
@@ -59,8 +59,8 @@ fn main() {
};
ProgramOutput::new(
self_program_id,
caller_program_id,
self_account_id,
caller_account_id,
instruction_words,
pre_states_clone,
post_states,
@@ -65,8 +65,8 @@ fn main() {
// Read input accounts.
let (
ProgramInput {
self_program_id,
caller_program_id,
self_account_id,
caller_account_id,
pre_states,
instruction,
},
@@ -89,8 +89,8 @@ fn main() {
};
ProgramOutput::new(
self_program_id,
caller_program_id,
self_account_id,
caller_account_id,
instruction_words,
pre_states,
post_states,
+8 -8
View File
@@ -16,8 +16,8 @@ fn unchanged_post_states(
fn main() {
let (
ProgramInput {
self_program_id,
caller_program_id,
self_account_id,
caller_account_id,
pre_states,
instruction,
},
@@ -25,7 +25,7 @@ fn main() {
) = read_lee_inputs::<Instruction>();
assert!(
caller_program_id.is_none(),
caller_account_id.is_none(),
"Bridge cannot be invoked through chain calls"
);
@@ -44,7 +44,7 @@ fn main() {
assert_eq!(
bridge.account_id,
bridge_core::compute_bridge_account_id(self_program_id),
bridge_core::compute_bridge_account_id(self_account_id.into()),
"First account must be bridge PDA"
);
@@ -56,7 +56,7 @@ fn main() {
assert_eq!(
receipt.account_id,
bridge_core::deposit_receipt_account_id(self_program_id, l1_deposit_op_id),
bridge_core::deposit_receipt_account_id(self_account_id.into(), l1_deposit_op_id),
"Third account must be the deposit-receipt PDA"
);
@@ -115,7 +115,7 @@ fn main() {
// assert_eq!(
// bridge.account_id,
// bridge_core::compute_bridge_account_id(self_program_id),
// bridge_core::compute_bridge_account_id(self_account_id.into()),
// "Second account must be bridge PDA"
// );
@@ -137,8 +137,8 @@ fn main() {
};
ProgramOutput::new(
self_program_id,
caller_program_id,
self_account_id,
caller_account_id,
instruction_words,
pre_states_clone,
post_states,
+24 -21
View File
@@ -4,7 +4,7 @@ use bridge_lock_core::{
};
use cross_zone_outbox_core::Instruction as OutboxInstruction;
use lee_core::{
account::{Account, AccountWithMetadata},
account::{Account, AccountId, AccountWithMetadata},
program::{
AccountPostState, ChainedCall, Claim, ProgramId, ProgramInput, ProgramOutput,
read_lee_inputs,
@@ -15,8 +15,8 @@ use wrapped_token_core::{Instruction as WrappedInstruction, MAX_MINT_AMOUNT};
fn main() {
let (
ProgramInput {
self_program_id,
caller_program_id,
self_account_id,
caller_account_id,
pre_states,
instruction,
},
@@ -24,7 +24,7 @@ fn main() {
) = read_lee_inputs::<Instruction>();
assert!(
caller_program_id.is_none(),
caller_account_id.is_none(),
"bridge_lock is only invoked as a top-level user transaction"
);
@@ -37,8 +37,8 @@ fn main() {
payload,
ordinal,
} => lock(
self_program_id,
caller_program_id,
self_account_id,
caller_account_id,
pre_states,
instruction_words,
amount,
@@ -52,8 +52,8 @@ fn main() {
outbox_program_id,
target_program_id,
} => init_config(
self_program_id,
caller_program_id,
self_account_id,
caller_account_id,
pre_states,
instruction_words,
outbox_program_id,
@@ -67,8 +67,8 @@ fn main() {
reason = "the emission fields are passed through verbatim"
)]
fn lock(
self_program_id: ProgramId,
caller_program_id: Option<ProgramId>,
self_account_id: AccountId,
caller_account_id: Option<AccountId>,
pre_states: Vec<AccountWithMetadata>,
instruction_words: Vec<u32>,
amount: u128,
@@ -78,6 +78,11 @@ fn lock(
payload: Vec<u8>,
ordinal: u32,
) {
// Recover the real `ProgramId` (RISC0 image id): on this branch every program account lives
// at the direct `AccountId::from(program_id)` bijection, so this round-trip is exact. Needed
// for the PDA-derivation helpers below, which are pinned to the actual image id.
let self_program_id = ProgramId::from(self_account_id);
// pre_states: [config PDA, holder holding (authorized), escrow PDA, outbox PDA].
let [config, holder, escrow, outbox] = <[AccountWithMetadata; 4]>::try_from(pre_states)
.expect("Lock requires config, holder, escrow, and outbox accounts");
@@ -131,8 +136,7 @@ fn lock(
// genuine holding: a caller cannot substitute an account owned by some other
// program to emit the mint without an actual lock.
assert_eq!(
holder.account.program_owner,
self_program_id.into(),
holder.account.program_owner, self_account_id,
"holder account must be a bridge_lock holding"
);
assert_eq!(
@@ -179,8 +183,8 @@ fn lock(
let config_post = AccountPostState::new(config.account.clone());
ProgramOutput::new(
self_program_id,
caller_program_id,
self_account_id,
caller_account_id,
instruction_words,
vec![config, holder, escrow, outbox.clone()],
vec![
@@ -197,8 +201,8 @@ fn lock(
/// Writes the outbox program and the mint target into the config PDA exactly once
/// at genesis.
fn init_config(
self_program_id: ProgramId,
caller_program_id: Option<ProgramId>,
self_account_id: AccountId,
caller_account_id: Option<AccountId>,
pre_states: Vec<AccountWithMetadata>,
instruction_words: Vec<u32>,
outbox_program_id: ProgramId,
@@ -209,7 +213,7 @@ fn init_config(
.expect("InitConfig requires the config account");
assert_eq!(
config.account_id,
config_account_id(self_program_id),
config_account_id(self_account_id.into()),
"account must be the bridge-lock config PDA"
);
// Init-once, idempotent under genesis replay: a `default` config is a first
@@ -218,8 +222,7 @@ fn init_config(
// `new_claimed_if_default` alone would not stop a later self-owned rewrite.
if config.account != Account::default() {
assert_eq!(
config.account.program_owner,
self_program_id.into(),
config.account.program_owner, self_account_id,
"bridge-lock config PDA is owned by another program"
);
assert_eq!(
@@ -238,8 +241,8 @@ fn init_config(
AccountPostState::new_claimed_if_default(config_account, Claim::Pda(config_seed()));
ProgramOutput::new(
self_program_id,
caller_program_id,
self_account_id,
caller_account_id,
instruction_words,
vec![config],
vec![config_post],
+4 -5
View File
@@ -39,8 +39,8 @@ fn update_if_multiple(
fn main() {
let (
ProgramInput {
self_program_id,
caller_program_id,
self_account_id,
caller_account_id,
pre_states,
instruction: timestamp,
},
@@ -60,7 +60,6 @@ fn main() {
}
// Verify all clock accounts are owned by this program (assigned at genesis).
let self_account_id: lee_core::account::AccountId = self_program_id.into();
if pre_01.account.program_owner != self_account_id
|| pre_10.account.program_owner != self_account_id
|| pre_50.account.program_owner != self_account_id
@@ -85,8 +84,8 @@ fn main() {
let (pre_50, post_50) = update_if_multiple(pre_50, 50, current_block_id, &updated_data);
ProgramOutput::new(
self_program_id,
caller_program_id,
self_account_id,
caller_account_id,
instruction_words,
vec![pre_01, pre_10, pre_50],
vec![post_01, post_10, post_50],
+23 -19
View File
@@ -4,7 +4,7 @@ use cross_zone_inbox_core::{
inbox_source_marker_account_id,
};
use lee_core::{
account::{Account, AccountWithMetadata},
account::{Account, AccountId, AccountWithMetadata},
program::{
AccountPostState, ChainedCall, Claim, ProgramId, ProgramInput, ProgramOutput,
read_lee_inputs,
@@ -18,8 +18,8 @@ fn unchanged(pre: &AccountWithMetadata) -> AccountPostState {
fn main() {
let (
ProgramInput {
self_program_id,
caller_program_id,
self_account_id,
caller_account_id,
pre_states,
instruction,
},
@@ -27,21 +27,21 @@ fn main() {
) = read_lee_inputs::<Instruction>();
assert!(
caller_program_id.is_none(),
caller_account_id.is_none(),
"Inbox is only invoked as a top-level sequencer-origin transaction"
);
match instruction {
Instruction::Dispatch(msg) => dispatch(
self_program_id,
caller_program_id,
self_account_id,
caller_account_id,
pre_states,
instruction_words,
&msg,
),
Instruction::InitConfig(config) => init_config(
self_program_id,
caller_program_id,
self_account_id,
caller_account_id,
pre_states,
instruction_words,
&config,
@@ -51,8 +51,8 @@ fn main() {
/// Delivers a finalized peer message to its target program, no-op on replay.
fn dispatch(
self_program_id: ProgramId,
caller_program_id: Option<ProgramId>,
self_account_id: AccountId,
caller_account_id: Option<AccountId>,
pre_states: Vec<AccountWithMetadata>,
instruction_words: Vec<u32>,
msg: &CrossZoneMessage,
@@ -62,6 +62,11 @@ fn dispatch(
"l1_inclusion_witness must be None in v1"
);
// Recover the real `ProgramId` (RISC0 image id): on this branch every program account lives
// at the direct `AccountId::from(program_id)` bijection, so this round-trip is exact. Needed
// for the PDA-derivation helpers below, which are pinned to the actual image id.
let self_program_id = ProgramId::from(self_account_id);
// pre_states layout: [config, seen_shard, source marker, then the target accounts].
let mut accounts = pre_states.into_iter();
let config = accounts.next().expect("config account required");
@@ -158,8 +163,8 @@ fn dispatch(
output_pre_states.extend(target_accounts);
ProgramOutput::new(
self_program_id,
caller_program_id,
self_account_id,
caller_account_id,
instruction_words,
output_pre_states,
post_states,
@@ -170,8 +175,8 @@ fn dispatch(
/// Writes the inbox config into the config PDA exactly once at genesis.
fn init_config(
self_program_id: ProgramId,
caller_program_id: Option<ProgramId>,
self_account_id: AccountId,
caller_account_id: Option<AccountId>,
pre_states: Vec<AccountWithMetadata>,
instruction_words: Vec<u32>,
config: &InboxConfig,
@@ -181,7 +186,7 @@ fn init_config(
.expect("InitConfig requires the config account");
assert_eq!(
config_meta.account_id,
inbox_config_account_id(self_program_id),
inbox_config_account_id(self_account_id.into()),
"account must be the inbox config PDA"
);
// Init-once, idempotent under genesis replay: a `default` config is a first
@@ -191,8 +196,7 @@ fn init_config(
// rewriting its own config data on a later call.
if config_meta.account != Account::default() {
assert_eq!(
config_meta.account.program_owner,
self_program_id.into(),
config_meta.account.program_owner, self_account_id,
"inbox config PDA is owned by another program"
);
assert_eq!(
@@ -211,8 +215,8 @@ fn init_config(
AccountPostState::new_claimed_if_default(config_account, Claim::Pda(inbox_config_seed()));
ProgramOutput::new(
self_program_id,
caller_program_id,
self_account_id,
caller_account_id,
instruction_words,
vec![config_meta],
vec![config_post],
@@ -73,7 +73,7 @@ impl OutboxRecord {
/// destination zone, and a per-emitter per-zone ordinal.
///
/// `emitter` is the program that called `Emit`, which the guest takes from
/// `caller_program_id` rather than from the instruction. Without it in the
/// `caller_account_id` rather than from the instruction. Without it in the
/// address two programs share a slot and one overwrites the other.
#[must_use]
pub fn outbox_pda(
+12 -7
View File
@@ -1,14 +1,14 @@
use cross_zone_outbox_core::{Instruction, OutboxRecord, outbox_pda, outbox_pda_seed};
use lee_core::{
account::{Account, AccountWithMetadata},
program::{AccountPostState, Claim, ProgramInput, ProgramOutput, read_lee_inputs},
program::{AccountPostState, Claim, ProgramId, ProgramInput, ProgramOutput, read_lee_inputs},
};
fn main() {
let (
ProgramInput {
self_program_id,
caller_program_id,
self_account_id,
caller_account_id,
pre_states,
instruction,
},
@@ -20,7 +20,7 @@ fn main() {
// immediate chained caller, not the top-level program that cross-zone
// discovery names; the two coincide only while every emitter refuses to be
// called by another program, which both do today.
let Some(emitter) = caller_program_id else {
let Some(emitter) = caller_account_id.map(ProgramId::from) else {
panic!("Outbox is only callable through a chain call from a user program");
};
@@ -45,7 +45,12 @@ fn main() {
assert_eq!(
outbox.account_id,
outbox_pda(self_program_id, emitter, &target_zone, ordinal),
outbox_pda(
ProgramId::from(self_account_id),
emitter,
&target_zone,
ordinal
),
"Account must be the outbox PDA for (emitter, target_zone, ordinal)"
);
@@ -86,8 +91,8 @@ fn main() {
);
ProgramOutput::new(
self_program_id,
caller_program_id,
self_account_id,
caller_account_id,
instruction_words,
vec![outbox],
vec![post],
+7 -7
View File
@@ -15,8 +15,8 @@ fn unchanged_post_states(
fn main() {
let (
ProgramInput {
self_program_id,
caller_program_id,
self_account_id,
caller_account_id,
pre_states,
instruction,
},
@@ -24,7 +24,7 @@ fn main() {
) = read_lee_inputs::<Instruction>();
assert!(
caller_program_id.is_none(),
caller_account_id.is_none(),
"Faucet cannot be invoked through chain calls"
);
@@ -43,7 +43,7 @@ fn main() {
assert_eq!(
faucet.account_id,
faucet_core::compute_faucet_account_id(self_program_id),
faucet_core::compute_faucet_account_id(self_account_id.into()),
"First account must be faucet PDA"
);
@@ -69,7 +69,7 @@ fn main() {
assert_eq!(
faucet.account_id,
faucet_core::compute_faucet_account_id(self_program_id),
faucet_core::compute_faucet_account_id(self_account_id.into()),
"First account must be faucet PDA"
);
@@ -88,8 +88,8 @@ fn main() {
};
ProgramOutput::new(
self_program_id,
caller_program_id,
self_account_id,
caller_account_id,
instruction_words,
pre_states_clone,
post_states,
+4 -4
View File
@@ -46,8 +46,8 @@ fn main() {
// It is expected to receive only two accounts: [pinata_account, winner_account]
let (
ProgramInput {
self_program_id,
caller_program_id,
self_account_id,
caller_account_id,
pre_states,
instruction: solution,
},
@@ -81,8 +81,8 @@ fn main() {
.expect("Overflow when adding prize to winner");
ProgramOutput::new(
self_program_id,
caller_program_id,
self_account_id,
caller_account_id,
instruction_words,
vec![pinata, winner],
vec![
+4 -4
View File
@@ -52,8 +52,8 @@ fn main() {
// winner_token_holding]
let (
ProgramInput {
self_program_id,
caller_program_id,
self_account_id,
caller_account_id,
pre_states,
instruction: solution,
},
@@ -99,8 +99,8 @@ fn main() {
.with_pda_seeds(vec![PdaSeed::new([0; 32])]);
ProgramOutput::new(
self_program_id,
caller_program_id,
self_account_id,
caller_account_id,
instruction_words,
vec![
pinata_definition,
+25 -21
View File
@@ -1,6 +1,6 @@
use cross_zone_inbox_core::inbox_source_marker_account_id;
use lee_core::{
account::{Account, AccountWithMetadata},
account::{Account, AccountId, AccountWithMetadata},
program::{AccountPostState, Claim, ProgramId, ProgramInput, ProgramOutput, read_lee_inputs},
};
use ping_core::{
@@ -11,8 +11,8 @@ use ping_core::{
fn main() {
let (
ProgramInput {
self_program_id,
caller_program_id,
self_account_id,
caller_account_id,
pre_states,
instruction,
},
@@ -21,15 +21,15 @@ fn main() {
match instruction {
ReceiverInstruction::Record { payload } => record(
self_program_id,
caller_program_id,
self_account_id,
caller_account_id,
pre_states,
instruction_words,
payload,
),
ReceiverInstruction::InitConfig(config) => init_config(
self_program_id,
caller_program_id,
self_account_id,
caller_account_id,
pre_states,
instruction_words,
&config,
@@ -38,12 +38,17 @@ fn main() {
}
fn record(
self_program_id: ProgramId,
caller_program_id: Option<ProgramId>,
self_account_id: AccountId,
caller_account_id: Option<AccountId>,
pre_states: Vec<AccountWithMetadata>,
instruction_words: Vec<u32>,
payload: Vec<u8>,
) {
// Recover the real `ProgramId` (RISC0 image id): on this branch every program account lives
// at the direct `AccountId::from(program_id)` bijection, so this round-trip is exact. Needed
// for the PDA-derivation helpers below, which are pinned to the actual image id.
let self_program_id = ProgramId::from(self_account_id);
// pre_states: [source marker, config PDA, record PDA].
let [marker, config, record] = <[AccountWithMetadata; 3]>::try_from(pre_states)
.expect("Record requires the source marker, config, and record accounts");
@@ -56,8 +61,8 @@ fn record(
let cfg = ReceiverConfig::from_bytes(&config.account.data.clone().into_inner())
.expect("config account holds a receiver config");
assert_eq!(
caller_program_id,
Some(cfg.deliverer),
caller_account_id,
Some(cfg.deliverer.into()),
"Record is only callable by the authorized deliverer (the cross-zone inbox)"
);
// Which peer sent it is this program's own business. Without this the record
@@ -82,8 +87,8 @@ fn record(
AccountPostState::new_claimed_if_default(post_account, Claim::Pda(ping_record_seed()));
ProgramOutput::new(
self_program_id,
caller_program_id,
self_account_id,
caller_account_id,
instruction_words,
vec![marker.clone(), config.clone(), record],
vec![
@@ -98,14 +103,14 @@ fn record(
/// Writes the deliverer and the authorized peer sources into the config PDA
/// exactly once at genesis.
fn init_config(
self_program_id: ProgramId,
caller_program_id: Option<ProgramId>,
self_account_id: AccountId,
caller_account_id: Option<AccountId>,
pre_states: Vec<AccountWithMetadata>,
instruction_words: Vec<u32>,
config_value: &ReceiverConfig,
) {
assert!(
caller_program_id.is_none(),
caller_account_id.is_none(),
"InitConfig is a top-level genesis transaction"
);
@@ -114,7 +119,7 @@ fn init_config(
.expect("InitConfig requires the config account");
assert_eq!(
config.account_id,
receiver_config_account_id(self_program_id),
receiver_config_account_id(self_account_id.into()),
"account must be the receiver config PDA"
);
// Init-once, idempotent under genesis replay: a `default` config is a first
@@ -123,8 +128,7 @@ fn init_config(
// `new_claimed_if_default` alone would not stop a later self-owned rewrite.
if config.account != Account::default() {
assert_eq!(
config.account.program_owner,
self_program_id.into(),
config.account.program_owner, self_account_id,
"receiver config PDA is owned by another program"
);
assert_eq!(
@@ -145,8 +149,8 @@ fn init_config(
);
ProgramOutput::new(
self_program_id,
caller_program_id,
self_account_id,
caller_account_id,
instruction_words,
vec![config],
vec![config_post],
+19 -20
View File
@@ -1,6 +1,6 @@
use cross_zone_outbox_core::Instruction as OutboxInstruction;
use lee_core::{
account::{Account, AccountWithMetadata},
account::{Account, AccountId, AccountWithMetadata},
program::{
AccountPostState, ChainedCall, Claim, ProgramId, ProgramInput, ProgramOutput,
read_lee_inputs,
@@ -13,8 +13,8 @@ use ping_core::{
fn main() {
let (
ProgramInput {
self_program_id,
caller_program_id,
self_account_id,
caller_account_id,
pre_states,
instruction,
},
@@ -22,7 +22,7 @@ fn main() {
) = read_lee_inputs::<SenderInstruction>();
assert!(
caller_program_id.is_none(),
caller_account_id.is_none(),
"ping_sender is only invoked as a top-level user transaction"
);
@@ -34,8 +34,8 @@ fn main() {
payload,
ordinal,
} => send(
self_program_id,
caller_program_id,
self_account_id,
caller_account_id,
pre_states,
instruction_words,
target_zone,
@@ -45,8 +45,8 @@ fn main() {
ordinal,
),
SenderInstruction::InitConfig { outbox_program_id } => init_config(
self_program_id,
caller_program_id,
self_account_id,
caller_account_id,
pre_states,
instruction_words,
outbox_program_id,
@@ -59,8 +59,8 @@ fn main() {
reason = "the emission fields are passed through verbatim"
)]
fn send(
self_program_id: ProgramId,
caller_program_id: Option<ProgramId>,
self_account_id: AccountId,
caller_account_id: Option<AccountId>,
pre_states: Vec<AccountWithMetadata>,
instruction_words: Vec<u32>,
target_zone: [u8; 32],
@@ -78,7 +78,7 @@ fn send(
// skip the real outbox and leave no record of itself.
assert_eq!(
config.account_id,
sender_config_account_id(self_program_id),
sender_config_account_id(self_account_id.into()),
"first account must be the ping-sender config PDA"
);
let outbox_program_id = read_outbox(&config.account.data.clone().into_inner())
@@ -99,8 +99,8 @@ fn send(
let config_post = AccountPostState::new(config.account.clone());
ProgramOutput::new(
self_program_id,
caller_program_id,
self_account_id,
caller_account_id,
instruction_words,
vec![config, outbox.clone()],
vec![config_post, AccountPostState::new(outbox.account)],
@@ -111,8 +111,8 @@ fn send(
/// Writes the outbox program id into the config PDA exactly once at genesis.
fn init_config(
self_program_id: ProgramId,
caller_program_id: Option<ProgramId>,
self_account_id: AccountId,
caller_account_id: Option<AccountId>,
pre_states: Vec<AccountWithMetadata>,
instruction_words: Vec<u32>,
outbox_program_id: ProgramId,
@@ -122,7 +122,7 @@ fn init_config(
.expect("InitConfig requires the config account");
assert_eq!(
config.account_id,
sender_config_account_id(self_program_id),
sender_config_account_id(self_account_id.into()),
"account must be the ping-sender config PDA"
);
// Init-once, idempotent under genesis replay: a `default` config is a first
@@ -131,8 +131,7 @@ fn init_config(
// `new_claimed_if_default` alone would not stop a later self-owned rewrite.
if config.account != Account::default() {
assert_eq!(
config.account.program_owner,
self_program_id.into(),
config.account.program_owner, self_account_id,
"ping-sender config PDA is owned by another program"
);
assert_eq!(
@@ -151,8 +150,8 @@ fn init_config(
AccountPostState::new_claimed_if_default(config_account, Claim::Pda(sender_config_seed()));
ProgramOutput::new(
self_program_id,
caller_program_id,
self_account_id,
caller_account_id,
instruction_words,
vec![config],
vec![config_post],
+4 -4
View File
@@ -12,8 +12,8 @@ use token_program::core::Instruction;
fn main() {
let (
ProgramInput {
self_program_id,
caller_program_id,
self_account_id,
caller_account_id,
pre_states,
instruction,
},
@@ -84,8 +84,8 @@ fn main() {
};
ProgramOutput::new(
self_program_id,
caller_program_id,
self_account_id,
caller_account_id,
instruction_words,
pre_states_clone,
post_states,
+4 -4
View File
@@ -22,8 +22,8 @@ fn unchanged_post_states(
fn main() {
let (
ProgramInput {
self_program_id,
caller_program_id,
self_account_id,
caller_account_id,
pre_states,
instruction,
},
@@ -83,8 +83,8 @@ fn main() {
};
ProgramOutput::new(
self_program_id,
caller_program_id,
self_account_id,
caller_account_id,
instruction_words,
pre_states_clone,
post_states,
+24 -20
View File
@@ -11,8 +11,8 @@ use wrapped_token_core::{
fn main() {
let (
ProgramInput {
self_program_id,
caller_program_id,
self_account_id,
caller_account_id,
pre_states,
instruction,
},
@@ -21,16 +21,16 @@ fn main() {
match instruction {
Instruction::Mint { recipient, amount } => mint(
self_program_id,
caller_program_id,
self_account_id,
caller_account_id,
pre_states,
instruction_words,
recipient,
amount,
),
Instruction::InitConfig(config) => init_config(
self_program_id,
caller_program_id,
self_account_id,
caller_account_id,
pre_states,
instruction_words,
&config,
@@ -39,13 +39,18 @@ fn main() {
}
fn mint(
self_program_id: lee_core::program::ProgramId,
caller_program_id: Option<lee_core::program::ProgramId>,
self_account_id: lee_core::account::AccountId,
caller_account_id: Option<lee_core::account::AccountId>,
pre_states: Vec<AccountWithMetadata>,
instruction_words: Vec<u32>,
recipient: [u8; 32],
amount: u128,
) {
// Recover the real `ProgramId` (RISC0 image id): on this branch every program account lives
// at the direct `AccountId::from(program_id)` bijection, so this round-trip is exact. Needed
// for the PDA-derivation helpers below, which are pinned to the actual image id.
let self_program_id = lee_core::program::ProgramId::from(self_account_id);
// pre_states: [source marker, config PDA, recipient holding PDA].
let [marker, config, holding] = <[AccountWithMetadata; 3]>::try_from(pre_states)
.expect("Mint requires the source marker, config, and recipient holding accounts");
@@ -60,8 +65,8 @@ fn mint(
let cfg = WrappedTokenConfig::from_bytes(&config.account.data.clone().into_inner())
.expect("config account holds a wrapped-token config");
assert_eq!(
caller_program_id,
Some(cfg.minter),
caller_account_id,
Some(cfg.minter.into()),
"Mint is only callable by the authorized minter (the cross-zone inbox)"
);
// The inbox vouches only that the message arrived; which peer sent it is this
@@ -102,8 +107,8 @@ fn mint(
let config_post = AccountPostState::new(config.account.clone());
ProgramOutput::new(
self_program_id,
caller_program_id,
self_account_id,
caller_account_id,
instruction_words,
vec![marker.clone(), config, holding],
vec![
@@ -118,14 +123,14 @@ fn mint(
/// Writes the minter and the authorized peer sources into the config PDA exactly
/// once at genesis.
fn init_config(
self_program_id: lee_core::program::ProgramId,
caller_program_id: Option<lee_core::program::ProgramId>,
self_account_id: lee_core::account::AccountId,
caller_account_id: Option<lee_core::account::AccountId>,
pre_states: Vec<AccountWithMetadata>,
instruction_words: Vec<u32>,
config_value: &WrappedTokenConfig,
) {
assert!(
caller_program_id.is_none(),
caller_account_id.is_none(),
"InitConfig is a top-level genesis transaction"
);
@@ -134,7 +139,7 @@ fn init_config(
.expect("InitConfig requires the config account");
assert_eq!(
config.account_id,
config_account_id(self_program_id),
config_account_id(self_account_id.into()),
"account must be the wrapped-token config PDA"
);
// Init-once, idempotent under genesis replay: a `default` config is a first
@@ -145,8 +150,7 @@ fn init_config(
// rewriting its own config data on a later call.
if config.account != Account::default() {
assert_eq!(
config.account.program_owner,
self_program_id.into(),
config.account.program_owner, self_account_id,
"wrapped-token config PDA is owned by another program"
);
assert_eq!(
@@ -165,8 +169,8 @@ fn init_config(
AccountPostState::new_claimed_if_default(config_account, Claim::Pda(config_seed()));
ProgramOutput::new(
self_program_id,
caller_program_id,
self_account_id,
caller_account_id,
instruction_words,
vec![config],
vec![config_post],