test(cross-zone): add ping_receiver and a single-zone inbox dispatch test

This commit is contained in:
moudyellaz 2026-06-19 17:50:50 +02:00
parent 30bd869ac2
commit 42078d0305
12 changed files with 222 additions and 2 deletions

11
Cargo.lock generated
View File

@ -4524,6 +4524,7 @@ dependencies = [
"borsh",
"bridge_core",
"clock_core",
"cross_zone_inbox_core",
"env_logger",
"faucet_core",
"hex",
@ -4531,6 +4532,7 @@ dependencies = [
"k256",
"lee_core",
"log",
"ping_core",
"rand 0.8.6",
"risc0-binfmt",
"risc0-build",
@ -7133,6 +7135,14 @@ version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
[[package]]
name = "ping_core"
version = "0.1.0"
dependencies = [
"lee_core",
"serde",
]
[[package]]
name = "pkcs1"
version = "0.7.5"
@ -7409,6 +7419,7 @@ dependencies = [
"cross_zone_outbox_core",
"faucet_core",
"lee_core",
"ping_core",
"risc0-zkvm",
"serde",
"token_core",

View File

@ -26,6 +26,7 @@ members = [
"programs/vault/core",
"programs/cross_zone_outbox/core",
"programs/cross_zone_inbox/core",
"programs/ping/core",
"lez/sequencer/core",
"lez/sequencer/service",
"lez/sequencer/service/protocol",
@ -84,6 +85,7 @@ bridge_core = { path = "programs/bridge/core" }
vault_core = { path = "programs/vault/core" }
cross_zone_outbox_core = { path = "programs/cross_zone_outbox/core" }
cross_zone_inbox_core = { path = "programs/cross_zone_inbox/core" }
ping_core = { path = "programs/ping/core" }
test_program_methods = { path = "test_program_methods" }
testnet_initial_state = { path = "lez/testnet_initial_state" }
keycard_wallet = { path = "lez/keycard_wallet" }

Binary file not shown.

View File

@ -34,6 +34,8 @@ risc0-binfmt = "3.0.2"
lee_core = { workspace = true, features = ["test_utils"] }
token_core.workspace = true
authenticated_transfer_core.workspace = true
cross_zone_inbox_core.workspace = true
ping_core.workspace = true
test_program_methods.workspace = true
env_logger.workspace = true

View File

@ -0,0 +1,104 @@
#![expect(
clippy::tests_outside_test_module,
clippy::arithmetic_side_effects,
reason = "We don't care about these in tests"
)]
use std::collections::BTreeMap;
use cross_zone_inbox_core::{
CrossZoneMessage, InboxConfig, Instruction, inbox_config_account_id,
inbox_seen_shard_account_id,
};
use lee_core::account::Account;
use ping_core::{ReceiverInstruction, ping_record_pda};
use crate::{
V03State,
program::Program,
public_transaction::{Message, WitnessSet},
validated_state_diff::ValidatedStateDiff,
};
/// Drives `cross_zone_inbox::Dispatch` directly through the state machine
/// (no watcher) and asserts the message is delivered to `ping_receiver`, which
/// records the payload into its own PDA.
#[test]
fn inbox_dispatch_delivers_payload_to_ping_receiver() {
let mut state = V03State::new_with_genesis_accounts(&[], vec![], 0);
// ping_receiver is a throwaway demo target, registered only in this test state.
state.insert_program(Program::ping_receiver());
let inbox_id = Program::cross_zone_inbox().id();
let receiver_id = Program::ping_receiver().id();
let self_zone = [1_u8; 32];
let src_zone = [2_u8; 32];
let src_block_id = 5;
// Seed the inbox config account (inbox-owned) allowing src_zone -> ping_receiver.
let mut allowed_targets = BTreeMap::new();
allowed_targets.insert(src_zone, vec![receiver_id]);
let config = InboxConfig {
self_zone,
allowed_peers: BTreeMap::new(),
allowed_targets,
};
let config_id = inbox_config_account_id(inbox_id);
state.force_insert_account(
config_id,
Account {
program_owner: inbox_id,
balance: 0_u128,
data: config
.to_bytes()
.try_into()
.expect("config fits in account data"),
nonce: 0_u128.into(),
},
);
// The payload is the ping_receiver instruction, serialized as risc0 words in
// little-endian bytes (the contract the inbox reverses when forwarding).
let inner = b"hello-cross-zone".to_vec();
let words = risc0_zkvm::serde::to_vec(&ReceiverInstruction::Record {
payload: inner.clone(),
})
.expect("serialize ping instruction");
let payload: Vec<u8> = words.iter().flat_map(|word| word.to_le_bytes()).collect();
let msg = CrossZoneMessage {
src_zone,
src_block_id,
src_tx_index: 0,
src_program_id: [9_u32; 8],
target_program_id: receiver_id,
payload,
l1_inclusion_witness: None,
};
let seen_id = inbox_seen_shard_account_id(inbox_id, &src_zone, src_block_id);
let record_id = ping_record_pda(receiver_id);
let message = Message::try_new(
inbox_id,
vec![config_id, seen_id, record_id],
vec![],
Instruction::Dispatch(msg),
)
.expect("build dispatch message");
let tx = crate::PublicTransaction::new(message, WitnessSet::from_raw_parts(vec![]));
let diff = ValidatedStateDiff::from_public_transaction(&tx, &state, 1, 0)
.expect("dispatch must validate and execute");
let public_diff = diff.public_diff();
let record = public_diff
.get(&record_id)
.expect("ping record account must change");
assert_eq!(
record.data.clone().into_inner(),
inner,
"ping_receiver must record the delivered payload"
);
}

View File

@ -33,6 +33,9 @@ mod signature;
mod state;
mod validated_state_diff;
#[cfg(test)]
mod cross_zone_dispatch_tests;
pub mod program_methods {
include!(concat!(env!("OUT_DIR"), "/program_methods/mod.rs"));
}

View File

@ -12,8 +12,8 @@ use crate::{
AMM_ELF, AMM_ID, ASSOCIATED_TOKEN_ACCOUNT_ELF, ASSOCIATED_TOKEN_ACCOUNT_ID,
AUTHENTICATED_TRANSFER_ELF, AUTHENTICATED_TRANSFER_ID, BRIDGE_ELF, BRIDGE_ID, CLOCK_ELF,
CLOCK_ID, CROSS_ZONE_INBOX_ELF, CROSS_ZONE_INBOX_ID, CROSS_ZONE_OUTBOX_ELF,
CROSS_ZONE_OUTBOX_ID, FAUCET_ELF, FAUCET_ID, PINATA_ELF, PINATA_ID, TOKEN_ELF, TOKEN_ID,
VAULT_ELF, VAULT_ID,
CROSS_ZONE_OUTBOX_ID, FAUCET_ELF, FAUCET_ID, PINATA_ELF, PINATA_ID, PING_RECEIVER_ELF,
PING_RECEIVER_ID, TOKEN_ELF, TOKEN_ID, VAULT_ELF, VAULT_ID,
},
};
@ -190,6 +190,14 @@ impl Program {
elf: CROSS_ZONE_INBOX_ELF.to_vec(),
}
}
#[must_use]
pub fn ping_receiver() -> Self {
Self {
id: PING_RECEIVER_ID,
elf: PING_RECEIVER_ELF.to_vec(),
}
}
}
// TODO: Testnet only. Refactor to prevent compilation on mainnet.

View File

@ -711,6 +711,11 @@ pub mod tests {
this.insert(Program::vault().id(), Program::vault());
this.insert(Program::faucet().id(), Program::faucet());
this.insert(Program::bridge().id(), Program::bridge());
this.insert(
Program::cross_zone_outbox().id(),
Program::cross_zone_outbox(),
);
this.insert(Program::cross_zone_inbox().id(), Program::cross_zone_inbox());
this
};

View File

@ -22,5 +22,6 @@ bridge_core.workspace = true
vault_core.workspace = true
cross_zone_outbox_core.workspace = true
cross_zone_inbox_core.workspace = true
ping_core.workspace = true
risc0-zkvm.workspace = true
serde = { workspace = true, default-features = false }

View File

@ -0,0 +1,47 @@
use lee_core::{
account::AccountWithMetadata,
program::{AccountPostState, Claim, ProgramInput, ProgramOutput, read_lee_inputs},
};
use ping_core::{ReceiverInstruction, ping_record_pda, ping_record_seed};
fn main() {
let (
ProgramInput {
self_program_id,
caller_program_id,
pre_states,
instruction,
},
instruction_words,
) = read_lee_inputs::<ReceiverInstruction>();
assert!(
caller_program_id.is_some(),
"ping_receiver is only callable through a chained call"
);
let payload = match instruction {
ReceiverInstruction::Record { payload } => payload,
};
let [record] = <[AccountWithMetadata; 1]>::try_from(pre_states)
.expect("Record requires exactly 1 account");
assert_eq!(
record.account_id,
ping_record_pda(self_program_id),
"Account must be the ping record PDA"
);
let mut post_account = record.account.clone();
post_account.data = payload.try_into().expect("payload fits in account data");
let post = AccountPostState::new_claimed_if_default(post_account, Claim::Pda(ping_record_seed()));
ProgramOutput::new(
self_program_id,
caller_program_id,
instruction_words,
vec![record],
vec![post],
)
.write();
}

View File

@ -0,0 +1,12 @@
[package]
name = "ping_core"
version = "0.1.0"
edition = "2024"
license = { workspace = true }
[lints]
workspace = true
[dependencies]
lee_core.workspace = true
serde = { workspace = true, features = ["alloc"] }

View File

@ -0,0 +1,25 @@
use lee_core::{
account::AccountId,
program::{PdaSeed, ProgramId},
};
use serde::{Deserialize, Serialize};
const PING_RECORD_SEED: [u8; 32] = *b"/LEZ/v0.3/PingRecord/0000000000/";
/// Instruction delivered to `ping_receiver` by the inbox: record the payload.
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum ReceiverInstruction {
Record { payload: Vec<u8> },
}
/// The account a `ping_receiver` records the latest delivered payload into.
#[must_use]
pub fn ping_record_pda(receiver_id: ProgramId) -> AccountId {
AccountId::for_public_pda(&receiver_id, &ping_record_seed())
}
/// Seed of the record PDA, exposed so the guest can claim the account.
#[must_use]
pub fn ping_record_seed() -> PdaSeed {
PdaSeed::new(PING_RECORD_SEED)
}