Skip to content

refactor: light program test make anchor programs optional deps#1976

Merged
ananas-block merged 3 commits intomainfrom
jorrit/chore-program-test-optional-deps
Oct 9, 2025
Merged

refactor: light program test make anchor programs optional deps#1976
ananas-block merged 3 commits intomainfrom
jorrit/chore-program-test-optional-deps

Conversation

@ananas-block
Copy link
Contributor

@ananas-block ananas-block commented Oct 8, 2025

Issue:

  • light-compressed-accounts are currently incompatible with light-program-test because light-program-test has anchor program dependencies

Summary by CodeRabbit

  • New Features

    • Added read-only compressed account support (v2), including CPI read paths and guarded mutations with clearer errors.
    • Enhanced program-test: richer configuration, dev-env gating, account loading from JSON, and improved indexer/setup flows.
    • Expanded tests covering SHA256 and Poseidon read-only scenarios.
  • Refactor

    • Simplified cryptographic/import paths and tightened public surface by removing unused conversions and re-scoping exports.
  • Chores

    • Updated dependencies and feature flags; made several components optional; added base64 and serial_test; introduced program ID constants.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 8, 2025

Walkthrough

Feature-gate refactors and dev-mode wiring across program-test; introduce read-only compressed account support in SDK (v2), with CPI integrations and errors; add utilities for loading accounts and program setup; adjust indexer imports/gating; extend ProgramTestConfig; add anchor tests for read-only paths; minor import/api removals/renames.

Changes

Cohort / File(s) Summary
SDK read-only accounts and CPI
sdk-libs/sdk/src/account.rs, sdk-libs/sdk/src/error.rs, sdk-libs/sdk/src/cpi/instruction.rs, sdk-libs/sdk/src/cpi/invoke.rs, sdk-libs/sdk/src/cpi/v1/invoke.rs, sdk-libs/sdk/src/cpi/v2/invoke.rs
Add read-only compressed account path (v2): new field, constructors, packing, Deref guards, and errors; CPI adds has_read_only_accounts, early handling in v2, and a write-to-context guard.
Program-test devenv gating, config, and indexer
sdk-libs/program-test/Cargo.toml, sdk-libs/program-test/src/accounts/mod.rs, .../accounts/test_accounts.rs, .../indexer/address_tree.rs, .../indexer/extensions.rs, .../indexer/state_tree.rs, .../indexer/test_indexer.rs, .../program_test/config.rs, .../program_test/light_program_test.rs, .../utils/create_account.rs, .../utils/load_accounts.rs, .../utils/mod.rs, .../utils/setup_light_programs.rs
Introduce/expand devenv feature: make several deps optional and add base64; gate modules/methods; add local constants; extend ProgramTestConfig with devenv fields; adjust init flows (create vs load accounts); add account-loading utility; update setup to use constant program IDs and conditional PDA derivation; import path updates.
Anchor test program: read-only flows
sdk-tests/sdk-anchor-test/programs/sdk-anchor-test/Cargo.toml, .../src/lib.rs, .../src/read_only.rs, .../tests/read_only.rs, .../tests/test.rs
Add serial_test dep; add module and handlers to exercise read-only (SHA256/Poseidon) via LightSystem CPI and low-level; new tests to create accounts, fetch proofs, and invoke read-only paths; apply serial execution.
SDK types rename/alias
sdk-libs/sdk-types/src/instruction/account_meta.rs
Replace CompressedAccountMetaBurn struct with CompressedAccountMetaReadOnly and type-alias Burn to ReadOnly; update trait impl target.
Indexer import scoping
sdk-libs/client/src/indexer/photon_indexer.rs
Stop re-exporting MerkleProofWithContext; import it locally inside v2 path.
Remove test conversion API
program-tests/utils/src/conversions.rs
Remove public function program_to_sdk_merkle_context and related block.
Minor hashing import changes
sdk-libs/program-test/src/indexer/address_tree.rs, .../indexer/state_tree.rs
Switch Poseidon import path to light_hasher::Poseidon; conditional import for DEFAULT_BATCH_ROOT_HISTORY_LEN.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor App as Caller
  participant SDK as SDK v2 Instruction Builder
  participant CPI as Target Program

  App->>SDK: Build CPI with LightAccount
  alt Account has read_only_account_hash
    SDK->>SDK: to_packed_read_only_account()
    note right of SDK: Collect read_only_accounts
  end
  SDK->>CPI: Invoke with read-only accounts (packed)
  CPI-->>App: Result
Loading
sequenceDiagram
  autonumber
  participant Caller
  participant SDK as inner_invoke_write_to_cpi_context_typed

  Caller->>SDK: Invoke write-to-CPI-context
  alt has_read_only_accounts() == true
    SDK-->>Caller: Err(ReadOnlyAccountsNotSupportedInCpiContext)
  else
    SDK-->>Caller: Proceed with serialization and invoke
  end
Loading
sequenceDiagram
  autonumber
  participant Harness as ProgramTest
  participant Files as Accounts on Disk
  participant Chain as Runtime/Indexer

  alt feature = devenv
    Harness->>Harness: Initialize protocol, trees, queues
    Harness->>Chain: Set accounts and indexer (batch size configured)
  else non-devenv
    Harness->>Files: find_accounts_dir()
    Files-->>Harness: Path
    Harness->>Files: load_all_accounts_from_dir()
    Files-->>Harness: HashMap<Pubkey, Account>
    Harness->>Harness: Verify batch size from queues
    Harness->>Chain: Set accounts and indexer (derived batch size)
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • sergeytimoshin
  • SwenSchaeferjohann

Poem

A bunny hopped through Merkle trees,
Packed read-only hashes with ease.
Devenv winds rewire the glade,
Test nets bloom where configs are made.
CPI burrows guard the write,
While proofs blink softly in the night.
Thump! Ship it—quick and light.

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 clearly summarizes the primary intent of the changeset as a refactor of the light program test crate to make anchor program dependencies optional, matching the core objective of the pull request. It is concise, focused on the main change, and avoids extraneous detail or noise. The phrasing is specific enough for a reviewer to understand the high-level purpose at a glance.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch jorrit/chore-program-test-optional-deps

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/chore-program-test-optional-deps branch from 8a986de to 31c76f7 Compare October 8, 2025 23:44
@ananas-block ananas-block force-pushed the jorrit/chore-program-test-optional-deps branch from 791bf94 to 9f25af8 Compare October 9, 2025 01:27
@ananas-block ananas-block force-pushed the jorrit/chore-program-test-optional-deps branch from 10fa9da to b9d2471 Compare October 9, 2025 02:03
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: 2

Caution

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

⚠️ Outside diff range comments (2)
sdk-libs/program-test/src/indexer/test_indexer.rs (2)

2283-2314: Fix infinite retry loop on non-success HTTP responses

Non-success HTTP statuses never decrement retries, causing an infinite loop. Decrement and backoff on all non-success cases.

-            while retries > 0 {
-                let response_result = client
-                    .post(format!("{}{}", SERVER_ADDRESS, PROVE_PATH))
-                    .header("Content-Type", "text/plain; charset=utf-8")
-                    .body(json_payload.clone())
-                    .send()
-                    .await;
-                if let Ok(response_result) = response_result {
-                    if response_result.status().is_success() {
-                        let body = response_result.text().await.unwrap();
+            while retries > 0 {
+                match client
+                    .post(format!("{}{}", SERVER_ADDRESS, PROVE_PATH))
+                    .header("Content-Type", "text/plain; charset=utf-8")
+                    .body(json_payload.clone())
+                    .send()
+                    .await
+                {
+                    Ok(resp) if resp.status().is_success() => {
+                        let body = resp.text().await.unwrap();
                         let proof_json = deserialize_gnark_proof_json(&body).unwrap();
                         let (proof_a, proof_b, proof_c) = proof_from_json_struct(proof_json);
                         let (proof_a, proof_b, proof_c) =
                             compress_proof(&proof_a, &proof_b, &proof_c);
                         return Ok(ValidityProofWithContext {
                             accounts: account_proof_inputs,
                             addresses: address_proof_inputs,
                             proof: CompressedProof {
                                 a: proof_a,
                                 b: proof_b,
                                 c: proof_c,
                             }
                             .into(),
                         });
-                    }
-                } else {
-                    println!("Error: {:#?}", response_result);
-                    tokio::time::sleep(Duration::from_secs(5)).await;
-                    retries -= 1;
-                }
+                    }
+                    Ok(resp) => {
+                        log::warn!("prove server non-success: {}", resp.status());
+                        retries -= 1;
+                        tokio::time::sleep(Duration::from_secs(5)).await;
+                    }
+                    Err(err) => {
+                        log::warn!("prove server error: {}", err);
+                        retries -= 1;
+                        tokio::time::sleep(Duration::from_secs(5)).await;
+                    }
+                }
             }

1226-1261: Guard underflow and avoid O(n²) removes in finalize_batched_address_tree_update

onchain_next_index - address_tree_index can underflow and slice/remove(0) is O(n²). Use saturating/checked arithmetic and drain.

-        let address_tree_index = address_tree.right_most_index();
-        let onchain_next_index = onchain_account.next_index;
-        let diff_onchain_indexer = onchain_next_index - address_tree_index as u64;
-        let addresses = address_tree.queue_elements[0..diff_onchain_indexer as usize].to_vec();
-        for _ in 0..diff_onchain_indexer {
-            address_tree.queue_elements.remove(0);
-        }
+        let address_tree_index = address_tree.right_most_index();
+        let onchain_next_index = onchain_account.next_index;
+        let diff_onchain_indexer =
+            onchain_next_index.saturating_sub(address_tree_index as u64) as usize;
+        if diff_onchain_indexer == 0 {
+            return;
+        }
+        let n = std::cmp::min(diff_onchain_indexer, address_tree.queue_elements.len());
+        let addresses: Vec<[u8; 32]> = address_tree.queue_elements.drain(0..n).collect();
🧹 Nitpick comments (10)
sdk-libs/sdk-types/src/instruction/account_meta.rs (1)

170-170: Consider adding a deprecation attribute to guide migration.

The type alias maintains backward compatibility, which is good. However, consider adding a #[deprecated] attribute to signal that consumers should migrate to CompressedAccountMetaReadOnly.

Apply this diff if deprecation is desired:

+#[deprecated(since = "TBD", note = "Use `CompressedAccountMetaReadOnly` instead")]
 pub type CompressedAccountMetaBurn = CompressedAccountMetaReadOnly;

If the alias is intended to remain indefinitely without deprecation, you can disregard this suggestion.

sdk-tests/sdk-anchor-test/programs/sdk-anchor-test/src/read_only.rs (2)

34-36: Consider preserving error context in tree_pubkeys mapping.

The error mapping to ReadOnlyError::InvalidAccount loses the original error context from tree_pubkeys(). While acceptable for test code, preserving the original error or providing more specific error variants could aid debugging.

Consider either:

  1. Preserving the original error by using ? directly if ReadOnlyError can wrap it, or
  2. Adding more specific error variants to ReadOnlyError (e.g., TreePubkeyError)

This pattern is repeated in all four test functions (lines 67-69, 101-103, 134-136).


20-152: Consider extracting common logic to reduce duplication.

There's significant code duplication across the four test functions:

  • SHA256 vs Poseidon variants differ only in the LightAccount type constructor
  • light_system_cpi vs lowlevel variants differ only in the CPI type used

For better maintainability, consider extracting shared logic into helper functions or using generics where possible. However, this is a test module and the explicit implementations may provide clearer test coverage, so this refactor is optional.

sdk-tests/sdk-anchor-test/programs/sdk-anchor-test/tests/read_only.rs (1)

107-161: Consider parameterizing account creation helpers to reduce duplication.

The create_compressed_account and create_compressed_account_poseidon helpers are nearly identical, differing only in the instruction used (lines 149-156 vs 311-318).

Consider extracting a parameterized helper function:

async fn create_compressed_account_with_instruction<F>(
    name: String,
    rpc: &mut LightProgramTest,
    payer: &Keypair,
    address: &[u8; 32],
    instruction_builder: F,
) -> Result<Signature, RpcError>
where
    F: FnOnce(ValidityProof, PackedAddressTreeInfo, u8, String) -> Instruction

However, for test code, explicit implementations may provide clearer coverage, so this refactor is optional.

Also applies to: 269-323

sdk-libs/program-test/src/indexer/test_indexer.rs (6)

631-669: Honor start_offset in get_queue_elements (v2)

start_offset is ignored; implement windowing to support pagination.

-            let end_offset = std::cmp::min(
-                num_elements as usize,
-                address_tree_bundle.queue_elements.len(),
-            );
-            let queue_elements = address_tree_bundle.queue_elements[0..end_offset].to_vec();
+            let start = _start_offset.unwrap_or(0) as usize;
+            let len = address_tree_bundle.queue_elements.len();
+            if start >= len {
+                return Ok(Response {
+                    context: Context { slot: self.get_current_slot() },
+                    value: QueueElementsResult { elements: vec![], first_value_queue_index: None },
+                });
+            }
+            let end = std::cmp::min(start + num_elements as usize, len);
+            let queue_elements = address_tree_bundle.queue_elements[start..end].to_vec();

Apply the same start/end windowing in the InputStateV2 and OutputStateV2 branches.

Also applies to: 675-742, 745-820


468-470: Replace println! with structured logging (debug/warn) and/or gate with debug_assertions

Use log::{debug,info,warn} instead of println! to avoid noisy stdout in tests and CI; gate verbose logs under debug.

-            println!("state_merkle_tree_pubkeys {:?}", state_merkle_tree_pubkeys);
-            println!("hashes {:?}", hashes);
+            log::debug!("state_merkle_tree_pubkeys {:?}", state_merkle_tree_pubkeys);
+            log::debug!("hashes {:?}", hashes);
@@
-                        println!("queue_element {:?}", queue_element);
+                        log::debug!("queue_element {:?}", queue_element);
@@
-                            println!("index {:?}", index);
-                            println!(
-                                "accounts.output_queue_batch_size {:?}",
-                                accounts.output_queue_batch_size
-                            );
+                            log::debug!("index {:?}", index);
+                            log::debug!(
+                                "accounts.output_queue_batch_size {:?}",
+                                accounts.output_queue_batch_size
+                            );
@@
-        println!(
+        log::info!(
             "creating Merkle tree bundle {:?}",
             self.state_merkle_trees
                 .iter()
                 .map(|x| x.accounts.merkle_tree)
                 .collect::<Vec<_>>()
         );
@@
-                println!("Test indexer didn't find input compressed accounts to nullify");
+                log::debug!("Test indexer didn't find input compressed accounts to nullify");
@@
-                println!("accounts {:?}", bundle.accounts);
+                log::debug!("accounts {:?}", bundle.accounts);
@@
-                    println!("Error: {:#?}", response_result);
+                    log::warn!("Error posting to prover: {:#?}", response_result);

Also applies to: 491-499, 1570-1575, 1832-1833, 1945-1946, 2310-2313


259-261: Avoid MSRV-fragile Option::is_none_or; use map_or for compatibility

map_or(true, ...) is widely supported and clearer.

-                acc.token_data.owner == *owner && mint.is_none_or(|m| acc.token_data.mint == m)
+                acc.token_data.owner == *owner && mint.map_or(true, |m| acc.token_data.mint == m)

Please confirm MSRV; if is_none_or is acceptable in your toolchain, this is optional.


1662-1673: Centralize token discriminators and owner ID; avoid literals inline

Define V1/V2 discriminators once and reuse for clarity and consistency.

-                    const TOKEN_COMPRESSED_ACCOUNT_DISCRIMINATOR: [u8; 8] =
-                        [2, 0, 0, 0, 0, 0, 0, 0];
-                    let is_v1_token = data.discriminator == TOKEN_COMPRESSED_ACCOUNT_DISCRIMINATOR;
-                    let is_v2_token = data.discriminator == [0, 0, 0, 0, 0, 0, 0, 3]; // V2 discriminator
+                    const TOKEN_COMPRESSED_ACCOUNT_DISCRIMINATOR_V1: [u8; 8] =
+                        [2, 0, 0, 0, 0, 0, 0, 0];
+                    const TOKEN_COMPRESSED_ACCOUNT_DISCRIMINATOR_V2: [u8; 8] =
+                        [0, 0, 0, 0, 0, 0, 0, 3];
+                    let is_v1_token = data.discriminator == TOKEN_COMPRESSED_ACCOUNT_DISCRIMINATOR_V1;
+                    let is_v2_token = data.discriminator == TOKEN_COMPRESSED_ACCOUNT_DISCRIMINATOR_V2;

If canonical discriminators exist in a shared crate, import them instead.


2253-2270: Use constants for heights in CombinedJsonStruct(Legacy)

Avoid magic 26; derive from constants to reduce drift.

-                            CombinedJsonStructLegacy {
+                            CombinedJsonStructLegacy {
                                 circuit_type: ProofType::Combined.to_string(),
-                                state_tree_height: 26,
-                                address_tree_height: 26,
+                                state_tree_height: STATE_MERKLE_TREE_HEIGHT as u32,
+                                address_tree_height: STATE_MERKLE_TREE_HEIGHT as u32, // Address V1 height = 26
                                 inclusion: inclusion_payload_legacy.unwrap().inputs,
                                 non_inclusion: non_inclusion_payload.inputs,
                             }

1270-1307: Typo: init_from_acounts → init_from_accounts (add forwarder to avoid breakage)

Rename for clarity; keep deprecated alias to preserve callers.

-pub async fn init_from_acounts(
+#[deprecated(note = "use init_from_accounts")]
+pub async fn init_from_acounts(
     payer: &Keypair,
     env: &TestAccounts,
     output_queue_batch_size: usize,
 ) -> Self {
@@
 }
+
+pub async fn init_from_accounts(
+    payer: &Keypair,
+    env: &TestAccounts,
+    output_queue_batch_size: usize,
+) -> Self {
+    Self::init_from_acounts(payer, env, output_queue_batch_size).await
+}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 184adff and b9d2471.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (27)
  • program-tests/utils/src/conversions.rs (0 hunks)
  • sdk-libs/client/src/indexer/photon_indexer.rs (2 hunks)
  • sdk-libs/program-test/Cargo.toml (3 hunks)
  • sdk-libs/program-test/src/accounts/mod.rs (1 hunks)
  • sdk-libs/program-test/src/accounts/test_accounts.rs (3 hunks)
  • sdk-libs/program-test/src/indexer/address_tree.rs (2 hunks)
  • sdk-libs/program-test/src/indexer/extensions.rs (1 hunks)
  • sdk-libs/program-test/src/indexer/state_tree.rs (1 hunks)
  • sdk-libs/program-test/src/indexer/test_indexer.rs (17 hunks)
  • sdk-libs/program-test/src/program_test/config.rs (5 hunks)
  • sdk-libs/program-test/src/program_test/light_program_test.rs (2 hunks)
  • sdk-libs/program-test/src/utils/create_account.rs (1 hunks)
  • sdk-libs/program-test/src/utils/load_accounts.rs (1 hunks)
  • sdk-libs/program-test/src/utils/mod.rs (1 hunks)
  • sdk-libs/program-test/src/utils/setup_light_programs.rs (4 hunks)
  • sdk-libs/sdk-types/src/instruction/account_meta.rs (1 hunks)
  • sdk-libs/sdk/src/account.rs (12 hunks)
  • sdk-libs/sdk/src/cpi/instruction.rs (1 hunks)
  • sdk-libs/sdk/src/cpi/invoke.rs (1 hunks)
  • sdk-libs/sdk/src/cpi/v1/invoke.rs (1 hunks)
  • sdk-libs/sdk/src/cpi/v2/invoke.rs (6 hunks)
  • sdk-libs/sdk/src/error.rs (2 hunks)
  • sdk-tests/sdk-anchor-test/programs/sdk-anchor-test/Cargo.toml (1 hunks)
  • sdk-tests/sdk-anchor-test/programs/sdk-anchor-test/src/lib.rs (3 hunks)
  • sdk-tests/sdk-anchor-test/programs/sdk-anchor-test/src/read_only.rs (1 hunks)
  • sdk-tests/sdk-anchor-test/programs/sdk-anchor-test/tests/read_only.rs (1 hunks)
  • sdk-tests/sdk-anchor-test/programs/sdk-anchor-test/tests/test.rs (1 hunks)
💤 Files with no reviewable changes (1)
  • program-tests/utils/src/conversions.rs
🧰 Additional context used
🧬 Code graph analysis (13)
sdk-libs/sdk/src/cpi/instruction.rs (2)
sdk-libs/sdk/src/cpi/v1/invoke.rs (1)
  • has_read_only_accounts (352-355)
sdk-libs/sdk/src/cpi/v2/invoke.rs (2)
  • has_read_only_accounts (188-190)
  • has_read_only_accounts (275-277)
sdk-tests/sdk-anchor-test/programs/sdk-anchor-test/src/read_only.rs (4)
sdk-libs/sdk/src/account.rs (4)
  • account (574-576)
  • account (638-640)
  • new_read_only (484-553)
  • new_read_only (879-953)
js/stateless.js/src/state/types.ts (1)
  • ValidityProof (397-410)
sdk-libs/sdk/src/cpi/instruction.rs (1)
  • new_cpi (15-15)
sdk-libs/sdk/src/cpi/v2/invoke.rs (2)
  • new_cpi (33-41)
  • new_cpi (194-202)
sdk-libs/sdk-types/src/instruction/account_meta.rs (1)
js/stateless.js/src/state/compressed-account.ts (1)
  • PackedStateTreeInfo (120-141)
sdk-libs/program-test/src/program_test/light_program_test.rs (3)
program-libs/hasher/src/hash_to_field_size.rs (1)
  • hash_to_bn254_field_size_be (91-93)
sdk-libs/program-test/src/accounts/initialize.rs (3)
  • initialize_accounts (39-252)
  • context (87-88)
  • context (291-292)
sdk-libs/program-test/src/utils/load_accounts.rs (1)
  • load_all_accounts_from_dir (68-140)
sdk-libs/program-test/src/utils/load_accounts.rs (1)
sdk-libs/program-test/src/program_test/light_program_test.rs (1)
  • new (55-338)
sdk-libs/sdk/src/cpi/v1/invoke.rs (2)
sdk-libs/sdk/src/cpi/instruction.rs (1)
  • has_read_only_accounts (101-101)
sdk-libs/sdk/src/cpi/v2/invoke.rs (2)
  • has_read_only_accounts (188-190)
  • has_read_only_accounts (275-277)
sdk-libs/program-test/src/program_test/config.rs (3)
program-libs/batched-merkle-tree/src/initialize_address_tree.rs (2)
  • default (36-51)
  • test_default (194-209)
program-libs/batched-merkle-tree/src/initialize_state_tree.rs (2)
  • default (45-63)
  • test_default (292-310)
programs/registry/src/protocol_config/state.rs (1)
  • default (51-70)
sdk-libs/sdk/src/account.rs (3)
sdk-libs/sdk/src/error.rs (3)
  • from (109-111)
  • from (115-142)
  • from (146-194)
program-libs/compressed-account/src/compressed_account.rs (3)
  • hash (78-84)
  • hash (339-353)
  • hash (378-390)
program-libs/hasher/src/lib.rs (1)
  • hash (28-28)
sdk-tests/sdk-anchor-test/programs/sdk-anchor-test/src/lib.rs (2)
sdk-tests/sdk-anchor-test/programs/sdk-anchor-test/tests/read_only.rs (5)
  • create_compressed_account_poseidon (269-323)
  • read_sha256_light_system_cpi (163-214)
  • read_poseidon_light_system_cpi (325-376)
  • read_sha256_lowlevel (216-267)
  • read_poseidon_lowlevel (378-429)
sdk-tests/sdk-anchor-test/programs/sdk-anchor-test/src/read_only.rs (4)
  • process_read_sha256_light_system_cpi (21-51)
  • process_read_poseidon_light_system_cpi (54-85)
  • process_read_sha256_lowlevel (88-118)
  • process_read_poseidon_lowlevel (121-152)
sdk-tests/sdk-anchor-test/programs/sdk-anchor-test/tests/read_only.rs (2)
js/stateless.js/src/rpc-interface.ts (1)
  • AddressWithTree (109-113)
js/stateless.js/src/utils/instruction.ts (2)
  • PackedAccounts (5-99)
  • SystemAccountMetaConfig (101-129)
sdk-libs/sdk/src/cpi/v2/invoke.rs (2)
sdk-libs/sdk/src/cpi/instruction.rs (1)
  • has_read_only_accounts (101-101)
sdk-libs/sdk/src/cpi/v1/invoke.rs (1)
  • has_read_only_accounts (352-355)
sdk-libs/program-test/src/indexer/test_indexer.rs (6)
sdk-libs/program-test/src/program_test/light_program_test.rs (2)
  • test_accounts (348-350)
  • indexer (340-342)
sdk-libs/program-test/src/accounts/address_tree.rs (1)
  • create_address_merkle_tree_and_queue_account (53-138)
sdk-libs/program-test/src/accounts/address_tree_v2.rs (1)
  • create_batch_address_merkle_tree (11-47)
sdk-libs/program-test/src/accounts/state_tree.rs (1)
  • create_state_merkle_tree_and_queue_account (106-216)
sdk-libs/program-test/src/accounts/state_tree_v2.rs (1)
  • create_batched_state_merkle_tree (17-109)
sdk-libs/client/src/indexer/tree_info.rs (1)
  • height (15-22)
sdk-libs/program-test/src/accounts/test_accounts.rs (3)
programs/registry/src/sdk.rs (1)
  • create_register_program_instruction (59-89)
programs/registry/src/utils.rs (2)
  • get_forester_pda (34-36)
  • get_protocol_config_pda_address (5-7)
sdk-libs/program-test/src/accounts/initialize.rs (1)
  • get_group_pda (254-260)
⏰ 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). (21)
  • GitHub Check: lint
  • GitHub Check: cli-v1
  • GitHub Check: stateless-js-v2
  • GitHub Check: Test program-libs-fast
  • GitHub Check: Test concurrent-merkle-tree
  • GitHub Check: Test batched-merkle-tree-simulate
  • GitHub Check: Test program-libs-slow
  • GitHub Check: cli-v2
  • 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 (system-cpi-test, ["cargo-test-sbf -p system-cpi-test", "cargo test -p light-syst...
  • GitHub Check: system-programs (system-cpi-test-v2-functional, ["cargo-test-sbf -p system-cpi-v2-test -- functio...
  • GitHub Check: system-programs (account-compression, ["cargo-test-sbf -p account-compression-test"])
  • GitHub Check: system-programs (system-cpi-test-v2-event, ["cargo-test-sbf -p system-cpi-v2-test -- event::parse"])
  • GitHub Check: system-programs (light-system-program, ["cargo-test-sbf -p system-test"])
  • GitHub Check: system-programs (light-registry, ["cargo-test-sbf -p registry-test"])
  • GitHub Check: system-programs (light-compressed-token, ["cargo-test-sbf -p compressed-token-test"])
  • GitHub Check: system-programs (sdk-test-program, ["cargo-test-sbf -p sdk-native-test", "cargo-test-sbf -p sdk-v...
  • GitHub Check: system-programs (sdk-libs, light-sdk-macros light-sdk light-program-test light-client batched-mer...
  • GitHub Check: system-programs (sdk-anchor-test-program, ["cargo-test-sbf -p sdk-anchor-test", "cargo-test-sbf -...
  • GitHub Check: stateless-js-v1
🔇 Additional comments (25)
sdk-libs/sdk-types/src/instruction/account_meta.rs (2)

173-178: LGTM! Rename improves semantic clarity.

The rename from CompressedAccountMetaBurn to CompressedAccountMetaReadOnly better reflects the struct's purpose. The type alias at line 170 ensures backward compatibility.


180-204: LGTM! Implementation correctly reflects read-only semantics. All CompressedAccountMetaBurn references alias to CompressedAccountMetaReadOnly and are covered.

sdk-libs/client/src/indexer/photon_indexer.rs (1)

1593-1593: Scoped import keeps v2 path tidy.

Pulling MerkleProofWithContext into the v2-only branch keeps the symbol hidden from non-v2 builds without changing behavior—looks good.

sdk-libs/sdk/src/cpi/v1/invoke.rs (1)

350-355: LGTM: Correct trait implementation for V1.

The has_read_only_accounts method correctly returns false since V1 doesn't support read-only accounts. This aligns with the trait definition and provides a consistent interface across V1 and V2 implementations.

sdk-libs/sdk/src/error.rs (1)

84-93: LGTM: Well-defined error variants for read-only account support.

The new error variants provide clear, actionable error messages for read-only account scenarios. The numeric mappings (16037-16040) are properly sequential and integrated into the From<LightSdkError> for u32 implementation.

sdk-libs/sdk/src/cpi/v2/invoke.rs (6)

47-52: LGTM: Correct early-return path for read-only accounts.

The early-return pattern correctly handles read-only accounts by converting them via to_packed_read_only_account() and bypassing standard account processing. This is consistent with the read-only account design.


104-109: LGTM: Consistent read-only handling in Poseidon variant.

The implementation mirrors the SHA256 variant's early-return pattern, ensuring consistent handling across hashers.


187-190: LGTM: Appropriate has_read_only_accounts implementation.

The method correctly checks for the presence of read-only accounts by testing if the vector is non-empty, aligning with the trait contract.


208-213: LGTM: Consistent early-return for AccountInfo variant.

The read-only account handling is properly implemented for the InstructionDataInvokeCpiWithAccountInfo type, maintaining consistency across all CPI instruction types.


228-233: LGTM: Complete Poseidon variant coverage.

The Poseidon hasher variant for InstructionDataInvokeCpiWithAccountInfo correctly implements the read-only early-return pattern.


274-277: LGTM: Consistent trait implementation.

The has_read_only_accounts implementation for InstructionDataInvokeCpiWithAccountInfo matches the pattern used in other types.

sdk-tests/sdk-anchor-test/programs/sdk-anchor-test/tests/read_only.rs (1)

22-105: LGTM: Well-structured integration tests for read-only accounts.

The tests provide comprehensive coverage of read-only account functionality across different hashers (SHA256/Poseidon) and CPI methods (LightSystemProgramCpi/lowlevel). The serial execution is appropriate for integration tests that modify on-chain state.

sdk-tests/sdk-anchor-test/programs/sdk-anchor-test/src/lib.rs (3)

4-4: LGTM: Module import properly added.

The mod read_only import correctly exposes the read-only test implementations for use in the program instructions.


183-224: LGTM: Poseidon variant properly implemented.

The create_compressed_account_poseidon function correctly uses the Poseidon hasher variant (light_sdk::account::poseidon::LightAccount) and follows the same pattern as the SHA256 variant, with the appropriate call to with_light_account_poseidon.


325-373: LGTM: Clean delegation to read_only module.

The wrapper functions provide a clean API surface while delegating to the read_only module implementations. The separation of concerns between the public API and implementation logic is well-maintained.

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

96-101: LGTM!

The trait method addition is well-documented and properly feature-gated, aligning with the existing cpi-context API surface. The implementations in v1 (returning false) and v2 (checking read_only_accounts.is_empty()) correctly reflect their respective support levels.

sdk-tests/sdk-anchor-test/programs/sdk-anchor-test/tests/test.rs (1)

16-16: LGTM!

The serial test execution is appropriate for integration tests that interact with shared RPC state and test environments. This prevents race conditions when multiple tests manipulate the same underlying resources concurrently.

Also applies to: 22-22

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

139-145: LGTM!

The runtime guard correctly prevents read-only accounts from being used in CPI context write operations. The early-exit placement is optimal, and the error message clearly directs users to the appropriate alternative (invoke_execute_cpi_context()).

sdk-libs/program-test/src/indexer/state_tree.rs (1)

5-5: Poseidon import alignment looks good.

Pointing the tree bundle straight at light_hasher::Poseidon keeps this module buildable even when the concurrent tree crate is disabled.

sdk-libs/program-test/src/utils/create_account.rs (1)

2-2: Switching to solana_pubkey::Pubkey looks right.

This keeps the helper usable without the account-compression dependency while staying ABI-compatible with system_instruction::create_account.

sdk-libs/program-test/Cargo.toml (1)

10-32: Feature gating and optional dependency wiring look consistent.

Marking the heavy program crates optional and tying them to the devenv feature matches the new cfg usage, so non-devenv builds can finally skip them.

sdk-libs/program-test/src/indexer/extensions.rs (1)

75-80: Good call gating the batched finalizer.

Keeping this async hook behind devenv mirrors the dependency gating and prevents non-devenv consumers from needing the batched tree stack.

sdk-libs/program-test/src/utils/mod.rs (1)

4-6: Module exposure matches the feature split.

Making load_accounts always available while keeping register_test_forester behind devenv lines up with the manifest changes.

sdk-libs/program-test/src/indexer/address_tree.rs (1)

3-21: Conditional imports are wired correctly.

Pulling DEFAULT_BATCH_ROOT_HISTORY_LEN from the batched tree crate only when devenv is on (and otherwise reusing the local constant) keeps non-devenv builds free of the optional dependency while still sharing the logic.

sdk-libs/program-test/src/accounts/test_accounts.rs (1)

123-191: The feature-split PDA initialization reads well.

Runtime derivation stays in the devenv block, while the non-devenv branch reuses fixed PDAs so the module compiles without pulling registry dependencies. Nice balance between flexibility and optional builds.

Copy link
Contributor

@SwenSchaeferjohann SwenSchaeferjohann left a comment

Choose a reason for hiding this comment

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

lgtm

@ananas-block ananas-block merged commit 38a9e9d into main Oct 9, 2025
33 of 36 checks passed
@ananas-block ananas-block deleted the jorrit/chore-program-test-optional-deps branch October 9, 2025 14:44
@coderabbitai coderabbitai bot mentioned this pull request Oct 25, 2025
@coderabbitai coderabbitai bot mentioned this pull request Nov 10, 2025
@coderabbitai coderabbitai bot mentioned this pull request Nov 18, 2025
@coderabbitai coderabbitai bot mentioned this pull request Jan 16, 2026
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.

2 participants