From 968c053e50ec3c1409245bc09b38206c79b8d60e Mon Sep 17 00:00:00 2001 From: ananas Date: Wed, 22 Oct 2025 01:32:06 +0100 Subject: [PATCH 1/2] chore: remove debug assert --- programs/system/src/cpi_context/process_cpi_context.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/programs/system/src/cpi_context/process_cpi_context.rs b/programs/system/src/cpi_context/process_cpi_context.rs index 48fd038334..42503c9ac7 100644 --- a/programs/system/src/cpi_context/process_cpi_context.rs +++ b/programs/system/src/cpi_context/process_cpi_context.rs @@ -183,8 +183,6 @@ pub fn copy_cpi_context_outputs( } bytes = inner_bytes; } - // Debug assert TODO: remove pre mainnet deployment - assert_eq!(bytes.len(), 4); } Ok(()) } From ef4b0c2a7904bbdb64ef8d24ba334027a15149c5 Mon Sep 17 00:00:00 2001 From: ananas Date: Wed, 22 Oct 2025 01:43:20 +0100 Subject: [PATCH 2/2] fix: consume AccountIterator on remaining() and remaining_unchecked() --- .../account-checks/docs/ACCOUNT_ITERATOR.md | 10 +++-- .../account-checks/src/account_iterator.rs | 8 ++-- .../account-checks/tests/account_iterator.rs | 42 +++++++++---------- 3 files changed, 33 insertions(+), 27 deletions(-) diff --git a/program-libs/account-checks/docs/ACCOUNT_ITERATOR.md b/program-libs/account-checks/docs/ACCOUNT_ITERATOR.md index ee6adac3a5..59bfc64e4c 100644 --- a/program-libs/account-checks/docs/ACCOUNT_ITERATOR.md +++ b/program-libs/account-checks/docs/ACCOUNT_ITERATOR.md @@ -122,17 +122,21 @@ fn next_option_mut( ### `remaining` ```rust -fn remaining(&self) -> Result<&'info [T], AccountError> +fn remaining(self) -> Result<&'info [T], AccountError> ``` - Returns all unprocessed accounts +- **Consumes the iterator** - cannot use iterator after calling this method - **Error:** `NotEnoughAccountKeys` (20014) if iterator exhausted +- Use case: Getting all remaining accounts for dynamic processing ### `remaining_unchecked` ```rust -fn remaining_unchecked(&self) -> Result<&'info [T], AccountError> +fn remaining_unchecked(self) -> Result<&'info [T], AccountError> ``` - Returns remaining accounts or empty slice if exhausted -- Never errors +- **Consumes the iterator** - cannot use iterator after calling this method +- Never errors - returns empty slice if no accounts remaining +- Use case: Optional remaining accounts where empty is acceptable ## Status Methods diff --git a/program-libs/account-checks/src/account_iterator.rs b/program-libs/account-checks/src/account_iterator.rs index 39b34e8e80..32ec2b0538 100644 --- a/program-libs/account-checks/src/account_iterator.rs +++ b/program-libs/account-checks/src/account_iterator.rs @@ -166,7 +166,7 @@ impl<'info, T: AccountInfoTrait> AccountIterator<'info, T> { /// Get all remaining accounts in the iterator. #[inline(always)] #[track_caller] - pub fn remaining(&self) -> Result<&'info [T], AccountError> { + pub fn remaining(self) -> Result<&'info [T], AccountError> { if self.position >= self.accounts.len() { #[cfg(feature = "std")] { @@ -182,10 +182,12 @@ impl<'info, T: AccountInfoTrait> AccountIterator<'info, T> { Ok(&self.accounts[self.position..]) } - /// Get all remaining accounts in the iterator. + /// Get all remaining accounts in the iterator without validation. + /// + /// Returns an empty slice if position is at or past the end. #[inline(always)] #[track_caller] - pub fn remaining_unchecked(&self) -> Result<&'info [T], AccountError> { + pub fn remaining_unchecked(self) -> Result<&'info [T], AccountError> { if self.position >= self.accounts.len() { Ok(&[]) } else { diff --git a/program-libs/account-checks/tests/account_iterator.rs b/program-libs/account-checks/tests/account_iterator.rs index f50e7d8acc..52aa429e98 100644 --- a/program-libs/account-checks/tests/account_iterator.rs +++ b/program-libs/account-checks/tests/account_iterator.rs @@ -627,51 +627,51 @@ fn test_boundary_position() { assert_eq!(iter.position(), 2); let remaining = iter.remaining().unwrap(); assert_eq!(remaining.len(), 1); - - // Consume last - iter.next_account("last").unwrap(); - assert!(iter.iterator_is_empty()); } #[test] -fn test_multiple_remaining_calls() { +fn test_remaining_consumes_iterator() { let accounts = create_pinocchio_accounts(4, false, false); let mut iter = AccountIterator::new(&accounts); iter.next_account("first").unwrap(); - // Call remaining multiple times - let remaining1 = iter.remaining().unwrap(); - let remaining2 = iter.remaining().unwrap(); - let remaining3 = iter.remaining().unwrap(); + // remaining() consumes the iterator + let remaining = iter.remaining().unwrap(); - assert_eq!(remaining1.len(), 3); - assert_eq!(remaining2.len(), 3); - assert_eq!(remaining3.len(), 3); - assert_eq!(iter.position(), 1); // Position unchanged + assert_eq!(remaining.len(), 3); + // Iterator is consumed, cannot use it anymore } #[test] fn test_remaining_unchecked_vs_remaining() { + // Test remaining() with accounts available let accounts = create_pinocchio_accounts(2, false, false); - let mut iter = AccountIterator::new(&accounts); - - // Both should work when accounts available + let iter = AccountIterator::new(&accounts); let remaining1 = iter.remaining().unwrap(); + assert_eq!(remaining1.len(), 2); + + // Test remaining_unchecked() with accounts available + let accounts = create_pinocchio_accounts(2, false, false); + let iter = AccountIterator::new(&accounts); let remaining2 = iter.remaining_unchecked().unwrap(); - assert_eq!(remaining1.len(), remaining2.len()); + assert_eq!(remaining2.len(), 2); - // Consume all + // Test remaining() when all consumed - should error + let accounts = create_pinocchio_accounts(2, false, false); + let mut iter = AccountIterator::new(&accounts); iter.next_account("first").unwrap(); iter.next_account("second").unwrap(); - - // remaining() should error assert_eq!( get_error(iter.remaining()), AccountError::NotEnoughAccountKeys ); - // remaining_unchecked() should return empty + // Test remaining_unchecked() when all consumed - should return empty + let accounts = create_pinocchio_accounts(2, false, false); + let mut iter = AccountIterator::new(&accounts); + iter.next_account("first").unwrap(); + iter.next_account("second").unwrap(); let unchecked = iter.remaining_unchecked().unwrap(); assert_eq!(unchecked.len(), 0); }