solana_transaction_context/
transaction_accounts.rs

1#[cfg(feature = "dev-context-only-utils")]
2use qualifier_attr::qualifiers;
3use {
4    crate::{
5        vm_slice::VmSlice, IndexOfAccount, MAX_ACCOUNT_DATA_GROWTH_PER_TRANSACTION,
6        MAX_ACCOUNT_DATA_LEN,
7    },
8    solana_account::{AccountSharedData, ReadableAccount, WritableAccount},
9    solana_instruction::error::InstructionError,
10    solana_pubkey::Pubkey,
11    std::{
12        cell::{Cell, UnsafeCell},
13        ops::{Deref, DerefMut},
14        ptr,
15        sync::Arc,
16    },
17};
18
19const GUEST_REGION_SIZE: u64 = 1 << 32;
20const GUEST_ACCOUNT_PAYLOAD_BASE_ADDRESS: u64 = 9 * GUEST_REGION_SIZE;
21
22/// This struct is shared with programs. Do not alter its fields.
23#[repr(C)]
24#[derive(Debug, PartialEq)]
25struct AccountSharedFields {
26    key: Pubkey,
27    owner: Pubkey,
28    lamports: u64,
29    // The payload is going to be filled with the guest virtual address of the account payload
30    // vector.
31    payload: VmSlice<u8>,
32}
33
34#[derive(Debug, PartialEq)]
35struct AccountPrivateFields {
36    rent_epoch: u64,
37    executable: bool,
38    payload: Arc<Vec<u8>>,
39}
40
41impl AccountPrivateFields {
42    fn payload_len(&self) -> usize {
43        self.payload.len()
44    }
45}
46
47#[derive(Debug, PartialEq)]
48pub struct TransactionAccountView<'a> {
49    abi_account: &'a AccountSharedFields,
50    private_fields: &'a AccountPrivateFields,
51}
52
53impl ReadableAccount for TransactionAccountView<'_> {
54    fn lamports(&self) -> u64 {
55        self.abi_account.lamports
56    }
57
58    fn data(&self) -> &[u8] {
59        self.private_fields.payload.as_slice()
60    }
61
62    fn owner(&self) -> &Pubkey {
63        &self.abi_account.owner
64    }
65
66    fn executable(&self) -> bool {
67        self.private_fields.executable
68    }
69
70    fn rent_epoch(&self) -> u64 {
71        self.private_fields.rent_epoch
72    }
73}
74
75impl PartialEq<AccountSharedData> for TransactionAccountView<'_> {
76    fn eq(&self, other: &AccountSharedData) -> bool {
77        other.lamports() == self.lamports()
78            && other.data() == self.data()
79            && other.owner() == self.owner()
80            && other.executable() == self.executable()
81            && other.rent_epoch() == self.rent_epoch()
82    }
83}
84
85#[derive(Debug)]
86pub struct TransactionAccountViewMut<'a> {
87    abi_account: &'a mut AccountSharedFields,
88    private_fields: &'a mut AccountPrivateFields,
89}
90
91impl TransactionAccountViewMut<'_> {
92    fn data_mut(&mut self) -> &mut Vec<u8> {
93        Arc::make_mut(&mut self.private_fields.payload)
94    }
95
96    pub(crate) fn resize(&mut self, new_len: usize, value: u8) {
97        self.data_mut().resize(new_len, value);
98        // SAFETY: We are synchronizing the lengths.
99        unsafe {
100            self.abi_account.payload.set_len(new_len as u64);
101        }
102    }
103
104    #[cfg_attr(feature = "dev-context-only-utils", qualifiers(pub))]
105    pub(crate) fn set_data_from_slice(&mut self, new_data: &[u8]) {
106        // If the buffer isn't shared, we're going to memcpy in place.
107        let Some(data) = Arc::get_mut(&mut self.private_fields.payload) else {
108            // If the buffer is shared, the cheapest thing to do is to clone the
109            // incoming slice and replace the buffer.
110            self.private_fields.payload = Arc::new(new_data.to_vec());
111            // SAFETY: We are synchronizing the lengths.
112            unsafe {
113                self.abi_account.payload.set_len(new_data.len() as u64);
114            }
115            return;
116        };
117
118        let new_len = new_data.len();
119
120        // Reserve additional capacity if needed. Here we make the assumption
121        // that growing the current buffer is cheaper than doing a whole new
122        // allocation to make `new_data` owned.
123        //
124        // This assumption holds true during CPI, especially when the account
125        // size doesn't change but the account is only changed in place. And
126        // it's also true when the account is grown by a small margin (the
127        // realloc limit is quite low), in which case the allocator can just
128        // update the allocation metadata without moving.
129        //
130        // Shrinking and copying in place is always faster than making
131        // `new_data` owned, since shrinking boils down to updating the Vec's
132        // length.
133
134        data.reserve(new_len.saturating_sub(data.len()));
135
136        // Safety:
137        // We just reserved enough capacity. We set data::len to 0 to avoid
138        // possible UB on panic (dropping uninitialized elements), do the copy,
139        // finally set the new length once everything is initialized.
140        #[allow(clippy::uninit_vec)]
141        // this is a false positive, the lint doesn't currently special case set_len(0)
142        unsafe {
143            data.set_len(0);
144            ptr::copy_nonoverlapping(new_data.as_ptr(), data.as_mut_ptr(), new_len);
145            data.set_len(new_len);
146            self.abi_account.payload.set_len(new_len as u64);
147        };
148    }
149
150    pub(crate) fn extend_from_slice(&mut self, data: &[u8]) {
151        self.data_mut().extend_from_slice(data);
152        // SAFETY: We are synchronizing the lengths.
153        unsafe {
154            self.abi_account
155                .payload
156                .set_len(self.private_fields.payload_len() as u64);
157        }
158    }
159
160    pub(crate) fn reserve(&mut self, additional: usize) {
161        if let Some(data) = Arc::get_mut(&mut self.private_fields.payload) {
162            data.reserve(additional)
163        } else {
164            let mut data =
165                Vec::with_capacity(self.private_fields.payload_len().saturating_add(additional));
166            data.extend_from_slice(self.private_fields.payload.as_slice());
167            self.private_fields.payload = Arc::new(data);
168        }
169    }
170
171    #[cfg_attr(feature = "dev-context-only-utils", qualifiers(pub))]
172    pub(crate) fn is_shared(&self) -> bool {
173        Arc::strong_count(&self.private_fields.payload) > 1
174    }
175}
176
177impl ReadableAccount for TransactionAccountViewMut<'_> {
178    fn lamports(&self) -> u64 {
179        self.abi_account.lamports
180    }
181
182    fn data(&self) -> &[u8] {
183        self.private_fields.payload.as_slice()
184    }
185
186    fn owner(&self) -> &Pubkey {
187        &self.abi_account.owner
188    }
189
190    fn executable(&self) -> bool {
191        self.private_fields.executable
192    }
193
194    fn rent_epoch(&self) -> u64 {
195        self.private_fields.rent_epoch
196    }
197}
198
199impl WritableAccount for TransactionAccountViewMut<'_> {
200    fn set_lamports(&mut self, lamports: u64) {
201        self.abi_account.lamports = lamports;
202    }
203
204    fn data_as_mut_slice(&mut self) -> &mut [u8] {
205        Arc::make_mut(&mut self.private_fields.payload).as_mut_slice()
206    }
207
208    fn set_owner(&mut self, owner: Pubkey) {
209        self.abi_account.owner = owner;
210    }
211
212    fn copy_into_owner_from_slice(&mut self, source: &[u8]) {
213        self.abi_account.owner.as_mut().copy_from_slice(source);
214    }
215
216    fn set_executable(&mut self, executable: bool) {
217        self.private_fields.executable = executable;
218    }
219
220    fn set_rent_epoch(&mut self, epoch: u64) {
221        self.private_fields.rent_epoch = epoch;
222    }
223
224    fn create(
225        _lamports: u64,
226        _data: Vec<u8>,
227        _owner: Pubkey,
228        _executable: bool,
229        _rent_epoch: u64,
230    ) -> Self {
231        panic!("It is not possible to create a TransactionAccountMutView");
232    }
233}
234
235/// An account key and the matching account
236pub type KeyedAccountSharedData = (Pubkey, AccountSharedData);
237pub(crate) type DeconstructedTransactionAccounts =
238    (Vec<KeyedAccountSharedData>, Box<[Cell<bool>]>, Cell<i64>);
239
240#[derive(Debug)]
241pub struct TransactionAccounts {
242    shared_account_fields: UnsafeCell<Box<[AccountSharedFields]>>,
243    private_account_fields: UnsafeCell<Box<[AccountPrivateFields]>>,
244    borrow_counters: Box<[BorrowCounter]>,
245    touched_flags: Box<[Cell<bool>]>,
246    resize_delta: Cell<i64>,
247    lamports_delta: Cell<i128>,
248}
249
250impl TransactionAccounts {
251    #[cfg(not(target_os = "solana"))]
252    pub(crate) fn new(accounts: Vec<KeyedAccountSharedData>) -> TransactionAccounts {
253        let touched_flags = vec![Cell::new(false); accounts.len()].into_boxed_slice();
254        let borrow_counters = vec![BorrowCounter::default(); accounts.len()].into_boxed_slice();
255        let (shared_accounts, private_fields) = accounts
256            .into_iter()
257            .enumerate()
258            .map(|(idx, item)| {
259                (
260                    AccountSharedFields {
261                        key: item.0,
262                        owner: *item.1.owner(),
263                        lamports: item.1.lamports(),
264                        payload: VmSlice::new(
265                            GUEST_ACCOUNT_PAYLOAD_BASE_ADDRESS
266                                .saturating_add(GUEST_REGION_SIZE.saturating_mul(idx as u64)),
267                            item.1.data().len() as u64,
268                        ),
269                    },
270                    AccountPrivateFields {
271                        rent_epoch: item.1.rent_epoch(),
272                        executable: item.1.executable(),
273                        payload: item.1.data_clone(),
274                    },
275                )
276            })
277            .collect::<(Vec<AccountSharedFields>, Vec<AccountPrivateFields>)>();
278
279        TransactionAccounts {
280            shared_account_fields: UnsafeCell::new(shared_accounts.into_boxed_slice()),
281            private_account_fields: UnsafeCell::new(private_fields.into_boxed_slice()),
282            borrow_counters,
283            touched_flags,
284            resize_delta: Cell::new(0),
285            lamports_delta: Cell::new(0),
286        }
287    }
288
289    pub(crate) fn len(&self) -> usize {
290        // SAFETY: The borrow is local to this function and is only reading length.
291        unsafe { (*self.shared_account_fields.get()).len() }
292    }
293
294    #[cfg(not(target_os = "solana"))]
295    pub fn touch(&self, index: IndexOfAccount) -> Result<(), InstructionError> {
296        self.touched_flags
297            .get(index as usize)
298            .ok_or(InstructionError::MissingAccount)?
299            .set(true);
300        Ok(())
301    }
302
303    pub(crate) fn update_accounts_resize_delta(
304        &self,
305        old_len: usize,
306        new_len: usize,
307    ) -> Result<(), InstructionError> {
308        let accounts_resize_delta = self.resize_delta.get();
309        self.resize_delta.set(
310            accounts_resize_delta.saturating_add((new_len as i64).saturating_sub(old_len as i64)),
311        );
312        Ok(())
313    }
314
315    pub(crate) fn can_data_be_resized(
316        &self,
317        old_len: usize,
318        new_len: usize,
319    ) -> Result<(), InstructionError> {
320        // The new length can not exceed the maximum permitted length
321        if new_len > MAX_ACCOUNT_DATA_LEN as usize {
322            return Err(InstructionError::InvalidRealloc);
323        }
324        // The resize can not exceed the per-transaction maximum
325        let length_delta = (new_len as i64).saturating_sub(old_len as i64);
326        if self.resize_delta.get().saturating_add(length_delta)
327            > MAX_ACCOUNT_DATA_GROWTH_PER_TRANSACTION
328        {
329            return Err(InstructionError::MaxAccountsDataAllocationsExceeded);
330        }
331        Ok(())
332    }
333
334    #[cfg_attr(feature = "dev-context-only-utils", qualifiers(pub))]
335    pub(crate) fn try_borrow_mut(
336        &self,
337        index: IndexOfAccount,
338    ) -> Result<AccountRefMut<'_>, InstructionError> {
339        let borrow_counter = self
340            .borrow_counters
341            .get(index as usize)
342            .ok_or(InstructionError::MissingAccount)?;
343        borrow_counter.try_borrow_mut()?;
344
345        // SAFETY: The borrow counter guarantees this is the only mutable borrow of this account.
346        // The unwrap is safe because accounts.len() == borrow_counters.len(), so the missing
347        // account error should have been returned above.
348        let svm_account = unsafe {
349            (*self.shared_account_fields.get())
350                .get_mut(index as usize)
351                .unwrap()
352        };
353        let private_fields = unsafe {
354            (*self.private_account_fields.get())
355                .get_mut(index as usize)
356                .unwrap()
357        };
358
359        let account = TransactionAccountViewMut {
360            abi_account: svm_account,
361            private_fields,
362        };
363
364        Ok(AccountRefMut {
365            account,
366            borrow_counter,
367        })
368    }
369
370    pub fn try_borrow(&self, index: IndexOfAccount) -> Result<AccountRef<'_>, InstructionError> {
371        let borrow_counter = self
372            .borrow_counters
373            .get(index as usize)
374            .ok_or(InstructionError::MissingAccount)?;
375        borrow_counter.try_borrow()?;
376
377        // SAFETY: The borrow counter guarantees there are no mutable borrow of this account.
378        // The unwrap is safe because accounts.len() == borrow_counters.len(), so the missing
379        // account error should have been returned above.
380        let svm_account = unsafe {
381            (*self.shared_account_fields.get())
382                .get(index as usize)
383                .unwrap()
384        };
385        let private_fields = unsafe {
386            (*self.private_account_fields.get())
387                .get(index as usize)
388                .unwrap()
389        };
390
391        let account = TransactionAccountView {
392            abi_account: svm_account,
393            private_fields,
394        };
395
396        Ok(AccountRef {
397            account,
398            borrow_counter,
399        })
400    }
401
402    pub(crate) fn add_lamports_delta(&self, balance: i128) -> Result<(), InstructionError> {
403        let delta = self.lamports_delta.get();
404        self.lamports_delta.set(
405            delta
406                .checked_add(balance)
407                .ok_or(InstructionError::ArithmeticOverflow)?,
408        );
409        Ok(())
410    }
411
412    pub(crate) fn get_lamports_delta(&self) -> i128 {
413        self.lamports_delta.get()
414    }
415
416    fn deconstruct_into_keyed_account_shared_data(&mut self) -> Vec<KeyedAccountSharedData> {
417        self.shared_account_fields
418            .get_mut()
419            .into_iter()
420            .zip(&mut *self.private_account_fields.get_mut())
421            .map(|(shared_fields, private_fields)| {
422                (
423                    shared_fields.key,
424                    AccountSharedData::create_from_existing_shared_data(
425                        shared_fields.lamports,
426                        private_fields.payload.clone(),
427                        shared_fields.owner,
428                        private_fields.executable,
429                        private_fields.rent_epoch,
430                    ),
431                )
432            })
433            .collect()
434    }
435
436    pub(crate) fn deconstruct_into_account_shared_data(&mut self) -> Vec<AccountSharedData> {
437        self.shared_account_fields
438            .get_mut()
439            .into_iter()
440            .zip(&mut *self.private_account_fields.get_mut())
441            .map(|(shared_fields, private_fields)| {
442                AccountSharedData::create_from_existing_shared_data(
443                    shared_fields.lamports,
444                    private_fields.payload.clone(),
445                    shared_fields.owner,
446                    private_fields.executable,
447                    private_fields.rent_epoch,
448                )
449            })
450            .collect()
451    }
452
453    pub(crate) fn take(mut self) -> DeconstructedTransactionAccounts {
454        let shared_data = self.deconstruct_into_keyed_account_shared_data();
455        (shared_data, self.touched_flags, self.resize_delta)
456    }
457
458    pub fn resize_delta(&self) -> i64 {
459        self.resize_delta.get()
460    }
461
462    pub(crate) fn account_key(&self, index: IndexOfAccount) -> Option<&Pubkey> {
463        // SAFETY: We never modify an account key, so returning a reference to it is safe.
464        unsafe {
465            (*self.shared_account_fields.get())
466                .get(index as usize)
467                .map(|acc| &acc.key)
468        }
469    }
470
471    pub(crate) fn account_keys_iter(&self) -> impl Iterator<Item = &Pubkey> {
472        // SAFETY: We never modify account keys, so returning an immutable reference to them is safe.
473        unsafe {
474            (*self.shared_account_fields.get())
475                .iter()
476                .map(|item| &item.key)
477        }
478    }
479}
480
481#[derive(Default, Debug, Clone)]
482struct BorrowCounter {
483    counter: Cell<i8>,
484}
485
486impl BorrowCounter {
487    #[inline]
488    fn is_writing(&self) -> bool {
489        self.counter.get() < 0
490    }
491
492    #[inline]
493    fn is_reading(&self) -> bool {
494        self.counter.get() > 0
495    }
496
497    #[inline]
498    fn try_borrow(&self) -> Result<(), InstructionError> {
499        if self.is_writing() {
500            return Err(InstructionError::AccountBorrowFailed);
501        }
502
503        if let Some(counter) = self.counter.get().checked_add(1) {
504            self.counter.set(counter);
505            return Ok(());
506        }
507
508        Err(InstructionError::AccountBorrowFailed)
509    }
510
511    #[inline]
512    fn try_borrow_mut(&self) -> Result<(), InstructionError> {
513        if self.is_writing() || self.is_reading() {
514            return Err(InstructionError::AccountBorrowFailed);
515        }
516
517        self.counter.set(self.counter.get().saturating_sub(1));
518
519        Ok(())
520    }
521
522    #[inline]
523    fn release_borrow(&self) {
524        self.counter.set(self.counter.get().saturating_sub(1));
525    }
526
527    #[inline]
528    fn release_borrow_mut(&self) {
529        self.counter.set(self.counter.get().saturating_add(1));
530    }
531}
532
533pub struct AccountRef<'a> {
534    account: TransactionAccountView<'a>,
535    borrow_counter: &'a BorrowCounter,
536}
537
538impl Drop for AccountRef<'_> {
539    fn drop(&mut self) {
540        self.borrow_counter.release_borrow();
541    }
542}
543
544impl<'a> Deref for AccountRef<'a> {
545    type Target = TransactionAccountView<'a>;
546    fn deref(&self) -> &Self::Target {
547        &self.account
548    }
549}
550
551#[derive(Debug)]
552pub struct AccountRefMut<'a> {
553    account: TransactionAccountViewMut<'a>,
554    borrow_counter: &'a BorrowCounter,
555}
556
557impl Drop for AccountRefMut<'_> {
558    fn drop(&mut self) {
559        // SAFETY: We are synchronizing the lengths.
560        unsafe {
561            self.account
562                .abi_account
563                .payload
564                .set_len(self.account.private_fields.payload_len() as u64);
565        }
566        self.borrow_counter.release_borrow_mut();
567    }
568}
569
570impl<'a> Deref for AccountRefMut<'a> {
571    type Target = TransactionAccountViewMut<'a>;
572    fn deref(&self) -> &Self::Target {
573        &self.account
574    }
575}
576
577impl DerefMut for AccountRefMut<'_> {
578    fn deref_mut(&mut self) -> &mut Self::Target {
579        &mut self.account
580    }
581}
582
583#[cfg(test)]
584mod tests {
585    use {
586        crate::transaction_accounts::TransactionAccounts, solana_account::AccountSharedData,
587        solana_instruction::error::InstructionError, solana_pubkey::Pubkey,
588    };
589
590    #[test]
591    fn test_missing_account() {
592        let accounts = vec![
593            (
594                Pubkey::new_unique(),
595                AccountSharedData::new(2, 1, &Pubkey::new_unique()),
596            ),
597            (
598                Pubkey::new_unique(),
599                AccountSharedData::new(2, 1, &Pubkey::new_unique()),
600            ),
601        ];
602
603        let tx_accounts = TransactionAccounts::new(accounts);
604
605        let res = tx_accounts.try_borrow(3);
606        assert_eq!(res.err(), Some(InstructionError::MissingAccount));
607
608        let res = tx_accounts.try_borrow_mut(3);
609        assert_eq!(res.err(), Some(InstructionError::MissingAccount));
610    }
611
612    #[test]
613    fn test_invalid_borrow() {
614        let accounts = vec![
615            (
616                Pubkey::new_unique(),
617                AccountSharedData::new(2, 1, &Pubkey::new_unique()),
618            ),
619            (
620                Pubkey::new_unique(),
621                AccountSharedData::new(2, 1, &Pubkey::new_unique()),
622            ),
623        ];
624
625        let tx_accounts = TransactionAccounts::new(accounts);
626
627        // Two immutable borrows are valid
628        {
629            let acc_1 = tx_accounts.try_borrow(0);
630            assert!(acc_1.is_ok());
631
632            let acc_2 = tx_accounts.try_borrow(1);
633            assert!(acc_2.is_ok());
634
635            let acc_1_new = tx_accounts.try_borrow(0);
636            assert!(acc_1_new.is_ok());
637
638            assert_eq!(acc_1.unwrap().account, acc_1_new.unwrap().account);
639        }
640
641        // Two mutable borrows are invalid
642        {
643            let acc_1 = tx_accounts.try_borrow_mut(0);
644            assert!(acc_1.is_ok());
645
646            let acc_2 = tx_accounts.try_borrow_mut(1);
647            assert!(acc_2.is_ok());
648
649            let acc_1_new = tx_accounts.try_borrow_mut(0);
650            assert_eq!(acc_1_new.err(), Some(InstructionError::AccountBorrowFailed));
651        }
652
653        // Mutable after immutable must fail
654        {
655            let acc_1 = tx_accounts.try_borrow(0);
656            assert!(acc_1.is_ok());
657
658            let acc_2 = tx_accounts.try_borrow(1);
659            assert!(acc_2.is_ok());
660
661            let acc_1_new = tx_accounts.try_borrow_mut(0);
662            assert_eq!(acc_1_new.err(), Some(InstructionError::AccountBorrowFailed));
663        }
664
665        // Immutable after mutable must fail
666        {
667            let acc_1 = tx_accounts.try_borrow_mut(0);
668            assert!(acc_1.is_ok());
669
670            let acc_2 = tx_accounts.try_borrow_mut(1);
671            assert!(acc_2.is_ok());
672
673            let acc_1_new = tx_accounts.try_borrow(0);
674            assert_eq!(acc_1_new.err(), Some(InstructionError::AccountBorrowFailed));
675        }
676
677        // Different scopes are good
678        {
679            let acc_1 = tx_accounts.try_borrow_mut(0);
680            assert!(acc_1.is_ok());
681        }
682
683        {
684            let acc_1 = tx_accounts.try_borrow_mut(0);
685            assert!(acc_1.is_ok());
686        }
687    }
688
689    #[test]
690    fn too_many_borrows() {
691        let accounts = vec![
692            (
693                Pubkey::new_unique(),
694                AccountSharedData::new(2, 1, &Pubkey::new_unique()),
695            ),
696            (
697                Pubkey::new_unique(),
698                AccountSharedData::new(2, 1, &Pubkey::new_unique()),
699            ),
700        ];
701
702        let tx_accounts = TransactionAccounts::new(accounts);
703        let mut borrows = Vec::new();
704        for i in 0..129 {
705            let acc = tx_accounts.try_borrow(1);
706            if i < 127 {
707                assert!(acc.is_ok());
708                borrows.push(acc.unwrap());
709            } else {
710                assert_eq!(acc.err(), Some(InstructionError::AccountBorrowFailed));
711            }
712        }
713    }
714}