light_sdk/lib.rs
1//! The base library to use Compressed Accounts in Solana on-chain Rust and Anchor programs.
2//!
3//! Compressed Accounts store state as account hashes in State Merkle trees.
4//! and unique addresses in Address Merkle trees.
5//! Validity proofs (zero-knowledge proofs) verify that compressed account
6//! state exists and new addresses do not exist yet.
7//!
8//! - No rent exemption payment required.
9//! - Constant 128-byte validity proof per transaction for one or multiple compressed accounts and addresses.
10//! - Compressed account data is sent as instruction data when accessed.
11//! - State and address trees are managed by the protocol.
12//!
13//! For full program examples, see the [Program Examples](https://github.com/Lightprotocol/program-examples).
14//! For detailed documentation, visit [zkcompression.com](https://www.zkcompression.com/).
15//! For pinocchio solana program development see [`light-sdk-pinocchio`](https://docs.rs/light-sdk-pinocchio).
16//! For rust client development see [`light-client`](https://docs.rs/light-client).
17//! For rust program testing see [`light-program-test`](https://docs.rs/light-program-test).
18//! For local test validator with light system programs see [Light CLI](https://www.npmjs.com/package/@lightprotocol/zk-compression-cli).
19//!
20//! # Using Compressed Accounts in Solana Programs
21//! 1. [`Instruction`](crate::instruction)
22//! - [`CompressedAccountMeta`](crate::instruction::account_meta::CompressedAccountMeta) - Compressed account metadata structs for instruction data.
23//! - [`PackedAccounts`](crate::instruction::PackedAccounts) - Abstraction to prepare accounts offchain for instructions with compressed accounts.
24//! - [`ValidityProof`](crate::instruction::ValidityProof) - Proves that new addresses don't exist yet, and compressed account state exists.
25//! 2. Compressed Account in Program
26//! - [`LightAccount`](crate::account) - Compressed account abstraction similar to anchor Account.
27//! - [`derive_address`](crate::address) - Create a compressed account address.
28//! - [`LightDiscriminator`] - DeriveMacro to derive a compressed account discriminator.
29//! 3. [`Cpi`](crate::cpi)
30//! - [`CpiAccounts`](crate::cpi::v1::CpiAccounts) - Prepare accounts to cpi the light system program.
31//! - [`LightSystemProgramCpi`](crate::cpi::v1::LightSystemProgramCpi) - Prepare instruction data to cpi the light system program.
32//! - [`InvokeLightSystemProgram::invoke`](crate::cpi) - Invoke the light system program via cpi.
33//!
34//! # Client Program Interaction Flow
35//! ```text
36//! ├─ 𝐂𝐥𝐢𝐞𝐧𝐭
37//! │ ├─ Get ValidityProof from RPC.
38//! │ ├─ pack accounts with PackedAccounts into PackedAddressTreeInfo and PackedStateTreeInfo.
39//! │ ├─ pack CompressedAccountMeta.
40//! │ ├─ Build Instruction from PackedAccounts and CompressedAccountMetas.
41//! │ └─ Send transaction.
42//! │
43//! └─ 𝐂𝐮𝐬𝐭𝐨𝐦 𝐏𝐫𝐨𝐠𝐫𝐚𝐦
44//! ├─ CpiAccounts parse accounts consistent with PackedAccounts.
45//! ├─ LightAccount instantiates from CompressedAccountMeta.
46//! │
47//! └─ 𝐋𝐢𝐠𝐡𝐭 𝐒𝐲𝐬𝐭𝐞𝐦 𝐏𝐫𝐨𝐠𝐫𝐚𝐦 𝐂𝐏𝐈
48//! ├─ Verify ValidityProof.
49//! ├─ Update State Merkle tree.
50//! ├─ Update Address Merkle tree.
51//! └─ Complete atomic state transition.
52//! ```
53//!
54//! # Features
55//! 1. `anchor` - Derives AnchorSerialize, AnchorDeserialize instead of BorshSerialize, BorshDeserialize.
56//!
57//! 2. `v2`
58//! - available on devnet, localnet, and light-program-test.
59//! - Support for optimized v2 light system program instructions.
60//!
61//! 3. `cpi-context` - Enables CPI context operations for batched compressed account operations.
62//! - available on devnet, localnet, and light-program-test.
63//! - Enables the use of one validity proof across multiple cpis from different programs in one instruction.
64//! - For example spending compressed tokens (owned by the ctoken program) and updating a compressed pda (owned by a custom program)
65//! with one validity proof.
66//! - An instruction should not use more than one validity proof.
67//! - Requires the v2 feature.
68//!
69//! ### Example Solana program code to create a compressed account
70//! ```rust, compile_fail
71//! use anchor_lang::{prelude::*, Discriminator};
72//! use light_sdk::{
73//! account::LightAccount,
74//! address::v1::derive_address,
75//! cpi::{v1::LightSystemProgramCpi, CpiAccounts, InvokeLightSystemProgram, LightCpiInstruction},
76//! derive_light_cpi_signer,
77//! instruction::{account_meta::CompressedAccountMeta, PackedAddressTreeInfo},
78//! CpiSigner, LightDiscriminator, LightHasher, ValidityProof,
79//! };
80//!
81//! declare_id!("2tzfijPBGbrR5PboyFUFKzfEoLTwdDSHUjANCw929wyt");
82//!
83//! pub const LIGHT_CPI_SIGNER: CpiSigner =
84//! derive_light_cpi_signer!("2tzfijPBGbrR5PboyFUFKzfEoLTwdDSHUjANCw929wyt");
85//!
86//! #[program]
87//! pub mod counter {
88//!
89//! use super::*;
90//!
91//! pub fn create_compressed_account<'info>(
92//! ctx: Context<'_, '_, '_, 'info, CreateCompressedAccount<'info>>,
93//! proof: ValidityProof,
94//! address_tree_info: PackedAddressTreeInfo,
95//! output_tree_index: u8,
96//! ) -> Result<()> {
97//! let light_cpi_accounts = CpiAccounts::new(
98//! ctx.accounts.fee_payer.as_ref(),
99//! ctx.remaining_accounts,
100//! crate::LIGHT_CPI_SIGNER,
101//! )?;
102//!
103//! let (address, address_seed) = derive_address(
104//! &[b"counter", ctx.accounts.fee_payer.key().as_ref()],
105//! &address_tree_info.get_tree_pubkey(&light_cpi_accounts)?,
106//! &crate::ID,
107//! );
108//! let new_address_params = address_tree_info
109//! .into_new_address_params_packed(address_seed);
110//!
111//! let mut my_compressed_account = LightAccount::<CounterAccount>::new_init(
112//! &crate::ID,
113//! Some(address),
114//! output_tree_index,
115//! );
116//!
117//! my_compressed_account.owner = ctx.accounts.fee_payer.key();
118//!
119//! LightSystemProgramCpi::new_cpi(crate::LIGHT_CPI_SIGNER, proof)
120//! .with_light_account(my_compressed_account)?
121//! .with_new_addresses(&[new_address_params])
122//! .invoke(light_cpi_accounts)
123//! }
124//! }
125//!
126//! #[derive(Accounts)]
127//! pub struct CreateCompressedAccount<'info> {
128//! #[account(mut)]
129//! pub fee_payer: Signer<'info>,
130//! }
131//!
132//! #[derive(Clone, Debug, Default, LightDiscriminator)]
133//!pub struct CounterAccount {
134//! pub owner: Pubkey,
135//! pub counter: u64
136//!}
137//! ```
138
139/// Compressed account abstraction similar to anchor Account.
140pub mod account;
141pub use account::sha::LightAccount;
142
143/// Functions to derive compressed account addresses.
144pub mod address;
145/// Utilities to invoke the light-system-program via cpi.
146pub mod cpi;
147pub mod error;
148/// Utilities to build instructions for programs with compressed accounts.
149pub mod instruction;
150pub mod legacy;
151pub mod proof;
152/// Transfer compressed sol between compressed accounts.
153pub mod transfer;
154pub mod utils;
155
156pub use proof::borsh_compat;
157pub mod interface;
158/// Backward-compat alias
159pub use interface as compressible;
160#[cfg(feature = "merkle-tree")]
161pub mod merkle_tree;
162
163// Re-export hasher types for macro-generated code
164pub mod hasher {
165 pub use light_hasher::{
166 errors, hash_to_field_size, sha256, to_byte_array, DataHasher, Hasher, HasherError,
167 Poseidon, Sha256,
168 };
169}
170
171// Re-export compressed_account types for macro-generated code
172pub mod compressed_account {
173 pub use light_compressed_account::{
174 address::derive_address,
175 instruction_data::{
176 data::NewAddressParamsAssignedPacked, with_account_info::CompressedAccountInfo,
177 },
178 };
179}
180
181// Re-export sdk_types for macro-generated code
182pub mod sdk_types {
183 #[cfg(feature = "cpi-context")]
184 pub use light_sdk_types::cpi_context_write::CpiContextWriteAccounts;
185 pub use light_sdk_types::{
186 cpi_accounts::CpiAccountsConfig, instruction::PackedAddressTreeInfo, RentSponsor,
187 };
188}
189
190#[cfg(feature = "anchor")]
191use anchor_lang::{AnchorDeserialize, AnchorSerialize};
192#[cfg(not(feature = "anchor"))]
193use borsh::{BorshDeserialize as AnchorDeserialize, BorshSerialize as AnchorSerialize};
194pub use interface::{
195 process_initialize_light_config, process_initialize_light_config_checked,
196 process_update_light_config, CompressAs, CompressedInitSpace, CompressionInfo,
197 HasCompressionInfo, LightConfig, Pack, Space, Unpack, COMPRESSIBLE_CONFIG_SEED,
198 MAX_ADDRESS_TREES_PER_SPACE,
199};
200pub use light_account_checks::{self, discriminator::Discriminator as LightDiscriminator};
201// Re-export as extern crate so downstream crates can use `::light_hasher::` paths
202pub extern crate light_hasher;
203#[cfg(feature = "poseidon")]
204use light_hasher::DataHasher;
205pub use light_macros::{derive_light_cpi_signer, derive_light_cpi_signer_pda};
206pub use light_sdk_macros::{
207 derive_light_rent_sponsor, derive_light_rent_sponsor_pda, LightDiscriminator, LightHasher,
208 LightHasherSha,
209};
210pub use light_sdk_types::{constants, CpiSigner};
211use solana_account_info::AccountInfo;
212use solana_cpi::invoke_signed;
213use solana_instruction::{AccountMeta, Instruction};
214use solana_program_error::ProgramError;
215use solana_pubkey::Pubkey;
216
217pub trait PubkeyTrait {
218 fn to_solana_pubkey(&self) -> Pubkey;
219 fn to_array(&self) -> [u8; 32];
220}
221
222impl PubkeyTrait for [u8; 32] {
223 fn to_solana_pubkey(&self) -> Pubkey {
224 Pubkey::from(*self)
225 }
226
227 fn to_array(&self) -> [u8; 32] {
228 *self
229 }
230}
231
232#[cfg(not(feature = "anchor"))]
233impl PubkeyTrait for Pubkey {
234 fn to_solana_pubkey(&self) -> Pubkey {
235 *self
236 }
237
238 fn to_array(&self) -> [u8; 32] {
239 self.to_bytes()
240 }
241}
242
243#[cfg(feature = "anchor")]
244impl PubkeyTrait for anchor_lang::prelude::Pubkey {
245 fn to_solana_pubkey(&self) -> Pubkey {
246 Pubkey::from(self.to_bytes())
247 }
248
249 fn to_array(&self) -> [u8; 32] {
250 self.to_bytes()
251 }
252}