Skip to main content

light_sdk_types/lca/
mod.rs

1//! # Vendored compressed-account types
2//!
3//! Compressed account struct and utility types for Light Protocol.
4//!
5//! | Type | Description |
6//! |------|-------------|
7//! | [`CompressedAccountError`] | Error codes 12001–12025 for account operations |
8//! | [`QueueType`] | Nullifier, address, and state queue variants |
9//! | [`TreeType`] | State and address tree version variants |
10//! | [`CpiSigner`] | Program ID, CPI signer pubkey, and bump |
11//! | [`address`] | Address derivation and seed structs |
12//! | [`compressed_account`] | Core compressed account struct |
13//! | [`constants`] | Program IDs and account discriminators as byte arrays |
14//! | [`discriminators`] | Instruction discriminators for `invoke`, `invoke_cpi`, and queue operations |
15//! | [`instruction_data`] | Instruction data types and proof structs |
16//! | [`pubkey`] | Re-export of `solana_pubkey::Pubkey` |
17
18#[cfg(not(feature = "std"))]
19pub use alloc::{vec, vec::Vec};
20use core::fmt::Display;
21#[cfg(feature = "std")]
22pub use std::{vec, vec::Vec};
23
24use light_hasher::HasherError;
25use thiserror::Error;
26
27pub mod address;
28pub mod compressed_account;
29pub mod constants;
30pub mod discriminators;
31pub use light_hasher::hash_chain;
32pub mod instruction_data;
33pub mod nullifier;
34pub mod pubkey;
35pub use instruction_data::traits::{InstructionDiscriminator, LightInstructionData};
36pub use light_hasher::bigint::bigint_to_be_bytes_array;
37#[cfg(feature = "alloc")]
38pub use light_hasher::hash_to_field_size::{
39    hash_to_bn254_field_size_be, hashv_to_bn254_field_size_be,
40};
41pub use pubkey::Pubkey;
42
43#[derive(Debug, Error, PartialEq)]
44pub enum CompressedAccountError {
45    #[error("Invalid input size, expected at most {0}")]
46    InputTooLarge(usize),
47    #[error("Invalid chunk size")]
48    InvalidChunkSize,
49    #[error("Invalid seeds")]
50    InvalidSeeds,
51    #[error("Invalid rollover threshold")]
52    InvalidRolloverThreshold,
53    #[error("Invalid input length")]
54    InvalidInputLength,
55    #[error("Hasher error {0}")]
56    HasherError(#[from] HasherError),
57    #[error("Invalid Account size.")]
58    InvalidAccountSize,
59    #[error("Account is mutable.")]
60    AccountMutable,
61    #[error("Account is already initialized.")]
62    AlreadyInitialized,
63    #[error("Invalid account balance.")]
64    InvalidAccountBalance,
65    #[error("Failed to borrow rent sysvar.")]
66    FailedBorrowRentSysvar,
67    #[error("Derive address error.")]
68    DeriveAddressError,
69    #[error("Invalid argument.")]
70    InvalidArgument,
71    #[error("Expected address for compressed account got None.")]
72    ZeroCopyExpectedAddress,
73    #[error("Expected address for compressed account got None.")]
74    InstructionDataExpectedAddress,
75    #[error("Compressed account data not initialized.")]
76    CompressedAccountDataNotInitialized,
77    #[error(
78        "Invalid CPI context configuration: cannot write to CPI context without valid context"
79    )]
80    InvalidCpiContext,
81    #[error("Expected discriminator for compressed account got None.")]
82    ExpectedDiscriminator,
83    #[error("Expected data hash for compressed account got None.")]
84    ExpectedDataHash,
85    #[error("Expected proof for compressed account got None.")]
86    InstructionDataExpectedProof,
87    #[error("Expected proof for compressed account got None.")]
88    ZeroCopyExpectedProof,
89    #[error("Invalid proof size: expected 128 bytes, got {0}")]
90    InvalidProofSize(usize),
91}
92
93// NOTE(vadorovsky): Unfortunately, we need to do it by hand.
94// `num_derive::ToPrimitive` doesn't support data-carrying enums.
95impl From<CompressedAccountError> for u32 {
96    fn from(e: CompressedAccountError) -> u32 {
97        match e {
98            CompressedAccountError::InputTooLarge(_) => 12001,
99            CompressedAccountError::InvalidChunkSize => 12002,
100            CompressedAccountError::InvalidSeeds => 12003,
101            CompressedAccountError::InvalidRolloverThreshold => 12004,
102            CompressedAccountError::InvalidInputLength => 12005,
103            CompressedAccountError::InvalidAccountSize => 12010,
104            CompressedAccountError::AccountMutable => 12011,
105            CompressedAccountError::AlreadyInitialized => 12012,
106            CompressedAccountError::InvalidAccountBalance => 12013,
107            CompressedAccountError::FailedBorrowRentSysvar => 12014,
108            CompressedAccountError::DeriveAddressError => 12015,
109            CompressedAccountError::InvalidArgument => 12016,
110            CompressedAccountError::ZeroCopyExpectedAddress => 12017,
111            CompressedAccountError::InstructionDataExpectedAddress => 12018,
112            CompressedAccountError::CompressedAccountDataNotInitialized => 12019,
113            CompressedAccountError::ExpectedDiscriminator => 12020,
114            CompressedAccountError::InstructionDataExpectedProof => 12021,
115            CompressedAccountError::ZeroCopyExpectedProof => 12022,
116            CompressedAccountError::ExpectedDataHash => 12023,
117            CompressedAccountError::InvalidCpiContext => 12024,
118            CompressedAccountError::InvalidProofSize(_) => 12025,
119            CompressedAccountError::HasherError(e) => u32::from(e),
120        }
121    }
122}
123
124#[cfg(feature = "solana")]
125impl From<CompressedAccountError> for solana_program_error::ProgramError {
126    fn from(e: CompressedAccountError) -> Self {
127        solana_program_error::ProgramError::Custom(e.into())
128    }
129}
130
131#[cfg(feature = "pinocchio")]
132impl From<CompressedAccountError> for pinocchio::program_error::ProgramError {
133    fn from(e: CompressedAccountError) -> Self {
134        pinocchio::program_error::ProgramError::Custom(e.into())
135    }
136}
137
138pub const NULLIFIER_QUEUE_TYPE_V1: u64 = 1;
139pub const ADDRESS_QUEUE_TYPE_V1: u64 = 2;
140pub const INPUT_STATE_QUEUE_TYPE_V2: u64 = 3;
141pub const ADDRESS_QUEUE_TYPE_V2: u64 = 4;
142pub const OUTPUT_STATE_QUEUE_TYPE_V2: u64 = 5;
143
144#[cfg_attr(
145    all(feature = "std", feature = "anchor"),
146    derive(anchor_lang::AnchorDeserialize, anchor_lang::AnchorSerialize)
147)]
148#[cfg_attr(
149    not(feature = "anchor"),
150    derive(borsh::BorshDeserialize, borsh::BorshSerialize)
151)]
152#[derive(Debug, PartialEq, Clone, Copy)]
153#[repr(u64)]
154#[cfg_attr(
155    any(all(feature = "std", feature = "anchor"), not(feature = "anchor")),
156    borsh(use_discriminant = false)
157)]
158pub enum QueueType {
159    NullifierV1 = NULLIFIER_QUEUE_TYPE_V1,
160    AddressV1 = ADDRESS_QUEUE_TYPE_V1,
161    InputStateV2 = INPUT_STATE_QUEUE_TYPE_V2,
162    AddressV2 = ADDRESS_QUEUE_TYPE_V2,
163    OutputStateV2 = OUTPUT_STATE_QUEUE_TYPE_V2,
164}
165
166impl From<u64> for QueueType {
167    fn from(value: u64) -> Self {
168        match value {
169            1 => QueueType::NullifierV1,
170            2 => QueueType::AddressV1,
171            3 => QueueType::InputStateV2,
172            4 => QueueType::AddressV2,
173            5 => QueueType::OutputStateV2,
174            _ => panic!("Invalid queue type"),
175        }
176    }
177}
178
179pub const STATE_MERKLE_TREE_TYPE_V1: u64 = 1;
180pub const ADDRESS_MERKLE_TREE_TYPE_V1: u64 = 2;
181pub const STATE_MERKLE_TREE_TYPE_V2: u64 = 3;
182pub const ADDRESS_MERKLE_TREE_TYPE_V2: u64 = 4;
183
184#[cfg_attr(
185    all(feature = "std", feature = "anchor"),
186    derive(anchor_lang::AnchorDeserialize, anchor_lang::AnchorSerialize)
187)]
188#[cfg_attr(
189    not(feature = "anchor"),
190    derive(borsh::BorshDeserialize, borsh::BorshSerialize)
191)]
192#[derive(Debug, Ord, PartialEq, PartialOrd, Eq, Clone, Copy)]
193#[repr(u64)]
194#[cfg_attr(
195    any(all(feature = "std", feature = "anchor"), not(feature = "anchor")),
196    borsh(use_discriminant = false)
197)]
198pub enum TreeType {
199    StateV1 = STATE_MERKLE_TREE_TYPE_V1,
200    AddressV1 = ADDRESS_MERKLE_TREE_TYPE_V1,
201    StateV2 = STATE_MERKLE_TREE_TYPE_V2,
202    AddressV2 = ADDRESS_MERKLE_TREE_TYPE_V2,
203    Unknown = 255,
204}
205
206impl Display for TreeType {
207    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
208        match self {
209            TreeType::StateV1 => write!(f, "StateV1"),
210            TreeType::AddressV1 => write!(f, "AddressV1"),
211            TreeType::StateV2 => write!(f, "StateV2"),
212            TreeType::AddressV2 => write!(f, "AddressV2"),
213            TreeType::Unknown => write!(f, "Unknown"),
214        }
215    }
216}
217
218#[allow(clippy::derivable_impls)]
219impl core::default::Default for TreeType {
220    fn default() -> Self {
221        TreeType::StateV2
222    }
223}
224
225// from u64
226impl From<u64> for TreeType {
227    fn from(value: u64) -> Self {
228        match value {
229            1 => TreeType::StateV1,
230            2 => TreeType::AddressV1,
231            3 => TreeType::StateV2,
232            4 => TreeType::AddressV2,
233            255 => TreeType::Unknown,
234            _ => panic!("Invalid TreeType"),
235        }
236    }
237}
238
239/// Configuration struct containing program ID, CPI signer, and bump for Light Protocol
240#[derive(Debug, Clone, Copy, PartialEq, Eq)]
241pub struct CpiSigner {
242    pub program_id: [u8; 32],
243    pub cpi_signer: [u8; 32],
244    pub bump: u8,
245}