Files
logos-execution-zone/lez/programs/ping_sender/src/main.rs
T
moudyellaz 968a17f867 Merge remote-tracking branch 'origin/dev' into moudy/cross-zone-target-auth
# Conflicts:
#	Cargo.lock
#	artifacts/lez/programs/amm.bin
#	artifacts/lez/programs/associated_token_account.bin
#	artifacts/lez/programs/authenticated_transfer.bin
#	artifacts/lez/programs/bridge.bin
#	artifacts/lez/programs/bridge_lock.bin
#	artifacts/lez/programs/clock.bin
#	artifacts/lez/programs/cross_zone_inbox.bin
#	artifacts/lez/programs/cross_zone_outbox.bin
#	artifacts/lez/programs/faucet.bin
#	artifacts/lez/programs/pinata.bin
#	artifacts/lez/programs/pinata_token.bin
#	artifacts/lez/programs/ping_receiver.bin
#	artifacts/lez/programs/ping_sender.bin
#	artifacts/lez/programs/token.bin
#	artifacts/lez/programs/vault.bin
#	artifacts/lez/programs/wrapped_token.bin
#	test_fixtures/fixtures/prebuilt_sequencer_db.dump
2026-08-19 23:40:53 +02:00

162 lines
4.8 KiB
Rust

use cross_zone_outbox_core::Instruction as OutboxInstruction;
use lee_core::{
account::{Account, AccountWithMetadata},
program::{
AccountPostState, ChainedCall, Claim, ProgramId, ProgramInput, ProgramOutput,
read_lee_inputs,
},
};
use ping_core::{
SenderInstruction, outbox_bytes, read_outbox, sender_config_account_id, sender_config_seed,
};
fn main() {
let (
ProgramInput {
self_program_id,
caller_program_id,
pre_states,
instruction,
},
instruction_words,
) = read_lee_inputs::<SenderInstruction>();
assert!(
caller_program_id.is_none(),
"ping_sender is only invoked as a top-level user transaction"
);
match instruction {
SenderInstruction::Send {
target_zone,
target_program_id,
target_accounts,
payload,
ordinal,
} => send(
self_program_id,
caller_program_id,
pre_states,
instruction_words,
target_zone,
target_program_id,
target_accounts,
payload,
ordinal,
),
SenderInstruction::InitConfig { outbox_program_id } => init_config(
self_program_id,
caller_program_id,
pre_states,
instruction_words,
outbox_program_id,
),
}
}
#[expect(
clippy::too_many_arguments,
reason = "the emission fields are passed through verbatim"
)]
fn send(
self_program_id: ProgramId,
caller_program_id: Option<ProgramId>,
pre_states: Vec<AccountWithMetadata>,
instruction_words: Vec<u32>,
target_zone: [u8; 32],
target_program_id: ProgramId,
target_accounts: Vec<[u8; 32]>,
payload: Vec<u8>,
ordinal: u32,
) {
// pre_states: [config PDA, outbox PDA]. The outbox claims its own slot, so
// ping_sender forwards it unchanged.
let [config, outbox] = <[AccountWithMetadata; 2]>::try_from(pre_states)
.expect("Send requires the config and outbox accounts");
// Pinned rather than caller-named: chaining elsewhere would let an emission
// skip the real outbox and leave no record of itself.
assert_eq!(
config.account_id,
sender_config_account_id(self_program_id),
"first account must be the ping-sender config PDA"
);
let outbox_program_id =
read_outbox(&config.account.data).expect("config account holds an outbox program id");
let call = ChainedCall::new(
outbox_program_id,
vec![outbox.clone()],
&OutboxInstruction::Emit {
target_zone,
target_program_id,
target_accounts,
payload,
ordinal,
},
);
let config_post = AccountPostState::new(config.account.clone());
ProgramOutput::new(
self_program_id,
caller_program_id,
instruction_words,
vec![config, outbox.clone()],
vec![config_post, AccountPostState::new(outbox.account)],
)
.with_chained_calls(vec![call])
.write();
}
/// 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>,
pre_states: Vec<AccountWithMetadata>,
instruction_words: Vec<u32>,
outbox_program_id: ProgramId,
) {
// pre_states: [config PDA].
let [config] = <[AccountWithMetadata; 1]>::try_from(pre_states)
.expect("InitConfig requires the config account");
assert_eq!(
config.account_id,
sender_config_account_id(self_program_id),
"account must be the ping-sender config PDA"
);
// Init-once, idempotent under genesis replay: a `default` config is a first
// init; an already-owned one must already pin exactly this outbox, since
// genesis is replayed onto seeded state during multi-sequencer reconstruction.
// `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(),
"ping-sender config PDA is owned by another program"
);
assert_eq!(
*config.account.data,
outbox_bytes(outbox_program_id),
"ping-sender config already pins a different outbox"
);
}
let mut config_account = config.account.clone();
config_account.data = outbox_bytes(outbox_program_id)
.to_vec()
.try_into()
.expect("outbox id fits in account data");
let config_post =
AccountPostState::new_claimed_if_default(config_account, Claim::Pda(sender_config_seed()));
ProgramOutput::new(
self_program_id,
caller_program_id,
instruction_words,
vec![config],
vec![config_post],
)
.write();
}