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    #[cfg_attr(feature = "dev-context-only-utils", qualifiers(pub))]
171    pub(crate) fn is_shared(&self) -> bool {
172        Arc::strong_count(&self.private_fields.payload) > 1
173    }
174}
175
176#[cfg(not(any(target_arch = "bpf", target_arch = "sbf")))]
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
199#[cfg(not(any(target_arch = "bpf", target_arch = "sbf")))]
200impl WritableAccount for TransactionAccountViewMut<'_> {
201    fn set_lamports(&mut self, lamports: u64) {
202        self.abi_account.lamports = lamports;
203    }
204
205    fn data_as_mut_slice(&mut self) -> &mut [u8] {
206        Arc::make_mut(&mut self.private_fields.payload).as_mut_slice()
207    }
208
209    fn set_owner(&mut self, owner: Pubkey) {
210        self.abi_account.owner = owner;
211    }
212
213    fn copy_into_owner_from_slice(&mut self, source: &[u8]) {
214        self.abi_account.owner.as_mut().copy_from_slice(source);
215    }
216
217    fn set_executable(&mut self, executable: bool) {
218        self.private_fields.executable = executable;
219    }
220
221    fn set_rent_epoch(&mut self, epoch: u64) {
222        self.private_fields.rent_epoch = epoch;
223    }
224}
225
226/// An account key and the matching account
227#[cfg(not(any(target_arch = "bpf", target_arch = "sbf")))]
228pub type KeyedAccountSharedData = (Pubkey, AccountSharedData);
229#[cfg(not(any(target_arch = "bpf", target_arch = "sbf")))]
230pub(crate) type DeconstructedTransactionAccounts =
231    (Vec<KeyedAccountSharedData>, Box<[Cell<bool>]>, Cell<i64>);
232
233#[derive(Debug)]
234#[cfg(not(any(target_arch = "bpf", target_arch = "sbf")))]
235pub struct TransactionAccounts {
236    shared_account_fields: Box<[UnsafeCell<AccountSharedFields>]>,
237    private_account_fields: Box<[UnsafeCell<AccountPrivateFields>]>,
238    borrow_counters: Box<[BorrowCounter]>,
239    touched_flags: Box<[Cell<bool>]>,
240    resize_delta: Cell<i64>,
241    lamports_delta: Cell<i128>,
242    _drop_on_bail_out: DropOnBailOut,
243}
244
245#[cfg(not(any(target_arch = "bpf", target_arch = "sbf")))]
246impl TransactionAccounts {
247    pub(crate) fn new_with_feature_flags(
248        accounts: Vec<KeyedAccountSharedData>,
249        drop_on_bail_out: DropOnBailOut,
250    ) -> TransactionAccounts {
251        let touched_flags = vec![Cell::new(false); accounts.len()].into_boxed_slice();
252        let borrow_counters = vec![BorrowCounter::default(); accounts.len()].into_boxed_slice();
253        let (shared_accounts, private_fields) = accounts
254            .into_iter()
255            .enumerate()
256            .map(|(idx, item)| {
257                (
258                    UnsafeCell::new(AccountSharedFields {
259                        key: item.0,
260                        owner: *item.1.owner(),
261                        lamports: item.1.lamports(),
262                        payload: VmSlice::new(
263                            GUEST_ACCOUNT_PAYLOAD_BASE_ADDRESS
264                                .saturating_add(GUEST_REGION_SIZE.saturating_mul(idx as u64)),
265                            item.1.data().len() as u64,
266                        ),
267                    }),
268                    UnsafeCell::new(AccountPrivateFields {
269                        rent_epoch: item.1.rent_epoch(),
270                        executable: item.1.executable(),
271                        payload: item.1.data_clone(),
272                    }),
273                )
274            })
275            .collect::<(
276                Vec<UnsafeCell<AccountSharedFields>>,
277                Vec<UnsafeCell<AccountPrivateFields>>,
278            )>();
279
280        TransactionAccounts {
281            shared_account_fields: shared_accounts.into_boxed_slice(),
282            private_account_fields: private_fields.into_boxed_slice(),
283            borrow_counters,
284            touched_flags,
285            resize_delta: Cell::new(0),
286            lamports_delta: Cell::new(0),
287            _drop_on_bail_out: drop_on_bail_out,
288        }
289    }
290
291    #[cfg(feature = "dev-context-only-utils")]
292    pub fn new(accounts: Vec<KeyedAccountSharedData>) -> TransactionAccounts {
293        TransactionAccounts::new_with_feature_flags(accounts, DropOnBailOut::Disabled)
294    }
295
296    pub(crate) fn len(&self) -> usize {
297        self.shared_account_fields.len()
298    }
299
300    pub fn touch(&self, index: IndexOfAccount) -> Result<(), InstructionError> {
301        self.touched_flags
302            .get(index as usize)
303            .ok_or(InstructionError::MissingAccount)?
304            .set(true);
305        Ok(())
306    }
307
308    pub(crate) fn update_accounts_resize_delta(
309        &self,
310        old_len: usize,
311        new_len: usize,
312    ) -> Result<(), InstructionError> {
313        let accounts_resize_delta = self.resize_delta.get();
314        self.resize_delta.set(
315            accounts_resize_delta.saturating_add((new_len as i64).saturating_sub(old_len as i64)),
316        );
317        Ok(())
318    }
319
320    pub(crate) fn can_data_be_resized(
321        &self,
322        old_len: usize,
323        new_len: usize,
324    ) -> Result<(), InstructionError> {
325        // The new length can not exceed the maximum permitted length
326        if new_len > MAX_ACCOUNT_DATA_LEN as usize {
327            return Err(InstructionError::InvalidRealloc);
328        }
329        // The resize can not exceed the per-transaction maximum
330        let length_delta = (new_len as i64).saturating_sub(old_len as i64);
331        if self.resize_delta.get().saturating_add(length_delta)
332            > MAX_ACCOUNT_DATA_GROWTH_PER_TRANSACTION
333        {
334            return Err(InstructionError::MaxAccountsDataAllocationsExceeded);
335        }
336        Ok(())
337    }
338
339    #[cfg_attr(feature = "dev-context-only-utils", qualifiers(pub))]
340    pub(crate) fn try_borrow_mut(
341        &self,
342        index: IndexOfAccount,
343    ) -> Result<AccountRefMut<'_>, InstructionError> {
344        let borrow_counter = self
345            .borrow_counters
346            .get(index as usize)
347            .ok_or(InstructionError::MissingAccount)?;
348        borrow_counter.try_borrow_mut()?;
349
350        // SAFETY: The borrow counter guarantees this is the only mutable borrow of this account.
351        // The unwrap is safe because accounts.len() == borrow_counters.len(), so the missing
352        // account error should have been returned above.
353        let svm_account = unsafe {
354            &mut *self
355                .shared_account_fields
356                .get(index as usize)
357                .unwrap()
358                .get()
359        };
360
361        let private_fields = unsafe {
362            &mut *self
363                .private_account_fields
364                .get(index as usize)
365                .unwrap()
366                .get()
367        };
368
369        let account = TransactionAccountViewMut {
370            abi_account: svm_account,
371            private_fields,
372        };
373
374        Ok(AccountRefMut {
375            account,
376            borrow_counter,
377        })
378    }
379
380    pub fn try_borrow(&self, index: IndexOfAccount) -> Result<AccountRef<'_>, InstructionError> {
381        let borrow_counter = self
382            .borrow_counters
383            .get(index as usize)
384            .ok_or(InstructionError::MissingAccount)?;
385        borrow_counter.try_borrow()?;
386
387        // SAFETY: The borrow counter guarantees there are no mutable borrow of this account.
388        // The unwrap is safe because accounts.len() == borrow_counters.len(), so the missing
389        // account error should have been returned above.
390        let svm_account = unsafe {
391            &*self
392                .shared_account_fields
393                .get(index as usize)
394                .unwrap()
395                .get()
396        };
397
398        let private_fields = unsafe {
399            &*self
400                .private_account_fields
401                .get(index as usize)
402                .unwrap()
403                .get()
404        };
405
406        let account = TransactionAccountView {
407            abi_account: svm_account,
408            private_fields,
409        };
410
411        Ok(AccountRef {
412            account,
413            borrow_counter,
414        })
415    }
416
417    pub(crate) fn add_lamports_delta(&self, balance: i128) -> Result<(), InstructionError> {
418        let delta = self.lamports_delta.get();
419        self.lamports_delta.set(
420            delta
421                .checked_add(balance)
422                .ok_or(InstructionError::ArithmeticOverflow)?,
423        );
424        Ok(())
425    }
426
427    pub(crate) fn get_lamports_delta(&self) -> i128 {
428        self.lamports_delta.get()
429    }
430
431    fn deconstruct_into_keyed_account_shared_data(&mut self) -> Vec<KeyedAccountSharedData> {
432        let shared_account_fields = std::mem::take(&mut self.shared_account_fields);
433        let private_account_fields = std::mem::take(&mut self.private_account_fields);
434        shared_account_fields
435            .into_iter()
436            .zip(private_account_fields)
437            .map(|(shared_fields_cell, private_fields_cell)| {
438                let shared_fields = shared_fields_cell.into_inner();
439                let private_fields = private_fields_cell.into_inner();
440                (
441                    shared_fields.key,
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            })
451            .collect()
452    }
453
454    pub(crate) fn deconstruct_into_account_shared_data(&mut self) -> Vec<AccountSharedData> {
455        let shared_account_fields = std::mem::take(&mut self.shared_account_fields);
456        let private_account_fields = std::mem::take(&mut self.private_account_fields);
457        shared_account_fields
458            .into_iter()
459            .zip(private_account_fields)
460            .map(|(shared_fields_cell, private_fields_cell)| {
461                let shared_fields = shared_fields_cell.into_inner();
462                let private_fields = private_fields_cell.into_inner();
463                AccountSharedData::create_from_existing_shared_data(
464                    shared_fields.lamports,
465                    private_fields.payload.clone(),
466                    shared_fields.owner,
467                    private_fields.executable,
468                    private_fields.rent_epoch,
469                )
470            })
471            .collect()
472    }
473
474    pub(crate) fn take(mut self) -> DeconstructedTransactionAccounts {
475        let shared_data = self.deconstruct_into_keyed_account_shared_data();
476        (shared_data, self.touched_flags, self.resize_delta)
477    }
478
479    pub fn resize_delta(&self) -> i64 {
480        self.resize_delta.get()
481    }
482
483    pub(crate) fn account_key(&self, index: IndexOfAccount) -> Option<&Pubkey> {
484        // SAFETY: We never modify an account key, so returning a reference to it is safe.
485        unsafe {
486            self.shared_account_fields
487                .get(index as usize)
488                .map(|acc| &(*acc.get()).key)
489        }
490    }
491
492    pub(crate) fn account_keys_iter(&self) -> impl Iterator<Item = &Pubkey> {
493        // SAFETY: We never modify account keys, so returning an immutable reference to them is safe.
494        unsafe {
495            self.shared_account_fields
496                .iter()
497                .map(|item| &(*item.get()).key)
498        }
499    }
500}
501
502#[derive(Default, Debug, Clone)]
503#[cfg(not(any(target_arch = "bpf", target_arch = "sbf")))]
504struct BorrowCounter {
505    counter: Cell<i8>,
506}
507
508#[cfg(not(any(target_arch = "bpf", target_arch = "sbf")))]
509impl BorrowCounter {
510    #[inline]
511    fn is_writing(&self) -> bool {
512        self.counter.get() < 0
513    }
514
515    #[inline]
516    fn is_reading(&self) -> bool {
517        self.counter.get() > 0
518    }
519
520    #[inline]
521    fn try_borrow(&self) -> Result<(), InstructionError> {
522        if self.is_writing() {
523            return Err(InstructionError::AccountBorrowFailed);
524        }
525
526        if let Some(counter) = self.counter.get().checked_add(1) {
527            self.counter.set(counter);
528            return Ok(());
529        }
530
531        Err(InstructionError::AccountBorrowFailed)
532    }
533
534    #[inline]
535    fn try_borrow_mut(&self) -> Result<(), InstructionError> {
536        if self.is_writing() || self.is_reading() {
537            return Err(InstructionError::AccountBorrowFailed);
538        }
539
540        self.counter.set(self.counter.get().saturating_sub(1));
541
542        Ok(())
543    }
544
545    #[inline]
546    fn release_borrow(&self) {
547        self.counter.set(self.counter.get().saturating_sub(1));
548    }
549
550    #[inline]
551    fn release_borrow_mut(&self) {
552        self.counter.set(self.counter.get().saturating_add(1));
553    }
554}
555
556#[cfg(not(any(target_arch = "bpf", target_arch = "sbf")))]
557pub struct AccountRef<'a> {
558    account: TransactionAccountView<'a>,
559    borrow_counter: &'a BorrowCounter,
560}
561
562#[cfg(not(any(target_arch = "bpf", target_arch = "sbf")))]
563impl Drop for AccountRef<'_> {
564    fn drop(&mut self) {
565        self.borrow_counter.release_borrow();
566    }
567}
568
569#[cfg(not(any(target_arch = "bpf", target_arch = "sbf")))]
570impl<'a> Deref for AccountRef<'a> {
571    type Target = TransactionAccountView<'a>;
572    fn deref(&self) -> &Self::Target {
573        &self.account
574    }
575}
576
577#[derive(Debug)]
578#[cfg(not(any(target_arch = "bpf", target_arch = "sbf")))]
579pub struct AccountRefMut<'a> {
580    account: TransactionAccountViewMut<'a>,
581    borrow_counter: &'a BorrowCounter,
582}
583
584#[cfg(not(any(target_arch = "bpf", target_arch = "sbf")))]
585impl Drop for AccountRefMut<'_> {
586    fn drop(&mut self) {
587        self.account
588            .abi_account
589            .payload
590            .set_len(self.account.private_fields.payload_len() as u64);
591        self.borrow_counter.release_borrow_mut();
592    }
593}
594
595#[cfg(not(any(target_arch = "bpf", target_arch = "sbf")))]
596impl<'a> Deref for AccountRefMut<'a> {
597    type Target = TransactionAccountViewMut<'a>;
598    fn deref(&self) -> &Self::Target {
599        &self.account
600    }
601}
602
603#[cfg(not(any(target_arch = "bpf", target_arch = "sbf")))]
604impl DerefMut for AccountRefMut<'_> {
605    fn deref_mut(&mut self) -> &mut Self::Target {
606        &mut self.account
607    }
608}
609
610#[cfg(all(test, not(target_arch = "sbf"), not(target_arch = "bpf")))]
611mod tests {
612    use {
613        crate::transaction_accounts::TransactionAccounts, solana_account::AccountSharedData,
614        solana_instruction_error::InstructionError, solana_pubkey::Pubkey,
615    };
616
617    #[test]
618    fn test_missing_account() {
619        let accounts = vec![
620            (
621                Pubkey::new_unique(),
622                AccountSharedData::new(2, 1, &Pubkey::new_unique()),
623            ),
624            (
625                Pubkey::new_unique(),
626                AccountSharedData::new(2, 1, &Pubkey::new_unique()),
627            ),
628        ];
629
630        let tx_accounts = TransactionAccounts::new(accounts);
631
632        let res = tx_accounts.try_borrow(3);
633        assert_eq!(res.err(), Some(InstructionError::MissingAccount));
634
635        let res = tx_accounts.try_borrow_mut(3);
636        assert_eq!(res.err(), Some(InstructionError::MissingAccount));
637    }
638
639    #[test]
640    fn test_invalid_borrow() {
641        let accounts = vec![
642            (
643                Pubkey::new_unique(),
644                AccountSharedData::new(2, 1, &Pubkey::new_unique()),
645            ),
646            (
647                Pubkey::new_unique(),
648                AccountSharedData::new(2, 1, &Pubkey::new_unique()),
649            ),
650        ];
651
652        let tx_accounts = TransactionAccounts::new(accounts);
653
654        // Two immutable borrows are valid
655        {
656            let acc_1 = tx_accounts.try_borrow(0);
657            assert!(acc_1.is_ok());
658
659            let acc_2 = tx_accounts.try_borrow(1);
660            assert!(acc_2.is_ok());
661
662            let acc_1_new = tx_accounts.try_borrow(0);
663            assert!(acc_1_new.is_ok());
664
665            assert_eq!(acc_1.unwrap().account, acc_1_new.unwrap().account);
666        }
667
668        // Two mutable borrows are invalid
669        {
670            let acc_1 = tx_accounts.try_borrow_mut(0);
671            assert!(acc_1.is_ok());
672
673            let acc_2 = tx_accounts.try_borrow_mut(1);
674            assert!(acc_2.is_ok());
675
676            let acc_1_new = tx_accounts.try_borrow_mut(0);
677            assert_eq!(acc_1_new.err(), Some(InstructionError::AccountBorrowFailed));
678        }
679
680        // Mutable after immutable must fail
681        {
682            let acc_1 = tx_accounts.try_borrow(0);
683            assert!(acc_1.is_ok());
684
685            let acc_2 = tx_accounts.try_borrow(1);
686            assert!(acc_2.is_ok());
687
688            let acc_1_new = tx_accounts.try_borrow_mut(0);
689            assert_eq!(acc_1_new.err(), Some(InstructionError::AccountBorrowFailed));
690        }
691
692        // Immutable after mutable must fail
693        {
694            let acc_1 = tx_accounts.try_borrow_mut(0);
695            assert!(acc_1.is_ok());
696
697            let acc_2 = tx_accounts.try_borrow_mut(1);
698            assert!(acc_2.is_ok());
699
700            let acc_1_new = tx_accounts.try_borrow(0);
701            assert_eq!(acc_1_new.err(), Some(InstructionError::AccountBorrowFailed));
702        }
703
704        // Different scopes are good
705        {
706            let acc_1 = tx_accounts.try_borrow_mut(0);
707            assert!(acc_1.is_ok());
708        }
709
710        {
711            let acc_1 = tx_accounts.try_borrow_mut(0);
712            assert!(acc_1.is_ok());
713        }
714    }
715
716    #[test]
717    fn too_many_borrows() {
718        let accounts = vec![
719            (
720                Pubkey::new_unique(),
721                AccountSharedData::new(2, 1, &Pubkey::new_unique()),
722            ),
723            (
724                Pubkey::new_unique(),
725                AccountSharedData::new(2, 1, &Pubkey::new_unique()),
726            ),
727        ];
728
729        let tx_accounts = TransactionAccounts::new(accounts);
730        let mut borrows = Vec::new();
731        for i in 0..129 {
732            let acc = tx_accounts.try_borrow(1);
733            if i < 127 {
734                assert!(acc.is_ok());
735                borrows.push(acc.unwrap());
736            } else {
737                assert_eq!(acc.err(), Some(InstructionError::AccountBorrowFailed));
738            }
739        }
740    }
741}