light_sdk_macros/
lib.rs

1extern crate proc_macro;
2use accounts::{process_light_accounts, process_light_system_accounts};
3use proc_macro::TokenStream;
4use syn::{parse_macro_input, DeriveInput, ItemMod, ItemStruct};
5use traits::process_light_traits;
6
7mod account;
8mod accounts;
9mod discriminator;
10mod hasher;
11mod program;
12mod traits;
13
14/// Adds required fields to your anchor instruction for applying a zk-compressed
15/// state transition.
16///
17/// ## Usage
18/// Add `#[light_system_accounts]` to your struct. Ensure it's applied before Anchor's
19/// `#[derive(Accounts)]` and Light's `#[derive(LightTraits)]`.
20///
21/// ## Example
22/// Note: You will have to build your program IDL using Anchor's `idl-build`
23/// feature, otherwise your IDL won't include these accounts.
24/// ```ignore
25/// #[light_system_accounts]
26/// #[derive(Accounts)]
27/// pub struct ExampleInstruction<'info> {
28///     pub my_program: Program<'info, MyProgram>,
29/// }
30/// ```
31/// This will expand to add the following fields to your struct:
32/// - `light_system_program`:           Verifies and applies zk-compression
33///                                     state transitions.
34/// - `registered_program_pda`:         A light protocol PDA to authenticate
35///                                     state tree updates.
36/// - `noop_program`:                   The SPL noop program to write
37///                                     compressed-account state as calldata to
38///                                     the Solana ledger.
39/// - `account_compression_authority`:  The authority for account compression
40///                                     operations.
41/// - `account_compression_program`:    Called by light_system_program. Updates
42///                                     state trees.
43/// - `system_program`:                 The Solana System program.
44#[proc_macro_attribute]
45pub fn light_system_accounts(_: TokenStream, input: TokenStream) -> TokenStream {
46    let input = parse_macro_input!(input as ItemStruct);
47
48    process_light_system_accounts(input)
49        .unwrap_or_else(|err| err.to_compile_error())
50        .into()
51}
52
53#[proc_macro_attribute]
54pub fn light_accounts(_: TokenStream, input: TokenStream) -> TokenStream {
55    let input = parse_macro_input!(input as ItemStruct);
56
57    match process_light_accounts(input) {
58        Ok(token_stream) => token_stream.into(),
59        Err(err) => TokenStream::from(err.to_compile_error()),
60    }
61}
62
63#[proc_macro_derive(LightAccounts, attributes(light_account))]
64pub fn light_accounts_derive(input: TokenStream) -> TokenStream {
65    let input = parse_macro_input!(input as ItemStruct);
66    accounts::process_light_accounts_derive(input)
67        .unwrap_or_else(|err| err.to_compile_error())
68        .into()
69}
70
71/// Implements traits on the given struct required for invoking The Light system
72/// program via CPI.
73///
74/// ## Usage
75/// Add `#[derive(LightTraits)]` to your struct which specifies the accounts
76/// required for your Anchor program instruction. Specify the attributes
77/// `self_program`, `fee_payer`, `authority`, and optionally `cpi_context` to
78/// the relevant fields.
79///
80/// ### Attributes
81/// - `self_program`:   Marks the field that represents the program invoking the
82///                     light system program, i.e. your program. You need to
83///                     list your program as part of the struct.
84/// - `fee_payer`:      Marks the field that represents the account responsible
85///                     for paying transaction fees. (Signer)
86///
87/// - `authority`:      TODO: explain authority.
88/// - `cpi_context`:    TODO: explain cpi_context.
89///
90/// ### Required accounts (must specify exact name).
91///
92/// - `light_system_program`:           Light systemprogram. verifies & applies
93///                                     compression state transitions.
94/// - `registered_program_pda`:         Light Systemprogram PDA
95/// - `noop_program`:                   SPL noop program
96/// - `account_compression_authority`:  TODO: explain.
97/// - `account_compression_program`:    Account Compression program.
98/// - `system_program`:                 The Solana Systemprogram.
99///
100/// ### Example
101/// ```ignore
102/// #[derive(Accounts, LightTraits)]
103/// pub struct ExampleInstruction<'info> {
104///     #[self_program]
105///     pub my_program: Program<'info, MyProgram>,
106///     #[fee_payer]
107///     pub payer: Signer<'info>,
108///     #[authority]
109///     pub user: AccountInfo<'info>,
110///     #[cpi_context]
111///     pub cpi_context_account: AccountInfo<'info>,
112///     pub light_system_program: AccountInfo<'info>,
113///     pub registered_program_pda: AccountInfo<'info>,
114///     pub noop_program: AccountInfo<'info>,
115///     pub account_compression_authority: AccountInfo<'info>,
116///     pub account_compression_program: AccountInfo<'info>,
117///     pub system_program: Program<'info, System>,
118/// }
119/// ```
120#[proc_macro_derive(
121    LightTraits,
122    attributes(self_program, fee_payer, authority, cpi_context)
123)]
124pub fn light_traits_derive(input: TokenStream) -> TokenStream {
125    let input = parse_macro_input!(input as DeriveInput);
126
127    match process_light_traits(input) {
128        Ok(token_stream) => token_stream.into(),
129        Err(err) => TokenStream::from(err.to_compile_error()),
130    }
131}
132
133#[proc_macro_derive(LightDiscriminator)]
134pub fn light_discriminator(input: TokenStream) -> TokenStream {
135    let input = parse_macro_input!(input as ItemStruct);
136    discriminator::discriminator(input)
137        .unwrap_or_else(|err| err.to_compile_error())
138        .into()
139}
140
141/// Makes the annotated struct hashable by implementing the following traits:
142///
143/// - [`AsByteVec`](light_hasher::bytes::AsByteVec), which makes the struct
144///   convertable to a 2D byte vector.
145/// - [`DataHasher`](light_hasher::DataHasher), which makes the struct hashable
146///   with the `hash()` method, based on the byte inputs from `AsByteVec`
147///   implementation.
148///
149/// This macro assumes that all the fields of the struct implement the
150/// `AsByteVec` trait. The trait is implemented by default for the most of
151/// standard Rust types (primitives, `String`, arrays and options carrying the
152/// former). If there is a field of a type not implementing the trait, there
153/// are two options:
154///
155/// 1. The most recommended one - annotating that type with the `light_hasher`
156///    macro as well.
157/// 2. Manually implementing the `AsByteVec` trait.
158///
159/// # Attributes
160///
161/// - `skip` - skips the given field, it doesn't get included neither in
162///   `AsByteVec` nor `DataHasher` implementation.
163/// - `truncate` - makes sure that the byte value does not exceed the BN254
164///   prime field modulus, by hashing it (with Keccak) and truncating it to 31
165///   bytes. It's generally a good idea to use it on any field which is
166///   expected to output more than 31 bytes.
167///
168/// # Examples
169///
170/// Compressed account with only primitive types as fields:
171///
172/// ```ignore
173/// #[derive(LightHasher)]
174/// pub struct MyCompressedAccount {
175///     a: i64,
176///     b: Option<u64>,
177/// }
178/// ```
179///
180/// Compressed account with fields which might exceed the BN254 prime field:
181///
182/// ```ignore
183/// #[derive(LightHasher)]
184/// pub struct MyCompressedAccount {
185///     a: i64
186///     b: Option<u64>,
187///     #[truncate]
188///     c: [u8; 32],
189///     #[truncate]
190///     d: String,
191/// }
192/// ```
193///
194/// Compressed account with fields we want to skip:
195///
196/// ```ignore
197/// #[derive(LightHasher)]
198/// pub struct MyCompressedAccount {
199///     a: i64
200///     b: Option<u64>,
201///     #[skip]
202///     c: [u8; 32],
203/// }
204/// ```
205///
206/// Compressed account with a nested struct:
207///
208/// ```ignore
209/// #[derive(LightHasher)]
210/// pub struct MyCompressedAccount {
211///     a: i64
212///     b: Option<u64>,
213///     c: MyStruct,
214/// }
215///
216/// #[derive(LightHasher)]
217/// pub struct MyStruct {
218///     a: i32
219///     b: u32,
220/// }
221/// ```
222///
223/// Compressed account with a type with a custom `AsByteVec` implementation:
224///
225/// ```ignore
226/// #[derive(LightHasher)]
227/// pub struct MyCompressedAccount {
228///     a: i64
229///     b: Option<u64>,
230///     c: RData,
231/// }
232///
233/// pub enum RData {
234///     A(Ipv4Addr),
235///     AAAA(Ipv6Addr),
236///     CName(String),
237/// }
238///
239/// impl AsByteVec for RData {
240///     fn as_byte_vec(&self) -> Vec<Vec<u8>> {
241///         match self {
242///             Self::A(ipv4_addr) => vec![ipv4_addr.octets().to_vec()],
243///             Self::AAAA(ipv6_addr) => vec![ipv6_addr.octets().to_vec()],
244///             Self::CName(cname) => cname.as_byte_vec(),
245///         }
246///     }
247/// }
248/// ```
249#[proc_macro_derive(LightHasher, attributes(skip, truncate))]
250pub fn light_hasher(input: TokenStream) -> TokenStream {
251    let input = parse_macro_input!(input as ItemStruct);
252    hasher::hasher(input)
253        .unwrap_or_else(|err| err.to_compile_error())
254        .into()
255}
256
257#[proc_macro_attribute]
258pub fn light_account(_: TokenStream, input: TokenStream) -> TokenStream {
259    let input = parse_macro_input!(input as ItemStruct);
260    account::account(input)
261        .unwrap_or_else(|err| err.to_compile_error())
262        .into()
263}
264
265#[proc_macro_attribute]
266pub fn light_program(_: TokenStream, input: TokenStream) -> TokenStream {
267    let input = parse_macro_input!(input as ItemMod);
268    program::program(input)
269        .unwrap_or_else(|err| err.to_compile_error())
270        .into()
271}