Skip to content

refactor: cpi context#1924

Closed
ananas-block wants to merge 4 commits intomainfrom
jorrit/refactor-cpi-context
Closed

refactor: cpi context#1924
ananas-block wants to merge 4 commits intomainfrom
jorrit/refactor-cpi-context

Conversation

@ananas-block
Copy link
Contributor

@ananas-block ananas-block commented Aug 30, 2025

State

  • do not merge we need to merge other changes first

Changes

Cpi context refactor

  1. account order changed from:
    • from: executing instruction compressed accounts .. cpi context compressed accounts
    • to: cpi context accounts .. executing instruction accounts
    • note this is a breaking change
    • afaik nobody is using cpi context accounts yet so we will deactivate the feature with v2 deployment and activate it again with this deployment
  2. Cpi context account state
    • replace NewAddressParamsPacked -> CpiContextNewAddressParamsAssignedPacked
    • to support address v2 derivation we store the owner in CpiContextNewAddressParamsAssignedPacked
    • readonly_addresses and readonly_accounts are placeholders
    • CpiContextInAccount and CpiContextOutAccount are structs without options for efficient zero copy
  3. ReInitCpiContextAccount instruction
    • reinitializes existing cpi context accounts to the new data structure CpiContextAccount -> CpiContextAccount2
    • reinitialization changes the account discriminator from CpiContextAccount::DISCRIMINATOR -> CpiContextAccount2::DISCRIMINATOR
    • CpiContextAccount2::DISCRIMINATOR is anchor derived

system program small instruction accounts

  • the feature gate which rendered passing of program account infos in cpis unnecessary has been dropped
  • added system-program and account-compression program accounts to expected accounts

system program cpi signer check

  1. replacesolana_pubkey::Pubkey::create_program_address with pinocchio_pubkey::derive_address

light-account-checks

  1. add AccountIterator

Summary by CodeRabbit

  • New Features

    • Reinitialize legacy CPI context accounts.
    • Write-to-CPI-context support across read-only and account-info flows.
    • Expanded zero‑copy/packed structures for compressed accounts.
    • Richer account iteration with clearer errors.
  • SDK

    • New small/CPI-context invocation helpers and account‑meta builders.
    • Default enables v2; streamlined feature flags.
    • Added packed read‑only/account types and assigned address support.
  • Performance

    • Optional runtime profiling across critical paths.
  • Refactor

    • Migrated to new CPI context format and discriminators.
  • Documentation

    • Removed unaudited “Audit” notice.
  • Chores

    • Cleaned workspace configs and test data updates.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 30, 2025

Walkthrough

Introduces a new CPI-context v2 system: adds zero-copy CPI context state/types, processing, and write paths; migrates discriminators; adds reinit instruction (feature-gated). Refactors account iteration/checks, expands error codes, and integrates profiling. Updates SDK and tests to new APIs, replaces small_ix with v2, and adjusts manifests/features.

Changes

Cohort / File(s) Summary
Cargo/Features & Manifests
programs/system/Cargo.toml, program-libs/compressed-account/Cargo.toml, program-libs/hasher/Cargo.toml, program-libs/account-checks/Cargo.toml, sdk-libs/sdk/Cargo.toml, sdk-libs/sdk-pinocchio/Cargo.toml, sdk-libs/sdk-types/Cargo.toml, .cargo/config.toml
Adds features (reinit, profiling, zero-copy), new deps (solana-msg, light-program-profiler, light-heap, etc.), expands default features (v2), removes small_ix, and drops toolchain rustflags/alias config.
System Program: CPI Context (new v2)
programs/system/src/cpi_context/* (account.rs, address.rs, instruction_data_trait.rs, process_cpi_context.rs, state.rs, mod.rs), programs/system/src/context.rs, programs/system/src/constants.rs, programs/system/src/errors.rs, programs/system/src/lib.rs
Introduces ZCpiContextAccount2, packed types, serialization, processing, output copying, and instruction-data trait impl; splits discriminators (v1/v2), adds reinit instruction (feature), new errors, and migrates internal wrappers to CPI context v2.
System Program: Processors & Invoke
programs/system/src/processor/* (process.rs, cpi.rs, create_inputs_cpi_data.rs, create_outputs_cpi_data.rs, read_only_, sum_check.rs, verify_proof.rs, insert_into_queues.rs, utils.rs), programs/system/src/invoke/` (instruction.rs, instruction_small.rs, processor.rs, verify_signer.rs, mod.rs)
Refactors to use AccountIterator, Result-returning accessors, profiling, CPI-data size limit, context index helpers with labels, updated signer derivation, bounds checks, and removal of old invoke_cpi account module; small-instruction path gains ExecutionAccounts.
Anchor system crate stubs
anchor-programs/system/src/*
Adds CpiContextAccount2/ZCpiContextAccount2 usage and an unimplemented public re_init function stub.
Compressed Account Lib
program-libs/compressed-account/src/* (compressed_account.rs, instruction_data/{mod.rs, zero_copy.rs, zero_copy_set.rs, traits.rs, cpi_context.rs, with_*`, data.rs, invoke_cpi.rs}, indexer_event/parse.rs, lib.rs, pubkey.rs)
Adds #[repr(C)]/ZeroCopyMut to many structs, expands errors, modifies instruction-data traits to return Result and include write_to_cpi_context, exposes zero-copy setters/initializers, adjusts parsing counts, and adds Pubkey zero-copy impls.
Account Checks Lib
program-libs/account-checks/src/* (account_iterator.rs, error.rs, lib.rs, account_info/pinocchio.rs, packed_accounts.rs)
Introduces AccountIterator and ProgramPackedAccounts with detailed errors; adds NotEnoughAccountKeys; inlining hints.
SDK Types & SDK
sdk-libs/sdk-types/src/* (constants.rs, cpi_accounts*.rs, instruction/tree_info.rs, lib.rs, cpi_context_write.rs), sdk-libs/sdk/src/cpi/* (mod.rs, invoke.rs, accounts_small_ix.rs, accounts_cpi_context.rs), sdk-libs/sdk-pinocchio/src/cpi/*
Switches gating to v2, adds CPI-context write accounts and account-metas builders, updates small path to config-driven metas, changes trait bounds to Clone, updates discriminators to v1/v2, and exposes new constructors and invokers.
Tests (System/Programs)
programs/system/tests/*, program-tests/system-*/*, program-tests/compressed-token-test/tests/test.rs, program-tests/create-address-test-program/*, programs/package.json
Adds extensive CPI-context v2 tests and reinit E2E; adjusts for new APIs, account ordering, and write-to-context; removes debug prints; updates JS scripts.
Tests (Libs)
program-libs/account-checks/tests/account_iterator.rs, program-libs/compressed-account/tests/*
Adds comprehensive tests for AccountIterator, zero-copy setters, Pubkey zero-copy, and account_option_config behavior with error paths.
Fixtures/CLI JSON
cli/accounts/*
Updates rentEpoch fields to 0 and replaces base64 blobs where applicable.
Misc Profiling Annotations
programs/system/src/account_compression_state/address.rs, many programs/system/src/processor/*, utils
Adds #[profile] on numerous functions; no signature changes except those noted above.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Client
  participant InvokingProgram
  participant SystemProgram
  participant CPIContextAccount
  participant CompressionProgram

  Note over Client,InvokingProgram: Invoke CPI with optional CPI context (v2)
  Client->>InvokingProgram: build CpiInputs + Account metas (v2)
  InvokingProgram->>SystemProgram: Instruction (Invoke)
  activate SystemProgram
  SystemProgram->>SystemProgram: Parse inputs (AccountIterator, Result<?>)
  alt first_set_context or set_context
    SystemProgram->>CPIContextAccount: set_cpi_context (store packed inputs/outputs)
    CPIContextAccount-->>SystemProgram: Ok
  else no context write
    SystemProgram-->>SystemProgram: proceed without CPI context mutation
  end
  SystemProgram->>CompressionProgram: enqueue inputs/outputs/proofs
  CompressionProgram-->>SystemProgram: Ok
  opt CPI context outputs present
    SystemProgram->>CPIContextAccount: copy_cpi_context_outputs(bytes)
    CPIContextAccount-->>SystemProgram: Ok
  end
  SystemProgram-->>InvokingProgram: Result
  deactivate SystemProgram
Loading
sequenceDiagram
  autonumber
  actor Operator
  participant SystemProgram
  participant CPIContextAccount(v1)
  participant CPIContextAccount(v2)

  Note over Operator,SystemProgram: Re-initialize legacy CPI context (feature "reinit")
  Operator->>SystemProgram: ReInitCpiContextAccount
  SystemProgram->>CPIContextAccount(v1): Deserialize legacy
  CPIContextAccount(v1)-->>SystemProgram: associated_merkle_tree
  SystemProgram->>CPIContextAccount(v2): cpi_context_account_new(RE_INIT=true, init params)
  CPIContextAccount(v2)-->>SystemProgram: ZCpiContextAccount2
  SystemProgram-->>Operator: Ok
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

Suggested reviewers

  • sergeytimoshin
  • SwenSchaeferjohann

Poem

A rabbit taps its tiny key,
New context sprouts—now v2! see?
Discriminators split in two,
Zero-copy hops right through.
Reinit burrows old to new,
Profiles trace each tunnel, too.
Thump! The tests all pass—yahoo! 🐇✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title "refactor: cpi context" is concise and accurately describes the primary intent of the changeset — a refactor of CPI context handling — and follows a clear "type: scope" style, making it specific and readable for teammates scanning PR history.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch jorrit/refactor-cpi-context

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@ananas-block ananas-block force-pushed the jorrit/refactor-cpi-context branch 4 times, most recently from 3179f80 to e540210 Compare August 31, 2025 18:32
@ananas-block ananas-block force-pushed the jorrit/refactor-cpi-context branch 2 times, most recently from 37ae132 to cee8fd4 Compare September 21, 2025 12:56
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 25

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (12)
programs/system/src/processor/read_only_address.rs (1)

17-28: Unchecked index can panic; validate index and variant before use.

remaining_accounts[... as usize] will panic on out‑of‑bounds, allowing untrusted input to abort the program. Use checked conversion + .get_mut() and keep the existing error mapping.

Apply:

-        let merkle_tree = if let AcpAccount::BatchedAddressTree(tree) =
-            &mut remaining_accounts[read_only_address.address_merkle_tree_account_index as usize]
-        {
-            tree
-        } else {
-            msg!(format!(
-                "Read only address account is not a BatchedAddressTree {:?}",
-                read_only_address
-            )
-            .as_str());
-            return Err(SystemProgramError::InvalidAccount.into());
-        };
+        let idx = usize::try_from(read_only_address.address_merkle_tree_account_index)
+            .map_err(|_| SystemProgramError::InvalidAccount)?;
+        let merkle_tree = match remaining_accounts.get_mut(idx) {
+            Some(AcpAccount::BatchedAddressTree(tree)) => tree,
+            _ => {
+                msg!(format!(
+                    "Read only address account is not a BatchedAddressTree {:?}",
+                    read_only_address
+                )
+                .as_str());
+                return Err(SystemProgramError::InvalidAccount.into());
+            }
+        };
programs/system/src/processor/create_address_cpi_data.rs (1)

44-49: Avoid unwrap in on‑chain path. Return a typed error instead.

unwrap() can abort the program; prefer a fallible conversion.

-                    context
-                        .get_legacy_merkle_context(new_address_params.address_queue_index())
-                        .unwrap()
-                        .rollover_fee,
+                    context
+                        .get_legacy_merkle_context(new_address_params.address_queue_index())
+                        .map(|ctx| ctx.rollover_fee)
+                        .ok_or(ProgramError::InvalidArgument)?,
program-libs/compressed-account/src/indexer_event/parse.rs (2)

376-399: Same guard needed for ACCOUNT_INFO path; fix comment and length check

Mirror the READ_ONLY fix to avoid split_at panics and correct the comment.

-            // Min len for a small instruction 4 accounts + 1 tree or queue
-            // Fee payer + authority + registered program + account compression program + account compression authority
+            // Min len incl. system_program:
+            // fee_payer + authority + registered_program + account_compression_program +
+            // account_compression_authority + system_program = 6
             if accounts.len() < 5 {
                 return Err(ParseIndexerEventError::DeserializeSystemInstructionError);
             }
@@
-            let system_accounts_len = if data.mode == 0 {
+            let system_accounts_len = if data.mode == 0 {
                 11
             } else {
-                let mut len = 6; // fee_payer + authority + registered_program + account_compression_program + account_compression_authority + system_program
+                let mut len = 6; // fee_payer + authority + registered_program + account_compression_program + account_compression_authority + system_program
                 if data.compress_or_decompress_lamports > 0 {
                     len += 1;
                 }
                 if !data.is_compress && data.compress_or_decompress_lamports > 0 {
                     len += 1;
                 }
                 if data.with_cpi_context {
                     len += 1;
                 }
                 len
             };
-            let accounts = accounts.split_at(system_accounts_len).1;
+            if accounts.len() < system_accounts_len {
+                return Err(ParseIndexerEventError::DeserializeSystemInstructionError);
+            }
+            let accounts = accounts.split_at(system_accounts_len).1;

329-351: Prevent panic: validate against computed system_accounts_len before split_at

Computed system_accounts_len can be > 5 while the current guard only checks for 5; split_at(system_accounts_len) may panic. Add a guard against system_accounts_len and update the comment to reflect inclusion of system_program.

Apply:

-            // Min len for a small instruction 3 accounts + 1 tree or queue
-            // Fee payer + authority + registered program + account compression program + account compression authority
+            // Min len incl. system_program:
+            // fee_payer + authority + registered_program + account_compression_program +
+            // account_compression_authority + system_program = 6
             if accounts.len() < 5 {
                 return Err(ParseIndexerEventError::DeserializeSystemInstructionError);
             }
@@
-            let system_accounts_len = if data.mode == 0 {
+            let system_accounts_len = if data.mode == 0 {
                 11
             } else {
-                let mut len = 6; // fee_payer + authority + registered_program + account_compression_program + account_compression_authority + system_program
+                let mut len = 6; // fee_payer + authority + registered_program + account_compression_program + account_compression_authority + system_program
                 if data.compress_or_decompress_lamports > 0 {
                     len += 1;
                 }
                 if !data.is_compress && data.compress_or_decompress_lamports > 0 {
                     len += 1;
                 }
                 if data.with_cpi_context {
                     len += 1;
                 }
                 len
             };
 
+            if accounts.len() < system_accounts_len {
+                return Err(ParseIndexerEventError::DeserializeSystemInstructionError);
+            }
             let accounts = accounts.split_at(system_accounts_len).1;

Optional: confirm the differing is_compress gating vs INVOKE/INVOKE_CPI is intentional.

programs/system/src/processor/sum_check.rs (1)

7-13: Profiling is enabled unconditionally — gate/remove #[profile] on hot-paths (sum_check).

Findings: #[profile] is applied to programs/system/src/processor/sum_check.rs (lines 7–13) and appears across programs/system/src (e.g., programs/system/src/utils.rs, programs/system/src/cpi_context/state.rs, programs/system/src/processor/process.rs). No Cargo feature or #[cfg] gating was found.

Action required:

  • Apply conditional attribute: #[cfg_attr(feature = "light_program_profiler", profile)] and add a corresponding Cargo feature (or make the profiler a no-op in release builds).
  • Remove or avoid #[profile] on critical hot-path functions like sum_check, or ensure profiling is disabled in production; run targeted microbenchmarks to confirm no runtime impact.
program-tests/utils/src/assert_rollover.rs (2)

154-156: Fix unsatisfiable lamports-conservation assertion in program-tests/utils/src/assert_rollover.rs

File: program-tests/utils/src/assert_rollover.rs (lines 154–156) — the current assertion reduces to 0 == new_merkle_tree_lamports + new_queue_lamports (always false). Replace RHS to exclude the duplicated old value:

-    assert_eq!(
-        old_merkle_tree_lamports,
-        new_merkle_tree_lamports + new_queue_lamports + old_merkle_tree_lamports
-    );
+    assert_eq!(
+        old_merkle_tree_lamports,
+        new_merkle_tree_lamports + new_queue_lamports
+    );

If rollover/network fees apply, include expected fees in the RHS or pass expected fee lamports into this helper and assert equality including fees.


118-122: Remove ambiguous comment and clarify index invariant. Replace the // Isn’t this wrong??? line with a clear note—e.g.

// index must be preserved across queue rollover
programs/system/src/processor/create_outputs_cpi_data.rs (1)

172-185: Bug: removing from context.addresses uses index from filtered iterator.

position() after filter(|x| x.is_some()) returns the index within the filtered view, not the original vector, so remove(position) can delete the wrong element whenever there are leading Nones. Use an index over the original iterator.

-        if let Some(address) = account.address() {
-            if let Some(position) = context
-                .addresses
-                .iter()
-                .filter(|x| x.is_some())
-                .position(|&x| x.unwrap() == address)
-            {
-                context.addresses.remove(position);
-            } else {
-                msg!(format!("context.addresses: {:?}", context.addresses).as_str());
-                return Err(SystemProgramError::InvalidAddress.into());
-            }
-        }
+        if let Some(address) = account.address() {
+            if let Some(position) = context
+                .addresses
+                .iter()
+                .position(|x| matches!(x, Some(a) if *a == address))
+            {
+                context.addresses.remove(position);
+            } else {
+                msg!(format!("context.addresses len: {}", context.addresses.len()).as_str());
+                return Err(SystemProgramError::InvalidAddress.into());
+            }
+        }
program-libs/compressed-account/src/instruction_data/with_readonly.rs (1)

525-767: Remove noisy debug prints in the randomized test; they will flood CI logs.

Printing full structs and serialized vectors for 1000 iters is excessive.

Apply:

-        println!("\n\ne2e test seed {}\n\n", seed);
+        eprintln!("e2e test seed {}", seed);
@@
-            println!("value {:?}", value);
-            println!("vec {:?}", vec);
+            // Uncomment for targeted debugging:
+            // eprintln!("value: {:?}\nvec: {:?}", value, vec);

Optionally reduce num_iters locally or gate verbose output behind an env var.

anchor-programs/system/src/cpi_context_account.rs (1)

50-72: Return type mismatch between function signature and implementation

The deserialize_cpi_context_account function returns ZCpiContextAccount2<'a> which includes a context field (line 47), but this field is not defined in the ZCpiContextAccount2 struct declaration (lines 44-48). This appears to be a compilation error.

The struct definition and the deserialization return value don't match. Add the missing context field to the struct:

 pub struct ZCpiContextAccount2<'a> {
     pub fee_payer: Ref<&'a mut [u8], light_compressed_account::pubkey::Pubkey>,
     pub associated_merkle_tree: Ref<&'a mut [u8], light_compressed_account::pubkey::Pubkey>,
+    pub context: Vec<ZInstructionDataInvokeCpi<'a>>,
 }
programs/system/src/processor/cpi.rs (1)

22-33: Update create_cpi_data_and_context call in process.rs
The call at programs/system/src/processor/process.rs:107 currently passes only ctx and num_output_compressed_accounts as u8; it must be updated to include the new parameters—num_leaves, num_nullifiers, num_new_addresses, hashed_pubkeys_capacity, cpi_data_len, invoking_program_id, and remaining_accounts.

programs/system/src/processor/create_inputs_cpi_data.rs (1)

77-84: Remove unwrap; return a proper error instead of panicking on-chain

get_legacy_merkle_context(...).unwrap() can panic if the context is missing. Return a program error like other branches.

-                AcpAccount::StateTree(_) => {
-                    is_batched = false;
-                    context
-                        .get_legacy_merkle_context(current_mt_index)
-                        .unwrap()
-                        .hashed_pubkey
-                }
+                AcpAccount::StateTree(_) => {
+                    is_batched = false;
+                    let legacy = context
+                        .get_legacy_merkle_context(current_mt_index)
+                        .ok_or(SystemProgramError::InputMerkleTreeIndexOutOfBounds)?;
+                    legacy.hashed_pubkey
+                }

Comment on lines +28 to +30
pub fn re_init_cpi_context_account(ctx: Context<InitializeCpiContextAccount>) -> Result<()> {
unimplemented!("anchor wrapper not implemented")
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Gate unimplemented Anchor wrappers to avoid accidental on-chain panics.

Since these handlers are unimplemented!(), ensure they can’t be compiled into a deployed SBF build.

Apply per-function gating now, and consider gating the entire #[program] block similarly:

+#[cfg(feature = "idl-build")]
 pub fn re_init_cpi_context_account(ctx: Context<InitializeCpiContextAccount>) -> Result<()> {
     unimplemented!("anchor wrapper not implemented")
 }

Additionally (outside this hunk), add at crate root:

#[cfg(all(target_os = "solana"), not(feature = "idl-build"))]
compile_error!("Anchor wrappers must not be built for on-chain use; enable feature `idl-build` only for IDL generation.");

⚠️ Potential issue

Incorrect context type for re-init instruction.

This should use the re-init accounts context, not InitializeCpiContextAccount. Otherwise the IDL and account constraints for re-init will be wrong.

Apply:

-pub fn re_init_cpi_context_account(ctx: Context<InitializeCpiContextAccount>) -> Result<()> {
+pub fn re_init_cpi_context_account(ctx: Context<ReInitCpiContextAccount>) -> Result<()> {
     unimplemented!("anchor wrapper not implemented")
 }

Please replace ReInitCpiContextAccount with the exact type name defined in instructions::* if it differs.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
pub fn re_init_cpi_context_account(ctx: Context<InitializeCpiContextAccount>) -> Result<()> {
unimplemented!("anchor wrapper not implemented")
}
pub fn re_init_cpi_context_account(ctx: Context<ReInitCpiContextAccount>) -> Result<()> {
unimplemented!("anchor wrapper not implemented")
}
🤖 Prompt for AI Agents
In anchor-programs/system/src/lib.rs around lines 28 to 30, the
re_init_cpi_context_account function is using the wrong context type
(Context<InitializeCpiContextAccount>); replace it with the re-init accounts
context type defined in instructions::* (e.g., Context<ReInitCpiContextAccount>
or the exact name in instructions), update any imports to bring that type into
scope, and adjust any references so the function signature and IDL reflect the
correct re-init account constraints.

Comment on lines +16 to 18
profile-program = []
profile-heap = ["dep:light-heap"]

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

🧩 Analysis chain

Gate profiling deps; avoid unconditional pulls.

Suggest feature-gating profiler/logging crates to keep minimal dependency sets for non‑profile builds.

 [features]
 default = []
@@
-profile-program = []
+profile-program = ["dep:solana-msg", "dep:light-program-profiler"]
 profile-heap = ["dep:light-heap"]
@@
-light-zero-copy = { workspace = true, features = ["std", "mut", "derive"] }
+light-zero-copy = { workspace = true, features = ["std", "mut", "derive"] }
@@
-solana-msg = { workspace = true }
+solana-msg = { workspace = true, optional = true }
@@
-light-program-profiler = { workspace = true }
+light-program-profiler = { workspace = true, optional = true }
 light-heap = { workspace = true, optional = true }

Verify usage is behind the feature:

Also applies to: 23-24, 27-27, 33-34


🏁 Script executed:

#!/bin/bash
rg -nP -C2 '\b(solana_msg!|profile!\(|light_program_profiler)\b'

Length of output: 8511


Gate profiling imports and macro calls behind the profile-program feature
Wrap every use light_program_profiler::profile, any profile!(…) invocation, and the use solana_msg::msg import in all crates (including program-libs/compressed-account/src/instruction_data/zero_copy_set.rs and every programs/system/... file) with #[cfg(feature = "profile-program")] so that optional dependencies remain unused when the feature is disabled.

fn cpi_context(&self) -> Option<CompressedCpiContext>;
fn bump(&self) -> Option<u8>;
fn account_option_config(&self) -> AccountOptions;
fn account_option_config(&self) -> Result<AccountOptions, CompressedAccountError>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

🧩 Analysis chain

Good: account_option_config now fallible.

Returning Result<AccountOptions, CompressedAccountError> enables early validation (e.g., CPI‑context presence). Implementations in with_account_info/with_readonly look consistent.

Please confirm all implementers updated (including zero_copy.rs stubs) and call sites handle errors rather than assuming success.


🏁 Script executed:

#!/bin/bash
rg -nP 'fn\s+account_option_config\(' -C2 program-libs/compressed-account/src
rg -nP 'account_option_config\(\)' -C2 --type=rust

Length of output: 15703


Fix remaining unimplemented account_option_config() in zero_copy.rs; ensure callers don't unwrap

  • Unimplemented stub at program-libs/compressed-account/src/instruction_data/zero_copy.rs:442 — implement it or make it unreachable.
  • Production call-site programs/system/src/lib.rs uses inputs.account_option_config()? (errors are propagated).
  • Many tests call .account_option_config().unwrap() (acceptable in tests); remove any non-test .unwrap() usages.

Comment on lines +442 to 444
fn account_option_config(&self) -> Result<AccountOptions, CompressedAccountError> {
unimplemented!()
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Unimplemented account_option_config() will panic at runtime if invoked.

Provide a minimal, correct implementation rather than unimplemented!().

Apply this diff:

-    fn account_option_config(&self) -> Result<AccountOptions, CompressedAccountError> {
-        unimplemented!()
-    }
+    fn account_option_config(&self) -> Result<AccountOptions, CompressedAccountError> {
+        let sol_pool_pda = self.compress_or_decompress_lamports().is_some();
+        let decompression_recipient = sol_pool_pda && !self.is_compress();
+        Ok(AccountOptions {
+            sol_pool_pda,
+            decompression_recipient,
+            cpi_context_account: false,
+            write_to_cpi_context: false,
+        })
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
fn account_option_config(&self) -> Result<AccountOptions, CompressedAccountError> {
unimplemented!()
}
fn account_option_config(&self) -> Result<AccountOptions, CompressedAccountError> {
let sol_pool_pda = self.compress_or_decompress_lamports().is_some();
let decompression_recipient = sol_pool_pda && !self.is_compress();
Ok(AccountOptions {
sol_pool_pda,
decompression_recipient,
cpi_context_account: false,
write_to_cpi_context: false,
})
}
🤖 Prompt for AI Agents
In program-libs/compressed-account/src/instruction_data/zero_copy.rs around
lines 442–444, replace the unimplemented!() with a real implementation that
constructs and returns an AccountOptions based on this struct's fields and
returns Result<AccountOptions, CompressedAccountError>; specifically map the
instance's signer/writable/optional/public-key related fields into the
AccountOptions struct, perform any necessary validation, and on success return
Ok(account_options) or on validation/conversion failure return the appropriate
CompressedAccountError via Err(...).

account-compression = { workspace = true, features = ["cpi"] }
light-compressed-account = { workspace = true, features = ["anchor"] }
light-sdk = { workspace = true, features = ["anchor", "v2", "small_ix"] }
light-sdk = { workspace = true, features = ["anchor", "v2"] }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

🧩 Analysis chain

Removed small_ix feature — looks aligned with CPI-context refactor; verify no lingering gates.

Change matches the PR direction to consolidate around the v2 path. Please confirm no crates/tests still gate code on cfg(feature = "small_ix") or expect the smaller IX layout.

Run this script to detect any remaining references and to inspect light-sdk’s declared features:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "1) Look for code gated by feature \"small_ix\""
rg -n --hidden -S -g '!**/target/**' -C2 '\bcfg\s*\(\s*feature\s*=\s*"small_ix"\s*\)'

echo
echo "2) Look for manifests enabling \"small_ix\""
rg -n --hidden -S -g '**/Cargo.toml' -C2 '"small_ix"'

echo
echo "3) Inspect light-sdk features"
LIGHTSDK=$(rg -nPl --hidden -g '**/Cargo.toml' '^\s*name\s*=\s*"light-sdk"\s*$' | head -n1)
echo "light-sdk manifest: ${LIGHTSDK:-<not found>}"
if [[ -n "${LIGHTSDK:-}" ]]; then
  awk 'f{if($0 ~ /^\[/){exit} print} /\[features\]/{f=1}' "$LIGHTSDK"
fi

echo
echo "4) Ensure this test program does not reference small_ix symbols"
rg -n -S -g 'program-tests/create-address-test-program/**' -C2 '(?i)\bsmall[_-]?ix\b|SmallIx'

echo
echo "Expected: no matches in (1), (2), (4); (3) shows anchor/v2 paths sufficient without small_ix."

Length of output: 193


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Repo root: $(pwd)"

echo
echo "1) Search for token occurrences of 'small_ix' / 'small-ix' / 'SmallIx'"
rg -n -uu -S -C3 '\bsmall[_-]?ix\b' || true

echo
echo "2) Search for cfg(...) feature uses that mention small_ix"
rg -n -uu -S -C3 '\bcfg\s*\([^)]*small[_-]?ix[^)]*\)' || true

echo
echo "3) Search Cargo.toml files for feature entries named small_ix"
rg -n -uu -S -C3 '"small_ix"' --glob '**/Cargo.toml' || true

echo
echo "4) Locate light-sdk Cargo.toml and print its [features] block (first match)"
LIGHTSDK_FILE=$(rg -n -uu -S --hidden --glob '**/Cargo.toml' '^\s*name\s*=\s*"light-sdk"\s*$' | sed -n '1p' | cut -d: -f1 || true)
echo "light-sdk manifest: ${LIGHTSDK_FILE:-<not found>}"
if [[ -n "${LIGHTSDK_FILE:-}" ]]; then
  awk 'f{if($0 ~ /^\[/){exit} print} /\[features\]/{f=1}' "$LIGHTSDK_FILE" || true
fi

echo
echo "5) Check the test program tree for small_ix mentions"
rg -n -uu -S -C3 '(?i)\bsmall[_-]?ix\b|SmallIx' program-tests/create-address-test-program/** || true

echo
echo "Search complete."

Length of output: 14948


Remaining runtime references to small_ix found — update tests/dev script or restore equivalent API.

  • No compile-time gating found: no cfg(feature = "small_ix") matches and no Cargo.toml with "small_ix" (feature removal confirmed).
  • Runtime/API references still remain and must be updated:
    • scripts/devenv.sh — exports CARGO_FEATURES="${CARGO_FEATURES:-small_ix}"
    • program-tests/system-cpi-v2-test/tests/invoke_cpi_with_read_only.rs — multiple uses of small_ix, config.small_ix, branches, and mode selection
    • program-tests/create-address-test-program/src/lib.rs — small_ix fn param, light_sdk::cpi::CpiAccountsSmall usage, and instruction field small_ix
  • Fix: remove or update these references to the removed feature (or reintroduce/alias an equivalent in light-sdk), then run CI/compile/tests to verify no regressions.

Comment on lines +294 to +303
let mut account_data = account_info
.try_borrow_mut_data()
.map_err(|_| SystemProgramError::BorrowingDataFailed)?;
// SAFETY: account_data is a valid RefMut<[u8]>, pointer and length are valid
let data = unsafe { slice::from_raw_parts_mut(account_data.as_mut_ptr(), account_data.len()) };
let (discriminator, data) = data.split_at_mut(8);
if discriminator != CPI_CONTEXT_ACCOUNT_2_DISCRIMINATOR {
msg!("Invalid cpi context account discriminator.");
return Err(SystemProgramError::InvalidCpiContextDiscriminator.into());
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Guard against short account data and avoid unnecessary unsafe.

split_at_mut(8) will panic on short data; switch to a bounds check and avoid raw-parts construction.

-    let mut account_data = account_info
-        .try_borrow_mut_data()
-        .map_err(|_| SystemProgramError::BorrowingDataFailed)?;
-    // SAFETY: account_data is a valid RefMut<[u8]>, pointer and length are valid
-    let data = unsafe { slice::from_raw_parts_mut(account_data.as_mut_ptr(), account_data.len()) };
-    let (discriminator, data) = data.split_at_mut(8);
+    let mut account_data = account_info
+        .try_borrow_mut_data()
+        .map_err(|_| SystemProgramError::BorrowingDataFailed)?;
+    let data: &mut [u8] = &mut *account_data;
+    if data.len() < 8 {
+        msg!("Cpi context account data too small.");
+        return Err(ProgramError::InvalidAccountData);
+    }
+    let (discriminator, data) = data.split_at_mut(8);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let mut account_data = account_info
.try_borrow_mut_data()
.map_err(|_| SystemProgramError::BorrowingDataFailed)?;
// SAFETY: account_data is a valid RefMut<[u8]>, pointer and length are valid
let data = unsafe { slice::from_raw_parts_mut(account_data.as_mut_ptr(), account_data.len()) };
let (discriminator, data) = data.split_at_mut(8);
if discriminator != CPI_CONTEXT_ACCOUNT_2_DISCRIMINATOR {
msg!("Invalid cpi context account discriminator.");
return Err(SystemProgramError::InvalidCpiContextDiscriminator.into());
}
let mut account_data = account_info
.try_borrow_mut_data()
.map_err(|_| SystemProgramError::BorrowingDataFailed)?;
let data: &mut [u8] = &mut *account_data;
if data.len() < 8 {
msg!("Cpi context account data too small.");
return Err(ProgramError::InvalidAccountData);
}
let (discriminator, data) = data.split_at_mut(8);
if discriminator != CPI_CONTEXT_ACCOUNT_2_DISCRIMINATOR {
msg!("Invalid cpi context account discriminator.");
return Err(SystemProgramError::InvalidCpiContextDiscriminator.into());
}
🤖 Prompt for AI Agents
In programs/system/src/cpi_context/state.rs around lines 294 to 303, the code
uses unsafe slice::from_raw_parts_mut and split_at_mut(8) which can panic on
short account data; replace this with a safe bounds check and non-unsafe
slicing: first check account_data.len() >= 8 and return
SystemProgramError::BorrowingDataFailed or InvalidCpiContextDiscriminator (per
existing semantics) if too short, then obtain a safe mutable slice (no raw
pointers) and compare the first 8 bytes against
CPI_CONTEXT_ACCOUNT_2_DISCRIMINATOR; remove the unsafe block entirely and use
safe slice indexing/splitting to avoid panics and unsoundness.

Comment on lines +416 to +431
// SAFETY: account_data is a valid RefMut<[u8]>, pointer and length are valid
let data = unsafe { slice::from_raw_parts_mut(account_data.as_mut_ptr(), account_data.len()) };
let (discriminator, data) = data.split_at_mut(8);
if RE_INIT {
// Check discriminator matches
if discriminator != CPI_CONTEXT_ACCOUNT_1_DISCRIMINATOR {
msg!("Invalid cpi context account discriminator.");
return Err(SystemProgramError::InvalidCpiContextDiscriminator.into());
}
// Zero out account data
data.fill(0);
} else if discriminator != [0u8; 8] {
msg!("Invalid cpi context account discriminator.");
return Err(SystemProgramError::InvalidCpiContextDiscriminator.into());
}
discriminator.copy_from_slice(&CPI_CONTEXT_ACCOUNT_2_DISCRIMINATOR);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Same here: short-data guard and safe slice for init path.

-    // SAFETY: account_data is a valid RefMut<[u8]>, pointer and length are valid
-    let data = unsafe { slice::from_raw_parts_mut(account_data.as_mut_ptr(), account_data.len()) };
-    let (discriminator, data) = data.split_at_mut(8);
+    let data: &mut [u8] = &mut *account_data;
+    if data.len() < 8 {
+        msg!("Cpi context account data too small.");
+        return Err(ProgramError::InvalidAccountData);
+    }
+    let (discriminator, data) = data.split_at_mut(8);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// SAFETY: account_data is a valid RefMut<[u8]>, pointer and length are valid
let data = unsafe { slice::from_raw_parts_mut(account_data.as_mut_ptr(), account_data.len()) };
let (discriminator, data) = data.split_at_mut(8);
if RE_INIT {
// Check discriminator matches
if discriminator != CPI_CONTEXT_ACCOUNT_1_DISCRIMINATOR {
msg!("Invalid cpi context account discriminator.");
return Err(SystemProgramError::InvalidCpiContextDiscriminator.into());
}
// Zero out account data
data.fill(0);
} else if discriminator != [0u8; 8] {
msg!("Invalid cpi context account discriminator.");
return Err(SystemProgramError::InvalidCpiContextDiscriminator.into());
}
discriminator.copy_from_slice(&CPI_CONTEXT_ACCOUNT_2_DISCRIMINATOR);
let data: &mut [u8] = &mut *account_data;
if data.len() < 8 {
msg!("Cpi context account data too small.");
return Err(ProgramError::InvalidAccountData);
}
let (discriminator, data) = data.split_at_mut(8);
if RE_INIT {
// Check discriminator matches
if discriminator != CPI_CONTEXT_ACCOUNT_1_DISCRIMINATOR {
msg!("Invalid cpi context account discriminator.");
return Err(SystemProgramError::InvalidCpiContextDiscriminator.into());
}
// Zero out account data
data.fill(0);
} else if discriminator != [0u8; 8] {
msg!("Invalid cpi context account discriminator.");
return Err(SystemProgramError::InvalidCpiContextDiscriminator.into());
}
discriminator.copy_from_slice(&CPI_CONTEXT_ACCOUNT_2_DISCRIMINATOR);
🤖 Prompt for AI Agents
In programs/system/src/cpi_context/state.rs around lines 416 to 431, add a
short-data guard and avoid using from_raw_parts_mut directly on account_data for
the init path: first check account_data.len() is at least 8 + expected payload
length and return an error if too short; then safely obtain mutable slices
(e.g., split_at_mut) from the safe RefMut (or use safe methods provided by the
account data wrapper) instead of raw pointer arithmetic; ensure the init branch
zeroes only the payload slice and that discriminator checks/assignments operate
on the validated 8-byte slice; keep the existing discriminator error returns but
perform length validation before any unsafe slice creation.

Comment on lines +51 to 59
let registered_program_pda = accounts.next_non_mut("registered_program_pda")?;

let authority = check_authority(accounts.next())?;
let account_compression_authority =
accounts.next_non_mut("account_compression_authority")?;
let account_compression_program =
accounts.next_non_mut("account_compression_program")?;

let registered_program_pda = check_non_mut_account_info(accounts.next())?;
let system_program = accounts.next_non_mut("system_program")?;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Validate program IDs for account_compression_program and system_program.

next_non_mut() only checks mutability; it doesn’t verify the passed accounts are the expected programs. This weakens authZ and could allow spoofed program accounts to slip through. Mirror invoke.rs by asserting identities.

Apply this diff:

 use crate::{
     accounts::{
         account_checks::{
-            check_option_cpi_context_account, check_option_decompression_recipient,
-            check_option_sol_pool_pda,
+            check_option_cpi_context_account, check_option_decompression_recipient,
+            check_option_sol_pool_pda, check_account_compression_program, check_system_program,
         },
         account_traits::{CpiContextAccountTrait, InvokeAccounts, SignerAccounts},
     },
     errors::SystemProgramError,
     Result,
 };
@@
-            let account_compression_program =
-                accounts.next_non_mut("account_compression_program")?;
+            let account_compression_program = {
+                let acc = accounts.next_non_mut("account_compression_program")?;
+                check_account_compression_program(acc)?;
+                acc
+            };
@@
-            let system_program = accounts.next_non_mut("system_program")?;
+            let system_program = {
+                let acc = accounts.next_non_mut("system_program")?;
+                check_system_program(acc)?;
+                acc
+            };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let registered_program_pda = accounts.next_non_mut("registered_program_pda")?;
let authority = check_authority(accounts.next())?;
let account_compression_authority =
accounts.next_non_mut("account_compression_authority")?;
let account_compression_program =
accounts.next_non_mut("account_compression_program")?;
let registered_program_pda = check_non_mut_account_info(accounts.next())?;
let system_program = accounts.next_non_mut("system_program")?;
use crate::{
accounts::{
account_checks::{
check_option_cpi_context_account, check_option_decompression_recipient,
check_option_sol_pool_pda, check_account_compression_program, check_system_program,
},
account_traits::{CpiContextAccountTrait, InvokeAccounts, SignerAccounts},
},
errors::SystemProgramError,
Result,
};
let registered_program_pda = accounts.next_non_mut("registered_program_pda")?;
let account_compression_authority =
accounts.next_non_mut("account_compression_authority")?;
let account_compression_program = {
let acc = accounts.next_non_mut("account_compression_program")?;
check_account_compression_program(acc)?;
acc
};
let system_program = {
let acc = accounts.next_non_mut("system_program")?;
check_system_program(acc)?;
acc
};

Comment on lines +78 to +82
let remaining_accounts = if !account_options.write_to_cpi_context {
accounts.remaining()?
} else {
&[]
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

remaining() will Err on zero remainder; use remaining_unchecked().

AccountIterator::remaining() returns NotEnoughAccountKeys when none remain, which will spuriously fail in the common case of exactly consumed inputs.

Apply this diff:

-        let remaining_accounts = if !account_options.write_to_cpi_context {
-            accounts.remaining()?
+        let remaining_accounts = if !account_options.write_to_cpi_context {
+            accounts.remaining_unchecked()?
         } else {
             &[]
         };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let remaining_accounts = if !account_options.write_to_cpi_context {
accounts.remaining()?
} else {
&[]
};
let remaining_accounts = if !account_options.write_to_cpi_context {
accounts.remaining_unchecked()?
} else {
&[]
};
🤖 Prompt for AI Agents
In programs/system/src/invoke_cpi/instruction_small.rs around lines 78 to 82,
the code calls accounts.remaining() which returns an Err when no accounts
remain; replace this with accounts.remaining_unchecked() to avoid spurious
NotEnoughAccountKeys errors when inputs are exactly consumed, ensuring you
preserve the existing branch logic (returning &[] when write_to_cpi_context is
true) and update any error handling/comments accordingly.

Comment on lines +56 to 57
ReInitCpiContextAccount,
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Build break when reinit feature is disabled: enum variant is unconditional but match arm is gated.

With reinit off, the match is non‑exhaustive. Gate the enum variant too (or add a fallback arm under not(feature="reinit")).

 pub enum InstructionDiscriminator {
     InitializeCpiContextAccount,
     Invoke,
     InvokeCpi,
     InvokeCpiWithReadOnly,
     InvokeCpiWithAccountInfo,
-    ReInitCpiContextAccount,
+    #[cfg(feature = "reinit")]
+    ReInitCpiContextAccount,
 }
@@
-        #[cfg(feature = "reinit")]
-        InstructionDiscriminator::ReInitCpiContextAccount => reinit_cpi_context_account(accounts),
+        #[cfg(feature = "reinit")]
+        InstructionDiscriminator::ReInitCpiContextAccount => reinit_cpi_context_account(accounts),
+        // Optional: if you prefer keeping the variant always present, add this instead:
+        // #[cfg(not(feature = "reinit"))]
+        // InstructionDiscriminator::ReInitCpiContextAccount => return Err(ProgramError::InvalidInstructionData),

Also applies to: 87-89

🤖 Prompt for AI Agents
In programs/system/src/lib.rs around lines 56-57 (and similarly at 87-89) an
enum variant ReInitCpiContextAccount is declared unconditionally while its match
arm is gated by #[cfg(feature = "reinit")], causing non‑exhaustive match when
the feature is disabled; fix by gating the enum variant with the same
#[cfg(feature = "reinit")] attribute (or alternatively add a #[cfg(not(feature =
"reinit"))] fallback match arm) so declaration and matching are consistent and
compilation succeeds with the feature off.

@ananas-block ananas-block force-pushed the jorrit/refactor-cpi-context branch from cee8fd4 to 281bf48 Compare September 21, 2025 14:55
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 13

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (7)
programs/system/src/processor/cpi.rs (2)

45-52: Bug: truncation before min (len() as u8).

Casting remaining_accounts.len() to u8 before min can undercount when len > 255 (e.g., 300 → 44). Min in usize first, then cast.

-    let bytes_size = InsertIntoQueuesInstructionDataMut::required_size_for_capacity(
-        num_leaves,
-        num_nullifiers,
-        num_new_addresses,
-        min(remaining_accounts.len() as u8, num_leaves),
-        min(remaining_accounts.len() as u8, num_nullifiers),
-        min(remaining_accounts.len() as u8, num_new_addresses),
-    );
+    let rem_len = remaining_accounts.len();
+    let capped_leaves = rem_len.min(num_leaves as usize) as u8;
+    let capped_nullifiers = rem_len.min(num_nullifiers as usize) as u8;
+    let capped_new_addrs = rem_len.min(num_new_addresses as usize) as u8;
+    let bytes_size = InsertIntoQueuesInstructionDataMut::required_size_for_capacity(
+        num_leaves,
+        num_nullifiers,
+        num_new_addresses,
+        capped_leaves,
+        capped_nullifiers,
+        capped_new_addrs,
+    );

39-41: AccountMeta param order is (pubkey, is_signer, is_writable) — flip the args.
AccountMeta::new(account_infos[0].key(), false, true) sets is_signer=false and is_writable=true (writable but not signer); change to AccountMeta::new(account_infos[0].key(), true, false) to make the PDA a signer but not writable.
File: programs/system/src/processor/cpi.rs lines 39–41.

programs/system/src/accounts/remaining_account_checks.rs (2)

120-128: OOB risk: slice uses fixed LEN without checking data length

Before slicing [8..StateMerkleTreeAccount::LEN], ensure data.len() >= StateMerkleTreeAccount::LEN to avoid panics on malformed accounts.

-                let merkle_tree = bytemuck::from_bytes::<StateMerkleTreeAccount>(
-                    &data[8..StateMerkleTreeAccount::LEN],
-                );
+                if data.len() < StateMerkleTreeAccount::LEN {
+                    return Err(SystemProgramError::InvalidAccount);
+                }
+                let merkle_tree = bytemuck::from_bytes::<StateMerkleTreeAccount>(&data[8..StateMerkleTreeAccount::LEN]);

164-167: Same OOB risk for AddressMerkleTreeAccount

Add the same length check for AddressMerkleTreeAccount::LEN.

-                let merkle_tree = bytemuck::from_bytes::<AddressMerkleTreeAccount>(
-                    &data[8..AddressMerkleTreeAccount::LEN],
-                );
+                if data.len() < AddressMerkleTreeAccount::LEN {
+                    return Err(SystemProgramError::InvalidAccount);
+                }
+                let merkle_tree = bytemuck::from_bytes::<AddressMerkleTreeAccount>(&data[8..AddressMerkleTreeAccount::LEN]);
program-libs/compressed-account/src/instruction_data/data.rs (1)

98-106: PackedReadOnlyAddress likely incurs tail padding (36B) while Borsh encodes 35B

repr(C) with [u8;32], u16, u8 → size_of = 36 due to tail padding to 2‑byte alignment. Anchor/Borsh will serialize 35 bytes. If this struct is used for zero‑copy over serialized data, that’s a mismatch. Either:

  • avoid ZeroCopyMut here and keep it purely serialized, or
  • introduce an explicit padding byte in both layout and serialization, or
  • split into “in‑memory” vs “serialized” representations.

Add a test to catch regressions.

+#[cfg(test)]
+mod layout_tests_packed_readonly {
+    use super::*;
+    #[test]
+    fn size_is_multiple_of_alignment() {
+        assert_eq!(core::mem::size_of::<PackedReadOnlyAddress>(), 36);
+    }
+}
anchor-programs/system/src/cpi_context_account.rs (1)

50-72: Fix unsafe zero-copy deserialization (prevent panics/UB).

  • Do not unwrap the borrow; replace account_info.try_borrow_mut_data().unwrap() with proper error mapping/return.
  • Validate buffer length (>= 8) and check the discriminator before indexing/slicing (&mut data[8..]) to avoid out‑of‑bounds panics.
  • Avoid unchecked unsafe slice::from_raw_parts_mut where possible; use split_at_mut or add a precise SAFETY comment documenting the invariants that guarantee pointer+length validity.
  • Audit and fix other occurrences of this pattern found in the repo: anchor-programs/system/src/cpi_context_account.rs (deserialize_cpi_context_account), programs/system/src/cpi_context/state.rs:298,417; programs/system/src/accounts/remaining_account_checks.rs:146,176; program-libs/batched-merkle-tree/src/merkle_tree.rs:165; programs/account-compression/src/context.rs:192,212; programs/account-compression/src/instructions/migrate_state.rs:205.
program-libs/compressed-account/src/instruction_data/zero_copy.rs (1)

487-489: cpi_context() should return None, not unimplemented!().

Current code will panic when invoked.

Apply:

-    fn cpi_context(&self) -> Option<CompressedCpiContext> {
-        unimplemented!()
-    }
+    fn cpi_context(&self) -> Option<CompressedCpiContext> {
+        None
+    }
🧹 Nitpick comments (43)
program-libs/zero-copy/src/vec.rs (1)

146-153: zero_out(): add trait bound; consider zeroing used region only

  • Add a method-level bound so this compiles regardless of the impl block bounds.
  • Optional: zero only the used portion to avoid O(capacity) work on large buffers.
-    pub fn zero_out(&mut self) {
+    pub fn zero_out(&mut self)
+    where
+        T: IntoBytes,
+    {
         *self.get_len_mut() = 0
             .try_into()
             .map_err(|_| ZeroCopyError::InvalidConversion)
             .unwrap();
-        self.slice.as_mut_bytes().fill(0);
+        // Optional perf: wipe only the active region
+        // self.as_mut_slice().as_mut_bytes().fill(0);
+        // Current behavior: wipe entire capacity
+        self.slice.as_mut_bytes().fill(0);
     }
programs/system/tests/cpi_context.rs (2)

132-145: Consider making calculate_vector_offsets public for reusability

This helper function calculates offsets for zero-copy vectors and could be useful in other test modules. Consider making it public rather than private inline if other modules need similar offset calculations.


327-332: Document the purpose of clean_input_data

The function clears specific fields but doesn't document why these particular fields need cleaning. Adding a brief doc comment would help clarify its role in test data preparation.

+/// Clears optional fields that should not be persisted in CPI context
 fn clean_input_data(instruction_data: &mut InstructionDataInvokeCpi) {
     instruction_data.cpi_context = None;
     instruction_data.compress_or_decompress_lamports = None;
     instruction_data.relay_fee = None;
     instruction_data.proof = None;
 }
programs/system/src/processor/cpi.rs (8)

7-7: Gate profiling import behind a feature.

Avoid unconditional dependency/overhead in production builds.

-use light_program_profiler::profile;
+#[cfg(feature = "profile")]
+use light_program_profiler::profile;

22-22: Gate the profiling attribute.

Keeps profiling opt‑in.

-#[profile]
+#[cfg_attr(feature = "profile", profile)]

82-82: Same here: gate the profiling attribute on the CPI invoke.

-#[profile]
+#[cfg_attr(feature = "profile", profile)]

42-42: Overflow risk when summing u8s for capacity.

num_nullifiers + num_leaves + num_new_addresses can overflow u8 (wraps in release, panics in debug). Compute in usize.

-    let account_indices =
-        Vec::<u8>::with_capacity((num_nullifiers + num_leaves + num_new_addresses) as usize);
+    let total_indices =
+        usize::from(num_nullifiers) + usize::from(num_leaves) + usize::from(num_new_addresses);
+    let account_indices = Vec::<u8>::with_capacity(total_indices);

56-60: Consider centralizing the 10KB constant.

Move MAX_CPI_BUFFER_SIZE to a shared constants module to keep on‑chain/off‑chain limits in sync.


64-64: Avoid unwrap on length conversion.

Safer and self‑documenting given the bound above.

-    bytes[8..12].copy_from_slice(&u32::try_from(byte_len - 12).unwrap().to_le_bytes());
+    debug_assert!(byte_len >= 12 && byte_len <= MAX_CPI_BUFFER_SIZE);
+    let vec_len = u32::try_from(byte_len - 12).expect("bounded by MAX_CPI_BUFFER_SIZE");
+    bytes[8..12].copy_from_slice(&vec_len.to_le_bytes());

71-71: Same overflow concern for addresses capacity.

Sum in usize to avoid u8 wrap.

-            addresses: Vec::with_capacity((num_nullifiers + num_new_addresses) as usize),
+            addresses: Vec::with_capacity(
+                usize::from(num_nullifiers) + usize::from(num_new_addresses),
+            ),

22-33: Introduce an args struct for create_cpi_data_and_context. Reduces call‑site brittleness and improves readability. One call site found: programs/system/src/processor/process.rs:107 — update that invocation to pass the new args struct.

sdk-libs/sdk-pinocchio/src/cpi/mod.rs (1)

2-8: Remove commented code or guard with cfg

Commented-out accounts_small lines should be deleted or reintroduced behind a feature flag to avoid API ambiguity.

-//#[cfg(feature = "v2")]
-//pub mod accounts_small;
...
-//#[cfg(feature = "v2")]
-//pub use accounts_small::*;
programs/system/src/accounts/remaining_account_checks.rs (1)

50-61: Profiler attribute gating

#[profile] requires light_program_profiler at build time. Consider cfg gating (e.g., cfg(feature = "profiler")) to avoid pulling it into all builds.

-#[profile]
+#[cfg_attr(feature = "profiler", profile)]
program-libs/compressed-account/src/indexer_event/parse.rs (2)

485-493: Avoid extra allocations when concatenating outputs

Current concat clones both vectors. Prefer reserve+extend to avoid copies.

-            output_compressed_accounts: [
-                associated_instructions.cpi_context_outputs.clone(),
-                associated_instructions
-                    .executing_system_instruction
-                    .output_compressed_accounts
-                    .clone(),
-            ]
-            .concat(),
+            output_compressed_accounts: {
+                let mut v = Vec::with_capacity(
+                    associated_instructions.cpi_context_outputs.len()
+                    + associated_instructions.executing_system_instruction.output_compressed_accounts.len()
+                );
+                v.extend_from_slice(&associated_instructions.cpi_context_outputs);
+                v.extend_from_slice(
+                    &associated_instructions.executing_system_instruction.output_compressed_accounts
+                );
+                v
+            },

327-333: Doc/comments mismatch

Comments mention “Min len for a small instruction 3/4 accounts + 1 tree or queue” but the new code requires at least 5 (fee_payer+authority+registered_program+account_compression_program+account_compression_authority). Update comments to match.

Also applies to: 375-381

program-libs/compressed-account/src/lib.rs (1)

38-41: Typos in user-facing messages

“thresold” → “threshold”; “lenght” → “length”.

-    #[error("Invalid rollover thresold")]
+    #[error("Invalid rollover threshold")]
@@
-    #[error("Invalid input lenght")]
+    #[error("Invalid input length")]
program-libs/compressed-account/src/instruction_data/cpi_context.rs (1)

25-43: Consider using Into<u8> trait for bool-to-u8 conversions.

The current implementation correctly casts bools to u8, but you could make the intent clearer and more idiomatic by implementing or using the Into<u8> trait for bool conversions.

 impl CompressedCpiContextTrait for ZCompressedCpiContext {
     fn first_set_context(&self) -> u8 {
-        self.first_set_context() as u8
+        u8::from(self.first_set_context())
     }
 
     fn set_context(&self) -> u8 {
-        self.set_context() as u8
+        u8::from(self.set_context())
     }
 }
 
 impl CompressedCpiContextTrait for CompressedCpiContext {
     fn first_set_context(&self) -> u8 {
-        self.first_set_context as u8
+        u8::from(self.first_set_context)
     }
 
     fn set_context(&self) -> u8 {
-        self.set_context as u8
+        u8::from(self.set_context)
     }
 }
programs/system/tests/invoke_cpi_instruction_small.rs (2)

136-136: Address the TODO comment for write_to_cpi_context testing.

The TODO indicates missing test coverage for the new write_to_cpi_context field. This should be tested to ensure the CPI context write functionality works correctly.

Would you like me to generate test cases for the write_to_cpi_context: true scenario?


503-510: Improve error handling for missing required accounts.

The comment mentions this will panic with index out of bounds, but the test actually returns an error. Consider making the assertion more specific about the expected error type.

-        assert!(result.is_err(), "Expected a panic due to missing accounts");
+        assert!(matches!(result, Err(ProgramError::NotEnoughAccountKeys)), 
+                "Expected NotEnoughAccountKeys error due to missing accounts");
program-libs/compressed-account/src/pubkey.rs (1)

124-130: Consider documenting why Pubkey is its own inner type.

The implementations of ZeroCopyStructInner and ZeroCopyStructInnerMut both return Pubkey as the inner type. While this is correct for a simple wrapper type, it might be worth adding a brief comment explaining this design choice.

+// Pubkey is a simple wrapper around [u8; 32] with no additional structure,
+// so it serves as its own inner type for zero-copy operations.
 impl ZeroCopyStructInner for Pubkey {
     type ZeroCopyInner = Pubkey;
 }

 impl ZeroCopyStructInnerMut for Pubkey {
     type ZeroCopyInnerMut = Pubkey;
 }
sdk-libs/sdk-pinocchio/src/cpi/accounts_small.rs (1)

5-5: Unused imports after commenting out code

AccountMeta and Pubkey are now unused. Clean them up to avoid warnings.

-use pinocchio::{account_info::AccountInfo, instruction::AccountMeta, pubkey::Pubkey};
+use pinocchio::account_info::AccountInfo;

Also drop unused light_sdk_types constants from the import list if this file no longer builds metas.

-use light_sdk_types::{
-    CpiAccountsSmall as GenericCpiAccountsSmall, ACCOUNT_COMPRESSION_AUTHORITY_PDA,
-    ACCOUNT_COMPRESSION_PROGRAM_ID, REGISTERED_PROGRAM_PDA, SMALL_SYSTEM_ACCOUNTS_LEN,
-};
+use light_sdk_types::CpiAccountsSmall as GenericCpiAccountsSmall;
program-libs/compressed-account/src/instruction_data/data.rs (2)

36-45: OK to mark packed params as repr(C) + ZeroCopyMut; add size checks

The field ordering yields a 36‑byte layout; add a small test to guard the layout.

+#[cfg(test)]
+mod layout_tests_new_address_params_packed {
+    use super::*;
+    #[test]
+    fn size_is_stable() {
+        assert_eq!(core::mem::size_of::<NewAddressParamsPacked>(), 36);
+    }
+}

47-58: Prefer u8 over bool for FFI‑stable layouts

assigned_to_account: bool is 1 byte today but can be brittle across FFI. If this struct crosses language/FFI boundaries, consider u8 (0/1) for stability.

-    pub assigned_to_account: bool,
+    pub assigned_to_account: u8, // 0 = false, 1 = true

Follow-up: adapt new()/assigned_account_index() accordingly.

sdk-libs/sdk/src/cpi/accounts_cpi_context.rs (1)

5-13: Consider more descriptive function name and add documentation.

The function creates account metas in a specific order but the name doesn't clearly convey this. Consider adding documentation to clarify the order and purpose of the returned accounts.

Consider this improvement:

+/// Creates account metas for CPI context operations in the following order:
+/// - fee_payer: writable signer
+/// - cpi_signer: read-only signer  
+/// - cpi_context: writable non-signer
 pub fn get_account_metas_from_config_cpi_context(
     config: CpiContextWriteAccounts<AccountInfo>,
 ) -> [AccountMeta; 3] {
sdk-libs/sdk-types/src/cpi_context_write.rs (2)

4-4: TODO comment should be addressed.

The TODO comment indicates this should be moved to ctoken types. Consider creating an issue to track this if it's not being addressed in this PR.

Do you want me to generate a GitHub issue to track moving this to ctoken types?


22-28: Consider using references instead of cloning.

The to_account_infos method clones all accounts. If the caller only needs references, this could be inefficient.

The existing to_account_info_refs method (lines 30-32) already provides the reference version, so this approach offers both options which is good design.

anchor-programs/system/src/cpi_context_account.rs (1)

44-48: Zero-copy struct needs proper validation.

The ZCpiContextAccount2 struct uses zero-copy references but lacks validation of the underlying data integrity during construction.

Consider adding validation methods to ensure data consistency:

impl<'a> ZCpiContextAccount2<'a> {
    pub fn validate(&self) -> Result<(), ZeroCopyError> {
        // Validate fee_payer is not all zeros when account is active
        // Validate associated_merkle_tree is a valid pubkey
        // Validate context vector integrity
        Ok(())
    }
}
program-libs/compressed-account/tests/zero_copy_set.rs (1)

978-1009: Good panic test but consider more robust validation.

The panic test for invalid address length is good for catching immediate failures, but consider adding non-panic error handling for production robustness.

Consider adding a non-panicking validation method:

pub fn validate_address_length(address: &[u8]) -> Result<(), CompressedAccountError> {
    if address.len() != 32 {
        return Err(CompressedAccountError::InvalidAddressLength);
    }
    Ok(())
}
programs/system/src/processor/create_inputs_cpi_data.rs (1)

57-90: Improved bounds checking but consider error message clarity.

The replacement of direct indexing with bounds-checked get() is a good security improvement. However, the error message in line 85 could be more descriptive.

Improve the error message for better debugging:

-                    msg!(format!("create_inputs_cpi_data {} ", current_mt_index).as_str());
+                    msg!("Invalid Merkle tree account type at index {}: expected BatchedStateTree or StateTree", current_mt_index);
programs/system/src/invoke_cpi/instruction_small.rs (1)

125-130: Inconsistent error handling pattern for optional fields.

get_sol_pool_pda() returns Ok(None) when exec_accounts is None, but get_registered_program_pda() and get_account_compression_authority() return Err. This creates an inconsistent API for optional accounts.

The current implementation might lead to confusion - either all optional account getters should return Result<Option<_>> or none should. Consider aligning the error handling pattern:

     fn get_sol_pool_pda(&self) -> Result<Option<&'info AccountInfo>> {
-        Ok(self
-            .exec_accounts
-            .as_ref()
-            .and_then(|exec| exec.sol_pool_pda))
+        self.exec_accounts
+            .as_ref()
+            .map(|exec| exec.sol_pool_pda)
+            .ok_or(SystemProgramError::CpiContextPassedAsSetContext.into())
     }
sdk-libs/sdk/src/cpi/invoke.rs (2)

107-109: Use a more descriptive error message for unsupported operations.

The unimplemented!() macro with a generic message doesn't provide enough context about why new_addresses must be new assigned addresses.

     if cpi_inputs.new_addresses.is_some() {
-        unimplemented!("new_addresses must be new assigned addresses.");
+        unimplemented!("Legacy new_addresses field is not supported in small instruction variant. Use new_assigned_addresses instead.");
     }

150-152: Duplicate validation logic between small and context write functions.

The validation for new_addresses is duplicated between create_light_system_progam_instruction_invoke_cpi_small and create_light_system_progam_instruction_invoke_cpi_context_write.

Consider extracting a validation helper to reduce duplication:

+fn validate_cpi_inputs(cpi_inputs: &CpiInputs) -> Result<()> {
+    if cpi_inputs.new_addresses.is_some() {
+        unimplemented!("Legacy new_addresses field is not supported. Use new_assigned_addresses instead.");
+    }
+    Ok(())
+}
+
 pub fn create_light_system_progam_instruction_invoke_cpi_small(
     cpi_inputs: CpiInputs,
     cpi_accounts: CpiAccountsSmall<'_, '_>,
 ) -> Result<Instruction> {
-    if cpi_inputs.new_addresses.is_some() {
-        unimplemented!("new_addresses must be new assigned addresses.");
-    }
+    validate_cpi_inputs(&cpi_inputs)?;
program-libs/compressed-account/src/instruction_data/with_account_info.rs (1)

501-507: Potential issue with CPI context generation in tests.

The get_rnd_cpi_context function uses rng.gen() && with_cpi_context for both flags, which means if with_cpi_context is false, both flags will always be false. However, the cpi_context is still created with these values, which might not reflect realistic scenarios where the struct exists but shouldn't be used.

Consider making the test data generation more explicit:

 fn get_rnd_cpi_context(rng: &mut StdRng, with_cpi_context: bool) -> CompressedCpiContext {
+    if !with_cpi_context {
+        // Return a default/empty context when not needed
+        return CompressedCpiContext::default();
+    }
     CompressedCpiContext {
-        first_set_context: rng.gen() && with_cpi_context,
-        set_context: rng.gen() && with_cpi_context,
+        first_set_context: rng.gen(),
+        set_context: rng.gen(),
         cpi_context_account_index: rng.gen(),
     }
 }
program-libs/compressed-account/src/instruction_data/zero_copy.rs (1)

319-327: Minor: prefer helper for clarity.

Use merkle_context.prove_by_index() rather than == 1 to avoid magic numbers.

Apply:

-            prove_by_index: merkle_context.prove_by_index == 1,
+            prove_by_index: merkle_context.prove_by_index(),
sdk-libs/sdk-types/src/cpi_accounts_small.rs (4)

10-19: Fix index annotations; comments don’t match enum layout.

LightSystemProgram precedes Authority, so the “index 0/1/2…” notes are wrong and can mislead.

Apply:

-    Authority, // index 0 - Cpi authority of the custom program, used to invoke the light system program.
-    RegisteredProgramPda, // index 1 - registered_program_pda
-    AccountCompressionAuthority, // index 2 - account_compression_authority
-    AccountCompressionProgram, // index 3 - account_compression_program
-    SystemProgram, // index 4 - system_program
+    Authority, // index 1 - CPI authority of the custom program, used to invoke the light system program
+    RegisteredProgramPda, // index 2 - registered_program_pda
+    AccountCompressionAuthority, // index 3 - account_compression_authority
+    AccountCompressionProgram, // index 4 - account_compression_program
+    SystemProgram, // index 5 - system_program
     SolPoolPda, // index 5 - Optional
     DecompressionRecipient, // index 6 - Optional
     CpiContext, // index 7 - Optional

103-113: CPI-context index math: verify with flags.

Index is decremented for sol_pool_pda/sol_compression_recipient, but not guarded by config.cpi_context. If cpi_context is disabled, this will still compute an index into accounts and rely on OOB. Consider early error or documenting the contract.


166-174: Unnecessary cloning in to_account_infos().

Returns owned Vec and clones every account. If callers can use &'a [T], prefer exposing a borrowed view or an iterator to reduce allocations/copies.

Alternative:

  • Keep to_account_infos() but add to_account_info_refs(&self) -> &'a [T] for zero-copy paths.

186-192: Minor: simplify collection.

Collect directly without explicit turbofish.

Apply:

-        Ok(self
-            .tree_accounts()?
-            .iter()
-            .map(|x| x.pubkey())
-            .collect::<Vec<T::Pubkey>>())
+        Ok(self.tree_accounts()?.iter().map(|x| x.pubkey()).collect())
programs/system/src/context.rs (1)

89-128: AccountMeta always writable; consider narrowing.

get_index_or_insert() marks all metas writable/signers=false. This may violate least-privilege and increase CU costs. If feasible, thread writability/signature requirements from call sites.

program-libs/account-checks/src/account_iterator.rs (3)

83-93: Name implies mutability but returns shared ref.

next_option_mut enforces writable but returns &T. Consider renaming to next_writable_option to avoid confusion.


143-154: remaining(): returning Err on exhaustion is surprising.

Most callers expect Ok([]). Since remaining_unchecked exists, consider making remaining() return Ok(&[]) when empty.

Apply:

-    if self.position >= self.accounts.len() {
-        let location = Location::caller();
-        let account_name = "remaining accounts";
-        solana_msg::msg!( ... );
-        return Err(AccountError::NotEnoughAccountKeys);
-    }
-    Ok(&self.accounts[self.position..])
+    Ok(if self.position >= self.accounts.len() {
+        &[]
+    } else {
+        &self.accounts[self.position..]
+    })

190-203: Minor logging nit.

“for account … at index …” has double spacing before file:line:col. Consider a single space.

programs/system/src/cpi_context/state.rs (1)

134-151: Potential truncation: assigned_account_index cast to u8.

assigned_index is usize; casting to u8 before checked_add can truncate. Validate with try_from first.

Apply:

-            let assigned_account_index = (assigned_index.unwrap_or(0) as u8)
-                .checked_add(pre_address_len as u8)
-                .ok_or(ZeroCopyError::Size)?;
+            let base: u8 = assigned_index
+                .map(u8::try_from)
+                .transpose()
+                .map_err(|_| ZeroCopyError::Size)?
+                .unwrap_or(0);
+            let assigned_account_index = base
+                .checked_add(u8::try_from(pre_address_len).map_err(|_| ZeroCopyError::Size)?)
+                .ok_or(ZeroCopyError::Size)?;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cee8fd4 and 281bf48.

⛔ Files ignored due to path filters (2)
  • Cargo.lock is excluded by !**/*.lock
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (107)
  • .cargo/config.toml (0 hunks)
  • anchor-programs/system/Cargo.toml (1 hunks)
  • anchor-programs/system/src/cpi_context_account.rs (2 hunks)
  • anchor-programs/system/src/lib.rs (1 hunks)
  • cli/accounts/batched_output_queue_6L7SzhYB3anwEQ9cphpJ1U7Scwj57bx2xueReg7R9cKU.json (1 hunks)
  • cli/accounts/cpi_context_cpi1uHzrEhBG733DoEJNgHCyRS3XmmyVNZx5fonubE4.json (1 hunks)
  • cli/accounts/cpi_context_cpi2cdhkH5roePvcudTgUL8ppEBfTay1desGh8G8QxK.json (1 hunks)
  • cli/accounts/epoch_pda_34w7KcLBXabMkHuXE2fY368vFe6kP3v5EJn8nPvQ8SKn.json (1 hunks)
  • cli/accounts/forester_epoch_pda_3FBt1BPQHCQkS8k3wrUXMfB6JBhtMhEqQXueHRw2ojZV.json (1 hunks)
  • cli/accounts/governance_authority_pda_CuEtcKkkbTn6qy2qxqDswq5U2ADsqoipYDAYfRvxPjcp.json (1 hunks)
  • cli/accounts/registered_forester_pda_2KNqEh23Se8AHecuzR1UkxL26euq2qXSpQPTH1jH7VqU.json (1 hunks)
  • program-libs/account-checks/Cargo.toml (1 hunks)
  • program-libs/account-checks/src/account_info/pinocchio.rs (1 hunks)
  • program-libs/account-checks/src/account_iterator.rs (1 hunks)
  • program-libs/account-checks/src/error.rs (2 hunks)
  • program-libs/account-checks/src/lib.rs (1 hunks)
  • program-libs/account-checks/src/packed_accounts.rs (1 hunks)
  • program-libs/account-checks/tests/account_iterator.rs (1 hunks)
  • program-libs/compressed-account/Cargo.toml (1 hunks)
  • program-libs/compressed-account/src/compressed_account.rs (6 hunks)
  • program-libs/compressed-account/src/indexer_event/parse.rs (5 hunks)
  • program-libs/compressed-account/src/instruction_data/compressed_proof.rs (2 hunks)
  • program-libs/compressed-account/src/instruction_data/cpi_context.rs (2 hunks)
  • program-libs/compressed-account/src/instruction_data/data.rs (3 hunks)
  • program-libs/compressed-account/src/instruction_data/invoke_cpi.rs (2 hunks)
  • program-libs/compressed-account/src/instruction_data/mod.rs (1 hunks)
  • program-libs/compressed-account/src/instruction_data/traits.rs (3 hunks)
  • program-libs/compressed-account/src/instruction_data/with_account_info.rs (5 hunks)
  • program-libs/compressed-account/src/instruction_data/with_readonly.rs (5 hunks)
  • program-libs/compressed-account/src/instruction_data/zero_copy.rs (6 hunks)
  • program-libs/compressed-account/src/instruction_data/zero_copy_set.rs (1 hunks)
  • program-libs/compressed-account/src/lib.rs (2 hunks)
  • program-libs/compressed-account/src/pubkey.rs (3 hunks)
  • program-libs/compressed-account/tests/account_option_config.rs (1 hunks)
  • program-libs/compressed-account/tests/pubkey.rs (1 hunks)
  • program-libs/compressed-account/tests/zero_copy_set.rs (1 hunks)
  • program-libs/hasher/Cargo.toml (2 hunks)
  • program-libs/hasher/src/to_byte_array.rs (1 hunks)
  • program-libs/zero-copy/src/vec.rs (2 hunks)
  • program-tests/compressed-token-test/tests/test.rs (1 hunks)
  • program-tests/create-address-test-program/Cargo.toml (1 hunks)
  • program-tests/create-address-test-program/src/lib.rs (5 hunks)
  • program-tests/sdk-anchor-test/package.json (0 hunks)
  • program-tests/system-cpi-test/tests/test.rs (4 hunks)
  • program-tests/system-cpi-v2-test/tests/invoke_cpi_with_read_only.rs (6 hunks)
  • program-tests/system-test/Cargo.toml (2 hunks)
  • program-tests/system-test/tests/test_re_init_cpi_account.rs (1 hunks)
  • program-tests/utils/Cargo.toml (1 hunks)
  • program-tests/utils/src/assert_rollover.rs (1 hunks)
  • program-tests/utils/src/legacy_cpi_context_account.rs (1 hunks)
  • program-tests/utils/src/lib.rs (1 hunks)
  • programs/account-compression/src/processor/insert_into_queues.rs (1 hunks)
  • programs/compressed-token/src/process_transfer.rs (2 hunks)
  • programs/package.json (1 hunks)
  • programs/system/Cargo.toml (2 hunks)
  • programs/system/README.md (0 hunks)
  • programs/system/src/account_compression_state/address.rs (3 hunks)
  • programs/system/src/accounts/account_checks.rs (7 hunks)
  • programs/system/src/accounts/account_traits.rs (1 hunks)
  • programs/system/src/accounts/init_context_account.rs (3 hunks)
  • programs/system/src/accounts/remaining_account_checks.rs (5 hunks)
  • programs/system/src/constants.rs (2 hunks)
  • programs/system/src/context.rs (13 hunks)
  • programs/system/src/cpi_context/account.rs (1 hunks)
  • programs/system/src/cpi_context/address.rs (1 hunks)
  • programs/system/src/cpi_context/instruction_data_trait.rs (1 hunks)
  • programs/system/src/cpi_context/mod.rs (1 hunks)
  • programs/system/src/cpi_context/process_cpi_context.rs (1 hunks)
  • programs/system/src/cpi_context/state.rs (1 hunks)
  • programs/system/src/errors.rs (2 hunks)
  • programs/system/src/invoke/instruction.rs (1 hunks)
  • programs/system/src/invoke_cpi/account.rs (0 hunks)
  • programs/system/src/invoke_cpi/instruction.rs (1 hunks)
  • programs/system/src/invoke_cpi/instruction_small.rs (2 hunks)
  • programs/system/src/invoke_cpi/mod.rs (0 hunks)
  • programs/system/src/invoke_cpi/process_cpi_context.rs (0 hunks)
  • programs/system/src/invoke_cpi/processor.rs (3 hunks)
  • programs/system/src/invoke_cpi/verify_signer.rs (6 hunks)
  • programs/system/src/lib.rs (5 hunks)
  • programs/system/src/processor/cpi.rs (5 hunks)
  • programs/system/src/processor/create_address_cpi_data.rs (2 hunks)
  • programs/system/src/processor/create_inputs_cpi_data.rs (5 hunks)
  • programs/system/src/processor/create_outputs_cpi_data.rs (9 hunks)
  • programs/system/src/processor/process.rs (6 hunks)
  • programs/system/src/processor/read_only_account.rs (2 hunks)
  • programs/system/src/processor/read_only_address.rs (1 hunks)
  • programs/system/src/processor/sol_compression.rs (8 hunks)
  • programs/system/src/processor/sum_check.rs (1 hunks)
  • programs/system/src/processor/verify_proof.rs (6 hunks)
  • programs/system/src/utils.rs (2 hunks)
  • programs/system/tests/cpi_context.rs (1 hunks)
  • programs/system/tests/invoke_cpi_instruction_small.rs (21 hunks)
  • programs/system/tests/invoke_instruction.rs (1 hunks)
  • sdk-libs/sdk-pinocchio/Cargo.toml (0 hunks)
  • sdk-libs/sdk-pinocchio/src/cpi/accounts_small.rs (2 hunks)
  • sdk-libs/sdk-pinocchio/src/cpi/mod.rs (1 hunks)
  • sdk-libs/sdk-types/Cargo.toml (0 hunks)
  • sdk-libs/sdk-types/src/constants.rs (1 hunks)
  • sdk-libs/sdk-types/src/cpi_accounts.rs (5 hunks)
  • sdk-libs/sdk-types/src/cpi_accounts_small.rs (4 hunks)
  • sdk-libs/sdk-types/src/cpi_context_write.rs (1 hunks)
  • sdk-libs/sdk-types/src/instruction/tree_info.rs (1 hunks)
  • sdk-libs/sdk-types/src/lib.rs (2 hunks)
  • sdk-libs/sdk/Cargo.toml (1 hunks)
  • sdk-libs/sdk/src/cpi/accounts_cpi_context.rs (1 hunks)
  • sdk-libs/sdk/src/cpi/accounts_small_ix.rs (1 hunks)
  • sdk-libs/sdk/src/cpi/invoke.rs (4 hunks)
⛔ Files not processed due to max files limit (7)
  • sdk-libs/sdk/src/cpi/mod.rs
  • sdk-libs/sdk/src/error.rs
  • sdk-libs/sdk/src/instruction/pack_accounts.rs
  • sdk-libs/sdk/src/instruction/system_accounts.rs
  • sdk-tests/sdk-anchor-test/programs/sdk-anchor-test/tests/test.rs
  • sdk-tests/sdk-native-test/tests/test.rs
  • sdk-tests/sdk-pinocchio-test/tests/test.rs
💤 Files with no reviewable changes (8)
  • .cargo/config.toml
  • sdk-libs/sdk-types/Cargo.toml
  • sdk-libs/sdk-pinocchio/Cargo.toml
  • programs/system/README.md
  • programs/system/src/invoke_cpi/mod.rs
  • programs/system/src/invoke_cpi/process_cpi_context.rs
  • program-tests/sdk-anchor-test/package.json
  • programs/system/src/invoke_cpi/account.rs
✅ Files skipped from review due to trivial changes (1)
  • program-libs/account-checks/tests/account_iterator.rs
🚧 Files skipped from review as they are similar to previous changes (43)
  • cli/accounts/forester_epoch_pda_3FBt1BPQHCQkS8k3wrUXMfB6JBhtMhEqQXueHRw2ojZV.json
  • program-libs/account-checks/src/packed_accounts.rs
  • programs/system/src/lib.rs
  • program-libs/compressed-account/src/instruction_data/traits.rs
  • cli/accounts/registered_forester_pda_2KNqEh23Se8AHecuzR1UkxL26euq2qXSpQPTH1jH7VqU.json
  • program-tests/utils/src/assert_rollover.rs
  • program-libs/compressed-account/src/instruction_data/compressed_proof.rs
  • programs/system/src/processor/create_address_cpi_data.rs
  • programs/system/src/processor/read_only_address.rs
  • program-tests/system-cpi-test/tests/test.rs
  • program-libs/hasher/Cargo.toml
  • programs/package.json
  • program-libs/account-checks/src/account_info/pinocchio.rs
  • program-tests/utils/Cargo.toml
  • programs/system/src/accounts/account_traits.rs
  • cli/accounts/governance_authority_pda_CuEtcKkkbTn6qy2qxqDswq5U2ADsqoipYDAYfRvxPjcp.json
  • program-libs/compressed-account/src/instruction_data/invoke_cpi.rs
  • programs/system/src/cpi_context/instruction_data_trait.rs
  • programs/system/src/cpi_context/address.rs
  • program-libs/compressed-account/Cargo.toml
  • programs/system/src/cpi_context/mod.rs
  • programs/system/src/processor/sum_check.rs
  • programs/system/src/processor/read_only_account.rs
  • programs/system/src/account_compression_state/address.rs
  • program-tests/compressed-token-test/tests/test.rs
  • programs/system/src/processor/sol_compression.rs
  • programs/system/src/errors.rs
  • program-tests/create-address-test-program/Cargo.toml
  • program-libs/account-checks/Cargo.toml
  • program-tests/utils/src/legacy_cpi_context_account.rs
  • program-tests/system-test/Cargo.toml
  • programs/system/src/invoke_cpi/processor.rs
  • program-libs/compressed-account/src/instruction_data/mod.rs
  • programs/compressed-token/src/process_transfer.rs
  • cli/accounts/batched_output_queue_6L7SzhYB3anwEQ9cphpJ1U7Scwj57bx2xueReg7R9cKU.json
  • program-libs/account-checks/src/lib.rs
  • program-libs/compressed-account/src/instruction_data/zero_copy_set.rs
  • programs/system/src/invoke_cpi/verify_signer.rs
  • cli/accounts/cpi_context_cpi1uHzrEhBG733DoEJNgHCyRS3XmmyVNZx5fonubE4.json
  • programs/system/src/cpi_context/account.rs
  • program-tests/utils/src/lib.rs
  • anchor-programs/system/Cargo.toml
  • programs/account-compression/src/processor/insert_into_queues.rs
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-08-14T00:36:53.191Z
Learnt from: ananas-block
PR: Lightprotocol/light-protocol#1909
File: program-libs/zero-copy/src/init_mut.rs:241-249
Timestamp: 2025-08-14T00:36:53.191Z
Learning: In the light-protocol zero-copy crate, performance is prioritized over safety checks for edge cases like Vec lengths exceeding u32::MAX, even when there might be wire format inconsistencies.

Applied to files:

  • program-libs/compressed-account/src/instruction_data/with_readonly.rs
  • program-libs/compressed-account/src/pubkey.rs
  • program-libs/zero-copy/src/vec.rs
🧬 Code graph analysis (34)
programs/system/src/invoke/instruction.rs (3)
programs/system/src/accounts/account_traits.rs (4)
  • get_registered_program_pda (6-6)
  • get_account_compression_authority (7-7)
  • get_sol_pool_pda (8-8)
  • get_decompression_recipient (9-9)
programs/system/src/invoke_cpi/instruction.rs (4)
  • get_registered_program_pda (101-103)
  • get_account_compression_authority (105-107)
  • get_sol_pool_pda (109-111)
  • get_decompression_recipient (113-115)
programs/system/src/invoke_cpi/instruction_small.rs (4)
  • get_registered_program_pda (111-116)
  • get_account_compression_authority (118-123)
  • get_sol_pool_pda (125-130)
  • get_decompression_recipient (132-137)
program-tests/create-address-test-program/src/lib.rs (3)
sdk-libs/sdk/src/cpi/accounts.rs (1)
  • get_account_metas_from_config (26-134)
sdk-libs/sdk-types/src/cpi_accounts_small.rs (5)
  • fee_payer (49-51)
  • account_infos (137-139)
  • new_with_config (41-47)
  • config (119-121)
  • system_accounts_end_offset (123-135)
sdk-libs/sdk/src/cpi/accounts_small_ix.rs (1)
  • to_account_metas_small (131-134)
programs/system/src/accounts/init_context_account.rs (2)
program-libs/account-checks/src/checks.rs (2)
  • check_owner (128-136)
  • check_signer (114-119)
programs/system/src/cpi_context/state.rs (2)
  • cpi_context_account_new (403-495)
  • new (378-389)
program-libs/compressed-account/src/instruction_data/with_account_info.rs (4)
program-libs/compressed-account/src/instruction_data/traits.rs (1)
  • account_option_config (24-24)
program-libs/compressed-account/src/instruction_data/with_readonly.rs (6)
  • account_option_config (265-283)
  • with_cpi_context (245-247)
  • rng (676-685)
  • rng (701-721)
  • rng (722-728)
  • rng (729-740)
program-libs/compressed-account/src/instruction_data/zero_copy.rs (3)
  • account_option_config (442-444)
  • account_option_config (560-572)
  • get_rnd_cpi_context (1014-1020)
programs/system/src/cpi_context/instruction_data_trait.rs (1)
  • account_option_config (75-82)
program-libs/compressed-account/src/instruction_data/zero_copy.rs (6)
program-libs/compressed-account/src/instruction_data/traits.rs (1)
  • account_option_config (24-24)
program-libs/compressed-account/src/instruction_data/with_account_info.rs (1)
  • account_option_config (308-328)
program-libs/compressed-account/src/instruction_data/with_readonly.rs (1)
  • account_option_config (265-283)
programs/system/src/cpi_context/instruction_data_trait.rs (1)
  • account_option_config (75-82)
program-libs/compressed-account/src/instruction_data/cpi_context.rs (2)
  • first_set_context (26-28)
  • first_set_context (36-38)
program-libs/compressed-account/src/instruction_data/zero_copy_set.rs (1)
  • first_set_context (122-122)
program-libs/compressed-account/src/instruction_data/with_readonly.rs (4)
program-libs/compressed-account/src/instruction_data/traits.rs (1)
  • account_option_config (24-24)
program-libs/compressed-account/src/instruction_data/with_account_info.rs (1)
  • account_option_config (308-328)
program-libs/compressed-account/src/instruction_data/zero_copy.rs (2)
  • account_option_config (442-444)
  • account_option_config (560-572)
programs/system/src/cpi_context/instruction_data_trait.rs (1)
  • account_option_config (75-82)
program-libs/compressed-account/src/instruction_data/data.rs (3)
js/stateless.js/src/state/types.ts (1)
  • OutputCompressedAccountWithPackedContext (170-173)
js/stateless.js/src/state/compressed-account.ts (1)
  • CompressedAccount (29-51)
js/stateless.js/src/utils/address.ts (1)
  • NewAddressParamsPacked (64-83)
programs/system/src/invoke_cpi/instruction_small.rs (3)
programs/system/src/accounts/account_checks.rs (3)
  • check_option_cpi_context_account (134-167)
  • check_option_decompression_recipient (117-130)
  • check_option_sol_pool_pda (170-183)
programs/system/src/invoke_cpi/instruction.rs (5)
  • from_account_infos (41-81)
  • get_registered_program_pda (101-103)
  • get_account_compression_authority (105-107)
  • get_sol_pool_pda (109-111)
  • get_decompression_recipient (113-115)
programs/system/src/accounts/account_traits.rs (4)
  • get_registered_program_pda (6-6)
  • get_account_compression_authority (7-7)
  • get_sol_pool_pda (8-8)
  • get_decompression_recipient (9-9)
programs/system/src/processor/process.rs (7)
program-libs/compressed-account/src/instruction_data/traits.rs (1)
  • cpi_context (22-22)
program-libs/compressed-account/src/instruction_data/with_account_info.rs (1)
  • cpi_context (354-364)
program-libs/compressed-account/src/instruction_data/with_readonly.rs (1)
  • cpi_context (312-322)
program-libs/compressed-account/src/instruction_data/zero_copy.rs (2)
  • cpi_context (487-489)
  • cpi_context (616-624)
programs/system/src/context.rs (1)
  • cpi_context (351-353)
programs/system/src/cpi_context/instruction_data_trait.rs (1)
  • cpi_context (66-68)
programs/system/src/cpi_context/process_cpi_context.rs (2)
  • process_cpi_context (48-88)
  • copy_cpi_context_outputs (144-190)
sdk-libs/sdk/src/cpi/accounts_cpi_context.rs (1)
sdk-libs/sdk-types/src/cpi_accounts.rs (3)
  • config (223-225)
  • new (21-28)
  • new (72-78)
sdk-libs/sdk-types/src/cpi_context_write.rs (2)
sdk-libs/sdk-types/src/cpi_accounts.rs (4)
  • bump (43-45)
  • bump (219-221)
  • invoking_program (146-151)
  • to_account_infos (276-283)
sdk-libs/sdk-types/src/cpi_accounts_small.rs (3)
  • bump (175-177)
  • invoking_program (179-181)
  • to_account_infos (166-174)
program-tests/system-test/tests/test_re_init_cpi_account.rs (2)
anchor-programs/system/src/cpi_context_account.rs (1)
  • deserialize_cpi_context_account (50-72)
program-tests/utils/src/legacy_cpi_context_account.rs (1)
  • get_legacy_cpi_context_account (4-12)
sdk-libs/sdk/src/cpi/accounts_small_ix.rs (6)
sdk-libs/sdk-types/src/cpi_accounts.rs (1)
  • config (223-225)
sdk-libs/sdk-types/src/cpi_accounts_small.rs (1)
  • config (119-121)
sdk-libs/sdk/src/error.rs (3)
  • from (92-94)
  • from (98-125)
  • from (129-170)
sdk-libs/sdk/src/instruction/system_accounts.rs (1)
  • default (48-70)
sdk-libs/sdk/src/cpi/accounts.rs (1)
  • try_from (139-161)
program-tests/create-address-test-program/src/lib.rs (1)
  • to_account_metas_small (258-344)
program-tests/system-cpi-v2-test/tests/invoke_cpi_with_read_only.rs (1)
sdk-libs/sdk/src/instruction/system_accounts.rs (3)
  • new (17-24)
  • get_light_system_account_metas (73-113)
  • default (48-70)
program-libs/compressed-account/tests/account_option_config.rs (2)
program-libs/compressed-account/tests/zero_copy_set.rs (6)
  • None (1474-1474)
  • None (1508-1508)
  • None (1542-1542)
  • None (1587-1587)
  • None (1677-1677)
  • None (1784-1784)
program-libs/compressed-account/src/instruction_data/with_readonly.rs (1)
  • with_cpi_context (245-247)
programs/system/src/context.rs (4)
programs/system/src/cpi_context/state.rs (6)
  • std (93-93)
  • std (95-95)
  • std (97-97)
  • std (98-98)
  • std (100-100)
  • std (472-472)
program-libs/compressed-account/src/instruction_data/traits.rs (3)
  • cpi_context (22-22)
  • output_accounts (16-16)
  • input_accounts (15-15)
programs/system/src/cpi_context/instruction_data_trait.rs (3)
  • cpi_context (66-68)
  • output_accounts (30-32)
  • input_accounts (26-28)
programs/system/src/cpi_context/process_cpi_context.rs (1)
  • set_cpi_context (91-137)
programs/system/tests/invoke_instruction.rs (2)
sdk-libs/sdk-types/src/cpi_accounts.rs (2)
  • account_compression_authority (167-172)
  • registered_program_pda (153-158)
sdk-libs/sdk-types/src/cpi_accounts_small.rs (2)
  • account_compression_authority (67-72)
  • registered_program_pda (60-65)
program-libs/compressed-account/src/instruction_data/cpi_context.rs (2)
program-libs/compressed-account/src/instruction_data/zero_copy.rs (2)
  • first_set_context (661-663)
  • set_context (657-659)
program-libs/compressed-account/src/instruction_data/zero_copy_set.rs (2)
  • first_set_context (122-122)
  • set_context (121-121)
programs/system/src/cpi_context/process_cpi_context.rs (4)
program-libs/batched-merkle-tree/src/queue.rs (2)
  • BatchedQueueAccount (662-662)
  • output_from_account_info (152-159)
programs/system/src/cpi_context/state.rs (3)
  • deserialize_cpi_context_account (276-280)
  • deserialize_cpi_context_account_cleared (283-287)
  • is_empty (63-70)
programs/system/src/context.rs (2)
  • cpi_context (351-353)
  • set_cpi_context (203-222)
program-libs/compressed-account/src/pubkey.rs (3)
  • new_zero_copy (59-64)
  • new_from_array (68-70)
  • to_bytes (189-191)
programs/system/src/invoke_cpi/instruction.rs (3)
programs/system/src/accounts/account_traits.rs (4)
  • get_registered_program_pda (6-6)
  • get_account_compression_authority (7-7)
  • get_sol_pool_pda (8-8)
  • get_decompression_recipient (9-9)
programs/system/src/invoke/instruction.rs (4)
  • get_registered_program_pda (98-100)
  • get_account_compression_authority (102-104)
  • get_sol_pool_pda (106-108)
  • get_decompression_recipient (110-112)
programs/system/src/invoke_cpi/instruction_small.rs (4)
  • get_registered_program_pda (111-116)
  • get_account_compression_authority (118-123)
  • get_sol_pool_pda (125-130)
  • get_decompression_recipient (132-137)
sdk-libs/sdk/src/cpi/invoke.rs (4)
sdk-libs/sdk/src/cpi/accounts_cpi_context.rs (1)
  • get_account_metas_from_config_cpi_context (5-13)
sdk-libs/sdk/src/cpi/accounts.rs (1)
  • get_account_metas_from_config (26-134)
sdk-libs/sdk/src/cpi/accounts_small_ix.rs (1)
  • to_account_metas_small (131-134)
sdk-libs/sdk-types/src/cpi_context_write.rs (1)
  • bump (14-16)
programs/system/src/processor/cpi.rs (5)
programs/system/src/accounts/account_traits.rs (2)
  • get_account_compression_authority (7-7)
  • get_registered_program_pda (6-6)
programs/system/src/invoke/instruction.rs (2)
  • get_account_compression_authority (102-104)
  • get_registered_program_pda (98-100)
programs/system/src/invoke_cpi/instruction.rs (2)
  • get_account_compression_authority (105-107)
  • get_registered_program_pda (101-103)
programs/system/src/invoke_cpi/instruction_small.rs (2)
  • get_account_compression_authority (118-123)
  • get_registered_program_pda (111-116)
programs/system/src/utils.rs (1)
  • get_registered_program_pda (13-15)
program-libs/compressed-account/tests/zero_copy_set.rs (2)
program-libs/compressed-account/src/instruction_data/data.rs (1)
  • assigned_account_index (72-78)
program-libs/compressed-account/src/instruction_data/with_readonly.rs (1)
  • with_cpi_context (245-247)
program-libs/account-checks/src/account_iterator.rs (1)
program-libs/account-checks/src/checks.rs (3)
  • check_mut (121-126)
  • check_non_mut (43-48)
  • check_signer (114-119)
programs/system/src/processor/create_inputs_cpi_data.rs (3)
programs/system/src/cpi_context/account.rs (1)
  • merkle_context (69-71)
program-libs/compressed-account/src/instruction_data/traits.rs (1)
  • merkle_context (49-49)
program-libs/compressed-account/src/instruction_data/with_account_info.rs (1)
  • merkle_context (86-88)
sdk-libs/sdk-types/src/cpi_accounts_small.rs (2)
sdk-libs/sdk-types/src/cpi_accounts.rs (8)
  • account_compression_program (174-179)
  • system_program (195-200)
  • to_account_infos (276-283)
  • account_infos (241-243)
  • bump (43-45)
  • bump (219-221)
  • invoking_program (146-151)
  • tree_pubkeys (258-264)
sdk-libs/sdk-types/src/cpi_context_write.rs (3)
  • to_account_infos (22-28)
  • bump (14-16)
  • invoking_program (18-20)
programs/system/tests/invoke_cpi_instruction_small.rs (2)
programs/system/tests/invoke_cpi_instruction.rs (4)
  • get_account_compression_authority_account_info (79-88)
  • get_account_compression_program_account_info (56-65)
  • get_mut_account_info (103-112)
  • get_system_program_account_info (115-124)
sdk-libs/sdk-types/src/cpi_accounts_small.rs (4)
  • account_compression_program (74-79)
  • system_program (81-86)
  • authority (53-58)
  • account_compression_authority (67-72)
programs/system/tests/cpi_context.rs (3)
programs/system/src/cpi_context/process_cpi_context.rs (1)
  • process_cpi_context (48-88)
anchor-programs/system/src/cpi_context_account.rs (1)
  • deserialize_cpi_context_account (50-72)
programs/system/src/cpi_context/state.rs (1)
  • deserialize_cpi_context_account_cleared (283-287)
programs/system/src/utils.rs (6)
programs/system/src/accounts/account_traits.rs (2)
  • get_registered_program_pda (6-6)
  • get_sol_pool_pda (8-8)
programs/system/src/invoke/instruction.rs (2)
  • get_registered_program_pda (98-100)
  • get_sol_pool_pda (106-108)
programs/system/src/invoke_cpi/instruction.rs (2)
  • get_registered_program_pda (101-103)
  • get_sol_pool_pda (109-111)
programs/system/src/invoke_cpi/instruction_small.rs (2)
  • get_registered_program_pda (111-116)
  • get_sol_pool_pda (125-130)
program-libs/account-checks/src/account_info/pinocchio.rs (1)
  • find_program_address (57-76)
programs/compressed-token/src/process_transfer.rs (1)
  • get_cpi_authority_pda (730-732)
programs/system/src/cpi_context/state.rs (3)
program-libs/account-checks/src/checks.rs (1)
  • check_owner (128-136)
programs/system/src/cpi_context/account.rs (5)
  • data (77-87)
  • data (137-147)
  • address (61-67)
  • address (121-127)
  • merkle_context (69-71)
program-libs/compressed-account/src/instruction_data/data.rs (2)
  • assigned_account_index (72-78)
  • new (61-70)
programs/system/src/accounts/account_checks.rs (4)
programs/system/src/cpi_context/state.rs (6)
  • std (93-93)
  • std (95-95)
  • std (97-97)
  • std (98-98)
  • std (100-100)
  • std (472-472)
program-libs/account-checks/src/checks.rs (9)
  • check_discriminator (62-62)
  • check_discriminator (78-87)
  • check_mut (121-126)
  • check_non_mut (43-48)
  • check_owner (128-136)
  • check_pda_seeds (151-161)
  • check_pda_seeds_with_bump (163-173)
  • check_program (138-149)
  • check_signer (114-119)
programs/system/src/context.rs (2)
  • cpi_context (351-353)
  • owner (296-298)
programs/system/src/cpi_context/account.rs (1)
  • owner (53-55)
program-libs/compressed-account/tests/pubkey.rs (1)
program-libs/compressed-account/src/pubkey.rs (1)
  • new_from_array (68-70)
programs/system/src/processor/create_outputs_cpi_data.rs (5)
programs/system/src/cpi_context/account.rs (4)
  • address (61-67)
  • address (121-127)
  • owner (53-55)
  • merkle_tree_index (153-155)
program-libs/compressed-account/src/instruction_data/traits.rs (7)
  • address (48-48)
  • address (69-69)
  • owner (13-13)
  • owner (37-39)
  • owner (46-46)
  • owner (73-73)
  • merkle_tree_index (74-74)
program-libs/compressed-account/src/instruction_data/with_account_info.rs (6)
  • address (82-84)
  • address (132-134)
  • owner (71-73)
  • owner (140-142)
  • owner (342-344)
  • merkle_tree_index (144-146)
program-libs/compressed-account/src/instruction_data/zero_copy.rs (8)
  • address (133-135)
  • address (346-348)
  • owner (125-127)
  • owner (340-342)
  • owner (460-472)
  • owner (537-548)
  • owner (582-593)
  • merkle_tree_index (129-131)
program-libs/compressed-account/src/pubkey.rs (1)
  • new_from_array (68-70)
sdk-libs/sdk-types/src/cpi_accounts.rs (1)
sdk-libs/sdk-types/src/cpi_accounts_small.rs (3)
  • tree_pubkeys (186-192)
  • to_account_infos (166-174)
  • account_infos (137-139)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (22)
  • GitHub Check: Test program-libs-slow
  • GitHub Check: Test batched-merkle-tree-simulate
  • GitHub Check: Test concurrent-merkle-tree
  • GitHub Check: Test program-libs-fast
  • GitHub Check: lint
  • GitHub Check: Forester e2e test
  • GitHub Check: system-programs (system-cpi-test-v2-other, ["cargo-test-sbf -p system-cpi-v2-test -- --skip funct...
  • GitHub Check: system-programs (random-e2e-test, ["cargo-test-sbf -p e2e-test"])
  • GitHub Check: system-programs (light-compressed-token, ["cargo-test-sbf -p compressed-token-test"])
  • GitHub Check: system-programs (light-registry, ["cargo-test-sbf -p registry-test"])
  • GitHub Check: system-programs (system-cpi-test-v2-functional, ["cargo-test-sbf -p system-cpi-v2-test -- functio...
  • GitHub Check: system-programs (system-cpi-test, ["cargo-test-sbf -p system-cpi-test", "cargo test -p light-syst...
  • GitHub Check: system-programs (light-system-program, ["cargo-test-sbf -p system-test"])
  • GitHub Check: system-programs (system-cpi-test-v2-event, ["cargo-test-sbf -p system-cpi-v2-test -- event::parse"])
  • GitHub Check: system-programs (account-compression, ["cargo-test-sbf -p account-compression-test"])
  • GitHub Check: cli-v2
  • GitHub Check: system-programs (sdk-anchor-test-program, ["cargo-test-sbf -p sdk-anchor-test", "cargo-test-sbf -...
  • GitHub Check: system-programs (sdk-libs, light-macros light-sdk light-program-test light-client light-batched-m...
  • GitHub Check: stateless-js-v2
  • GitHub Check: system-programs (sdk-test-program, ["cargo-test-sbf -p sdk-native-test"])
  • GitHub Check: cli-v1
  • GitHub Check: stateless-js-v1

AccountError::InvalidProgramId => 12017,
AccountError::ProgramNotExecutable => 12018,
AccountError::AccountNotZeroed => 12019,
AccountError::NotEnoughAccountKeys => 12020,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

🧩 Analysis chain

Confirm code mapping uniqueness across crates

Ensure 12020 is unused elsewhere and mirrored consistently in any cross‑crate From conversions.


🏁 Script executed:

#!/bin/bash
# Verify there are no conflicting assignments of 12020
rg -nP '\b12020\b' -C2
# Check other error enums mapping to overlapping ranges
rg -nP '->\s*120\d{2}' -C2 program-libs

Length of output: 1468


Fix duplicate error code 12020

12020 is assigned in both:

  • program-libs/compressed-account/src/lib.rs:98 — CompressedAccountError::ExpectedDiscriminator => 12020
  • program-libs/account-checks/src/error.rs:57 — AccountError::NotEnoughAccountKeys => 12020

Action: pick a unique numeric code for one of these mappings (and update any cross‑crate From impls), or centralize shared error codes in a common crate so mappings don't collide.

🤖 Prompt for AI Agents
In program-libs/account-checks/src/error.rs around line 57, the
AccountError::NotEnoughAccountKeys is mapped to numeric code 12020 which
duplicates CompressedAccountError::ExpectedDiscriminator (12020) in
program-libs/compressed-account/src/lib.rs; pick a new unique error code for one
of these mappings (or move shared codes into a central crate and reference them)
and then update any cross-crate From/Into impls or match arms that translate
between these error enums to use the new code constant so there are no
collisions at runtime.

Comment on lines +560 to 572
fn account_option_config(&self) -> Result<AccountOptions, CompressedAccountError> {
let sol_pool_pda = self.compress_or_decompress_lamports().is_some();
let decompression_recipient = sol_pool_pda && !self.is_compress();
let cpi_context_account = self.cpi_context().is_some();
let write_to_cpi_context = false; // Not used

Ok(AccountOptions {
sol_pool_pda,
decompression_recipient,
cpi_context_account,
write_to_cpi_context,
})
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Inconsistent AccountOptions: write_to_cpi_context is always false.

Align with with_account_info/with_readonly: derive write_to_cpi_context from set_context/first_set_context and validate presence.

Apply:

-    fn account_option_config(&self) -> Result<AccountOptions, CompressedAccountError> {
-        let sol_pool_pda = self.compress_or_decompress_lamports().is_some();
-        let decompression_recipient = sol_pool_pda && !self.is_compress();
-        let cpi_context_account = self.cpi_context().is_some();
-        let write_to_cpi_context = false; // Not used
-
-        Ok(AccountOptions {
-            sol_pool_pda,
-            decompression_recipient,
-            cpi_context_account,
-            write_to_cpi_context,
-        })
-    }
+    fn account_option_config(&self) -> Result<AccountOptions, CompressedAccountError> {
+        let sol_pool_pda = self.compress_or_decompress_lamports().is_some();
+        let decompression_recipient = sol_pool_pda && !self.is_compress();
+        let ctx = self.cpi_context();
+        let cpi_context_account = ctx.is_some();
+        let write_to_cpi_context = ctx
+            .map(|c| c.first_set_context || c.set_context)
+            .unwrap_or(false);
+        if write_to_cpi_context && !cpi_context_account {
+            return Err(CompressedAccountError::InvalidCpiContext);
+        }
+        Ok(AccountOptions {
+            sol_pool_pda,
+            decompression_recipient,
+            cpi_context_account,
+            write_to_cpi_context,
+        })
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
fn account_option_config(&self) -> Result<AccountOptions, CompressedAccountError> {
let sol_pool_pda = self.compress_or_decompress_lamports().is_some();
let decompression_recipient = sol_pool_pda && !self.is_compress();
let cpi_context_account = self.cpi_context().is_some();
let write_to_cpi_context = false; // Not used
Ok(AccountOptions {
sol_pool_pda,
decompression_recipient,
cpi_context_account,
write_to_cpi_context,
})
}
fn account_option_config(&self) -> Result<AccountOptions, CompressedAccountError> {
let sol_pool_pda = self.compress_or_decompress_lamports().is_some();
let decompression_recipient = sol_pool_pda && !self.is_compress();
let ctx = self.cpi_context();
let cpi_context_account = ctx.is_some();
let write_to_cpi_context = ctx
.map(|c| c.first_set_context || c.set_context)
.unwrap_or(false);
if write_to_cpi_context && !cpi_context_account {
return Err(CompressedAccountError::InvalidCpiContext);
}
Ok(AccountOptions {
sol_pool_pda,
decompression_recipient,
cpi_context_account,
write_to_cpi_context,
})
}
🤖 Prompt for AI Agents
In program-libs/compressed-account/src/instruction_data/zero_copy.rs around
lines 560–572, write_to_cpi_context is hardcoded to false; change it to be
derived from whether the account is being written to via
set_context/first_set_context and validate that a cpi_context exists when it is
set. Specifically, compute write_to_cpi_context = self.set_context().is_some()
|| self.first_set_context().is_some(), and if that value is true but
self.cpi_context().is_none() return an appropriate CompressedAccountError;
otherwise construct AccountOptions with the computed write_to_cpi_context so it
accurately reflects intent.

Comment on lines +145 to +146
// SAFETY: owner() returns a valid pointer to a 32-byte aligned Pubkey
solana_pubkey::Pubkey::new_from_array(unsafe { *account_info.owner() }),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Unsafe pointer dereference needs additional validation.

The comment states "SAFETY: owner() returns a valid pointer to a 32-byte aligned Pubkey" but there's no runtime validation of this assumption before the unsafe dereference.

Consider adding a debug assertion or validation before the unsafe operation:

+                // Validate pointer alignment in debug builds
+                debug_assert!(account_info.owner() as *const _ as usize % std::mem::align_of::<[u8; 32]>() == 0);
                 // SAFETY: owner() returns a valid pointer to a 32-byte aligned Pubkey
                 solana_pubkey::Pubkey::new_from_array(unsafe { *account_info.owner() }),

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In programs/system/src/accounts/account_checks.rs around lines 145 to 146, the
unsafe dereference of account_info.owner() assumes the pointer is valid and
32-byte aligned without runtime checks; add a lightweight validation before the
unsafe block: obtain the raw pointer, assert it's not null and that its address
is aligned to std::mem::align_of::<Pubkey>() (or 32) using debug_assert! (or
return an error in non-debug builds), then perform the unsafe dereference to
create the Pubkey; alternatively replace the unsafe deref with a safe conversion
path such as using owner().as_ref().copied() or equivalent safe helper that
handles null/invalid pointers.

Comment on lines +68 to 85
#[profile]
pub fn reinit_cpi_context_account(accounts: &[AccountInfo]) -> Result<()> {
if accounts.is_empty() {
return Err(ProgramError::NotEnoughAccountKeys);
}
let cpi_context_account = &accounts[0];
let associated_merkle_tree = {
let data = cpi_context_account.try_borrow_data()?;
CpiContextAccount::deserialize(&mut &data[8..])
.map_err(|_| ProgramError::BorshIoError)?
.associated_merkle_tree
};
// Initialize account with data.
cpi_context_account
.serialize(&mut &mut cpi_context_account_data[8..])
.unwrap();
let params: CpiContextAccountInitParams =
CpiContextAccountInitParams::new(associated_merkle_tree);
cpi_context_account_new::<true>(cpi_context_account, params)?;

Ok(())
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Critical security validation needed for reinit function.

The reinit_cpi_context_account function deserializes existing account data without proper owner and discriminator validation. This could allow unauthorized reinit operations.

Add proper validation before deserialization:

 pub fn reinit_cpi_context_account(accounts: &[AccountInfo]) -> Result<()> {
     if accounts.is_empty() {
         return Err(ProgramError::NotEnoughAccountKeys);
     }
     let cpi_context_account = &accounts[0];
+    
+    // Validate owner before attempting deserialization
+    check_owner(&ID, cpi_context_account)?;
+    
+    // Validate discriminator before deserialization  
+    let data = cpi_context_account.try_borrow_data()?;
+    if data.len() < 8 || &data[0..8] != CPI_CONTEXT_ACCOUNT_1_DISCRIMINATOR {
+        return Err(SystemProgramError::InvalidCpiContextDiscriminator.into());
+    }
+    drop(data);
+    
     let associated_merkle_tree = {
         let data = cpi_context_account.try_borrow_data()?;
         CpiContextAccount::deserialize(&mut &data[8..])
             .map_err(|_| ProgramError::BorshIoError)?
             .associated_merkle_tree
     };

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In programs/system/src/accounts/init_context_account.rs around lines 68 to 85,
before calling try_borrow_data and deserializing the account, validate the
account owner and the Borsh/discriminator: ensure accounts[0].owner equals the
expected program id (the program that owns CpiContextAccount) and verify the
data length is at least the discriminator + payload size, then check the first 8
bytes (or the crate's defined discriminator) match CpiContextAccount's
discriminator; only after those checks proceed to deserialize &data[8..] and
extract associated_merkle_tree, otherwise return an appropriate ProgramError
(e.g., IllegalOwner or InvalidAccountData).

Comment on lines +39 to +42
#[cfg(feature = "reinit")]
RE_INIT_CPI_CONTEXT_ACCOUNT_INSTRUCTION => {
Ok(InstructionDiscriminator::ReInitCpiContextAccount)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Consider security implications of feature-gated instruction.

The RE_INIT_CPI_CONTEXT_ACCOUNT_INSTRUCTION discriminator is gated behind cfg(feature = "reinit"), which means it can be conditionally compiled out. This could lead to runtime errors if the feature flag state differs between compilation and deployment environments.

Consider these potential issues:

  1. If the feature is disabled in production but enabled during testing, the discriminator won't be recognized
  2. This could lead to SystemProgramError::InvalidInstructionDataDiscriminator errors in production
  3. Runtime feature detection would be safer than compile-time gating
// Consider adding runtime validation instead of compile-time gating
match array {
    // ... other discriminators
    RE_INIT_CPI_CONTEXT_ACCOUNT_INSTRUCTION => {
        if !is_reinit_feature_enabled() {
            return Err(SystemProgramError::FeatureNotEnabled.into());
        }
        Ok(InstructionDiscriminator::ReInitCpiContextAccount)
    }
    // ...
}
🤖 Prompt for AI Agents
In programs/system/src/constants.rs around lines 39 to 42, the
RE_INIT_CPI_CONTEXT_ACCOUNT_INSTRUCTION discriminator is feature-gated with
cfg(feature = "reinit"), which can cause mismatches between compile-time
features and runtime expectations; remove the compile-time gating for this
discriminator and instead keep the discriminator match arm always present,
adding a runtime check that calls a feature-enabled validator (e.g.
is_reinit_feature_enabled()) before returning
InstructionDiscriminator::ReInitCpiContextAccount, and return a clear
SystemProgramError::FeatureNotEnabled when the runtime check fails so the
instruction is rejected safely at runtime.

Comment on lines +64 to 66
#[profile]
fn check_tree_height<const IS_STATE_TREE: bool>(
tree_height: &mut u8,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Same here: gate the attribute; consider skipping tiny leafs

check_tree_height is trivial; profiling it adds noise with little value. Gate it, and consider removing profiling from this leaf entirely.

Apply:

-#[profile]
+#[cfg_attr(feature = "profile-program", light_program_profiler::profile)]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
#[profile]
fn check_tree_height<const IS_STATE_TREE: bool>(
tree_height: &mut u8,
#[cfg_attr(feature = "profile-program", light_program_profiler::profile)]
fn check_tree_height<const IS_STATE_TREE: bool>(
tree_height: &mut u8,
🤖 Prompt for AI Agents
In programs/system/src/processor/verify_proof.rs around lines 64 to 66, the
#[profile] attribute on check_tree_height is unnecessary noise for a trivial
function; either remove the attribute entirely or gate it behind a compile
feature so profiling is opt-in. Change the file so the attribute is wrapped with
a conditional compilation guard (e.g. only applied when a "profiling" feature or
cfg is enabled) or delete the #[profile] line to skip profiling for this tiny
leaf function.

Comment on lines +87 to 90
#[profile]
pub fn read_address_roots<'a, 'b: 'a>(
remaining_accounts: &[AcpAccount<'_>],
new_address_params: impl Iterator<Item = &'a (dyn NewAddress<'b> + 'a)>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Gate profiling for read_address_roots

Hot path; ensure profiling is feature‑toggled.

Apply:

-#[profile]
+#[cfg_attr(feature = "profile-program", light_program_profiler::profile)]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
#[profile]
pub fn read_address_roots<'a, 'b: 'a>(
remaining_accounts: &[AcpAccount<'_>],
new_address_params: impl Iterator<Item = &'a (dyn NewAddress<'b> + 'a)>,
#[cfg_attr(feature = "profile-program", light_program_profiler::profile)]
pub fn read_address_roots<'a, 'b: 'a>(
remaining_accounts: &[AcpAccount<'_>],
new_address_params: impl Iterator<Item = &'a (dyn NewAddress<'b> + 'a)>,
🤖 Prompt for AI Agents
In programs/system/src/processor/verify_proof.rs around lines 87–90, the
#[profile] attribute is on a hot path and must be feature‑gated; replace the
bare attribute with a conditional one so profiling is only enabled when a cargo
feature is set (e.g. change to #[cfg_attr(feature = "profiling", profile)] on
the function) and add the "profiling" feature to the crate's Cargo.toml if not
present so the attribute is compiled in only when explicitly enabled.

Comment on lines +116 to 118
#[profile]
fn read_root<const IS_READ_ONLY: bool, const IS_STATE: bool>(
merkle_tree_account: &AcpAccount<'_>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Gate profiling for read_root; this is a very hot helper

Profiling this tight helper can significantly skew CU usage; enable only when needed. Optionally profile just the legacy branches if that’s what you’re targeting.

Apply:

-#[profile]
+#[cfg_attr(feature = "profile-program", light_program_profiler::profile)]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
#[profile]
fn read_root<const IS_READ_ONLY: bool, const IS_STATE: bool>(
merkle_tree_account: &AcpAccount<'_>,
#[cfg_attr(feature = "profile-program", light_program_profiler::profile)]
fn read_root<const IS_READ_ONLY: bool, const IS_STATE: bool>(
merkle_tree_account: &AcpAccount<'_>,
🤖 Prompt for AI Agents
In programs/system/src/processor/verify_proof.rs around lines 116–118, the
#[profile] attribute is applied unconditionally to the very hot read_root helper
which skews CU usage; gate the profiling so it only runs when explicitly
enabled: replace the unconditional attribute with a cfg-gated form (e.g., use
cfg_attr(feature = "profile_read_root", profile) or cfg(feature =
"profile_read_root") to enable profiling only when the feature is enabled), and
if you only want to profile legacy branches consider applying the
cfg_attr/profile only to those specific code paths instead of the whole
function.

Comment on lines 161 to 164
#[allow(clippy::too_many_arguments)]
#[profile]
pub fn verify_proof(
roots: &[[u8; 32]],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Gate profiling for verify_proof

Top‑level is a good place to keep profiling; still, feature‑gate it.

Apply:

-#[profile]
+#[cfg_attr(feature = "profile-program", light_program_profiler::profile)]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
#[allow(clippy::too_many_arguments)]
#[profile]
pub fn verify_proof(
roots: &[[u8; 32]],
#[allow(clippy::too_many_arguments)]
#[cfg_attr(feature = "profile-program", light_program_profiler::profile)]
pub fn verify_proof(
roots: &[[u8; 32]],
🤖 Prompt for AI Agents
In programs/system/src/processor/verify_proof.rs around lines 161 to 164, the
#[profile] attribute on verify_proof should be feature-gated; replace it with a
cfg_attr so profiling is only enabled when the profiling feature is turned on
(for example: #[cfg_attr(feature = "gate_profiling", profile)]), keeping the
attribute semantics but preventing it from compiling in when the feature is not
enabled.

Comment on lines +276 to 283
pub fn to_account_infos(&self) -> Vec<T> {
// Skip system light program
let refs = &self.account_infos()[1..];
let mut account_infos = Vec::with_capacity(1 + refs.len());
account_infos.push(self.fee_payer().clone());
account_infos.extend_from_slice(refs);
account_infos
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Breaking change: to_account_infos now returns owned Vec instead of Vec<&'a T>.

The change from returning references to owned values is a breaking API change. The method now requires T: Clone and returns cloned accounts. This change should be clearly documented in migration guides as it will require updates in all calling code.

Ensure that:

  1. All consumers of this API are updated
  2. Performance implications of cloning are acceptable
  3. Migration documentation is provided
🤖 Prompt for AI Agents
In sdk-libs/sdk-types/src/cpi_accounts.rs around lines 276 to 283, the method
was changed to return an owned Vec<T> (requiring T: Clone and cloning account
entries) which is a breaking API change; restore the previous non-owning API by
returning Vec<&'a T> (or add a separate owned-returning method if ownership
semantics are desired). Specifically, change the signature back to Vec<&'a T>,
build the vector of references (skip index 0 as before), reserve appropriate
capacity, push a reference to fee_payer and extend with the slice of refs as
references, and remove any Clone bounds; alternatively, if you must keep the
owned-returning variant, add a new method name (e.g., to_owned_account_infos)
and document the new behavior and performance implications in the migration
notes so callers can opt-in.

@ananas-block ananas-block force-pushed the jorrit/refactor-cpi-context branch from 281bf48 to 686213b Compare September 23, 2025 16:28
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
sdk-libs/sdk-pinocchio/src/cpi/accounts_small.rs (1)

10-68: Remove or gate the commented-out public API in sdk-libs/sdk-pinocchio/src/cpi/accounts_small.rs

to_account_metas_small is commented out in the pinocchio crate but the symbol is still present/used elsewhere; either delete the commented block, restore it behind a cfg/feature, or update callers to the active implementation.

  • Commented block: sdk-libs/sdk-pinocchio/src/cpi/accounts_small.rs (lines ~10–68)
  • Active implementation: sdk-libs/sdk/src/cpi/accounts_small_ix.rs:131
  • Call sites: sdk-libs/sdk/src/cpi/invoke.rs:137; program-tests/create-address-test-program/src/lib.rs:79
  • Test-only manual impl: program-tests/create-address-test-program/src/lib.rs:258
programs/system/src/processor/create_outputs_cpi_data.rs (1)

101-107: Don’t unwrap merkle context; return a program error instead

Unwrapping can panic at runtime. Propagate a concrete error.

Apply:

-                    let merkle_context = context
-                        .get_legacy_merkle_context(current_index as u8)
-                        .unwrap();
+                    let merkle_context = context
+                        .get_legacy_merkle_context(current_index as u8)
+                        .ok_or(SystemProgramError::InputMerkleTreeIndexOutOfBounds)?;
🧹 Nitpick comments (27)
program-tests/utils/src/lib.rs (1)

31-31: Clarify legacy/test-only intent with a module-level doc comment (or make it private if not needed).

If this is consumed only within this crate, prefer mod over pub mod. Otherwise, add a brief doc to signal its legacy scope.

- pub mod legacy_cpi_context_account;
+ /// Legacy CPI-context account helpers for tests; keep until v2 rollout completes.
+ pub mod legacy_cpi_context_account;
programs/system/src/processor/create_inputs_cpi_data.rs (3)

57-60: Consider extracting the error message to a constant.

The match expression handling is correct, but the error handling could be more maintainable.

+const INPUT_MERKLE_TREE_INDEX_ERROR: &str = "Input merkle tree index out of bounds";
+
             current_hashed_mt = match &accounts
                 .get(current_mt_index as usize)
-                .ok_or(SystemProgramError::InputMerkleTreeIndexOutOfBounds)?
+                .ok_or_else(|| {
+                    msg!(INPUT_MERKLE_TREE_INDEX_ERROR);
+                    SystemProgramError::InputMerkleTreeIndexOutOfBounds
+                })?

85-85: Replace debug print with structured logging.

The msg! call with format string concatenation is not ideal for production code and could impact performance.

-                    msg!(format!("create_inputs_cpi_data {} ", current_mt_index).as_str());
+                    msg!("Invalid account type for merkle tree index: {}", current_mt_index);

109-109: Remove unnecessary empty line.

The blank line after the tree index assignment is unnecessary and affects code consistency.

         )?;
-
         cpi_ix_data.nullifiers[j] = InsertNullifierInput {
programs/system/src/accounts/account_checks.rs (1)

75-88: Minor: rename local vars for clarity (generic helper)
Variable names reference “decompression_recipient” in a generic helper; rename to avoid confusion.

Apply:

 pub fn anchor_option_mut_account_info(
     account_info: Option<&AccountInfo>,
 ) -> Result<Option<&AccountInfo>> {
-    let option_decompression_recipient = account_info.ok_or(ProgramError::NotEnoughAccountKeys)?;
-    let decompression_recipient = if *option_decompression_recipient.key() == crate::ID {
+    let option_account_info = account_info.ok_or(ProgramError::NotEnoughAccountKeys)?;
+    let result = if *option_account_info.key() == crate::ID {
         None
     } else {
-        check_mut(option_decompression_recipient).map_err(ProgramError::from)?;
-        Some(option_decompression_recipient)
+        check_mut(option_account_info).map_err(ProgramError::from)?;
+        Some(option_account_info)
     };
-    Ok(decompression_recipient)
+    Ok(result)
 }
sdk-libs/sdk-pinocchio/src/cpi/accounts_small.rs (3)

37-39: Prefer explicit System Program ID over Pubkey::default() for clarity.

Default happens to equal System Program, but be explicit to avoid ambiguity.

Apply:

-    // 6. System Program (readonly) - always default pubkey
-    account_metas.push(AccountMeta::readonly(&Pubkey::default()));
+    // 6. System Program (readonly)
+    account_metas.push(AccountMeta::readonly(&pinocchio::system_program::ID));

51-53: CPI-context account ordering: ensure it matches the new “CPI context first” spec.

Right now cpi_context is appended among optionals; when re-enabling, push CPI-context accounts first to match the v2 ordering.

Would you like me to propose an updated ordering here consistent with the system-wide spec?


55-57: Deterministic tree account order.

Ensure tree_accounts() returns a stable order (or sort by key) to avoid nondeterministic metas across runs.

programs/system/src/invoke_cpi/verify_signer.rs (1)

128-129: Test ignore: prefer cfg-based gating over blanket ignore.
Consider cfg_attr to only ignore on non-Solana targets (or behind a feature), so the test still runs where supported.

programs/system/src/processor/create_address_cpi_data.rs (1)

53-59: Simplify invoking-program-id selection; avoid shadowing.

Use Option::or to prefer owner() and fall back to context, with tighter typing.

Apply:

-                let invoking_program_id_bytes = if let Some(bytes) = new_address_params.owner() {
-                    Ok(bytes)
-                } else if let Some(invoking_program_id_clone) = invoking_program_id_clone.as_ref() {
-                    Ok(invoking_program_id_clone)
-                } else {
-                    Err(SystemProgramError::DeriveAddressError)
-                }?;
+                let invoking_program_id_bytes = new_address_params
+                    .owner()
+                    .or(invoking_program_id_clone.as_ref())
+                    .ok_or(SystemProgramError::DeriveAddressError)?;
programs/system/src/invoke_cpi/processor.rs (1)

63-66: Clarify “deserialize to clear” side‑effect.

Add a brief comment to make the clear-on-deserialize semantics explicit for maintainers.

Apply:

     // 4. clear cpi context account
     if cpi_context_inputs_len > 0 {
-        deserialize_cpi_context_account_cleared(accounts.get_cpi_context_account().unwrap())?;
+        // Reads with `cleared = true` to zero/reset the CPI context.
+        // Dropping the returned zero-copy guard persists the cleared state.
+        deserialize_cpi_context_account_cleared(accounts.get_cpi_context_account().unwrap())?;
     }
programs/system/Cargo.toml (1)

29-31: Consider feature-gating optional profiling dependencies

Similar to the compressed-account crate, the profiling dependencies could be made optional to reduce binary size when profiling is disabled.

-profile-program = ["light-program-profiler/profile-program"]
-profile-heap = ["light-program-profiler/profile-heap", "dep:light-heap"]
+profile-program = ["dep:light-program-profiler", "light-program-profiler/profile-program", "dep:solana-msg"]
+profile-heap = ["dep:light-program-profiler", "light-program-profiler/profile-heap", "dep:light-heap", "dep:solana-msg"]

 [dependencies]
-solana-msg = { workspace = true }
-light-program-profiler = { workspace = true }
+solana-msg = { workspace = true, optional = true }
+light-program-profiler = { workspace = true, optional = true }
sdk-libs/sdk/src/cpi/mod.rs (1)

51-53: Clarify CPI module organization and document preferred API

accounts_cpi_context is compiled unconditionally; accounts_small_ix is gated by feature "v2" and sdk-libs/sdk/src/cpi/accounts.rs still exists. Add module-level docs (sdk-libs/sdk/src/cpi/mod.rs) and top-of-file comments in accounts_cpi_context.rs, accounts_small_ix.rs, and accounts.rs that state:

  • which module to use for new code,
  • the migration path between them,
  • whether accounts_small_ix is deprecated or retained for compatibility (consider #[deprecated] or a feature-note in README).
programs/system/src/processor/create_outputs_cpi_data.rs (1)

182-183: Log content may be noisy; confirm acceptability

Logging the entire context.addresses vector could be verbose and leak state in logs. Consider trimming or hashing entries.

sdk-libs/sdk-types/src/instruction/tree_info.rs (1)

32-39: Extra Clone bound may be unnecessary

get_tree_pubkey doesn’t use Clone. If not required by callers, drop the Clone bound to keep the API maximally generic.

programs/system/tests/invoke_cpi_instruction_small.rs (1)

115-126: Improve null safety in test assertions.

The test uses unwrap() on Line 123 after checking is_some() on Lines 115-121, but then uses unwrap() again without the safety check. Consider using consistent null-safe patterns.

-        assert!(invoke_cpi_instruction_small
-            .exec_accounts
-            .as_ref()
-            .unwrap()
-            .sol_pool_pda
-            .is_none());
-        assert!(invoke_cpi_instruction_small
-            .exec_accounts
-            .unwrap()
-            .decompression_recipient
-            .is_none());
+        let exec_accounts = invoke_cpi_instruction_small
+            .exec_accounts
+            .as_ref()
+            .unwrap();
+        assert!(exec_accounts.sol_pool_pda.is_none());
+        assert!(exec_accounts.decompression_recipient.is_none());
programs/system/src/cpi_context/process_cpi_context.rs (1)

232-242: Consider using debug-only logging for merkle tree mismatch.

The error message includes debug information using solana_pubkey::Pubkey::new_from_array. Consider wrapping this in a debug-only block to reduce binary size in production.

     if *cpi_context_account.associated_merkle_tree != first_merkle_tree_pubkey.to_pubkey_bytes() {
-        msg!(format!(
-            "first_merkle_tree_pubkey {:?} != associated_merkle_tree {:?}",
-            solana_pubkey::Pubkey::new_from_array(first_merkle_tree_pubkey),
-            solana_pubkey::Pubkey::new_from_array(
-                cpi_context_account.associated_merkle_tree.to_bytes()
-            )
-        )
-        .as_str());
+        #[cfg(debug_assertions)]
+        {
+            msg!(format!(
+                "first_merkle_tree_pubkey {:?} != associated_merkle_tree {:?}",
+                solana_pubkey::Pubkey::new_from_array(first_merkle_tree_pubkey),
+                solana_pubkey::Pubkey::new_from_array(
+                    cpi_context_account.associated_merkle_tree.to_bytes()
+                )
+            )
+            .as_str());
+        }
         return Err(SystemProgramError::CpiContextAssociatedMerkleTreeMismatch.into());
     }
programs/system/tests/cpi_context.rs (1)

134-145: Consider making this helper function public for reuse.

The calculate_vector_offsets function is a useful utility that could benefit other test modules working with zero-copy structures.

-#[inline]
-fn calculate_vector_offsets(
+#[inline]
+pub fn calculate_vector_offsets(
program-libs/account-checks/src/account_iterator.rs (3)

95-102: Position advances on failed checks; confirm intended consumption semantics.

next_signer_mut consumes the next account (advances position) before validation. On failure, the iterator state cannot be rewound. If callers expect retry/recovery, consider peeking before advancing.

Example (preserve position on failure):

 pub fn next_signer_mut(&mut self, account_name: &str) -> Result<&'info T, AccountError> {
-    let account_info = self.next_signer(account_name)?;
-    check_mut(account_info)
+    let pos = self.position;
+    let account_info = self.next_signer(account_name)?;
+    if let Err(e) = check_mut(account_info) {
+        // restore position so caller can decide
+        self.position = pos;
+        self.print_on_error(&e, account_name, Location::caller());
+        return Err(e);
+    }
     .inspect_err(|e| self.print_on_error(e, account_name, Location::caller()))?;
     Ok(account_info)
 }

140-154: remaining() should not error on zero remaining accounts.

Returning Err(NotEnoughAccountKeys) when none remain is surprising. Prefer Ok(&[]) and reserve errors for out‑of‑bounds consumption.

Apply:

 pub fn remaining(&self) -> Result<&'info [T], AccountError> {
-    if self.position >= self.accounts.len() {
-        let location = Location::caller();
-        let account_name = "remaining accounts";
-        solana_msg::msg!(
-            "ERROR: Not enough accounts. Requested '{}' at index {} but only {} accounts available. {}:{}:{}",
-            account_name, self.position, self.accounts.len(), location.file(), location.line(), location.column()
-        );
-        return Err(AccountError::NotEnoughAccountKeys);
-    }
-    Ok(&self.accounts[self.position..])
+    Ok(if self.position >= self.accounts.len() {
+        &[]
+    } else {
+        &self.accounts[self.position..]
+    })
 }

190-202: Log string nit: stray period and spacing.

Message reads "ERROR: {}. for account ... {}:{}:{}"; consider removing the extra period and double space for cleaner logs.

-            "ERROR: {}. for account '{}' at index {}  {}:{}:{}",
+            "ERROR: {} for account '{}' at index {} {}:{}:{}",
programs/system/src/cpi_context/state.rs (2)

185-201: Replace return Err(...)? with plain return Err(...).

return Err(SystemProgramError::Unimplemented)?; is redundant and confusing.

-                msg!("readonly_addresses are not supported when writing into cpi context account");
-                return Err(SystemProgramError::Unimplemented)?;
+                msg!("readonly_addresses are not supported when writing into cpi context account");
+                return Err(SystemProgramError::Unimplemented);

Repeat for the read_only_accounts branch.


328-340: Clearing path: consider zeroing only payload, not entire tail.

data.fill(0) wipes the entire remaining region. If subsequent fields are appended after output slices, this may be wasteful. Zero only the vector payload regions if needed.

sdk-libs/sdk/src/cpi/invoke.rs (4)

61-72: Constructor naming: clarify “assigned” vs. “packed”.

new_with_assigned_address parallels new_with_address but only supports assigned addresses elsewhere. Consider deprecating the generic constructor or documenting when each should be used to avoid runtime unimplemented!.


103-144: Avoid runtime panics for unsupported new_addresses.

Using unimplemented! will abort the transaction. Return a typed error for better UX and testability.

-    if cpi_inputs.new_addresses.is_some() {
-        unimplemented!("new_addresses must be new assigned addresses.");
-    }
+    if cpi_inputs.new_addresses.is_some() {
+        return Err(LightSdkError::Unsupported("new_addresses must be new assigned addresses").into());
+    }

Apply similarly in the CPI-context path below.


146-186: Duplicate builder logic between “small” and “cpi_context” variants.

The input construction is duplicated. Extract a shared helper to reduce drift.

Example:

fn build_with_account_info_inputs(
    cpi_inputs: CpiInputs,
    bump: u8,
    invoking_program_id: Pubkey,
) -> Result<Vec<u8>> { /* build InstructionDataInvokeCpiWithAccountInfo and serialize */ }

103-106: Typo in function name (“progam”).

Public function create_light_system_progam_instruction_invoke_cpi_small has a typo; consider adding a correctly spelled alias and deprecating the old name to avoid breaking changes.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 281bf48 and 686213b.

⛔ Files ignored due to path filters (2)
  • Cargo.lock is excluded by !**/*.lock
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (107)
  • .cargo/config.toml (0 hunks)
  • anchor-programs/system/Cargo.toml (1 hunks)
  • anchor-programs/system/src/cpi_context_account.rs (2 hunks)
  • anchor-programs/system/src/lib.rs (1 hunks)
  • cli/accounts/batched_output_queue_6L7SzhYB3anwEQ9cphpJ1U7Scwj57bx2xueReg7R9cKU.json (1 hunks)
  • cli/accounts/cpi_context_cpi1uHzrEhBG733DoEJNgHCyRS3XmmyVNZx5fonubE4.json (1 hunks)
  • cli/accounts/cpi_context_cpi2cdhkH5roePvcudTgUL8ppEBfTay1desGh8G8QxK.json (1 hunks)
  • cli/accounts/epoch_pda_34w7KcLBXabMkHuXE2fY368vFe6kP3v5EJn8nPvQ8SKn.json (1 hunks)
  • cli/accounts/forester_epoch_pda_3FBt1BPQHCQkS8k3wrUXMfB6JBhtMhEqQXueHRw2ojZV.json (1 hunks)
  • cli/accounts/governance_authority_pda_CuEtcKkkbTn6qy2qxqDswq5U2ADsqoipYDAYfRvxPjcp.json (1 hunks)
  • cli/accounts/registered_forester_pda_2KNqEh23Se8AHecuzR1UkxL26euq2qXSpQPTH1jH7VqU.json (1 hunks)
  • program-libs/account-checks/Cargo.toml (1 hunks)
  • program-libs/account-checks/src/account_info/pinocchio.rs (1 hunks)
  • program-libs/account-checks/src/account_iterator.rs (1 hunks)
  • program-libs/account-checks/src/error.rs (2 hunks)
  • program-libs/account-checks/src/lib.rs (1 hunks)
  • program-libs/account-checks/src/packed_accounts.rs (1 hunks)
  • program-libs/account-checks/tests/account_iterator.rs (1 hunks)
  • program-libs/compressed-account/Cargo.toml (1 hunks)
  • program-libs/compressed-account/src/compressed_account.rs (6 hunks)
  • program-libs/compressed-account/src/indexer_event/parse.rs (5 hunks)
  • program-libs/compressed-account/src/instruction_data/compressed_proof.rs (2 hunks)
  • program-libs/compressed-account/src/instruction_data/cpi_context.rs (2 hunks)
  • program-libs/compressed-account/src/instruction_data/data.rs (3 hunks)
  • program-libs/compressed-account/src/instruction_data/invoke_cpi.rs (2 hunks)
  • program-libs/compressed-account/src/instruction_data/mod.rs (1 hunks)
  • program-libs/compressed-account/src/instruction_data/traits.rs (3 hunks)
  • program-libs/compressed-account/src/instruction_data/with_account_info.rs (5 hunks)
  • program-libs/compressed-account/src/instruction_data/with_readonly.rs (5 hunks)
  • program-libs/compressed-account/src/instruction_data/zero_copy.rs (6 hunks)
  • program-libs/compressed-account/src/instruction_data/zero_copy_set.rs (1 hunks)
  • program-libs/compressed-account/src/lib.rs (2 hunks)
  • program-libs/compressed-account/src/pubkey.rs (3 hunks)
  • program-libs/compressed-account/tests/account_option_config.rs (1 hunks)
  • program-libs/compressed-account/tests/pubkey.rs (1 hunks)
  • program-libs/compressed-account/tests/zero_copy_set.rs (1 hunks)
  • program-libs/hasher/Cargo.toml (2 hunks)
  • program-libs/hasher/src/to_byte_array.rs (1 hunks)
  • program-libs/zero-copy/src/vec.rs (2 hunks)
  • program-tests/compressed-token-test/tests/test.rs (1 hunks)
  • program-tests/create-address-test-program/Cargo.toml (1 hunks)
  • program-tests/create-address-test-program/src/lib.rs (5 hunks)
  • program-tests/sdk-anchor-test/package.json (0 hunks)
  • program-tests/system-cpi-test/tests/test.rs (4 hunks)
  • program-tests/system-cpi-v2-test/tests/invoke_cpi_with_read_only.rs (6 hunks)
  • program-tests/system-test/Cargo.toml (2 hunks)
  • program-tests/system-test/tests/test_re_init_cpi_account.rs (1 hunks)
  • program-tests/utils/Cargo.toml (1 hunks)
  • program-tests/utils/src/assert_rollover.rs (1 hunks)
  • program-tests/utils/src/legacy_cpi_context_account.rs (1 hunks)
  • program-tests/utils/src/lib.rs (1 hunks)
  • programs/account-compression/src/processor/insert_into_queues.rs (1 hunks)
  • programs/compressed-token/src/process_transfer.rs (2 hunks)
  • programs/package.json (1 hunks)
  • programs/system/Cargo.toml (2 hunks)
  • programs/system/README.md (0 hunks)
  • programs/system/src/account_compression_state/address.rs (3 hunks)
  • programs/system/src/accounts/account_checks.rs (7 hunks)
  • programs/system/src/accounts/account_traits.rs (1 hunks)
  • programs/system/src/accounts/init_context_account.rs (3 hunks)
  • programs/system/src/accounts/remaining_account_checks.rs (5 hunks)
  • programs/system/src/constants.rs (2 hunks)
  • programs/system/src/context.rs (13 hunks)
  • programs/system/src/cpi_context/account.rs (1 hunks)
  • programs/system/src/cpi_context/address.rs (1 hunks)
  • programs/system/src/cpi_context/instruction_data_trait.rs (1 hunks)
  • programs/system/src/cpi_context/mod.rs (1 hunks)
  • programs/system/src/cpi_context/process_cpi_context.rs (1 hunks)
  • programs/system/src/cpi_context/state.rs (1 hunks)
  • programs/system/src/errors.rs (2 hunks)
  • programs/system/src/invoke/instruction.rs (1 hunks)
  • programs/system/src/invoke_cpi/account.rs (0 hunks)
  • programs/system/src/invoke_cpi/instruction.rs (1 hunks)
  • programs/system/src/invoke_cpi/instruction_small.rs (2 hunks)
  • programs/system/src/invoke_cpi/mod.rs (0 hunks)
  • programs/system/src/invoke_cpi/processor.rs (3 hunks)
  • programs/system/src/invoke_cpi/verify_signer.rs (6 hunks)
  • programs/system/src/lib.rs (5 hunks)
  • programs/system/src/processor/cpi.rs (5 hunks)
  • programs/system/src/processor/create_address_cpi_data.rs (2 hunks)
  • programs/system/src/processor/create_inputs_cpi_data.rs (5 hunks)
  • programs/system/src/processor/create_outputs_cpi_data.rs (9 hunks)
  • programs/system/src/processor/process.rs (6 hunks)
  • programs/system/src/processor/read_only_account.rs (2 hunks)
  • programs/system/src/processor/read_only_address.rs (1 hunks)
  • programs/system/src/processor/sol_compression.rs (8 hunks)
  • programs/system/src/processor/sum_check.rs (1 hunks)
  • programs/system/src/processor/verify_proof.rs (6 hunks)
  • programs/system/src/utils.rs (2 hunks)
  • programs/system/tests/cpi_context.rs (1 hunks)
  • programs/system/tests/invoke_cpi_instruction_small.rs (21 hunks)
  • programs/system/tests/invoke_instruction.rs (1 hunks)
  • sdk-libs/sdk-pinocchio/Cargo.toml (0 hunks)
  • sdk-libs/sdk-pinocchio/src/cpi/accounts_small.rs (2 hunks)
  • sdk-libs/sdk-pinocchio/src/cpi/mod.rs (1 hunks)
  • sdk-libs/sdk-types/Cargo.toml (0 hunks)
  • sdk-libs/sdk-types/src/constants.rs (1 hunks)
  • sdk-libs/sdk-types/src/cpi_accounts.rs (5 hunks)
  • sdk-libs/sdk-types/src/cpi_accounts_small.rs (4 hunks)
  • sdk-libs/sdk-types/src/cpi_context_write.rs (1 hunks)
  • sdk-libs/sdk-types/src/instruction/tree_info.rs (1 hunks)
  • sdk-libs/sdk-types/src/lib.rs (2 hunks)
  • sdk-libs/sdk/Cargo.toml (1 hunks)
  • sdk-libs/sdk/src/cpi/accounts_cpi_context.rs (1 hunks)
  • sdk-libs/sdk/src/cpi/accounts_small_ix.rs (1 hunks)
  • sdk-libs/sdk/src/cpi/invoke.rs (4 hunks)
  • sdk-libs/sdk/src/cpi/mod.rs (1 hunks)
⛔ Files not processed due to max files limit (6)
  • sdk-libs/sdk/src/error.rs
  • sdk-libs/sdk/src/instruction/pack_accounts.rs
  • sdk-libs/sdk/src/instruction/system_accounts.rs
  • sdk-tests/sdk-anchor-test/programs/sdk-anchor-test/tests/test.rs
  • sdk-tests/sdk-native-test/tests/test.rs
  • sdk-tests/sdk-pinocchio-test/tests/test.rs
💤 Files with no reviewable changes (7)
  • programs/system/README.md
  • programs/system/src/invoke_cpi/mod.rs
  • programs/system/src/invoke_cpi/account.rs
  • .cargo/config.toml
  • program-tests/sdk-anchor-test/package.json
  • sdk-libs/sdk-pinocchio/Cargo.toml
  • sdk-libs/sdk-types/Cargo.toml
🚧 Files skipped from review as they are similar to previous changes (55)
  • cli/accounts/epoch_pda_34w7KcLBXabMkHuXE2fY368vFe6kP3v5EJn8nPvQ8SKn.json
  • sdk-libs/sdk-pinocchio/src/cpi/mod.rs
  • program-libs/compressed-account/src/instruction_data/invoke_cpi.rs
  • program-tests/utils/Cargo.toml
  • cli/accounts/governance_authority_pda_CuEtcKkkbTn6qy2qxqDswq5U2ADsqoipYDAYfRvxPjcp.json
  • program-tests/utils/src/assert_rollover.rs
  • cli/accounts/cpi_context_cpi1uHzrEhBG733DoEJNgHCyRS3XmmyVNZx5fonubE4.json
  • programs/system/src/processor/read_only_account.rs
  • program-tests/compressed-token-test/tests/test.rs
  • cli/accounts/forester_epoch_pda_3FBt1BPQHCQkS8k3wrUXMfB6JBhtMhEqQXueHRw2ojZV.json
  • programs/system/src/accounts/remaining_account_checks.rs
  • program-tests/utils/src/legacy_cpi_context_account.rs
  • programs/system/src/invoke/instruction.rs
  • sdk-libs/sdk-types/src/lib.rs
  • program-libs/compressed-account/src/lib.rs
  • programs/system/src/invoke_cpi/instruction.rs
  • sdk-libs/sdk-types/src/cpi_context_write.rs
  • program-libs/hasher/Cargo.toml
  • programs/system/src/processor/read_only_address.rs
  • programs/system/src/cpi_context/instruction_data_trait.rs
  • programs/system/src/account_compression_state/address.rs
  • program-libs/compressed-account/src/instruction_data/zero_copy_set.rs
  • programs/system/src/cpi_context/address.rs
  • programs/package.json
  • anchor-programs/system/Cargo.toml
  • program-libs/account-checks/tests/account_iterator.rs
  • programs/system/src/processor/sum_check.rs
  • program-libs/compressed-account/src/instruction_data/compressed_proof.rs
  • program-libs/compressed-account/src/instruction_data/traits.rs
  • sdk-libs/sdk/src/cpi/accounts_cpi_context.rs
  • program-libs/zero-copy/src/vec.rs
  • program-libs/account-checks/src/error.rs
  • program-libs/compressed-account/tests/pubkey.rs
  • cli/accounts/registered_forester_pda_2KNqEh23Se8AHecuzR1UkxL26euq2qXSpQPTH1jH7VqU.json
  • programs/system/src/processor/sol_compression.rs
  • program-libs/account-checks/src/account_info/pinocchio.rs
  • program-libs/account-checks/src/lib.rs
  • program-libs/hasher/src/to_byte_array.rs
  • cli/accounts/batched_output_queue_6L7SzhYB3anwEQ9cphpJ1U7Scwj57bx2xueReg7R9cKU.json
  • program-libs/compressed-account/src/compressed_account.rs
  • programs/system/tests/invoke_instruction.rs
  • programs/account-compression/src/processor/insert_into_queues.rs
  • programs/system/src/accounts/account_traits.rs
  • anchor-programs/system/src/lib.rs
  • sdk-libs/sdk-types/src/constants.rs
  • program-tests/system-cpi-test/tests/test.rs
  • program-tests/create-address-test-program/Cargo.toml
  • program-libs/compressed-account/tests/zero_copy_set.rs
  • programs/compressed-token/src/process_transfer.rs
  • program-libs/account-checks/Cargo.toml
  • program-tests/system-test/tests/test_re_init_cpi_account.rs
  • sdk-libs/sdk/Cargo.toml
  • programs/system/src/utils.rs
  • program-tests/system-test/Cargo.toml
  • program-libs/compressed-account/src/indexer_event/parse.rs
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-08-14T00:36:53.191Z
Learnt from: ananas-block
PR: Lightprotocol/light-protocol#1909
File: program-libs/zero-copy/src/init_mut.rs:241-249
Timestamp: 2025-08-14T00:36:53.191Z
Learning: In the light-protocol zero-copy crate, performance is prioritized over safety checks for edge cases like Vec lengths exceeding u32::MAX, even when there might be wire format inconsistencies.

Applied to files:

  • program-libs/compressed-account/src/pubkey.rs
  • program-libs/compressed-account/src/instruction_data/with_readonly.rs
🧬 Code graph analysis (30)
programs/system/src/cpi_context/mod.rs (6)
programs/system/src/cpi_context/account.rs (2)
  • address (61-67)
  • address (121-127)
program-libs/compressed-account/src/instruction_data/traits.rs (2)
  • address (48-48)
  • address (69-69)
program-libs/compressed-account/src/instruction_data/with_account_info.rs (2)
  • address (82-84)
  • address (132-134)
program-libs/compressed-account/src/instruction_data/with_readonly.rs (1)
  • address (84-86)
program-libs/compressed-account/src/instruction_data/zero_copy.rs (2)
  • address (133-135)
  • address (346-348)
programs/system/src/cpi_context/process_cpi_context.rs (1)
  • process_cpi_context (48-88)
programs/system/src/lib.rs (7)
program-libs/compressed-account/src/instruction_data/traits.rs (1)
  • cpi_context (22-22)
program-libs/compressed-account/src/instruction_data/with_account_info.rs (1)
  • cpi_context (354-364)
program-libs/compressed-account/src/instruction_data/with_readonly.rs (1)
  • cpi_context (312-322)
program-libs/compressed-account/src/instruction_data/zero_copy.rs (2)
  • cpi_context (487-489)
  • cpi_context (616-624)
programs/system/src/context.rs (1)
  • cpi_context (351-353)
programs/system/src/cpi_context/instruction_data_trait.rs (1)
  • cpi_context (66-68)
programs/system/src/accounts/init_context_account.rs (1)
  • reinit_cpi_context_account (69-85)
program-libs/compressed-account/src/instruction_data/cpi_context.rs (3)
program-libs/compressed-account/src/instruction_data/zero_copy.rs (2)
  • first_set_context (661-663)
  • set_context (657-659)
program-libs/compressed-account/src/instruction_data/zero_copy_set.rs (2)
  • first_set_context (122-122)
  • set_context (121-121)
js/stateless.js/src/state/types.ts (1)
  • CompressedCpiContext (353-368)
program-libs/account-checks/src/packed_accounts.rs (1)
program-libs/account-checks/src/account_iterator.rs (1)
  • len (175-177)
programs/system/src/invoke_cpi/processor.rs (2)
programs/system/src/invoke_cpi/process_cpi_context.rs (1)
  • process_cpi_context (37-90)
programs/system/src/cpi_context/state.rs (1)
  • deserialize_cpi_context_account_cleared (283-287)
programs/system/src/invoke_cpi/instruction_small.rs (4)
programs/system/src/accounts/account_checks.rs (3)
  • check_option_cpi_context_account (134-167)
  • check_option_decompression_recipient (117-130)
  • check_option_sol_pool_pda (170-183)
programs/system/src/invoke/instruction.rs (5)
  • from_account_infos (42-84)
  • get_registered_program_pda (98-100)
  • get_account_compression_authority (102-104)
  • get_sol_pool_pda (106-108)
  • get_decompression_recipient (110-112)
program-libs/account-checks/src/account_iterator.rs (1)
  • new (22-28)
programs/system/src/accounts/account_traits.rs (4)
  • get_registered_program_pda (6-6)
  • get_account_compression_authority (7-7)
  • get_sol_pool_pda (8-8)
  • get_decompression_recipient (9-9)
programs/system/tests/cpi_context.rs (4)
anchor-programs/system/src/cpi_context_account.rs (1)
  • deserialize_cpi_context_account (50-72)
programs/system/src/cpi_context/state.rs (2)
  • deserialize_cpi_context_account_cleared (283-287)
  • output_data_len (74-76)
programs/system/src/errors.rs (2)
  • from (158-233)
  • from (237-239)
programs/system/src/invoke_cpi/process_cpi_context.rs (1)
  • tests (227-812)
programs/system/src/context.rs (5)
program-libs/compressed-account/src/instruction_data/traits.rs (3)
  • cpi_context (22-22)
  • output_accounts (16-16)
  • input_accounts (15-15)
program-libs/compressed-account/src/instruction_data/zero_copy.rs (6)
  • cpi_context (487-489)
  • cpi_context (616-624)
  • output_accounts (483-485)
  • output_accounts (607-609)
  • input_accounts (478-481)
  • input_accounts (611-614)
programs/system/src/cpi_context/instruction_data_trait.rs (3)
  • cpi_context (66-68)
  • output_accounts (30-32)
  • input_accounts (26-28)
programs/system/src/cpi_context/process_cpi_context.rs (1)
  • set_cpi_context (91-137)
programs/system/src/invoke_cpi/process_cpi_context.rs (1)
  • set_cpi_context (92-135)
program-libs/compressed-account/src/instruction_data/data.rs (3)
js/stateless.js/src/state/types.ts (1)
  • OutputCompressedAccountWithPackedContext (170-173)
js/stateless.js/src/state/compressed-account.ts (1)
  • CompressedAccount (29-51)
js/stateless.js/src/utils/address.ts (1)
  • NewAddressParamsPacked (64-83)
program-libs/compressed-account/tests/account_option_config.rs (2)
js/stateless.js/src/state/types.ts (2)
  • CompressedCpiContext (353-368)
  • InstructionDataInvokeCpi (311-345)
program-libs/compressed-account/src/instruction_data/with_readonly.rs (1)
  • with_cpi_context (245-247)
program-libs/compressed-account/src/instruction_data/with_account_info.rs (3)
program-libs/compressed-account/src/instruction_data/traits.rs (1)
  • account_option_config (24-24)
program-libs/compressed-account/src/instruction_data/with_readonly.rs (6)
  • account_option_config (265-283)
  • with_cpi_context (245-247)
  • rng (676-685)
  • rng (701-721)
  • rng (722-728)
  • rng (729-740)
program-libs/compressed-account/src/instruction_data/zero_copy.rs (3)
  • account_option_config (442-444)
  • account_option_config (560-572)
  • get_rnd_cpi_context (1014-1020)
sdk-libs/sdk/src/cpi/accounts_small_ix.rs (6)
sdk-libs/sdk-types/src/cpi_accounts.rs (1)
  • config (223-225)
sdk-libs/sdk-types/src/cpi_accounts_small.rs (1)
  • config (119-121)
sdk-libs/sdk/src/error.rs (3)
  • from (92-94)
  • from (98-125)
  • from (129-170)
sdk-libs/sdk/src/instruction/system_accounts.rs (1)
  • default (48-70)
sdk-libs/sdk/src/cpi/accounts.rs (1)
  • try_from (139-161)
program-tests/create-address-test-program/src/lib.rs (1)
  • to_account_metas_small (258-344)
programs/system/src/processor/cpi.rs (5)
programs/system/src/accounts/account_traits.rs (2)
  • get_account_compression_authority (7-7)
  • get_registered_program_pda (6-6)
programs/system/src/invoke/instruction.rs (2)
  • get_account_compression_authority (102-104)
  • get_registered_program_pda (98-100)
programs/system/src/invoke_cpi/instruction.rs (2)
  • get_account_compression_authority (105-107)
  • get_registered_program_pda (101-103)
programs/system/src/invoke_cpi/instruction_small.rs (2)
  • get_account_compression_authority (118-123)
  • get_registered_program_pda (111-116)
programs/system/src/utils.rs (1)
  • get_registered_program_pda (13-15)
sdk-libs/sdk-types/src/cpi_accounts.rs (1)
sdk-libs/sdk-types/src/cpi_accounts_small.rs (3)
  • tree_pubkeys (186-192)
  • to_account_infos (166-174)
  • account_infos (137-139)
programs/system/src/processor/create_outputs_cpi_data.rs (4)
programs/system/src/cpi_context/account.rs (4)
  • address (61-67)
  • address (121-127)
  • owner (53-55)
  • merkle_tree_index (153-155)
program-libs/compressed-account/src/instruction_data/traits.rs (7)
  • address (48-48)
  • address (69-69)
  • owner (13-13)
  • owner (37-39)
  • owner (46-46)
  • owner (73-73)
  • merkle_tree_index (74-74)
program-libs/compressed-account/src/instruction_data/with_account_info.rs (6)
  • address (82-84)
  • address (132-134)
  • owner (71-73)
  • owner (140-142)
  • owner (342-344)
  • merkle_tree_index (144-146)
program-libs/compressed-account/src/pubkey.rs (1)
  • new_from_array (68-70)
program-libs/compressed-account/src/instruction_data/with_readonly.rs (4)
program-libs/compressed-account/src/instruction_data/traits.rs (1)
  • account_option_config (24-24)
program-libs/compressed-account/src/instruction_data/with_account_info.rs (1)
  • account_option_config (308-328)
program-libs/compressed-account/src/instruction_data/zero_copy.rs (2)
  • account_option_config (442-444)
  • account_option_config (560-572)
programs/system/src/cpi_context/instruction_data_trait.rs (1)
  • account_option_config (75-82)
program-libs/compressed-account/src/instruction_data/zero_copy.rs (6)
program-libs/compressed-account/src/instruction_data/traits.rs (1)
  • account_option_config (24-24)
program-libs/compressed-account/src/instruction_data/with_account_info.rs (1)
  • account_option_config (308-328)
program-libs/compressed-account/src/instruction_data/with_readonly.rs (1)
  • account_option_config (265-283)
programs/system/src/cpi_context/instruction_data_trait.rs (1)
  • account_option_config (75-82)
program-libs/compressed-account/src/instruction_data/cpi_context.rs (2)
  • first_set_context (26-28)
  • first_set_context (36-38)
program-libs/compressed-account/src/instruction_data/zero_copy_set.rs (1)
  • first_set_context (122-122)
programs/system/src/cpi_context/process_cpi_context.rs (5)
program-libs/batched-merkle-tree/src/queue.rs (2)
  • BatchedQueueAccount (662-662)
  • output_from_account_info (152-159)
programs/system/src/cpi_context/account.rs (4)
  • data (77-87)
  • data (137-147)
  • address (61-67)
  • address (121-127)
programs/system/src/cpi_context/state.rs (3)
  • deserialize_cpi_context_account (276-280)
  • deserialize_cpi_context_account_cleared (283-287)
  • is_empty (63-70)
programs/system/src/context.rs (2)
  • cpi_context (351-353)
  • set_cpi_context (203-222)
program-libs/compressed-account/src/pubkey.rs (2)
  • new_from_array (68-70)
  • to_bytes (189-191)
program-tests/create-address-test-program/src/lib.rs (4)
sdk-libs/sdk/src/cpi/accounts.rs (1)
  • get_account_metas_from_config (26-134)
sdk-libs/sdk-types/src/cpi_accounts.rs (6)
  • fee_payer (128-130)
  • account_infos (241-243)
  • new_with_config (91-97)
  • config (223-225)
  • tree_accounts (251-256)
  • tree_accounts (259-263)
sdk-libs/sdk-types/src/cpi_accounts_small.rs (7)
  • fee_payer (49-51)
  • account_infos (137-139)
  • new_with_config (41-47)
  • config (119-121)
  • system_accounts_end_offset (123-135)
  • tree_accounts (147-154)
  • tree_accounts (187-191)
sdk-libs/sdk/src/cpi/accounts_small_ix.rs (1)
  • to_account_metas_small (131-134)
programs/system/src/cpi_context/account.rs (2)
program-libs/compressed-account/src/compressed_account.rs (4)
  • hash_with_hashed_values (260-315)
  • hash_with_hashed_values (320-338)
  • hash_with_hashed_values (359-377)
  • merkle_context (220-231)
program-libs/compressed-account/src/instruction_data/traits.rs (19)
  • hash_with_hashed_values (53-59)
  • hash_with_hashed_values (75-81)
  • owner (13-13)
  • owner (37-39)
  • owner (46-46)
  • owner (73-73)
  • lamports (47-47)
  • lamports (68-68)
  • address (48-48)
  • address (69-69)
  • merkle_context (49-49)
  • has_data (50-50)
  • has_data (70-70)
  • data (51-51)
  • data (72-72)
  • skip (52-52)
  • skip (71-71)
  • root_index (61-61)
  • merkle_tree_index (74-74)
programs/system/src/accounts/init_context_account.rs (2)
program-libs/account-checks/src/checks.rs (2)
  • check_owner (128-136)
  • check_signer (114-119)
programs/system/src/cpi_context/state.rs (2)
  • cpi_context_account_new (403-495)
  • new (378-389)
programs/system/src/accounts/account_checks.rs (3)
programs/system/src/cpi_context/state.rs (6)
  • std (93-93)
  • std (95-95)
  • std (97-97)
  • std (98-98)
  • std (100-100)
  • std (472-472)
program-libs/account-checks/src/checks.rs (9)
  • check_discriminator (62-62)
  • check_discriminator (78-87)
  • check_mut (121-126)
  • check_non_mut (43-48)
  • check_owner (128-136)
  • check_pda_seeds (151-161)
  • check_pda_seeds_with_bump (163-173)
  • check_program (138-149)
  • check_signer (114-119)
programs/system/src/cpi_context/account.rs (1)
  • owner (53-55)
sdk-libs/sdk/src/cpi/invoke.rs (5)
sdk-libs/sdk-types/src/cpi_accounts.rs (4)
  • cpi_context (202-213)
  • account_infos (241-243)
  • bump (43-45)
  • bump (219-221)
sdk-libs/sdk-types/src/cpi_accounts_small.rs (3)
  • cpi_context (102-113)
  • account_infos (137-139)
  • bump (175-177)
sdk-libs/sdk/src/cpi/accounts_cpi_context.rs (1)
  • get_account_metas_from_config_cpi_context (5-13)
sdk-libs/sdk/src/cpi/accounts.rs (1)
  • get_account_metas_from_config (26-134)
sdk-libs/sdk/src/cpi/accounts_small_ix.rs (1)
  • to_account_metas_small (131-134)
programs/system/src/processor/process.rs (2)
programs/system/src/cpi_context/process_cpi_context.rs (2)
  • process_cpi_context (48-88)
  • copy_cpi_context_outputs (144-190)
programs/system/src/invoke_cpi/process_cpi_context.rs (2)
  • process_cpi_context (37-90)
  • copy_cpi_context_outputs (141-164)
program-tests/system-cpi-v2-test/tests/invoke_cpi_with_read_only.rs (1)
program-tests/create-address-test-program/src/lib.rs (1)
  • create_invoke_read_only_account_info_instruction (235-256)
programs/system/src/cpi_context/state.rs (3)
program-libs/account-checks/src/checks.rs (1)
  • check_owner (128-136)
programs/system/src/cpi_context/account.rs (4)
  • data (77-87)
  • data (137-147)
  • address (61-67)
  • address (121-127)
program-libs/compressed-account/src/instruction_data/data.rs (2)
  • assigned_account_index (72-78)
  • new (61-70)
programs/system/tests/invoke_cpi_instruction_small.rs (2)
programs/system/tests/invoke_cpi_instruction.rs (8)
  • get_account_compression_authority_account_info (79-88)
  • get_account_compression_program_account_info (56-65)
  • get_authority_account_info (22-31)
  • get_fee_payer_account_info (11-20)
  • get_mut_account_info (103-112)
  • get_registered_program_pda_account_info (34-43)
  • get_self_program_account_info (129-138)
  • get_system_program_account_info (115-124)
sdk-libs/sdk-types/src/cpi_accounts_small.rs (6)
  • account_compression_program (74-79)
  • system_program (81-86)
  • fee_payer (49-51)
  • authority (53-58)
  • registered_program_pda (60-65)
  • account_compression_authority (67-72)
sdk-libs/sdk-types/src/cpi_accounts_small.rs (2)
sdk-libs/sdk-types/src/cpi_accounts.rs (8)
  • account_compression_program (174-179)
  • system_program (195-200)
  • to_account_infos (276-283)
  • account_infos (241-243)
  • bump (43-45)
  • bump (219-221)
  • invoking_program (146-151)
  • tree_pubkeys (258-264)
sdk-libs/sdk-types/src/cpi_context_write.rs (3)
  • to_account_infos (22-28)
  • bump (14-16)
  • invoking_program (18-20)
programs/system/src/invoke_cpi/verify_signer.rs (6)
program-libs/compressed-account/src/instruction_data/traits.rs (1)
  • bump (23-23)
program-libs/compressed-account/src/instruction_data/with_account_info.rs (1)
  • bump (304-306)
program-libs/compressed-account/src/instruction_data/with_readonly.rs (1)
  • bump (289-291)
program-libs/compressed-account/src/instruction_data/zero_copy.rs (2)
  • bump (436-438)
  • bump (552-554)
programs/system/src/context.rs (1)
  • bump (259-261)
programs/system/src/cpi_context/instruction_data_trait.rs (1)
  • bump (70-73)
program-libs/account-checks/src/account_iterator.rs (1)
program-libs/account-checks/src/checks.rs (3)
  • check_mut (121-126)
  • check_non_mut (43-48)
  • check_signer (114-119)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (22)
  • GitHub Check: Test program-libs-slow
  • GitHub Check: cli-v2
  • GitHub Check: Test batched-merkle-tree-simulate
  • GitHub Check: Test program-libs-fast
  • GitHub Check: Test concurrent-merkle-tree
  • GitHub Check: stateless-js-v2
  • GitHub Check: system-programs (system-cpi-test-v2-functional, ["cargo-test-sbf -p system-cpi-v2-test -- functio...
  • GitHub Check: system-programs (light-compressed-token, ["cargo-test-sbf -p compressed-token-test"])
  • GitHub Check: system-programs (system-cpi-test-v2-event, ["cargo-test-sbf -p system-cpi-v2-test -- event::parse"])
  • GitHub Check: system-programs (sdk-libs, light-macros light-sdk light-program-test light-client light-batched-m...
  • GitHub Check: system-programs (random-e2e-test, ["cargo-test-sbf -p e2e-test"])
  • GitHub Check: system-programs (system-cpi-test, ["cargo-test-sbf -p system-cpi-test", "cargo test -p light-syst...
  • GitHub Check: system-programs (system-cpi-test-v2-other, ["cargo-test-sbf -p system-cpi-v2-test -- --skip funct...
  • GitHub Check: lint
  • GitHub Check: system-programs (account-compression, ["cargo-test-sbf -p account-compression-test"])
  • GitHub Check: system-programs (light-registry, ["cargo-test-sbf -p registry-test"])
  • GitHub Check: system-programs (light-system-program, ["cargo-test-sbf -p system-test"])
  • GitHub Check: cli-v1
  • GitHub Check: system-programs (sdk-test-program, ["cargo-test-sbf -p sdk-native-test"])
  • GitHub Check: system-programs (sdk-anchor-test-program, ["cargo-test-sbf -p sdk-anchor-test", "cargo-test-sbf -...
  • GitHub Check: Forester e2e test
  • GitHub Check: stateless-js-v1

Comment on lines +28 to +35
#[aligned_sized(anchor)]
#[derive(Debug, PartialEq, Default)]
#[account]
#[repr(C)]
pub struct CpiContextAccount2 {
pub fee_payer: Pubkey,
pub associated_merkle_tree: Pubkey,
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Layout/deserialization mismatch between v1 and v2 context accounts

CpiContextAccount2 removes the context Vec, but deserialize_cpi_context_account still reads a length and iterates context items. On v2 data this will read past the struct and return corrupted data.

Parse by discriminator and handle both layouts:

-use anchor_lang::prelude::*;
+use anchor_lang::{prelude::*, Discriminator};
@@
-pub fn deserialize_cpi_context_account<'info, 'a>(
-    account_info: &AccountInfo<'info>,
-) -> std::result::Result<ZCpiContextAccount2<'a>, ZeroCopyError> {
-    let mut account_data = account_info.try_borrow_mut_data().unwrap();
-    let data = unsafe { slice::from_raw_parts_mut(account_data.as_mut_ptr(), account_data.len()) };
-    let (fee_payer, data) =
-        Ref::<&'a mut [u8], light_compressed_account::pubkey::Pubkey>::from_prefix(&mut data[8..])?;
-    let (associated_merkle_tree, data) =
-        Ref::<&'a mut [u8], light_compressed_account::pubkey::Pubkey>::from_prefix(data)?;
-    let (len, data) = Ref::<&'a mut [u8], U32>::from_prefix(data)?;
-    let mut data = &*data;
-    let mut context = Vec::new();
-    for _ in 0..(u64::from(*len)) as usize {
-        let (context_item, new_data) = ZInstructionDataInvokeCpi::zero_copy_at(data)?;
-        context.push(context_item);
-        data = new_data;
-    }
-    Ok(ZCpiContextAccount2 { fee_payer, associated_merkle_tree, context })
-}
+pub fn deserialize_cpi_context_account<'info, 'a>(
+    account_info: &AccountInfo<'info>,
+) -> std::result::Result<ZCpiContextAccount2<'a>, ZeroCopyError> {
+    let mut account_data = account_info.try_borrow_mut_data().map_err(|_| ZeroCopyError::BorrowMut)?;
+    let data = unsafe { slice::from_raw_parts_mut(account_data.as_mut_ptr(), account_data.len()) };
+    let discr = &data[..8];
+    // Old layout with context Vec
+    if discr == CpiContextAccount::discriminator() {
+        let (fee_payer, data) =
+            Ref::<&'a mut [u8], light_compressed_account::pubkey::Pubkey>::from_prefix(&mut data[8..])?;
+        let (associated_merkle_tree, data) =
+            Ref::<&'a mut [u8], light_compressed_account::pubkey::Pubkey>::from_prefix(data)?;
+        let (len, data) = Ref::<&'a mut [u8], U32>::from_prefix(data)?;
+        let mut data = &*data;
+        let mut context = Vec::new();
+        for _ in 0..(u64::from(*len)) as usize {
+            let (context_item, new_data) = ZInstructionDataInvokeCpi::zero_copy_at(data)?;
+            context.push(context_item);
+            data = new_data;
+        }
+        return Ok(ZCpiContextAccount2 { fee_payer, associated_merkle_tree, context });
+    }
+    // New v2 layout without context Vec
+    if discr == CpiContextAccount2::discriminator() {
+        let (fee_payer, data) =
+            Ref::<&'a mut [u8], light_compressed_account::pubkey::Pubkey>::from_prefix(&mut data[8..])?;
+        let (associated_merkle_tree, _rest) =
+            Ref::<&'a mut [u8], light_compressed_account::pubkey::Pubkey>::from_prefix(data)?;
+        return Ok(ZCpiContextAccount2 { fee_payer, associated_merkle_tree, context: Vec::new() });
+    }
+    Err(ZeroCopyError::InvalidDiscriminator)
+}

Also consider returning a versioned enum if callers need to distinguish v1 vs v2.

Also applies to: 44-72, 50-53

Comment on lines +308 to +327
fn account_option_config(
&self,
) -> Result<super::traits::AccountOptions, CompressedAccountError> {
let sol_pool_pda = self.compress_or_decompress_lamports().is_some();
let decompression_recipient = sol_pool_pda && !self.is_compress();
let cpi_context_account = self.cpi_context().is_some();
let write_to_cpi_context =
self.cpi_context.first_set_context() || self.cpi_context.set_context();

// Validate: if we want to write to CPI context, we must have a CPI context
if write_to_cpi_context && !cpi_context_account {
return Err(CompressedAccountError::InvalidCpiContext);
}

Ok(AccountOptions {
sol_pool_pda,
decompression_recipient,
cpi_context_account,
write_to_cpi_context,
})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix invalid field access and derive write_to_cpi_context from optional context

self.cpi_context is not a field on this type; this will not compile. Compute the flag via self.cpi_context() Option instead.

Apply:

-        let write_to_cpi_context =
-            self.cpi_context.first_set_context() || self.cpi_context.set_context();
+        let write_to_cpi_context = self
+            .cpi_context()
+            .map(|c| c.first_set_context || c.set_context)
+            .unwrap_or(false);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
fn account_option_config(
&self,
) -> Result<super::traits::AccountOptions, CompressedAccountError> {
let sol_pool_pda = self.compress_or_decompress_lamports().is_some();
let decompression_recipient = sol_pool_pda && !self.is_compress();
let cpi_context_account = self.cpi_context().is_some();
let write_to_cpi_context =
self.cpi_context.first_set_context() || self.cpi_context.set_context();
// Validate: if we want to write to CPI context, we must have a CPI context
if write_to_cpi_context && !cpi_context_account {
return Err(CompressedAccountError::InvalidCpiContext);
}
Ok(AccountOptions {
sol_pool_pda,
decompression_recipient,
cpi_context_account,
write_to_cpi_context,
})
fn account_option_config(
&self,
) -> Result<super::traits::AccountOptions, CompressedAccountError> {
let sol_pool_pda = self.compress_or_decompress_lamports().is_some();
let decompression_recipient = sol_pool_pda && !self.is_compress();
let cpi_context_account = self.cpi_context().is_some();
let write_to_cpi_context = self
.cpi_context()
.map(|c| c.first_set_context || c.set_context)
.unwrap_or(false);
// Validate: if we want to write to CPI context, we must have a CPI context
if write_to_cpi_context && !cpi_context_account {
return Err(CompressedAccountError::InvalidCpiContext);
}
Ok(AccountOptions {
sol_pool_pda,
decompression_recipient,
cpi_context_account,
write_to_cpi_context,
})
🤖 Prompt for AI Agents
In program-libs/compressed-account/src/instruction_data/with_account_info.rs
around lines 308 to 327, the code incorrectly accesses self.cpi_context as a
field and tries to call methods on it; replace that with the Option returned by
self.cpi_context(): compute cpi_context_account as self.cpi_context().is_some(),
and derive write_to_cpi_context by matching the Option (e.g., if let Some(ctx) =
self.cpi_context() { ctx.first_set_context() || ctx.set_context() } else { false
}), keep the existing validation that write_to_cpi_context requires
cpi_context_account, and return AccountOptions with those corrected booleans.

Comment on lines +37 to 46
#[profile]
pub fn cpi_signer_check(
invoking_program: &Pubkey,
authority: &Pubkey,
bump: Option<u8>,
) -> Result<()> {
let derived_signer = if let Some(bump) = bump {
let seeds = [CPI_AUTHORITY_PDA_SEED, &[bump][..]];
solana_pubkey::Pubkey::create_program_address(
&seeds,
&solana_pubkey::Pubkey::new_from_array(*invoking_program),
)
.map_err(|_| ProgramError::from(SystemProgramError::CpiSignerCheckFailed))?
.to_bytes()
let seeds = [CPI_AUTHORITY_PDA_SEED];
pinocchio_pubkey::derive_address(&seeds, Some(bump), invoking_program)
} else {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

🧩 Analysis chain

Switch to pinocchio_pubkey::derive_address: confirm equivalence to Solana semantics.
Ensure derive_address enforces off-curve program address semantics identical to Pubkey::create_program_address when a bump is provided.

  • Optional: de-duplicate seeds binding outside the match for clarity.
-    let derived_signer = if let Some(bump) = bump {
-        let seeds = [CPI_AUTHORITY_PDA_SEED];
-        pinocchio_pubkey::derive_address(&seeds, Some(bump), invoking_program)
-    } else {
-        // Kept for backwards compatibility with instructions, invoke, and invoke cpi.
-        let seeds = [CPI_AUTHORITY_PDA_SEED];
+    let seeds = [CPI_AUTHORITY_PDA_SEED];
+    let derived_signer = if let Some(bump) = bump {
+        pinocchio_pubkey::derive_address(&seeds, Some(bump), invoking_program)
+    } else {
         solana_pubkey::Pubkey::try_find_program_address(
             &seeds,
             &solana_pubkey::Pubkey::new_from_array(*invoking_program),
         )
         .ok_or(ProgramError::InvalidSeeds)?
         .0
         .to_bytes()
     };

To confirm equivalence, please check docs:


🌐 Web query:

Does pinocchio_pubkey::derive_address guarantee off-curve program address derivation compatible with Solana's Pubkey::create_program_address (including bump semantics)?

💡 Result:

Short answer: No.

Why: pinocchio_pubkey::derive_address computes the same hash-based address (and has a const version) without calling Solana's create_program_address syscall, but its docs explicitly say it does NOT validate that the result is off-curve and it does not perform the canonical bump search — it expects seeds and (optional) bump to already be valid. So it can reproduce create_program_address when given the same seeds+bump, but it does not guarantee off-curve/canonical-bump semantics by itself; you must still use create_program_address / find_program_address (or otherwise validate) when you need that guarantee. [1][2][3]

Sources:

  • pinocchio_pubkey::derive_address docs (notes that it avoids the syscall and does not validate off-curve). [1]
  • pinocchio_pubkey::derive_address_const docs. [2]
  • Solana Pubkey create_program_address / bump semantics (find/create vs canonical bump explanation). [3]

Don't rely on pinocchio_pubkey::derive_address for PDA validation — enforce off-curve/canonical-bump

pinocchio_pubkey::derive_address only computes the hash; it does NOT validate the result is off-curve or perform the canonical bump search. Using it when a bump is supplied can accept an invalid/on-curve signer.

  • Action (programs/system/src/invoke_cpi/verify_signer.rs — ~lines 37–46): when bump is Some, validate the derived address instead of trusting derive_address alone — either call solana_pubkey::Pubkey::create_program_address with the seeds + bump (handle its error) or verify the derive_address result by comparing/confirming via solana_pubkey::Pubkey::try_find_program_address / create_program_address to ensure off-curve and canonical-bump semantics.
  • Optional: hoist seeds out of the branches for clarity (diff below).
-    let derived_signer = if let Some(bump) = bump {
-        let seeds = [CPI_AUTHORITY_PDA_SEED];
-        pinocchio_pubkey::derive_address(&seeds, Some(bump), invoking_program)
-    } else {
-        // Kept for backwards compatibility with instructions, invoke, and invoke cpi.
-        let seeds = [CPI_AUTHORITY_PDA_SEED];
+    let seeds = [CPI_AUTHORITY_PDA_SEED];
+    let derived_signer = if let Some(bump) = bump {
+        pinocchio_pubkey::derive_address(&seeds, Some(bump), invoking_program)
+    } else {
         solana_pubkey::Pubkey::try_find_program_address(
             &seeds,
             &solana_pubkey::Pubkey::new_from_array(*invoking_program),
         )
         .ok_or(ProgramError::InvalidSeeds)?
         .0
         .to_bytes()
     };

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines 61 to 66
cpi_ix_data.addresses[i].tree_index = context.get_index_or_insert(
new_address_params.address_merkle_tree_account_index(),
remaining_accounts,
);
"V2 address tree",
)?;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

🧩 Analysis chain

Confirm V2 path intentionally leaves queue_index unset.

In the V2 branch, only tree_index is set (no queue_index). If downstream uses queue_index only for V1, this is fine; otherwise, populate or gate accordingly.

Run:

Expected: no V2-only consumers require queue_index.


🏁 Script executed:

#!/bin/bash
# Inspect usages of `queue_index` to confirm it's not required for V2.
rg -nP -C3 '\bqueue_index\b'

Length of output: 51826


🏁 Script executed:

#!/bin/bash
set -euo pipefail
echo "== create_address_cpi_data.rs =="
nl -ba programs/system/src/processor/create_address_cpi_data.rs | sed -n '1,240p'

Length of output: 73


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== programs/system/src/processor/create_address_cpi_data.rs =="
awk '{printf "%4d: %s\n", NR, $0}' programs/system/src/processor/create_address_cpi_data.rs | sed -n '1,240p' || true

echo
echo "== programs/account-compression/src/processor/insert_addresses.rs =="
awk '{printf "%4d: %s\n", NR, $0}' programs/account-compression/src/processor/insert_addresses.rs | sed -n '1,240p' || true

echo
echo "== program-libs/compressed-account/src/instruction_data/insert_into_queues.rs =="
awk '{printf "%4d: %s\n", NR, $0}' program-libs/compressed-account/src/instruction_data/insert_into_queues.rs | sed -n '1,240p' || true

echo
echo "== programs/system/src/processor/create_inputs_cpi_data.rs =="
awk '{printf "%4d: %s\n", NR, $0}' programs/system/src/processor/create_inputs_cpi_data.rs | sed -n '1,240p' || true

Length of output: 29315


🏁 Script executed:

#!/bin/bash
set -euo pipefail
echo "== Search for NewAddress trait and address_* methods =="
rg -n --hidden "trait\s+NewAddress|address_queue_index\(|address_merkle_tree_account_index\(" || true

echo
echo "== Show trait definition in light_compressed_account (if present) =="
rg -n "trait\s+NewAddress" || true
rg -n "pub trait NewAddress" || true
rg -n "impl .* NewAddress" || true

Length of output: 3531


🏁 Script executed:

#!/bin/bash
set -euo pipefail
echo "== program-libs/compressed-account/src/instruction_data/traits.rs =="
awk '{printf "%4d: %s\n", NR, $0}' program-libs/compressed-account/src/instruction_data/traits.rs | sed -n '1,240p' || true

echo
echo "== program-libs/compressed-account/src/instruction_data/zero_copy.rs (around implementations) =="
awk '{printf "%4d: %s\n", NR, $0}' program-libs/compressed-account/src/instruction_data/zero_copy.rs | sed -n '1,220p' || true
awk '{printf "%4d: %s\n", NR, $0}' program-libs/compressed-account/src/instruction_data/zero_copy.rs | sed -n '760,820p' || true

echo
echo "== programs/system/src/cpi_context/address.rs =="
awk '{printf "%4d: %s\n", NR, $0}' programs/system/src/cpi_context/address.rs | sed -n '1,240p' || true

echo
echo "== programs/system/src/cpi_context/state.rs =="
awk '{printf "%4d: %s\n", NR, $0}' programs/system/src/cpi_context/state.rs | sed -n '1,300p' || true

echo
echo "== programs/system/src/context.rs (around get_index_or_insert and set_rollover_fee) =="
awk '{printf "%4d: %s\n", NR, $0}' programs/system/src/context.rs | sed -n '1,220p' || true
awk '{printf "%4d: %s\n", NR, $0}' programs/system/src/context.rs | sed -n '220,520p' || true

Length of output: 46731


V2 branch must set addresses[i].queue_index (bug — needs fix).

BatchedAddressTree path only sets tree_index but account-compression::insert_addresses groups/looks up by queue_index, so V2 entries must set queue_index to the same context index (e.g. assign cpi_ix_data.addresses[i].queue_index = cpi_ix_data.addresses[i].tree_index or call get_index_or_insert again). Fix in programs/system/src/processor/create_address_cpi_data.rs (BatchedAddressTree branch ~lines 61–66); downstream consumer: programs/account-compression/src/processor/insert_addresses.rs.

🤖 Prompt for AI Agents
In programs/system/src/processor/create_address_cpi_data.rs around lines 61 to
66, the V2 BatchedAddressTree branch sets cpi_ix_data.addresses[i].tree_index
via context.get_index_or_insert but does not set addresses[i].queue_index;
account-compression::insert_addresses groups/looks up by queue_index so you must
set queue_index to the same index. Fix by assigning
cpi_ix_data.addresses[i].queue_index = cpi_ix_data.addresses[i].tree_index (or
by calling get_index_or_insert again to obtain the same context index)
immediately after setting tree_index so both fields are populated for downstream
insert_addresses.

};
use light_hasher::{Hasher, Poseidon};
use light_program_profiler::profile;
use pinocchio::{account_info::AccountInfo, msg, program_error::ProgramError};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Wrong Pubkey type path; will not compile

solana_pubkey::Pubkey is not in scope here. Use pinocchio’s Pubkey (already a dependency in this file).

Apply:

- use pinocchio::{account_info::AccountInfo, msg, program_error::ProgramError};
+ use pinocchio::{account_info::AccountInfo, msg, program_error::ProgramError, pubkey::Pubkey as SolPubkey};

and

-                    solana_pubkey::Pubkey::new_from_array(output_account.owner().into())
+                    SolPubkey::new_from_array(output_account.owner().into())

Also applies to: 260-263

🤖 Prompt for AI Agents
In programs/system/src/processor/create_outputs_cpi_data.rs around line 11 (and
similarly at lines 260-263), the code references solana_pubkey::Pubkey which is
the wrong path and will not compile; replace those references with pinocchio’s
Pubkey and add an import like use pinocchio::pubkey::Pubkey; (or change
fully-qualified solana_pubkey::Pubkey to pinocchio::pubkey::Pubkey) so all
Pubkey uses come from the pinocchio crate already used in this file.

Comment on lines 165 to 174
/// Create a vector of account info references
pub fn to_account_infos(&self) -> Vec<&'a T> {
let mut account_infos = Vec::with_capacity(1 + self.accounts.len() - PROGRAM_ACCOUNTS_LEN);
account_infos.push(self.fee_payer());
self.accounts[PROGRAM_ACCOUNTS_LEN..]
pub fn to_account_infos(&self) -> Vec<T> {
let mut account_infos = Vec::with_capacity(1 + self.accounts.len());
account_infos.push(self.fee_payer().clone());
// Skip system light program
self.accounts[1..]
.iter()
.for_each(|acc| account_infos.push(acc));
.for_each(|acc| account_infos.push(acc.clone()));
account_infos
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Same as CpiAccounts: cloning variant should be opt-in

Expose a borrowing method to avoid forcing T: Clone and extra allocations.

Add a borrowing variant and keep the owned one as opt-in:

-    pub fn to_account_infos(&self) -> Vec<T> {
-        let mut account_infos = Vec::with_capacity(1 + self.accounts.len());
-        account_infos.push(self.fee_payer().clone());
-        // Skip system light program
-        self.accounts[1..]
-            .iter()
-            .for_each(|acc| account_infos.push(acc.clone()));
-        account_infos
-    }
+    pub fn to_account_infos(&self) -> Vec<&'a T> {
+        let mut account_infos = Vec::with_capacity(1 + self.accounts.len());
+        account_infos.push(self.fee_payer());
+        // Skip system light program
+        account_infos.extend(self.accounts[1..].iter());
+        account_infos
+    }
+
+    #[allow(dead_code)]
+    pub fn to_account_infos_owned(&self) -> Vec<T> {
+        let mut account_infos = Vec::with_capacity(1 + self.accounts.len());
+        account_infos.push(self.fee_payer().clone());
+        self.accounts[1..].iter().for_each(|acc| account_infos.push(acc.clone()));
+        account_infos
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/// Create a vector of account info references
pub fn to_account_infos(&self) -> Vec<&'a T> {
let mut account_infos = Vec::with_capacity(1 + self.accounts.len() - PROGRAM_ACCOUNTS_LEN);
account_infos.push(self.fee_payer());
self.accounts[PROGRAM_ACCOUNTS_LEN..]
pub fn to_account_infos(&self) -> Vec<T> {
let mut account_infos = Vec::with_capacity(1 + self.accounts.len());
account_infos.push(self.fee_payer().clone());
// Skip system light program
self.accounts[1..]
.iter()
.for_each(|acc| account_infos.push(acc));
.for_each(|acc| account_infos.push(acc.clone()));
account_infos
}
/// Create a vector of account info references
pub fn to_account_infos(&self) -> Vec<&'a T> {
let mut account_infos = Vec::with_capacity(1 + self.accounts.len());
account_infos.push(self.fee_payer());
// Skip system light program
account_infos.extend(self.accounts[1..].iter());
account_infos
}
#[allow(dead_code)]
pub fn to_account_infos_owned(&self) -> Vec<T> {
let mut account_infos = Vec::with_capacity(1 + self.accounts.len());
account_infos.push(self.fee_payer().clone());
self.accounts[1..].iter().for_each(|acc| account_infos.push(acc.clone()));
account_infos
}

@ananas-block
Copy link
Contributor Author

changes have been merged as part of a3497b1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant