Skip to main content

solana_transaction_context/
transaction_accounts.rs

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