light_sdk_macros/
lib.rs

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