1#[cfg(feature = "anchor-lang")]
2use anchor_lang::{prelude::*, Bumps};
3use solana_program::{
4 account_info::AccountInfo, program_error::ProgramError, pubkey, pubkey::Pubkey,
5};
6
7pub use self::{cpi::*, deser::MessageDataRef};
8use self::{deser::DeserializeRef, utils::split_at_checked};
9
10pub mod chains;
11mod cpi;
12mod deser;
13#[cfg(feature = "extension")]
14pub mod extension;
15mod utils;
16
17#[deprecated(note = "Please use `UipEndpoint::id()` instead")]
18pub const UIP_PROGRAM_ID: Pubkey = pubkey!("uipby67GWuDDt1jZTWFdXNrsSu83kcxt9r5CLPTKGhX");
19
20pub const EXECUTE_DISCRIMINATOR: &[u8] = b"execute\0";
23const MESSAGE_DISCRIMINATOR: &[u8] = b"message\0";
24
25pub struct UipEndpoint;
27
28impl UipEndpoint {
29 pub const fn id() -> Pubkey {
31 pubkey!("uipby67GWuDDt1jZTWFdXNrsSu83kcxt9r5CLPTKGhX")
32 }
33}
34
35#[cfg(feature = "anchor-lang")]
36impl Id for UipEndpoint {
37 fn id() -> Pubkey {
38 Self::id()
39 }
40}
41
42pub fn parse_uip_message<'a>(
46 message: &'a AccountInfo,
47 message_data: &'a [u8],
48 expected_dest_addr: &Pubkey,
49) -> core::result::Result<MessageDataRef<'a>, ProgramError> {
50 if message.owner != &UipEndpoint::id() {
51 return Err(ProgramError::InvalidAccountOwner);
52 }
53 if !message.is_signer {
54 return Err(ProgramError::MissingRequiredSignature);
55 }
56
57 let Some(rest) = message_data.strip_prefix(MESSAGE_DISCRIMINATOR) else {
58 return Err(ProgramError::InvalidAccountData);
59 };
60
61 deserialize_message(rest, expected_dest_addr)
62}
63
64fn deserialize_message<'a>(
65 data: &'a [u8],
66 expected_dest_addr: &Pubkey,
67) -> core::result::Result<MessageDataRef<'a>, ProgramError> {
68 let data = &mut &data[..];
69 let mut read_data = |count| -> core::result::Result<&[u8], ProgramError> {
70 let (left, right) =
71 split_at_checked(data, count).ok_or(ProgramError::InvalidAccountData)?;
72 *data = right;
73 Ok(left)
74 };
75
76 let _message_status = read_data(1)?;
77
78 let option_tag = read_data(1)?[0];
79 if option_tag == 0 {
80 return Err(ProgramError::InvalidAccountData);
81 }
82
83 let _payer = read_data(32)?;
84
85 let _loaded_at = read_data(8)?;
86
87 let msg_data = MessageDataRef::deserialize_ref(data).ok_or(ProgramError::InvalidAccountData)?;
88
89 if &msg_data.dest_addr != expected_dest_addr {
90 return Err(ProgramError::InvalidAccountData);
91 }
92
93 Ok(msg_data)
94}
95
96#[cfg(feature = "anchor-lang")]
98pub fn route_instruction<'info, Handler, Accs, InstructionData, HandlerParams>(
99 program_id: &Pubkey,
100 handler: Handler,
101 accounts: &'info [AccountInfo<'info>],
102 instruction_data: InstructionData,
103 params: HandlerParams,
104) -> Result<()>
105where
106 Handler: FnOnce(Context<Accs>, HandlerParams) -> Result<()>,
107 Accs: Accounts<'info, Accs::Bumps> + Bumps + AccountsExit<'info>,
108 Accs::Bumps: Default,
109 InstructionData: AnchorSerialize,
110{
111 let mut data = Vec::new();
112 instruction_data.serialize(&mut data)?;
113
114 let remaining_accounts = &mut &accounts[..];
115 let mut bumps = Default::default();
116 let mut accounts = Accs::try_accounts(
117 program_id,
118 remaining_accounts,
119 &data,
120 &mut bumps,
121 &mut Default::default(),
122 )?;
123 handler(Context::new(program_id, &mut accounts, remaining_accounts, bumps), params)?;
124 accounts.exit(program_id)
125}
126
127#[cfg(test)]
128mod tests {
129 #[cfg(feature = "anchor-lang")]
130 use rand::{random, thread_rng, Rng};
131 #[cfg(feature = "anchor-lang")]
132 use uip_endpoint::state::{
133 Message, MessageData, MessageInfo, MessageStatus, Proposal, SrcChainData,
134 };
135
136 use super::*;
137
138 #[test]
139 fn test_program_id() {
140 assert_eq!(UipEndpoint::id(), uip_endpoint::ID_CONST);
141 }
142
143 #[test]
144 fn test_discriminators() {
145 assert_eq!(EXECUTE_DISCRIMINATOR, uip_endpoint::DESTINATION_EXECUTE_DISCRIMINATOR);
146 }
147
148 #[test]
149 #[cfg(feature = "anchor-lang")]
150 fn test_anchor_discriminators() {
151 assert_eq!(MESSAGE_DISCRIMINATOR, Message::DISCRIMINATOR);
152 }
153
154 #[test]
155 #[cfg(feature = "anchor-lang")]
156 fn test_deserialize_message() {
157 let mut rng = thread_rng();
158
159 for _ in 0..100 {
160 let expected_dest_addr = Pubkey::new_from_array(random());
161
162 let expected_total_fee = random();
163 let expected_selector = random();
164 let expected_payload: Vec<_> = (0..rng.gen_range(0..1024)).map(|_| random()).collect();
165 let expected_src_chain_id = random();
166 let expected_sender_addr: Vec<_> =
167 (0..rng.gen_range(0..32)).map(|_| random()).collect();
168 let expected_reserved: Vec<_> = (0..rng.gen_range(0..100)).map(|_| random()).collect();
169 let expected_transmitter_params: Vec<_> =
170 (0..rng.gen_range(0..100)).map(|_| random()).collect();
171 let expected_src_block_number = random();
172 let expected_src_op_tx_id = [random(); 2];
173
174 let expected_msg_hash = random();
175
176 let msg = Message {
177 status: MessageStatus::Signed,
178 info: Some(MessageInfo {
179 payer: Pubkey::new_from_array(random()),
180 hash: expected_msg_hash,
181 loaded_at: random(),
182 msg_data: MessageData {
183 initial_proposal: Proposal {
184 total_fee: expected_total_fee,
185 selector: expected_selector,
186 sender_addr: expected_sender_addr.clone(),
187 dest_addr: expected_dest_addr,
188 payload: expected_payload.clone(),
189 reserved: expected_reserved.clone(),
190 transmitter_params: expected_transmitter_params.clone(),
191 },
192 src_chain_data: SrcChainData {
193 src_chain_id: expected_src_chain_id,
194 src_block_number: expected_src_block_number,
195 src_op_tx_id: expected_src_op_tx_id,
196 },
197 },
198 }),
199 };
200
201 let data = msg.try_to_vec().unwrap();
202 let MessageDataRef {
203 payload,
204 src_chain_id,
205 sender_addr,
206 msg_hash,
207 total_fee,
208 selector,
209 reserved,
210 dest_addr,
211 transmitter_params,
212 src_block_number,
213 src_op_tx_id,
214 } = deserialize_message(&data, &expected_dest_addr).unwrap();
215 assert_eq!(payload, expected_payload);
216 assert_eq!(src_chain_id, expected_src_chain_id);
217 assert_eq!(sender_addr, expected_sender_addr);
218 assert_eq!(msg_hash, &expected_msg_hash);
219 assert_eq!(total_fee, expected_total_fee);
220 assert_eq!(selector, &expected_selector);
221 assert_eq!(reserved, expected_reserved);
222 assert_eq!(dest_addr, expected_dest_addr);
223 assert_eq!(transmitter_params, expected_transmitter_params);
224 assert_eq!(src_block_number, expected_src_block_number);
225 assert_eq!(src_op_tx_id, &expected_src_op_tx_id);
226 }
227 }
228}