// file: crates/ksp-interface-lib/src/program_instruction.rs // version: 1 /// Maximum opaque data payload admitted by one passive Program instruction contract. pub const MAX_PROGRAM_INSTRUCTION_DATA_LEN: usize = 10 * 1024; /// Passive, bounded Program instruction wire contract. /// /// The instruction preserves the caller-provided Program identity, ordered /// account metas and opaque data bytes without applying Program-specific /// semantics. Construction enforces only the Interface-owned admission bounds. pub struct ProgramInstruction { program_id: crate::Pubkey, accounts: std::vec::Vec, data: std::vec::Vec, } impl ProgramInstruction { /// Creates one passive Program instruction after enforcing Interface admission bounds. /// /// The provided vectors are consumed directly. Their account order and duplicates /// are preserved exactly when construction succeeds. pub fn try_new(program_id: crate::Pubkey, accounts: std::vec::Vec, data: std::vec::Vec) -> ksp_core_lib::Result { if accounts.len() > crate::MAX_PROGRAM_INSTRUCTION_ACCOUNTS { return std::result::Result::Err( ksp_core_lib::Error::new( crate::ERROR_CODE_PROGRAM_INSTRUCTION_LIMIT_EXCEEDED, "Program instruction account count exceeds the Interface admission limit", ) .with_context("field", "accounts") .with_context("actual_len", accounts.len().to_string()) .with_context("maximum_len", crate::MAX_PROGRAM_INSTRUCTION_ACCOUNTS.to_string()), ); } if data.len() > crate::MAX_PROGRAM_INSTRUCTION_DATA_LEN { return std::result::Result::Err( ksp_core_lib::Error::new( crate::ERROR_CODE_PROGRAM_INSTRUCTION_LIMIT_EXCEEDED, "Program instruction data length exceeds the Interface admission limit", ) .with_context("field", "data") .with_context("actual_len", data.len().to_string()) .with_context("maximum_len", crate::MAX_PROGRAM_INSTRUCTION_DATA_LEN.to_string()), ); } return std::result::Result::Ok(Self { program_id, accounts, data }); } /// Returns the ordered account metas exactly as admitted at construction. #[must_use] pub fn accounts(&self) -> &[crate::ProgramAccountMeta] { return self.accounts.as_slice(); } /// Returns the opaque Program instruction data bytes. #[must_use] pub fn data(&self) -> &[u8] { return self.data.as_slice(); } /// Returns the Program identity. #[must_use] pub const fn program_id(&self) -> &crate::Pubkey { return &self.program_id; } } impl std::fmt::Debug for ProgramInstruction { fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { return formatter .debug_struct("ProgramInstruction") .field("program_id", &self.program_id) .field("account_count", &self.accounts.len()) .field("data_len", &self.data.len()) .finish(); } } #[cfg(test)] #[path = "../unit_tests/program_instruction.rs"] mod tests;