1use std::ops::{Deref, DerefMut};
2
3use anchor_lang::prelude::{
4 AccountInfo, AnchorDeserialize, AnchorSerialize, ProgramError, Pubkey, Result,
5};
6use light_hasher::{DataHasher, Discriminator, Hasher, Poseidon};
7use light_utils::hash_to_bn254_field_size_be;
8
9use crate::{
10 address::{derive_address, NewAddressParamsPacked},
11 merkle_context::{
12 pack_merkle_context, MerkleContext, PackedAddressMerkleContext, PackedMerkleContext,
13 RemainingAccounts,
14 },
15 program_merkle_context::unpack_address_merkle_context,
16};
17
18pub trait LightAccounts: Sized {
19 fn try_light_accounts(
20 inputs: Vec<Vec<u8>>,
21 merkle_context: PackedMerkleContext,
22 merkle_tree_root_index: u16,
23 address_merkle_context: PackedAddressMerkleContext,
24 address_merkle_tree_root_index: u16,
25 remaining_accounts: &[AccountInfo],
26 ) -> Result<Self>;
27 fn new_address_params(&self) -> Vec<NewAddressParamsPacked>;
28 fn input_accounts(
29 &self,
30 remaining_accounts: &[AccountInfo],
31 ) -> Result<Vec<PackedCompressedAccountWithMerkleContext>>;
32 fn output_accounts(
33 &self,
34 remaining_accounts: &[AccountInfo],
35 ) -> Result<Vec<OutputCompressedAccountWithPackedContext>>;
36}
37
38pub enum LightAccount<T>
40where
41 T: AnchorDeserialize + AnchorSerialize + Clone + DataHasher + Discriminator,
42{
43 Init(LightInitAccount<T>),
44 Mut(LightMutAccount<T>),
45 Close(LightCloseAccount<T>),
46}
47
48impl<T> LightAccount<T>
49where
50 T: AnchorDeserialize + AnchorSerialize + Clone + DataHasher + Default + Discriminator,
51{
52 pub fn new_init(
53 merkle_context: &PackedMerkleContext,
54 address_merkle_context: &PackedAddressMerkleContext,
55 address_merkle_tree_root_index: u16,
56 ) -> Self {
57 Self::Init(LightInitAccount::new(
58 merkle_context,
59 address_merkle_context,
60 address_merkle_tree_root_index,
61 ))
62 }
63
64 pub fn try_from_slice_mut(
65 v: &[u8],
66 merkle_context: &PackedMerkleContext,
67 merkle_tree_root_index: u16,
68 address_merkle_context: &PackedAddressMerkleContext,
69 ) -> Result<Self> {
70 Ok(Self::Mut(LightMutAccount::try_from_slice(
71 v,
72 merkle_context,
73 merkle_tree_root_index,
74 address_merkle_context,
75 )?))
76 }
77
78 pub fn try_from_slice_close(
79 v: &[u8],
80 merkle_context: &PackedMerkleContext,
81 merkle_tree_root_index: u16,
82 address_merkle_context: &PackedAddressMerkleContext,
83 ) -> Result<Self> {
84 Ok(Self::Close(LightCloseAccount::try_from_slice(
85 v,
86 merkle_context,
87 merkle_tree_root_index,
88 address_merkle_context,
89 )?))
90 }
91
92 pub fn set_address_seed(&mut self, address_seed: [u8; 32]) {
93 match self {
94 Self::Init(light_init_account) => light_init_account.set_address_seed(address_seed),
95 Self::Mut(light_mut_account) => light_mut_account.set_address_seed(address_seed),
96 Self::Close(light_close_account) => light_close_account.set_address_seed(address_seed),
97 }
98 }
99
100 pub fn new_address_params(&self) -> Option<NewAddressParamsPacked> {
101 match self {
102 Self::Init(self_init) => Some(self_init.new_address_params()),
103 Self::Mut(_) => None,
104 Self::Close(_) => None,
105 }
106 }
107
108 pub fn input_compressed_account(
109 &self,
110 program_id: &Pubkey,
111 remaining_accounts: &[AccountInfo],
112 ) -> Result<Option<PackedCompressedAccountWithMerkleContext>> {
113 match self {
114 Self::Init(_) => Ok(None),
115 Self::Mut(light_mut_account) => {
116 let account =
117 light_mut_account.input_compressed_account(program_id, remaining_accounts)?;
118 Ok(Some(account))
119 }
120 Self::Close(light_close_account) => {
121 let account =
122 light_close_account.input_compressed_account(program_id, remaining_accounts)?;
123 Ok(Some(account))
124 }
125 }
126 }
127
128 pub fn output_compressed_account(
129 &self,
130 program_id: &Pubkey,
131 remaining_accounts: &[AccountInfo],
132 ) -> Result<Option<OutputCompressedAccountWithPackedContext>> {
133 match self {
134 Self::Init(light_init_account) => {
135 let account =
136 light_init_account.output_compressed_account(program_id, remaining_accounts)?;
137 Ok(Some(account))
138 }
139 Self::Mut(light_mut_account) => {
140 let account =
141 light_mut_account.output_compressed_account(program_id, remaining_accounts)?;
142 Ok(Some(account))
143 }
144 Self::Close(_) => Ok(None),
145 }
146 }
147}
148
149impl<T> Deref for LightAccount<T>
150where
151 T: AnchorDeserialize + AnchorSerialize + Clone + DataHasher + Discriminator,
152{
153 type Target = T;
154
155 fn deref(&self) -> &Self::Target {
156 match self {
157 Self::Init(light_init_account) => &light_init_account.output_account,
158 Self::Mut(light_mut_account) => &light_mut_account.output_account,
159 Self::Close(light_close_account) => &light_close_account.input_account,
160 }
161 }
162}
163
164impl<T> DerefMut for LightAccount<T>
165where
166 T: AnchorDeserialize + AnchorSerialize + Clone + DataHasher + Discriminator,
167{
168 fn deref_mut(&mut self) -> &mut Self::Target {
169 match self {
170 Self::Init(light_init_account) => &mut light_init_account.output_account,
171 Self::Mut(light_mut_account) => &mut light_mut_account.output_account,
172 Self::Close(light_close_account) => &mut light_close_account.input_account,
173 }
174 }
175}
176
177pub struct LightInitAccount<T>
178where
179 T: AnchorDeserialize + AnchorSerialize + Clone + DataHasher + Discriminator,
180{
181 output_account: T,
182 address_seed: Option<[u8; 32]>,
183 merkle_context: PackedMerkleContext,
184 address_merkle_context: PackedAddressMerkleContext,
185 address_merkle_tree_root_index: u16,
186}
187
188impl<T> LightInitAccount<T>
189where
190 T: AnchorDeserialize + AnchorSerialize + Clone + Default + DataHasher + Discriminator,
191{
192 pub fn new(
193 merkle_context: &PackedMerkleContext,
194 address_merkle_context: &PackedAddressMerkleContext,
195 address_merkle_tree_root_index: u16,
196 ) -> Self {
197 let output_account = T::default();
198
199 Self {
200 output_account,
201 address_seed: None,
202 merkle_context: *merkle_context,
203 address_merkle_context: *address_merkle_context,
204 address_merkle_tree_root_index,
205 }
206 }
207
208 pub fn set_address_seed(&mut self, address_seed: [u8; 32]) {
209 self.address_seed = Some(address_seed);
210 }
211
212 pub fn new_address_params(&self) -> NewAddressParamsPacked {
213 NewAddressParamsPacked {
214 seed: self.address_seed.unwrap(),
215 address_merkle_tree_account_index: self
216 .address_merkle_context
217 .address_merkle_tree_pubkey_index,
218 address_queue_account_index: self.address_merkle_context.address_queue_pubkey_index,
219 address_merkle_tree_root_index: self.address_merkle_tree_root_index,
220 }
221 }
222
223 pub fn output_compressed_account(
224 &self,
225 program_id: &Pubkey,
226 remaining_accounts: &[AccountInfo],
227 ) -> Result<OutputCompressedAccountWithPackedContext> {
228 output_compressed_account(
229 &self.output_account,
230 &self.address_seed.unwrap(),
231 program_id,
232 &self.merkle_context,
233 &self.address_merkle_context,
234 remaining_accounts,
235 )
236 }
237}
238
239impl<T> Deref for LightInitAccount<T>
240where
241 T: AnchorDeserialize + AnchorSerialize + Clone + DataHasher + Discriminator,
242{
243 type Target = T;
244
245 fn deref(&self) -> &Self::Target {
246 &self.output_account
247 }
248}
249
250impl<T> DerefMut for LightInitAccount<T>
251where
252 T: AnchorDeserialize + AnchorSerialize + Clone + DataHasher + Discriminator,
253{
254 fn deref_mut(&mut self) -> &mut Self::Target {
255 &mut self.output_account
256 }
257}
258
259pub struct LightMutAccount<T>
260where
261 T: AnchorDeserialize + AnchorSerialize + Clone + DataHasher + Discriminator,
262{
263 input_account: T,
264 output_account: T,
265 address_seed: Option<[u8; 32]>,
266 merkle_context: PackedMerkleContext,
267 merkle_tree_root_index: u16,
268 address_merkle_context: PackedAddressMerkleContext,
269}
270
271impl<T> LightMutAccount<T>
272where
273 T: AnchorDeserialize + AnchorSerialize + Clone + DataHasher + Discriminator,
274{
275 pub fn try_from_slice(
276 v: &[u8],
277 merkle_context: &PackedMerkleContext,
278 merkle_tree_root_index: u16,
279 address_merkle_context: &PackedAddressMerkleContext,
280 ) -> Result<Self> {
281 let account = T::try_from_slice(v)?;
282
283 Ok(Self {
284 input_account: account.clone(),
285 output_account: account,
286 address_seed: None,
287 merkle_context: *merkle_context,
288 merkle_tree_root_index,
289 address_merkle_context: *address_merkle_context,
290 })
291 }
292
293 pub fn set_address_seed(&mut self, address_seed: [u8; 32]) {
294 self.address_seed = Some(address_seed);
295 }
296
297 pub fn input_compressed_account(
298 &self,
299 program_id: &Pubkey,
300 remaining_accounts: &[AccountInfo],
301 ) -> Result<PackedCompressedAccountWithMerkleContext> {
302 input_compressed_account(
303 &self.input_account,
304 &self.address_seed.unwrap(),
305 program_id,
306 &self.merkle_context,
307 self.merkle_tree_root_index,
308 &self.address_merkle_context,
309 remaining_accounts,
310 )
311 }
312
313 pub fn output_compressed_account(
314 &self,
315 program_id: &Pubkey,
316 remaining_accounts: &[AccountInfo],
317 ) -> Result<OutputCompressedAccountWithPackedContext> {
318 output_compressed_account(
319 &self.output_account,
320 &self.address_seed.unwrap(),
321 program_id,
322 &self.merkle_context,
323 &self.address_merkle_context,
324 remaining_accounts,
325 )
326 }
327}
328
329impl<T> Deref for LightMutAccount<T>
330where
331 T: AnchorDeserialize + AnchorSerialize + Clone + DataHasher + Discriminator,
332{
333 type Target = T;
334
335 fn deref(&self) -> &Self::Target {
336 &self.output_account
337 }
338}
339
340impl<T> DerefMut for LightMutAccount<T>
341where
342 T: AnchorDeserialize + AnchorSerialize + Clone + DataHasher + Discriminator,
343{
344 fn deref_mut(&mut self) -> &mut Self::Target {
345 &mut self.output_account
346 }
347}
348
349pub struct LightCloseAccount<T>
350where
351 T: AnchorDeserialize + AnchorSerialize + Clone + DataHasher + Discriminator,
352{
353 input_account: T,
354 address_seed: Option<[u8; 32]>,
355 merkle_context: PackedMerkleContext,
356 merkle_tree_root_index: u16,
357 address_merkle_context: PackedAddressMerkleContext,
358}
359
360impl<T> LightCloseAccount<T>
361where
362 T: AnchorDeserialize + AnchorSerialize + Clone + DataHasher + Discriminator,
363{
364 pub fn try_from_slice(
365 v: &[u8],
366 merkle_context: &PackedMerkleContext,
367 merkle_tree_root_index: u16,
368 address_merkle_context: &PackedAddressMerkleContext,
369 ) -> Result<Self> {
370 let input_account = T::try_from_slice(v)?;
371
372 Ok(Self {
373 input_account,
374 address_seed: None,
375 merkle_context: *merkle_context,
376 merkle_tree_root_index,
377 address_merkle_context: *address_merkle_context,
378 })
379 }
380
381 pub fn set_address_seed(&mut self, address_seed: [u8; 32]) {
382 self.address_seed = Some(address_seed);
383 }
384
385 pub fn input_compressed_account(
386 &self,
387 program_id: &Pubkey,
388 remaining_accounts: &[AccountInfo],
389 ) -> Result<PackedCompressedAccountWithMerkleContext> {
390 input_compressed_account(
391 &self.input_account,
392 &self.address_seed.unwrap(),
393 program_id,
394 &self.merkle_context,
395 self.merkle_tree_root_index,
396 &self.address_merkle_context,
397 remaining_accounts,
398 )
399 }
400}
401
402impl<T> Deref for LightCloseAccount<T>
403where
404 T: AnchorDeserialize + AnchorSerialize + Clone + DataHasher + Discriminator,
405{
406 type Target = T;
407
408 fn deref(&self) -> &Self::Target {
409 &self.input_account
410 }
411}
412
413impl<T> DerefMut for LightCloseAccount<T>
414where
415 T: AnchorDeserialize + AnchorSerialize + Clone + DataHasher + Discriminator,
416{
417 fn deref_mut(&mut self) -> &mut Self::Target {
418 &mut self.input_account
419 }
420}
421
422#[derive(Debug, PartialEq, Default, Clone, AnchorDeserialize, AnchorSerialize)]
423pub struct CompressedAccount {
424 pub owner: Pubkey,
425 pub lamports: u64,
426 pub address: Option<[u8; 32]>,
427 pub data: Option<CompressedAccountData>,
428}
429
430impl CompressedAccount {
433 pub fn hash_with_hashed_values<H: Hasher>(
434 &self,
435 &owner_hashed: &[u8; 32],
436 &merkle_tree_hashed: &[u8; 32],
437 leaf_index: &u32,
438 ) -> Result<[u8; 32]> {
439 let capacity = 3
440 + std::cmp::min(self.lamports, 1) as usize
441 + self.address.is_some() as usize
442 + self.data.is_some() as usize * 2;
443 let mut vec: Vec<&[u8]> = Vec::with_capacity(capacity);
444 vec.push(owner_hashed.as_slice());
445
446 let leaf_index = leaf_index.to_le_bytes();
448 vec.push(leaf_index.as_slice());
449
450 vec.push(merkle_tree_hashed.as_slice());
451
452 let mut lamports_bytes = [1, 0, 0, 0, 0, 0, 0, 0, 0];
456 if self.lamports != 0 {
457 lamports_bytes[1..].copy_from_slice(&self.lamports.to_le_bytes());
458 vec.push(lamports_bytes.as_slice());
459 }
460
461 if self.address.is_some() {
462 vec.push(self.address.as_ref().unwrap().as_slice());
463 }
464
465 let mut discriminator_bytes = [2, 0, 0, 0, 0, 0, 0, 0, 0];
466 if let Some(data) = &self.data {
467 discriminator_bytes[1..].copy_from_slice(&data.discriminator);
468 vec.push(&discriminator_bytes);
469 vec.push(&data.data_hash);
470 }
471 let hash = H::hashv(&vec).map_err(ProgramError::from)?;
472 Ok(hash)
473 }
474
475 pub fn hash<H: Hasher>(
476 &self,
477 &merkle_tree_pubkey: &Pubkey,
478 leaf_index: &u32,
479 ) -> Result<[u8; 32]> {
480 self.hash_with_hashed_values::<H>(
481 &hash_to_bn254_field_size_be(&self.owner.to_bytes())
482 .unwrap()
483 .0,
484 &hash_to_bn254_field_size_be(&merkle_tree_pubkey.to_bytes())
485 .unwrap()
486 .0,
487 leaf_index,
488 )
489 }
490}
491
492#[derive(Debug, PartialEq, Default, Clone, AnchorDeserialize, AnchorSerialize)]
493pub struct CompressedAccountData {
494 pub discriminator: [u8; 8],
495 pub data: Vec<u8>,
496 pub data_hash: [u8; 32],
497}
498
499#[derive(Debug, PartialEq, Default, Clone, AnchorDeserialize, AnchorSerialize)]
500pub struct CompressedAccountWithMerkleContext {
501 pub compressed_account: CompressedAccount,
502 pub merkle_context: MerkleContext,
503}
504
505#[derive(Debug, PartialEq, Default, Clone, AnchorDeserialize, AnchorSerialize)]
506pub struct PackedCompressedAccountWithMerkleContext {
507 pub compressed_account: CompressedAccount,
508 pub merkle_context: PackedMerkleContext,
509 pub root_index: u16,
511 pub read_only: bool,
513}
514
515impl CompressedAccountWithMerkleContext {
516 pub fn hash(&self) -> Result<[u8; 32]> {
517 self.compressed_account.hash::<Poseidon>(
518 &self.merkle_context.merkle_tree_pubkey,
519 &self.merkle_context.leaf_index,
520 )
521 }
522}
523
524#[derive(Debug, PartialEq, Default, Clone, AnchorDeserialize, AnchorSerialize)]
525pub struct OutputCompressedAccountWithPackedContext {
526 pub compressed_account: CompressedAccount,
527 pub merkle_tree_index: u8,
528}
529
530pub fn serialize_and_hash_account<T>(
531 account: &T,
532 address_seed: &[u8; 32],
533 program_id: &Pubkey,
534 address_merkle_context: &PackedAddressMerkleContext,
535 remaining_accounts: &[AccountInfo],
536) -> Result<CompressedAccount>
537where
538 T: AnchorSerialize + DataHasher + Discriminator,
539{
540 let data = account.try_to_vec()?;
541 let data_hash = account.hash::<Poseidon>().map_err(ProgramError::from)?;
542 let compressed_account_data = CompressedAccountData {
543 discriminator: T::discriminator(),
544 data,
545 data_hash,
546 };
547
548 let address_merkle_context =
549 unpack_address_merkle_context(*address_merkle_context, remaining_accounts);
550 let address = derive_address(address_seed, &address_merkle_context);
551
552 let compressed_account = CompressedAccount {
553 owner: *program_id,
554 lamports: 0,
555 address: Some(address),
556 data: Some(compressed_account_data),
557 };
558
559 Ok(compressed_account)
560}
561
562pub fn input_compressed_account<T>(
563 account: &T,
564 address_seed: &[u8; 32],
565 program_id: &Pubkey,
566 merkle_context: &PackedMerkleContext,
567 merkle_tree_root_index: u16,
568 address_merkle_context: &PackedAddressMerkleContext,
569 remaining_accounts: &[AccountInfo],
570) -> Result<PackedCompressedAccountWithMerkleContext>
571where
572 T: AnchorSerialize + DataHasher + Discriminator,
573{
574 let compressed_account = serialize_and_hash_account(
575 account,
576 address_seed,
577 program_id,
578 address_merkle_context,
579 remaining_accounts,
580 )?;
581
582 Ok(PackedCompressedAccountWithMerkleContext {
583 compressed_account,
584 merkle_context: *merkle_context,
585 root_index: merkle_tree_root_index,
586 read_only: false,
587 })
588}
589
590pub fn output_compressed_account<T>(
591 account: &T,
592 address_seed: &[u8; 32],
593 program_id: &Pubkey,
594 merkle_context: &PackedMerkleContext,
595 address_merkle_context: &PackedAddressMerkleContext,
596 remaining_accounts: &[AccountInfo],
597) -> Result<OutputCompressedAccountWithPackedContext>
598where
599 T: AnchorSerialize + DataHasher + Discriminator,
600{
601 let compressed_account = serialize_and_hash_account(
602 account,
603 address_seed,
604 program_id,
605 address_merkle_context,
606 remaining_accounts,
607 )?;
608
609 Ok(OutputCompressedAccountWithPackedContext {
610 compressed_account,
611 merkle_tree_index: merkle_context.merkle_tree_pubkey_index,
612 })
613}
614
615pub fn pack_compressed_accounts(
616 compressed_accounts: &[CompressedAccountWithMerkleContext],
617 root_indices: &[u16],
618 remaining_accounts: &mut RemainingAccounts,
619) -> Vec<PackedCompressedAccountWithMerkleContext> {
620 compressed_accounts
621 .iter()
622 .zip(root_indices.iter())
623 .map(|(x, root_index)| PackedCompressedAccountWithMerkleContext {
624 compressed_account: x.compressed_account.clone(),
625 merkle_context: pack_merkle_context(x.merkle_context, remaining_accounts),
626 root_index: *root_index,
627 read_only: false,
628 })
629 .collect::<Vec<_>>()
630}
631
632pub fn pack_compressed_account(
633 compressed_account: CompressedAccountWithMerkleContext,
634 root_index: u16,
635 remaining_accounts: &mut RemainingAccounts,
636) -> PackedCompressedAccountWithMerkleContext {
637 pack_compressed_accounts(&[compressed_account], &[root_index], remaining_accounts)[0].clone()
638}