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