1use {
2 crate::{
3 cpi::validate::check_account_change,
4 errors::{RuntimeError, RuntimeResult},
5 },
6 solana_account::Account,
7 solana_address::Address,
8 solana_instruction::AccountMeta,
9 std::{collections::HashMap, mem::size_of},
10};
11
12const ALIGN_OF_U64: usize = 8;
13const MAX_PERMITTED_DATA_INCREASE: usize = 10240;
14const NON_DUP_MARKER: u8 = 0xff;
15
16struct Serializer {
17 buffer: Vec<u8>,
18}
19
20impl Serializer {
21 fn new() -> Self {
22 Self { buffer: Vec::new() }
23 }
24
25 fn write<T>(&mut self, value: T) {
26 let bytes =
27 unsafe { std::slice::from_raw_parts(&value as *const T as *const u8, size_of::<T>()) };
28 self.buffer.extend_from_slice(bytes);
29 }
30
31 fn write_all(&mut self, data: &[u8]) {
32 self.buffer.extend_from_slice(data);
33 }
34
35 fn write_account_data(&mut self, data: &[u8]) {
36 self.write_all(data);
37 self.buffer
38 .extend(std::iter::repeat_n(0u8, MAX_PERMITTED_DATA_INCREASE));
39 let alignment_needed = (ALIGN_OF_U64 - (self.buffer.len() % ALIGN_OF_U64)) % ALIGN_OF_U64;
40 self.buffer
41 .extend(std::iter::repeat_n(0u8, alignment_needed));
42 }
43
44 fn finish(self) -> Vec<u8> {
45 self.buffer
46 }
47}
48
49enum SerializeAccount {
50 Account(Address, Account, bool, bool),
51 Duplicate(u8),
52}
53
54pub fn serialize_parameters(
55 accounts: &HashMap<Address, Account>,
56 account_metas: &[AccountMeta],
57 instruction_data: &[u8],
58 program_id: &Address,
59) -> RuntimeResult<(Vec<u8>, Vec<usize>, usize)> {
60 let mut seen: HashMap<Address, usize> = HashMap::new();
61 let mut serialize_accounts = Vec::with_capacity(account_metas.len());
62
63 for (i, meta) in account_metas.iter().enumerate() {
64 if let Some(&first_idx) = seen.get(&meta.pubkey) {
65 serialize_accounts.push(SerializeAccount::Duplicate(first_idx as u8));
66 } else {
67 seen.insert(meta.pubkey, i);
68 let account = accounts.get(&meta.pubkey).ok_or_else(|| {
69 RuntimeError::MissingAccount(format!("Missing account data for {}", meta.pubkey))
70 })?;
71 serialize_accounts.push(SerializeAccount::Account(
72 meta.pubkey,
73 account.clone(),
74 meta.is_signer,
75 meta.is_writable,
76 ));
77 }
78 }
79
80 let mut s = Serializer::new();
81 let mut pre_lens: Vec<usize> = Vec::new();
82 s.write::<u64>((serialize_accounts.len() as u64).to_le());
83
84 for account in serialize_accounts {
85 match account {
86 SerializeAccount::Account(pubkey, acct, is_signer, is_writable) => {
87 s.write::<u8>(NON_DUP_MARKER);
88 s.write::<u8>(is_signer as u8);
89 s.write::<u8>(is_writable as u8);
90 s.write::<u8>(acct.executable as u8);
91 s.write_all(&[0u8; 4]);
92 s.write_all(pubkey.as_ref());
93 s.write_all(acct.owner.as_ref());
94 s.write::<u64>(acct.lamports.to_le());
95 pre_lens.push(acct.data.len());
96 s.write::<u64>((acct.data.len() as u64).to_le());
97 s.write_account_data(&acct.data);
98 s.write::<u64>(acct.rent_epoch.to_le());
99 }
100 SerializeAccount::Duplicate(position) => {
101 s.write::<u8>(position);
102 s.write_all(&[0u8; 7]);
103 }
104 }
105 }
106
107 s.write::<u64>((instruction_data.len() as u64).to_le());
108 let instruction_data_offset = s.buffer.len();
109 s.write_all(instruction_data);
110 s.write_all(program_id.as_ref());
111
112 Ok((s.finish(), pre_lens, instruction_data_offset))
113}
114
115struct Deserializer<'a> {
116 buffer: &'a [u8],
117 offset: usize,
118}
119
120impl<'a> Deserializer<'a> {
121 fn new(buffer: &'a [u8]) -> Self {
122 Self { buffer, offset: 0 }
123 }
124
125 fn read_u64(&mut self) -> u64 {
126 let bytes: [u8; 8] = self.buffer[self.offset..self.offset + 8]
127 .try_into()
128 .unwrap_or([0u8; 8]);
129 self.offset += 8;
130 u64::from_le_bytes(bytes)
131 }
132
133 fn skip(&mut self, n: usize) {
134 self.offset += n;
135 }
136
137 fn read_address(&mut self) -> Address {
138 let bytes: [u8; 32] = self.buffer[self.offset..self.offset + 32]
139 .try_into()
140 .unwrap_or([0u8; 32]);
141 self.offset += 32;
142 Address::from(bytes)
143 }
144
145 fn read_account_data(&mut self, post_len: usize, pre_len: usize) -> &'a [u8] {
146 let data = &self.buffer[self.offset..self.offset + post_len];
147 self.offset += pre_len;
149 self.offset += MAX_PERMITTED_DATA_INCREASE;
150 let align = (ALIGN_OF_U64 - (self.offset % ALIGN_OF_U64)) % ALIGN_OF_U64;
151 self.offset += align;
152 data
153 }
154}
155
156pub fn deserialize_parameters(
157 accounts: &mut HashMap<Address, Account>,
158 account_metas: &[AccountMeta],
159 input: &[u8],
160 pre_lens: &[usize],
161 callee_program_id: &Address,
162) -> RuntimeResult<()> {
163 let mut d = Deserializer::new(input);
164 let _num_accounts = d.read_u64();
165 let mut seen: HashMap<Address, usize> = HashMap::new();
166 let mut pre_len_idx = 0;
167
168 for (i, meta) in account_metas.iter().enumerate() {
169 if seen.contains_key(&meta.pubkey) {
170 d.skip(8); continue;
172 }
173 seen.insert(meta.pubkey, i);
174
175 let pre_len = pre_lens.get(pre_len_idx).copied().unwrap_or(0);
176 pre_len_idx += 1;
177
178 d.skip(8); d.skip(32); let owner = d.read_address();
181 let lamports = d.read_u64();
182 let data_len = d.read_u64() as usize;
183 let data = d.read_account_data(data_len, pre_len);
184 let _rent_epoch = d.read_u64();
185
186 if meta.is_writable
187 && let Some(account) = accounts.get_mut(&meta.pubkey)
188 {
189 check_account_change(
190 callee_program_id,
191 &meta.pubkey,
192 account,
193 &owner,
194 lamports,
195 data,
196 )?;
197 account.lamports = lamports;
198 account.data = data.to_vec();
199 account.owner = owner;
200 }
201 }
202 Ok(())
203}
204
205#[cfg(test)]
206mod tests {
207 use super::*;
208
209 const PROGRAM: Address = Address::new_from_array([1u8; 32]);
210 const OTHER: Address = Address::new_from_array([2u8; 32]);
211 const ACCT_1: Address = Address::new_from_array([10u8; 32]);
212 const ACCT_2: Address = Address::new_from_array([11u8; 32]);
213
214 fn make_account(owner: Address, lamports: u64, data: &[u8]) -> Account {
215 Account {
216 lamports,
217 data: data.to_vec(),
218 owner,
219 executable: false,
220 rent_epoch: 0,
221 }
222 }
223
224 fn accounts_map(list: &[(Address, Account)]) -> HashMap<Address, Account> {
225 list.iter().cloned().collect()
226 }
227
228 #[test]
229 fn e2e_accounts_preserved() {
230 let mut accounts = accounts_map(&[
231 (ACCT_1, make_account(PROGRAM, 100, b"hello")),
232 (ACCT_2, make_account(PROGRAM, 200, b"world")),
233 ]);
234 let metas = vec![
235 AccountMeta {
236 pubkey: ACCT_1,
237 is_signer: false,
238 is_writable: true,
239 },
240 AccountMeta {
241 pubkey: ACCT_2,
242 is_signer: false,
243 is_writable: true,
244 },
245 ];
246 let (input, pre_lens, _) =
247 serialize_parameters(&accounts, &metas, b"ix", &PROGRAM).unwrap();
248 deserialize_parameters(&mut accounts, &metas, &input, &pre_lens, &PROGRAM).unwrap();
249
250 assert_eq!(accounts[&ACCT_1].lamports, 100);
251 assert_eq!(accounts[&ACCT_1].data, b"hello");
252 assert_eq!(accounts[&ACCT_2].lamports, 200);
253 assert_eq!(accounts[&ACCT_2].data, b"world");
254 }
255
256 #[test]
257 fn read_only_account_not_updated() {
258 let mut accounts = accounts_map(&[(ACCT_1, make_account(PROGRAM, 100, b"data"))]);
259 let metas = vec![AccountMeta {
260 pubkey: ACCT_1,
261 is_signer: false,
262 is_writable: false,
263 }];
264 let (mut input, pre_lens, _) =
265 serialize_parameters(&accounts, &metas, b"", &PROGRAM).unwrap();
266
267 let lamport_offset = 8 + 8 + 32 + 32;
269 input[lamport_offset..lamport_offset + 8].copy_from_slice(&999u64.to_le_bytes());
270
271 deserialize_parameters(&mut accounts, &metas, &input, &pre_lens, &PROGRAM).unwrap();
272 assert_eq!(accounts[&ACCT_1].lamports, 100); }
274
275 #[test]
276 fn non_owner_data_change_rejected() {
277 let mut accounts = accounts_map(&[(ACCT_1, make_account(OTHER, 100, b"original"))]);
278 let metas = vec![AccountMeta {
279 pubkey: ACCT_1,
280 is_signer: false,
281 is_writable: true,
282 }];
283 let (mut input, pre_lens, _) =
284 serialize_parameters(&accounts, &metas, b"", &PROGRAM).unwrap();
285
286 let data_offset = 8 + 8 + 32 + 32 + 8 + 8;
288 input[data_offset..data_offset + 4].copy_from_slice(b"fake");
289
290 let result = deserialize_parameters(&mut accounts, &metas, &input, &pre_lens, &PROGRAM);
291 assert!(result.is_err());
292 assert_eq!(accounts[&ACCT_1].data, b"original");
293 }
294
295 #[test]
296 fn non_owner_lamport_debit_rejected() {
297 let mut accounts = accounts_map(&[(ACCT_1, make_account(OTHER, 100, b""))]);
298 let metas = vec![AccountMeta {
299 pubkey: ACCT_1,
300 is_signer: false,
301 is_writable: true,
302 }];
303 let (mut input, pre_lens, _) =
304 serialize_parameters(&accounts, &metas, b"", &PROGRAM).unwrap();
305
306 let lamport_offset = 8 + 8 + 32 + 32;
307 input[lamport_offset..lamport_offset + 8].copy_from_slice(&50u64.to_le_bytes());
308
309 let result = deserialize_parameters(&mut accounts, &metas, &input, &pre_lens, &PROGRAM);
310 assert!(result.is_err());
311 assert_eq!(accounts[&ACCT_1].lamports, 100);
312 }
313
314 #[test]
315 fn missing_account_errors() {
316 let accounts = accounts_map(&[]);
317 let metas = vec![AccountMeta {
318 pubkey: ACCT_1,
319 is_signer: false,
320 is_writable: false,
321 }];
322 let result = serialize_parameters(&accounts, &metas, b"", &PROGRAM);
323 assert!(result.is_err());
324 }
325
326 #[test]
327 fn check_instruction_data_offset() {
328 let accounts = accounts_map(&[(ACCT_1, make_account(PROGRAM, 100, b"data"))]);
329 let metas = vec![AccountMeta {
330 pubkey: ACCT_1,
331 is_signer: false,
332 is_writable: true,
333 }];
334 let ix_data = b"instruction";
335 let (input, _, offset) =
336 serialize_parameters(&accounts, &metas, ix_data, &PROGRAM).unwrap();
337
338 assert_eq!(&input[offset..(offset + ix_data.len())], ix_data);
340 }
341
342 #[test]
343 fn check_instruction_data_offset_no_data() {
344 let accounts = accounts_map(&[(ACCT_1, make_account(PROGRAM, 100, b"data"))]);
345 let metas = vec![AccountMeta {
346 pubkey: ACCT_1,
347 is_signer: false,
348 is_writable: true,
349 }];
350 let ix_data = b""; let (input, _, offset) =
352 serialize_parameters(&accounts, &metas, ix_data, &PROGRAM).unwrap();
353
354 assert_eq!(&input[offset..offset + 32], PROGRAM.as_ref());
356 }
357}