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#[repr(C)]
23#[derive(Debug, PartialEq)]
24struct AccountSharedFields {
25 key: Pubkey,
26 owner: Pubkey,
27 lamports: u64,
28 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 let Some(data) = Arc::get_mut(&mut self.private_fields.payload) else {
115 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 data.reserve(new_len.saturating_sub(data.len()));
139
140 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#[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 if new_len > MAX_ACCOUNT_DATA_LEN as usize {
327 return Err(InstructionError::InvalidRealloc);
328 }
329 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 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 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 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 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 {
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 {
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 {
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 {
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 {
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}