Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 26 additions & 6 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 6 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ members = [
"anchor-programs/system",
"sdk-libs/client",
"sdk-libs/compressed-token-sdk",
"sdk-libs/event",
"sdk-libs/token-client",
"sdk-libs/macros",
"sdk-libs/sdk",
Expand Down Expand Up @@ -166,19 +167,20 @@ light-indexed-merkle-tree = { version = "3.0.0", path = "program-libs/indexed-me
light-concurrent-merkle-tree = { version = "3.0.0", path = "program-libs/concurrent-merkle-tree" }
light-sparse-merkle-tree = { version = "0.2.0", path = "sparse-merkle-tree" }
light-client = { path = "sdk-libs/client", version = "0.15.0" }
light-hasher = { path = "program-libs/hasher", version = "4.0.0" }
light-event = { path = "sdk-libs/event", version = "0.1.0" }
light-hasher = { path = "program-libs/hasher", version = "4.0.0", default-features = false }
light-macros = { path = "program-libs/macros", version = "2.1.0" }
light-merkle-tree-reference = { path = "program-tests/merkle-tree", version = "3.0.1" }
light-heap = { path = "program-libs/heap", version = "2.0.0" }
light-prover-client = { path = "prover/client", version = "3.0.0" }
light-sdk = { path = "sdk-libs/sdk", version = "0.15.0" }
light-sdk-pinocchio = { path = "sdk-libs/sdk-pinocchio", version = "0.13.0" }
light-sdk-macros = { path = "sdk-libs/macros", version = "0.15.0" }
light-sdk-types = { path = "sdk-libs/sdk-types", version = "0.15.0" }
light-compressed-account = { path = "program-libs/compressed-account", version = "0.5.0" }
light-sdk-types = { path = "sdk-libs/sdk-types", version = "0.15.0", default-features = false }
light-compressed-account = { path = "program-libs/compressed-account", version = "0.5.0", default-features = false }
light-compressible = { path = "program-libs/compressible", version = "0.1.0" }
light-ctoken-types = { path = "program-libs/ctoken-types", version = "0.1.0" }
light-account-checks = { path = "program-libs/account-checks", version = "0.4.0" }
light-account-checks = { path = "program-libs/account-checks", version = "0.4.0", default-features = false }
Comment on lines +172 to +184
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

🧩 Analysis chain

Major refactoring: default-features = false for 4 core crates.

Disabling default features for light-hasher, light-sdk-types, light-compressed-account, and light-account-checks is a significant architectural change enabling no_std support. This requires ALL downstream crate dependencies to explicitly enable needed features or risk compilation failure.

Verify that every workspace member depending on these 4 crates has explicit feature enablement where required. The 7 provided files show this pattern, but a comprehensive audit of all workspace members is needed to prevent regressions.


🏁 Script executed:

#!/bin/bash
# Description: Verify all workspace members properly handle default-features = false for core crates.
# Find all workspace members and check if they depend on the 4 affected crates without explicit features.

affected_crates="light-hasher|light-sdk-types|light-compressed-account|light-account-checks"

echo "=== Checking for potential unhandled feature defaults in workspace members ==="
fd 'Cargo.toml' --type f | while read -r toml; do
  # Skip the root Cargo.toml (already reviewed)
  if [[ "$toml" == "Cargo.toml" ]]; then
    continue
  fi
  
  # Search for dependencies on the 4 affected crates
  if grep -E "^\s*(light-hasher|light-sdk-types|light-compressed-account|light-account-checks)\s*=" "$toml" > /dev/null; then
    # Check if the line has explicit features
    if grep -E "^\s*(light-hasher|light-sdk-types|light-compressed-account|light-account-checks)\s*=.*features\s*=" "$toml" > /dev/null; then
      # Has explicit features, OK
      :
    else
      # Depends on affected crate WITHOUT explicit features - potential issue
      echo "⚠️  $toml depends on affected crate(s) without explicit features:"
      grep -E "^\s*(light-hasher|light-sdk-types|light-compressed-account|light-account-checks)\s*=" "$toml"
    fi
  fi
done

Length of output: 914


I've identified a critical issue. The script found 5 workspace members that depend on the 4 affected crates using { workspace = true } without explicit feature enablement. These will inherit default-features = false from the root workspace.

Let me verify if the 4 core crates are actually designed to compile with no features enabled:

Now I need to verify if the 4 core crates are designed to compile with no features. Let me check their feature definitions:Let me check the actual Cargo.toml files of the 4 core crates to see their feature definitions:


---

🏁 Script executed:

```shell
#!/bin/bash
# Check feature definitions in the 4 affected core crates
echo "=== Checking feature definitions in core crates ==="

for crate_dir in "program-libs/hasher" "program-libs/account-checks" "program-libs/compressed-account" "sdk-libs/sdk-types"; do
  if [ -f "$crate_dir/Cargo.toml" ]; then
    echo ""
    echo "--- $crate_dir/Cargo.toml ---"
    # Extract [features] section and show what features are defined
    sed -n '/^\[features\]/,/^\[/p' "$crate_dir/Cargo.toml" | head -20
  else
    echo "⚠️  $crate_dir/Cargo.toml not found"
  fi
done

Length of output: 1493


Now I need to check if the 5 downstream crates can actually compile with the 4 core crates having default-features = false. Let me inspect whether they have feature gates or conditional code that requires those features:


---

🏁 Script executed:

```shell
#!/bin/bash
# Check the 5 affected workspace members for code that depends on features from core crates
echo "=== Checking if affected crates can compile with default-features = false ==="

# The 5 affected crates
affected_members=(
  "program-libs/bloom-filter"
  "program-libs/compressible"
  "program-libs/hash-set"
  "program-tests/merkle-tree"
  "sdk-libs/sdk-types"
)

for member in "${affected_members[@]}"; do
  if [ -d "$member" ]; then
    echo ""
    echo "--- $member ---"
    
    # Check for feature-gated code that references the core crates
    echo "Checking for conditional imports or usage patterns..."
    rg -n 'use light_(hasher|account_checks|compressed_account)' "$member/src" --type rust -A2 -B2 2>/dev/null | head -15 || echo "  (no matches)"
    
    # Check for #[cfg(feature = ...)] patterns
    if rg -n '#\[cfg\(feature' "$member/src" --type rust 2>/dev/null | grep -qE 'solana|std|alloc'; then
      echo "⚠️  Found feature-gated code:"
      rg -n '#\[cfg\(feature' "$member/src" --type rust 2>/dev/null | head -10
    else
      echo "  (no feature-gated patterns found)"
    fi
  fi
done

Length of output: 4630


Based on the investigation, I've identified critical compilation issues. The workspace change sets default-features = false for 4 core crates, but those crates still have default = ["std"] or default = ["alloc"] defined. Additionally, 5 workspace members depend on these crates via { workspace = true } without explicit feature enablement, meaning they inherit the default-features = false setting.

The critical problems:

  1. program-tests/merkle-tree uses light_hasher APIs in main code (lib.rs line 5, indexed.rs line 3) but won't have access to the std feature required to compile those APIs.

  2. program-libs/compressible uses light_account_checks in main code (config.rs line 2) without feature gates—no guarantee it works with no features enabled.

  3. sdk-libs/sdk-types imports from light_compressed_account in main code with conditionally-gated std/alloc code, but won't have those features enabled.

The 4 affected crates (light-hasher, light-account-checks, light-compressed-account, sdk-types) still have default = ["std"] or default = ["alloc"]. When default-features = false is applied at workspace level, these defaults are ignored entirely. The crates will compile with NO features unless their code is refactored to not require std/alloc internally.

Critical fixes required:

  1. Either remove the default = [...] lines from the 4 core crates' Cargo.toml files (change to default = []), OR
  2. Explicitly enable required features in each of the 5 affected workspace members' dependencies (change { workspace = true } to { workspace = true, features = ["std"] } etc.)

The refactoring is incomplete—the PR must ensure all affected crates either compile without defaults or explicitly re-enable needed features.

🤖 Prompt for AI Agents
Cargo.toml lines 171-183: the workspace sets default-features = false so crates
that still declare default = ["std"] or ["alloc"] (light-hasher,
light-account-checks, light-compressed-account, sdk-types) end up built with NO
features, breaking dependents; fix by either removing or clearing the default
feature declarations in those four crates' Cargo.toml (change default =
["std"/"alloc"] to default = []) OR update each workspace-dependent entry that
uses { workspace = true } to explicitly enable the required features (e.g., {
workspace = true, features = ["std"] } or ["alloc"] as appropriate) for all
affected members (including program-tests/merkle-tree,
program-libs/compressible, sdk-libs/sdk-types and the other two workspace
dependents), and ensure any conditional code is either refactored or properly
feature-gated.

light-verifier = { path = "program-libs/verifier", version = "4.0.0" }
light-zero-copy = { path = "program-libs/zero-copy", version = "0.4.0" }
light-zero-copy-derive = { path = "program-libs/zero-copy-derive", version = "0.4.0" }
Expand Down
2 changes: 1 addition & 1 deletion forester-utils/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ light-hash-set = { workspace = true }
light-hasher = { workspace = true, features = ["poseidon"] }
light-concurrent-merkle-tree = { workspace = true }
light-indexed-merkle-tree = { workspace = true }
light-compressed-account = { workspace = true }
light-compressed-account = { workspace = true, features = ["std"] }
light-batched-merkle-tree = { workspace = true }
light-merkle-tree-metadata = { workspace = true }
light-sparse-merkle-tree = { workspace = true }
Expand Down
2 changes: 1 addition & 1 deletion forester/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ solana-account-decoder = { workspace = true }
solana-program = { workspace = true }
account-compression = { workspace = true }
light-batched-merkle-tree = { workspace = true }
light-compressed-account = { workspace = true }
light-compressed-account = { workspace = true, features = ["std"] }
light-system-program-anchor = { workspace = true, features = ["cpi"] }
light-hash-set = { workspace = true, features = ["solana"] }
light-hasher = { workspace = true, features = ["poseidon"] }
Expand Down
1 change: 0 additions & 1 deletion js/stateless.js/src/rpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1590,7 +1590,6 @@ export class Rpc extends Connection implements CompressionApiInterface {
const timeout = isLocalTest(this.rpcEndpoint) ? 10000 : 20000;
const interval = isLocalTest(this.rpcEndpoint) ? 100 : 200;
const startTime = Date.now();
// eslint-disable-next-line no-constant-condition
while (true) {
const indexerSlot = await this.getIndexerSlot();

Expand Down
4 changes: 3 additions & 1 deletion program-libs/account-checks/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ license = "Apache-2.0"
edition = "2021"

[features]
default = []
default = ["std"]
std = []
Comment on lines +10 to +11
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

Violates coding guideline: default build should enable none.

The coding guideline explicitly states for program-libs/account-checks/**/Cargo.toml: "Define features solana, pinocchio, and test-only in Cargo.toml; default build should enable none." Setting default = ["std"] contradicts this requirement.

Apply this diff to align with the guideline:

-default = ["std"]
+default = []
 std = []

Based on coding guidelines.

📝 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
default = ["std"]
std = []
default = []
std = []
🤖 Prompt for AI Agents
In program-libs/account-checks/Cargo.toml around lines 10 to 11, the Cargo.toml
sets default = ["std"] which violates the guideline that the default build
should enable none; update the file to set default = [] and ensure the features
section explicitly defines the features "solana", "pinocchio", and "test-only"
(with any optional dependencies mapped under those feature names) so the default
build enables no features.

solana = [
"solana-program-error",
"solana-sysvar",
Expand All @@ -34,6 +35,7 @@ rand = { workspace = true, optional = true }
pinocchio = { workspace = true }
borsh = { workspace = true }
solana-pubkey = { workspace = true, features = ["curve25519", "sha2"] }
light-account-checks = { workspace = true, features = ["solana", "pinocchio", "test-only", "std"] }

[lints.rust.unexpected_cfgs]
level = "allow"
Expand Down
8 changes: 4 additions & 4 deletions program-libs/account-checks/src/account_info/solana.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ use crate::error::AccountError;
impl AccountInfoTrait for solana_account_info::AccountInfo<'_> {
type Pubkey = solana_pubkey::Pubkey;
type DataRef<'b>
= std::cell::Ref<'b, [u8]>
= core::cell::Ref<'b, [u8]>
where
Self: 'b;
type DataRefMut<'b>
= std::cell::RefMut<'b, [u8]>
= core::cell::RefMut<'b, [u8]>
where
Self: 'b;

Expand Down Expand Up @@ -48,14 +48,14 @@ impl AccountInfoTrait for solana_account_info::AccountInfo<'_> {
fn try_borrow_data(&self) -> Result<Self::DataRef<'_>, AccountError> {
self.data
.try_borrow()
.map(|r| std::cell::Ref::map(r, |data| &**data))
.map(|r| core::cell::Ref::map(r, |data| &**data))
.map_err(Into::into)
}

fn try_borrow_mut_data(&self) -> Result<Self::DataRefMut<'_>, AccountError> {
self.data
.try_borrow_mut()
.map(|r| std::cell::RefMut::map(r, |data| &mut **data))
.map(|r| core::cell::RefMut::map(r, |data| &mut **data))
.map_err(Into::into)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

#[cfg(feature = "pinocchio")]
pub mod pinocchio {
extern crate std;
use std::vec::Vec;

use pinocchio::{account_info::AccountInfo, instruction::Account, pubkey::Pubkey};
use rand::{prelude::Rng, thread_rng};

Expand All @@ -23,7 +26,7 @@
let account_size = 88; // Total size of Account struct

// Allocate memory for Account + data
let mut raw_data = vec![0u8; account_size + data.len()];

Check failure on line 29 in program-libs/account-checks/src/account_info/test_account_info.rs

View workflow job for this annotation

GitHub Actions / system-programs (sdk-libs, light-sdk-macros light-sdk light-program-test light-client light-compr...

cannot find macro `vec` in this scope

Check failure on line 29 in program-libs/account-checks/src/account_info/test_account_info.rs

View workflow job for this annotation

GitHub Actions / programs (light-system-program-compression, ["cargo-test-sbf -p system-test -- test_with_compress...

cannot find macro `vec` in this scope

// Set the boolean flags - use 1 for true as the AccountInfo implementation checks for non-zero
// IMPORTANT: borrow_state needs to be 0xFF (all bits set) to indicate unborrowed state
Expand Down Expand Up @@ -54,14 +57,14 @@

// Create the AccountInfo by pointing to our raw Account data
let account_ptr = raw_data.as_mut_ptr() as *mut Account;
let mut account_info_raw = vec![0u8; core::mem::size_of::<AccountInfo>()];

Check failure on line 60 in program-libs/account-checks/src/account_info/test_account_info.rs

View workflow job for this annotation

GitHub Actions / system-programs (sdk-libs, light-sdk-macros light-sdk light-program-test light-client light-compr...

cannot find macro `vec` in this scope

Check failure on line 60 in program-libs/account-checks/src/account_info/test_account_info.rs

View workflow job for this annotation

GitHub Actions / programs (light-system-program-compression, ["cargo-test-sbf -p system-test -- test_with_compress...

cannot find macro `vec` in this scope
account_info_raw[0..8].copy_from_slice(&(account_ptr as u64).to_le_bytes());

// Need to leak the memory so it doesn't get dropped while the AccountInfo is still using it
std::mem::forget(raw_data);
std::mem::forget(account_info_raw);
core::mem::forget(raw_data);
core::mem::forget(account_info_raw);

unsafe { std::mem::transmute::<*mut Account, AccountInfo>(account_ptr) }
unsafe { core::mem::transmute::<*mut Account, AccountInfo>(account_ptr) }
}

#[test]
Expand Down Expand Up @@ -103,9 +106,10 @@
}
}

#[cfg(feature = "solana")]
#[cfg(all(feature = "solana", feature = "std"))]
pub mod solana_program {
use std::{cell::RefCell, rc::Rc};
extern crate std;
use std::{cell::RefCell, rc::Rc, vec, vec::Vec};

use solana_account_info::AccountInfo;
use solana_pubkey::Pubkey;
Expand Down
41 changes: 26 additions & 15 deletions program-libs/account-checks/src/account_iterator.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::panic::Location;
use core::panic::Location;

use crate::{
checks::{check_mut, check_non_mut, check_signer},
Expand Down Expand Up @@ -46,13 +46,16 @@ impl<'info, T: AccountInfoTrait> AccountIterator<'info, T> {
/// * `Err(AccountError::NotEnoughAccountKeys)` - If no more accounts are available
#[track_caller]
#[inline(always)]
pub fn next_account(&mut self, account_name: &str) -> Result<&'info T, AccountError> {
pub fn next_account(&mut self, _account_name: &str) -> Result<&'info T, AccountError> {
if self.position >= self.accounts.len() {
let location = Location::caller();
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()
);
#[cfg(feature = "std")]
{
let location = Location::caller();
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);
}

Expand Down Expand Up @@ -165,12 +168,15 @@ impl<'info, T: AccountInfoTrait> AccountIterator<'info, T> {
#[track_caller]
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()
);
#[cfg(feature = "std")]
{
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..])
Expand Down Expand Up @@ -212,6 +218,7 @@ impl<'info, T: AccountInfoTrait> AccountIterator<'info, T> {

#[cold]
fn print_on_error(&self, error: &AccountError, account_name: &str, location: &Location) {
#[cfg(feature = "std")]
solana_msg::msg!(
"ERROR: {}. for account '{}' at index {} {}:{}:{}",
error,
Expand All @@ -221,6 +228,8 @@ impl<'info, T: AccountInfoTrait> AccountIterator<'info, T> {
location.line(),
location.column()
);
#[cfg(not(feature = "std"))]
let _ = (error, account_name, location);
}
#[cold]
fn print_on_error_pubkey(
Expand All @@ -231,7 +240,7 @@ impl<'info, T: AccountInfoTrait> AccountIterator<'info, T> {
account_name: &str,
location: &Location,
) {
#[cfg(feature = "solana")]
#[cfg(all(feature = "std", feature = "solana"))]
solana_msg::msg!(
"ERROR: {}. for account '{}' address: {:?}, expected: {:?}, at index {} {}:{}:{}",
error,
Expand All @@ -243,7 +252,7 @@ impl<'info, T: AccountInfoTrait> AccountIterator<'info, T> {
location.line(),
location.column()
);
#[cfg(not(feature = "solana"))]
#[cfg(all(feature = "std", not(feature = "solana")))]
solana_msg::msg!(
"ERROR: {}. for account '{}' address: {:?}, expected: {:?}, at index {} {}:{}:{}",
error,
Expand All @@ -255,5 +264,7 @@ impl<'info, T: AccountInfoTrait> AccountIterator<'info, T> {
location.line(),
location.column()
);
#[cfg(not(feature = "std"))]
let _ = (error, pubkey1, pubkey2, account_name, location);
}
}
Loading
Loading