miraland_program/
account_info.rs

1//! Account information.
2
3use {
4    crate::{
5        clock::Epoch, debug_account_data::*, entrypoint::MAX_PERMITTED_DATA_INCREASE,
6        program_error::ProgramError, program_memory::sol_memset, pubkey::Pubkey,
7    },
8    std::{
9        cell::{Ref, RefCell, RefMut},
10        fmt,
11        rc::Rc,
12        slice::from_raw_parts_mut,
13    },
14};
15
16/// Account information
17#[derive(Clone)]
18#[repr(C)]
19pub struct AccountInfo<'a> {
20    /// Public key of the account
21    pub key: &'a Pubkey,
22    /// The lamports in the account.  Modifiable by programs.
23    pub lamports: Rc<RefCell<&'a mut u64>>,
24    /// The data held in this account.  Modifiable by programs.
25    pub data: Rc<RefCell<&'a mut [u8]>>,
26    /// Program that owns this account
27    pub owner: &'a Pubkey,
28    /// The epoch at which this account will next owe rent
29    pub rent_epoch: Epoch,
30    /// Was the transaction signed by this account's public key?
31    pub is_signer: bool,
32    /// Is the account writable?
33    pub is_writable: bool,
34    /// This account's data contains a loaded program (and is now read-only)
35    pub executable: bool,
36}
37
38impl<'a> fmt::Debug for AccountInfo<'a> {
39    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40        let mut f = f.debug_struct("AccountInfo");
41
42        f.field("key", &self.key)
43            .field("owner", &self.owner)
44            .field("is_signer", &self.is_signer)
45            .field("is_writable", &self.is_writable)
46            .field("executable", &self.executable)
47            .field("rent_epoch", &self.rent_epoch)
48            .field("lamports", &self.lamports())
49            .field("data.len", &self.data_len());
50        debug_account_data(&self.data.borrow(), &mut f);
51
52        f.finish_non_exhaustive()
53    }
54}
55
56impl<'a> AccountInfo<'a> {
57    pub fn signer_key(&self) -> Option<&Pubkey> {
58        if self.is_signer {
59            Some(self.key)
60        } else {
61            None
62        }
63    }
64
65    pub fn unsigned_key(&self) -> &Pubkey {
66        self.key
67    }
68
69    pub fn lamports(&self) -> u64 {
70        **self.lamports.borrow()
71    }
72
73    pub fn try_lamports(&self) -> Result<u64, ProgramError> {
74        Ok(**self.try_borrow_lamports()?)
75    }
76
77    /// Return the account's original data length when it was serialized for the
78    /// current program invocation.
79    ///
80    /// # Safety
81    ///
82    /// This method assumes that the original data length was serialized as a u32
83    /// integer in the 4 bytes immediately preceding the serialized account key.
84    pub unsafe fn original_data_len(&self) -> usize {
85        let key_ptr = self.key as *const _ as *const u8;
86        let original_data_len_ptr = key_ptr.offset(-4) as *const u32;
87        *original_data_len_ptr as usize
88    }
89
90    pub fn data_len(&self) -> usize {
91        self.data.borrow().len()
92    }
93
94    pub fn try_data_len(&self) -> Result<usize, ProgramError> {
95        Ok(self.try_borrow_data()?.len())
96    }
97
98    pub fn data_is_empty(&self) -> bool {
99        self.data.borrow().is_empty()
100    }
101
102    pub fn try_data_is_empty(&self) -> Result<bool, ProgramError> {
103        Ok(self.try_borrow_data()?.is_empty())
104    }
105
106    pub fn try_borrow_lamports(&self) -> Result<Ref<&mut u64>, ProgramError> {
107        self.lamports
108            .try_borrow()
109            .map_err(|_| ProgramError::AccountBorrowFailed)
110    }
111
112    pub fn try_borrow_mut_lamports(&self) -> Result<RefMut<&'a mut u64>, ProgramError> {
113        self.lamports
114            .try_borrow_mut()
115            .map_err(|_| ProgramError::AccountBorrowFailed)
116    }
117
118    pub fn try_borrow_data(&self) -> Result<Ref<&mut [u8]>, ProgramError> {
119        self.data
120            .try_borrow()
121            .map_err(|_| ProgramError::AccountBorrowFailed)
122    }
123
124    pub fn try_borrow_mut_data(&self) -> Result<RefMut<&'a mut [u8]>, ProgramError> {
125        self.data
126            .try_borrow_mut()
127            .map_err(|_| ProgramError::AccountBorrowFailed)
128    }
129
130    /// Realloc the account's data and optionally zero-initialize the new
131    /// memory.
132    ///
133    /// Note:  Account data can be increased within a single call by up to
134    /// `miraland_program::entrypoint::MAX_PERMITTED_DATA_INCREASE` bytes.
135    ///
136    /// Note: Memory used to grow is already zero-initialized upon program
137    /// entrypoint and re-zeroing it wastes compute units.  If within the same
138    /// call a program reallocs from larger to smaller and back to larger again
139    /// the new space could contain stale data.  Pass `true` for `zero_init` in
140    /// this case, otherwise compute units will be wasted re-zero-initializing.
141    ///
142    /// # Safety
143    ///
144    /// This method makes assumptions about the layout and location of memory
145    /// referenced by `AccountInfo` fields. It should only be called for
146    /// instances of `AccountInfo` that were created by the runtime and received
147    /// in the `process_instruction` entrypoint of a program.
148    pub fn realloc(&self, new_len: usize, zero_init: bool) -> Result<(), ProgramError> {
149        let mut data = self.try_borrow_mut_data()?;
150        let old_len = data.len();
151
152        // Return early if length hasn't changed
153        if new_len == old_len {
154            return Ok(());
155        }
156
157        // Return early if the length increase from the original serialized data
158        // length is too large and would result in an out of bounds allocation.
159        let original_data_len = unsafe { self.original_data_len() };
160        if new_len.saturating_sub(original_data_len) > MAX_PERMITTED_DATA_INCREASE {
161            return Err(ProgramError::InvalidRealloc);
162        }
163
164        // realloc
165        unsafe {
166            let data_ptr = data.as_mut_ptr();
167
168            // First set new length in the serialized data
169            *(data_ptr.offset(-8) as *mut u64) = new_len as u64;
170
171            // Then recreate the local slice with the new length
172            *data = from_raw_parts_mut(data_ptr, new_len)
173        }
174
175        if zero_init {
176            let len_increase = new_len.saturating_sub(old_len);
177            if len_increase > 0 {
178                sol_memset(&mut data[old_len..], 0, len_increase);
179            }
180        }
181
182        Ok(())
183    }
184
185    #[rustversion::attr(since(1.72), allow(invalid_reference_casting))]
186    pub fn assign(&self, new_owner: &Pubkey) {
187        // Set the non-mut owner field
188        unsafe {
189            std::ptr::write_volatile(
190                self.owner as *const Pubkey as *mut [u8; 32],
191                new_owner.to_bytes(),
192            );
193        }
194    }
195
196    pub fn new(
197        key: &'a Pubkey,
198        is_signer: bool,
199        is_writable: bool,
200        lamports: &'a mut u64,
201        data: &'a mut [u8],
202        owner: &'a Pubkey,
203        executable: bool,
204        rent_epoch: Epoch,
205    ) -> Self {
206        Self {
207            key,
208            is_signer,
209            is_writable,
210            lamports: Rc::new(RefCell::new(lamports)),
211            data: Rc::new(RefCell::new(data)),
212            owner,
213            executable,
214            rent_epoch,
215        }
216    }
217
218    pub fn deserialize_data<T: serde::de::DeserializeOwned>(&self) -> Result<T, bincode::Error> {
219        bincode::deserialize(&self.data.borrow())
220    }
221
222    pub fn serialize_data<T: serde::Serialize>(&self, state: &T) -> Result<(), bincode::Error> {
223        if bincode::serialized_size(state)? > self.data_len() as u64 {
224            return Err(Box::new(bincode::ErrorKind::SizeLimit));
225        }
226        bincode::serialize_into(&mut self.data.borrow_mut()[..], state)
227    }
228}
229
230/// Constructs an `AccountInfo` from self, used in conversion implementations.
231pub trait IntoAccountInfo<'a> {
232    fn into_account_info(self) -> AccountInfo<'a>;
233}
234impl<'a, T: IntoAccountInfo<'a>> From<T> for AccountInfo<'a> {
235    fn from(src: T) -> Self {
236        src.into_account_info()
237    }
238}
239
240/// Provides information required to construct an `AccountInfo`, used in
241/// conversion implementations.
242pub trait Account {
243    fn get(&mut self) -> (&mut u64, &mut [u8], &Pubkey, bool, Epoch);
244}
245
246/// Convert (&'a Pubkey, &'a mut T) where T: Account into an `AccountInfo`
247impl<'a, T: Account> IntoAccountInfo<'a> for (&'a Pubkey, &'a mut T) {
248    fn into_account_info(self) -> AccountInfo<'a> {
249        let (key, account) = self;
250        let (lamports, data, owner, executable, rent_epoch) = account.get();
251        AccountInfo::new(
252            key, false, false, lamports, data, owner, executable, rent_epoch,
253        )
254    }
255}
256
257/// Convert (&'a Pubkey, bool, &'a mut T)  where T: Account into an
258/// `AccountInfo`.
259impl<'a, T: Account> IntoAccountInfo<'a> for (&'a Pubkey, bool, &'a mut T) {
260    fn into_account_info(self) -> AccountInfo<'a> {
261        let (key, is_signer, account) = self;
262        let (lamports, data, owner, executable, rent_epoch) = account.get();
263        AccountInfo::new(
264            key, is_signer, false, lamports, data, owner, executable, rent_epoch,
265        )
266    }
267}
268
269/// Convert &'a mut (Pubkey, T) where T: Account into an `AccountInfo`.
270impl<'a, T: Account> IntoAccountInfo<'a> for &'a mut (Pubkey, T) {
271    fn into_account_info(self) -> AccountInfo<'a> {
272        let (ref key, account) = self;
273        let (lamports, data, owner, executable, rent_epoch) = account.get();
274        AccountInfo::new(
275            key, false, false, lamports, data, owner, executable, rent_epoch,
276        )
277    }
278}
279
280/// Convenience function for accessing the next item in an [`AccountInfo`]
281/// iterator.
282///
283/// This is simply a wrapper around [`Iterator::next`] that returns a
284/// [`ProgramError`] instead of an option.
285///
286/// # Errors
287///
288/// Returns [`ProgramError::NotEnoughAccountKeys`] if there are no more items in
289/// the iterator.
290///
291/// # Examples
292///
293/// ```
294/// use miraland_program::{
295///    account_info::{AccountInfo, next_account_info},
296///    entrypoint::ProgramResult,
297///    pubkey::Pubkey,
298/// };
299/// # use miraland_program::program_error::ProgramError;
300///
301/// pub fn process_instruction(
302///     program_id: &Pubkey,
303///     accounts: &[AccountInfo],
304///     instruction_data: &[u8],
305/// ) -> ProgramResult {
306///     let accounts_iter = &mut accounts.iter();
307///     let signer = next_account_info(accounts_iter)?;
308///     let payer = next_account_info(accounts_iter)?;
309///
310///     // do stuff ...
311///
312///     Ok(())
313/// }
314/// # let p = Pubkey::new_unique();
315/// # let l = &mut 0;
316/// # let d = &mut [0u8];
317/// # let a = AccountInfo::new(&p, false, false, l, d, &p, false, 0);
318/// # let accounts = &[a.clone(), a];
319/// # process_instruction(
320/// #    &Pubkey::new_unique(),
321/// #    accounts,
322/// #    &[],
323/// # )?;
324/// # Ok::<(), ProgramError>(())
325/// ```
326pub fn next_account_info<'a, 'b, I: Iterator<Item = &'a AccountInfo<'b>>>(
327    iter: &mut I,
328) -> Result<I::Item, ProgramError> {
329    iter.next().ok_or(ProgramError::NotEnoughAccountKeys)
330}
331
332/// Convenience function for accessing multiple next items in an [`AccountInfo`]
333/// iterator.
334///
335/// Returns a slice containing the next `count` [`AccountInfo`]s.
336///
337/// # Errors
338///
339/// Returns [`ProgramError::NotEnoughAccountKeys`] if there are not enough items
340/// in the iterator to satisfy the request.
341///
342/// # Examples
343///
344/// ```
345/// use miraland_program::{
346///    account_info::{AccountInfo, next_account_info, next_account_infos},
347///    entrypoint::ProgramResult,
348///    pubkey::Pubkey,
349/// };
350/// # use miraland_program::program_error::ProgramError;
351///
352/// pub fn process_instruction(
353///     program_id: &Pubkey,
354///     accounts: &[AccountInfo],
355///     instruction_data: &[u8],
356/// ) -> ProgramResult {
357///     let accounts_iter = &mut accounts.iter();
358///     let signer = next_account_info(accounts_iter)?;
359///     let payer = next_account_info(accounts_iter)?;
360///     let outputs = next_account_infos(accounts_iter, 3)?;
361///
362///     // do stuff ...
363///
364///     Ok(())
365/// }
366/// # let p = Pubkey::new_unique();
367/// # let l = &mut 0;
368/// # let d = &mut [0u8];
369/// # let a = AccountInfo::new(&p, false, false, l, d, &p, false, 0);
370/// # let accounts = &[a.clone(), a.clone(), a.clone(), a.clone(), a];
371/// # process_instruction(
372/// #    &Pubkey::new_unique(),
373/// #    accounts,
374/// #    &[],
375/// # )?;
376/// # Ok::<(), ProgramError>(())
377/// ```
378pub fn next_account_infos<'a, 'b: 'a>(
379    iter: &mut std::slice::Iter<'a, AccountInfo<'b>>,
380    count: usize,
381) -> Result<&'a [AccountInfo<'b>], ProgramError> {
382    let accounts = iter.as_slice();
383    if accounts.len() < count {
384        return Err(ProgramError::NotEnoughAccountKeys);
385    }
386    let (accounts, remaining) = accounts.split_at(count);
387    *iter = remaining.iter();
388    Ok(accounts)
389}
390
391impl<'a> AsRef<AccountInfo<'a>> for AccountInfo<'a> {
392    fn as_ref(&self) -> &AccountInfo<'a> {
393        self
394    }
395}
396
397#[cfg(test)]
398mod tests {
399    use super::*;
400
401    #[test]
402    fn test_next_account_infos() {
403        let k1 = Pubkey::new_unique();
404        let k2 = Pubkey::new_unique();
405        let k3 = Pubkey::new_unique();
406        let k4 = Pubkey::new_unique();
407        let k5 = Pubkey::new_unique();
408        let l1 = &mut 0;
409        let l2 = &mut 0;
410        let l3 = &mut 0;
411        let l4 = &mut 0;
412        let l5 = &mut 0;
413        let d1 = &mut [0u8];
414        let d2 = &mut [0u8];
415        let d3 = &mut [0u8];
416        let d4 = &mut [0u8];
417        let d5 = &mut [0u8];
418
419        let infos = &[
420            AccountInfo::new(&k1, false, false, l1, d1, &k1, false, 0),
421            AccountInfo::new(&k2, false, false, l2, d2, &k2, false, 0),
422            AccountInfo::new(&k3, false, false, l3, d3, &k3, false, 0),
423            AccountInfo::new(&k4, false, false, l4, d4, &k4, false, 0),
424            AccountInfo::new(&k5, false, false, l5, d5, &k5, false, 0),
425        ];
426        let infos_iter = &mut infos.iter();
427        let info1 = next_account_info(infos_iter).unwrap();
428        let info2_3_4 = next_account_infos(infos_iter, 3).unwrap();
429        let info5 = next_account_info(infos_iter).unwrap();
430
431        assert_eq!(k1, *info1.key);
432        assert_eq!(k2, *info2_3_4[0].key);
433        assert_eq!(k3, *info2_3_4[1].key);
434        assert_eq!(k4, *info2_3_4[2].key);
435        assert_eq!(k5, *info5.key);
436    }
437
438    #[test]
439    fn test_account_info_as_ref() {
440        let k = Pubkey::new_unique();
441        let l = &mut 0;
442        let d = &mut [0u8];
443        let info = AccountInfo::new(&k, false, false, l, d, &k, false, 0);
444        assert_eq!(info.key, info.as_ref().key);
445    }
446
447    #[test]
448    fn test_account_info_debug_data() {
449        let key = Pubkey::new_unique();
450        let mut lamports = 42;
451        let mut data = vec![5; 80];
452        let data_str = format!("{:?}", Hex(&data[..MAX_DEBUG_ACCOUNT_DATA]));
453        let info = AccountInfo::new(&key, false, false, &mut lamports, &mut data, &key, false, 0);
454        assert_eq!(
455            format!("{info:?}"),
456            format!(
457                "AccountInfo {{ \
458                key: {}, \
459                owner: {}, \
460                is_signer: {}, \
461                is_writable: {}, \
462                executable: {}, \
463                rent_epoch: {}, \
464                lamports: {}, \
465                data.len: {}, \
466                data: {}, .. }}",
467                key,
468                key,
469                false,
470                false,
471                false,
472                0,
473                lamports,
474                data.len(),
475                data_str,
476            )
477        );
478
479        let mut data = vec![5; 40];
480        let data_str = format!("{:?}", Hex(&data));
481        let info = AccountInfo::new(&key, false, false, &mut lamports, &mut data, &key, false, 0);
482        assert_eq!(
483            format!("{info:?}"),
484            format!(
485                "AccountInfo {{ \
486                key: {}, \
487                owner: {}, \
488                is_signer: {}, \
489                is_writable: {}, \
490                executable: {}, \
491                rent_epoch: {}, \
492                lamports: {}, \
493                data.len: {}, \
494                data: {}, .. }}",
495                key,
496                key,
497                false,
498                false,
499                false,
500                0,
501                lamports,
502                data.len(),
503                data_str,
504            )
505        );
506
507        let mut data = vec![];
508        let info = AccountInfo::new(&key, false, false, &mut lamports, &mut data, &key, false, 0);
509        assert_eq!(
510            format!("{info:?}"),
511            format!(
512                "AccountInfo {{ \
513                key: {}, \
514                owner: {}, \
515                is_signer: {}, \
516                is_writable: {}, \
517                executable: {}, \
518                rent_epoch: {}, \
519                lamports: {}, \
520                data.len: {}, .. }}",
521                key,
522                key,
523                false,
524                false,
525                false,
526                0,
527                lamports,
528                data.len(),
529            )
530        );
531    }
532}