1use std::ops::Neg;
2
3use anchor_lang::{
4 prelude::*,
5 solana_program::{
6 hash::{hash, hashv},
7 msg,
8 program_pack::Pack,
9 sysvar,
10 },
11};
12use anchor_spl::token::Transfer;
13use ark_bn254::FrParameters;
14use ark_ff::{
15 bytes::{FromBytes, ToBytes},
16 BigInteger, BigInteger256, FpParameters,
17};
18use ark_std::vec::Vec;
19use groth16_solana::groth16::{Groth16Verifier, Groth16Verifyingkey};
20use light_merkle_tree::HashFunction;
21use light_merkle_tree_program::{
22 program::LightMerkleTreeProgram,
23 state::TransactionMerkleTree,
24 utils::{
25 accounts::create_and_check_pda,
26 constants::{POOL_CONFIG_SEED, POOL_SEED, TRANSACTION_MERKLE_TREE_SEED},
27 },
28};
29
30use crate::{
31 accounts::LightAccounts,
32 cpi_instructions::{
33 insert_nullifiers_cpi, insert_two_leaves_cpi, insert_two_leaves_event_cpi,
34 invoke_indexer_transaction_event, unshield_sol_cpi, unshield_spl_cpi,
35 },
36 errors::VerifierSdkError,
37 state::TransactionIndexerEvent,
38 utils::{change_endianness, close_account::close_account, truncate_to_circuit},
39};
40pub const VERIFIER_STATE_SEED: &[u8] = b"VERIFIER_STATE";
41type G1 = ark_ec::short_weierstrass_jacobian::GroupAffine<ark_bn254::g1::Parameters>;
42
43pub trait Config {
44 const ID: Pubkey;
46}
47
48#[derive(Clone)]
49pub struct Transaction<
50 'a,
51 'b,
52 'c,
53 'info,
54 const NR_CHECKED_INPUTS: usize,
55 const NR_LEAVES: usize,
56 const NR_NULLIFIERS: usize,
57 const NR_PUBLIC_INPUTS: usize,
58 A: LightAccounts<'info>,
59> {
60 pub input: TransactionInput<'a, 'b, 'c, 'info, NR_CHECKED_INPUTS, NR_LEAVES, NR_NULLIFIERS, A>,
62 pub merkle_root: [u8; 32],
64 pub tx_integrity_hash: [u8; 32],
65 pub mint_pubkey: [u8; 32],
66 pub transferred_funds: bool,
67 pub computed_tx_integrity_hash: bool,
68 pub verified_proof: bool,
69 pub inserted_leaves: bool,
70 pub inserted_nullifier: bool,
71 pub fetched_root: bool,
72 pub fetched_mint: bool,
73}
74
75pub struct Message<'a> {
76 pub content: &'a Vec<u8>,
77 pub hash: [u8; 32],
78}
79
80impl<'a> Message<'a> {
81 pub fn new(content: &'a Vec<u8>) -> Self {
82 let hash = hash(content).to_bytes();
83 Message { hash, content }
84 }
85}
86
87pub struct Proof {
88 pub a: [u8; 64],
89 pub b: [u8; 128],
90 pub c: [u8; 64],
91}
92
93pub struct Amounts {
94 pub spl: [u8; 32],
95 pub sol: [u8; 32],
96}
97
98#[derive(Clone)]
99pub struct TransactionInput<
100 'a,
101 'b,
102 'c,
103 'info,
104 const NR_CHECKED_INPUTS: usize,
105 const NR_LEAVES: usize,
106 const NR_NULLIFIERS: usize,
107 A: LightAccounts<'info>,
108> {
109 pub ctx: &'a Context<'a, 'b, 'c, 'info, A>,
110 pub proof: &'a Proof,
111 pub public_amount: &'a Amounts,
112 pub message: Option<&'a Message<'a>>,
113 pub checked_public_inputs: &'a [[u8; 32]; NR_CHECKED_INPUTS],
114 pub nullifiers: &'a [[u8; 32]; NR_NULLIFIERS],
115 pub leaves: &'a [[[u8; 32]; 2]; NR_LEAVES],
116 pub encrypted_utxos: &'a Vec<u8>,
117 pub relayer_fee: u64,
118 pub merkle_root_index: usize,
119 pub pool_type: &'a [u8; 32],
120 pub verifyingkey: &'a Groth16Verifyingkey<'a>,
121}
122
123impl<
124 'a,
125 'b,
126 'c,
127 'info,
128 const NR_CHECKED_INPUTS: usize,
129 const NR_LEAVES: usize,
130 const NR_NULLIFIERS: usize,
131 const NR_PUBLIC_INPUTS: usize,
132 A: LightAccounts<'info>,
133 >
134 Transaction<'a, 'b, 'c, 'info, NR_CHECKED_INPUTS, NR_LEAVES, NR_NULLIFIERS, NR_PUBLIC_INPUTS, A>
135{
136 pub fn new(
137 input: TransactionInput<'a, 'b, 'c, 'info, NR_CHECKED_INPUTS, NR_LEAVES, NR_NULLIFIERS, A>,
138 ) -> Transaction<
139 'a,
140 'b,
141 'c,
142 'info,
143 NR_CHECKED_INPUTS,
144 NR_LEAVES,
145 NR_NULLIFIERS,
146 NR_PUBLIC_INPUTS,
147 A,
148 > {
149 Transaction {
150 input,
151 merkle_root: [0u8; 32],
152 tx_integrity_hash: [0u8; 32],
153 mint_pubkey: [0u8; 32],
154 transferred_funds: false,
155 computed_tx_integrity_hash: false,
156 verified_proof: false,
157 inserted_leaves: false,
158 inserted_nullifier: false,
159 fetched_root: false,
160 fetched_mint: false,
161 }
162 }
163
164 pub fn transact(&mut self) -> Result<()> {
167 self.emit_indexer_transaction_event()?;
168 self.compute_tx_integrity_hash()?;
169 self.fetch_root()?;
170 self.fetch_mint()?;
171 self.verify()?;
172 self.check_remaining_accounts()?;
173 self.insert_leaves()?;
174 self.insert_nullifiers()?;
175 self.transfer_user_funds()?;
176 self.transfer_fee()?;
177 self.check_completion()
178 }
179
180 pub fn emit_indexer_transaction_event(&mut self) -> Result<()> {
181 let mut leaves_vec: Vec<[u8; 32]> = Vec::new();
183
184 let merkle_tree = self.input.ctx.accounts.get_transaction_merkle_tree();
185 let merkle_tree = merkle_tree.load_mut()?;
186
187 let first_leaf_index = merkle_tree.next_queued_index;
188
189 for (_i, leaves) in self.input.leaves.iter().enumerate() {
190 let leaf_left = change_endianness(&leaves[0]);
191 let leaf_right = change_endianness(&leaves[1]);
192 leaves_vec.push(leaf_left);
193 leaves_vec.push(leaf_right);
194 }
195
196 let message = match &self.input.message {
197 Some(message) => message.content.clone(),
198 None => Vec::<u8>::new(),
199 };
200 let transaction_data_event = TransactionIndexerEvent {
201 leaves: leaves_vec.clone(),
202 public_amount_sol: self.input.public_amount.sol,
203 public_amount_spl: self.input.public_amount.spl,
204 relayer_fee: self.input.relayer_fee,
205 encrypted_utxos: self.input.encrypted_utxos.clone(),
206 nullifiers: self.input.nullifiers.to_vec(),
207 first_leaf_index,
208 message,
209 };
210
211 invoke_indexer_transaction_event(
212 &transaction_data_event,
213 &self.input.ctx.accounts.get_log_wrapper().to_account_info(),
214 &self
215 .input
216 .ctx
217 .accounts
218 .get_transaction_merkle_tree()
219 .to_account_info(),
220 )?;
221
222 let event_hash = self.compute_event_hash(first_leaf_index);
223 self.insert_event_leaves(event_hash)?;
224
225 Ok(())
226 }
227
228 fn compute_event_hash(&mut self, first_leaf_index: u64) -> [u8; 32] {
229 let nullifiers_hash = hashv(
230 self.input
231 .nullifiers
232 .iter()
233 .map(|arr| arr.as_slice())
234 .collect::<Vec<_>>()
235 .as_slice(),
236 );
237
238 let leaves_hash = hashv(
239 self.input
240 .leaves
241 .iter()
242 .flat_map(|two_d| two_d.iter())
243 .map(|one_d| &one_d[..])
244 .collect::<Vec<_>>()
245 .as_slice(),
246 );
247
248 let message_hash = match self.input.message {
249 Some(message) => message.hash,
250 None => [0u8; 32],
251 };
252 let encrypted_utxos_hash = hash(self.input.encrypted_utxos.as_slice());
253
254 let event_hash = hashv(&[
255 leaves_hash.to_bytes().as_slice(),
256 self.input.public_amount.spl.as_slice(),
257 self.input.public_amount.sol.as_slice(),
258 self.input.relayer_fee.to_le_bytes().as_slice(),
259 encrypted_utxos_hash.to_bytes().as_slice(),
260 nullifiers_hash.to_bytes().as_slice(),
261 first_leaf_index.to_le_bytes().as_slice(),
262 message_hash.as_slice(),
263 ]);
264
265 event_hash.to_bytes()
266 }
267
268 fn insert_event_leaves(&mut self, event_hash: [u8; 32]) -> Result<()> {
270 let event_merkle_tree = self.input.ctx.accounts.get_event_merkle_tree();
271 if event_merkle_tree.load()?.merkle_tree.hash_function != HashFunction::Sha256 {
272 return err!(VerifierSdkError::EventMerkleTreeInvalidHashFunction);
273 }
274 insert_two_leaves_event_cpi(
275 self.input.ctx.program_id,
276 &self
277 .input
278 .ctx
279 .accounts
280 .get_program_merkle_tree()
281 .to_account_info(),
282 &self.input.ctx.accounts.get_authority().to_account_info(),
283 &event_merkle_tree.to_account_info(),
284 &self
285 .input
286 .ctx
287 .accounts
288 .get_system_program()
289 .to_account_info(),
290 &self
291 .input
292 .ctx
293 .accounts
294 .get_registered_verifier_pda()
295 .to_account_info(),
296 &event_hash,
297 &[0; 32],
298 )?;
299
300 Ok(())
301 }
302
303 pub fn verify(&mut self) -> Result<()> {
305 if !self.computed_tx_integrity_hash {
306 msg!("Tried to verify proof without computing integrity hash.");
307 }
308
309 if !self.fetched_mint {
310 msg!("Tried to verify proof without fetching mind.");
311 }
312
313 if !self.fetched_root {
314 msg!("Tried to verify proof without fetching root.");
315 }
316
317 assert_eq!(
318 NR_PUBLIC_INPUTS,
319 5 + NR_NULLIFIERS + NR_LEAVES * 2 + NR_CHECKED_INPUTS,
320 );
321
322 let mut public_inputs: [[u8; 32]; NR_PUBLIC_INPUTS] = [[0u8; 32]; NR_PUBLIC_INPUTS];
323
324 public_inputs[0] = self.merkle_root;
325 public_inputs[1] = self.input.public_amount.spl;
326 public_inputs[2] = self.tx_integrity_hash;
327 public_inputs[3] = self.input.public_amount.sol;
328 public_inputs[4] = self.mint_pubkey;
329
330 for (i, input) in self.input.nullifiers.iter().enumerate() {
331 public_inputs[5 + i] = *input;
332 }
333
334 for (i, input) in self.input.leaves.iter().enumerate() {
335 public_inputs[5 + NR_NULLIFIERS + i * 2] = input[0];
336 public_inputs[5 + NR_NULLIFIERS + i * 2 + 1] = input[1];
337 }
338
339 for (i, input) in self.input.checked_public_inputs.iter().enumerate() {
340 public_inputs[5 + NR_NULLIFIERS + NR_LEAVES * 2 + i] = *input;
341 }
342
343 let proof_a_neg_g1: G1 = <G1 as FromBytes>::read(
344 &*[&change_endianness(&self.input.proof.a)[..], &[0u8][..]].concat(),
345 )
346 .unwrap();
347 let mut proof_a_neg_buf = [0u8; 65];
348 <G1 as ToBytes>::write(&proof_a_neg_g1.neg(), &mut proof_a_neg_buf[..]).unwrap();
349 let mut proof_a_neg = [0u8; 64];
350 proof_a_neg.copy_from_slice(&proof_a_neg_buf[..64]);
351
352 let proof_a_neg = change_endianness(&proof_a_neg);
353
354 let mut verifier = Groth16Verifier::new(
355 &proof_a_neg,
356 &self.input.proof.b,
357 &self.input.proof.c,
358 &public_inputs,
359 self.input.verifyingkey,
360 )
361 .unwrap();
362 match verifier.verify() {
365 Ok(_) => {
366 self.verified_proof = true;
367 Ok(())
368 }
369 Err(e) => {
370 msg!("Public Inputs:");
371 msg!("merkle tree root {:?}", self.merkle_root);
372 msg!("public_amount_spl {:?}", self.input.public_amount.spl);
373 msg!("tx_integrity_hash {:?}", self.tx_integrity_hash);
374 msg!("public_amount_sol {:?}", self.input.public_amount.sol);
375 msg!("mint_pubkey {:?}", self.mint_pubkey);
376 msg!("nullifiers {:?}", self.input.nullifiers);
377 msg!("leaves {:?}", self.input.leaves);
378 msg!(
379 "checked_public_inputs {:?}",
380 self.input.checked_public_inputs
381 );
382 msg!("error {:?}", e);
383 err!(VerifierSdkError::ProofVerificationFailed)
384 }
385 }
386 }
387
388 pub fn compute_tx_integrity_hash(&mut self) -> Result<()> {
392 let message_hash = match self.input.message {
393 Some(message) => message.hash,
394 None => [0u8; 32],
395 };
396 let recipient_spl = match self.input.ctx.accounts.get_recipient_spl().as_ref() {
397 Some(recipient_spl) => recipient_spl.key().to_bytes(),
398 None => [0u8; 32],
399 };
400 let tx_integrity_hash = hashv(&[
401 &message_hash,
402 &recipient_spl,
403 &self
404 .input
405 .ctx
406 .accounts
407 .get_recipient_sol()
408 .as_ref()
409 .unwrap()
410 .key()
411 .to_bytes(),
412 &self
413 .input
414 .ctx
415 .accounts
416 .get_signing_address()
417 .key()
418 .to_bytes(),
419 &self.input.relayer_fee.to_le_bytes(),
420 self.input.encrypted_utxos,
421 ]);
422 self.tx_integrity_hash = truncate_to_circuit(&tx_integrity_hash.to_bytes());
450 self.computed_tx_integrity_hash = true;
451 Ok(())
452 }
453
454 pub fn fetch_root(&mut self) -> Result<()> {
456 let merkle_tree = self.input.ctx.accounts.get_transaction_merkle_tree();
457 let merkle_tree = merkle_tree.load()?;
458 self.merkle_root = change_endianness(&merkle_tree.roots[self.input.merkle_root_index]);
459 self.fetched_root = true;
460 Ok(())
461 }
462
463 pub fn fetch_mint(&mut self) -> Result<()> {
466 match &self.input.ctx.accounts.get_sender_spl() {
467 Some(sender_spl) => {
468 match spl_token::state::Account::unpack(sender_spl.data.borrow().as_ref()) {
469 Ok(sender_spl) => {
470 if self.input.public_amount.spl[24..32] == vec![0u8; 8] {
476 self.mint_pubkey = [0u8; 32];
477 } else {
478 self.mint_pubkey = [
479 vec![0u8],
480 hash(&sender_spl.mint.to_bytes()).try_to_vec()?[1..].to_vec(),
481 ]
482 .concat()
483 .try_into()
484 .unwrap();
485 }
486
487 self.fetched_mint = true;
488 Ok(())
489 }
490 Err(_) => {
491 self.mint_pubkey = [0u8; 32];
492 self.fetched_mint = true;
493 Ok(())
494 }
495 }
496 }
497 None => {
498 self.mint_pubkey = [0u8; 32];
499 self.fetched_mint = true;
500 Ok(())
501 }
502 }
503 }
504
505 fn check_remaining_accounts(&self) -> Result<()> {
510 let nr_nullifiers_leaves = NR_NULLIFIERS + NR_LEAVES;
511 let remaining_accounts_len = self.input.ctx.remaining_accounts.len();
512 if remaining_accounts_len != nr_nullifiers_leaves && remaining_accounts_len != nr_nullifiers_leaves + 2
515 {
516 msg!(
517 "remaining_accounts.len() {} (expected {} or {})",
518 remaining_accounts_len,
519 nr_nullifiers_leaves,
520 nr_nullifiers_leaves + 1
521 );
522 return err!(VerifierSdkError::InvalidNrRemainingAccounts);
523 }
524
525 Ok(())
526 }
527
528 pub fn insert_leaves(&mut self) -> Result<()> {
530 if !self.verified_proof {
531 msg!("Tried to insert leaves without verifying the proof.");
532 return err!(VerifierSdkError::ProofNotVerified);
533 }
534
535 let transaction_merkle_tree = self.get_transaction_merkle_tree()?;
536
537 for (i, leaves) in self.input.leaves.iter().enumerate() {
539 let mut msg = Vec::new();
540
541 if self.input.encrypted_utxos.len() > i * 256 {
542 msg.append(&mut self.input.encrypted_utxos[i * 256..(i + 1) * 256].to_vec());
543 }
544
545 insert_two_leaves_cpi(
547 self.input.ctx.program_id,
548 &self
549 .input
550 .ctx
551 .accounts
552 .get_program_merkle_tree()
553 .to_account_info(),
554 &self.input.ctx.accounts.get_authority().to_account_info(),
555 &self.input.ctx.remaining_accounts[NR_NULLIFIERS + i].to_account_info(),
556 &transaction_merkle_tree,
557 &self
558 .input
559 .ctx
560 .accounts
561 .get_system_program()
562 .to_account_info(),
563 &self
564 .input
565 .ctx
566 .accounts
567 .get_registered_verifier_pda()
568 .to_account_info(),
569 change_endianness(&leaves[0]),
570 change_endianness(&leaves[1]),
571 msg,
572 )?;
573 }
574
575 self.inserted_leaves = true;
576 Ok(())
577 }
578
579 fn get_transaction_merkle_tree(&self) -> Result<AccountInfo<'info>> {
593 let index = NR_NULLIFIERS + self.input.leaves.len();
595
596 match self.input.ctx.remaining_accounts.get(index) {
597 Some(transaction_merkle_tree) => {
598 let transaction_merkle_tree = transaction_merkle_tree.to_account_info();
599 self.validate_transaction_merkle_tree(&transaction_merkle_tree)?;
600 Ok(transaction_merkle_tree)
601 }
602 None => Ok(self
603 .input
604 .ctx
605 .accounts
606 .get_transaction_merkle_tree()
607 .to_account_info()),
608 }
609 }
610
611 fn validate_transaction_merkle_tree(
612 &self,
613 transaction_merkle_tree: &AccountInfo,
614 ) -> Result<()> {
615 let transaction_merkle_tree: AccountLoader<TransactionMerkleTree> =
616 AccountLoader::try_from(transaction_merkle_tree)?;
617 let index = transaction_merkle_tree.load()?.merkle_tree_nr;
618 let (pubkey, _) = Pubkey::find_program_address(
619 &[TRANSACTION_MERKLE_TREE_SEED, index.to_le_bytes().as_ref()],
620 &LightMerkleTreeProgram::id(),
621 );
622 if transaction_merkle_tree.key() != pubkey {
623 msg!(
624 "Transaction Merkle tree address is invalid, expected: {}, got: {}",
625 pubkey,
626 transaction_merkle_tree.key()
627 );
628 return err!(VerifierSdkError::InvalidTransactionMerkleTreeAddress);
629 }
630 Ok(())
631 }
632
633 pub fn insert_nullifiers(&mut self) -> Result<()> {
635 if !self.verified_proof {
636 msg!("Tried to insert nullifiers without verifying the proof.");
637 return err!(VerifierSdkError::ProofNotVerified);
638 }
639
640 insert_nullifiers_cpi(
641 self.input.ctx.program_id,
642 &self
643 .input
644 .ctx
645 .accounts
646 .get_program_merkle_tree()
647 .to_account_info(),
648 &self.input.ctx.accounts.get_authority().to_account_info(),
649 &self
650 .input
651 .ctx
652 .accounts
653 .get_system_program()
654 .to_account_info()
655 .clone(),
656 &self
657 .input
658 .ctx
659 .accounts
660 .get_registered_verifier_pda()
661 .to_account_info(),
662 self.input.nullifiers.to_vec(),
663 self.input.ctx.remaining_accounts.to_vec(),
664 )?;
665
666 self.inserted_nullifier = true;
667 Ok(())
668 }
669
670 pub fn transfer_user_funds(&mut self) -> Result<()> {
672 if !self.verified_proof {
673 msg!("Tried to transfer funds without verifying the proof.");
674 return err!(VerifierSdkError::ProofNotVerified);
675 }
676
677 msg!("transferring user funds");
678 let (pub_amount_checked, _) =
680 self.check_amount(0, change_endianness(&self.input.public_amount.spl))?;
681
682 if pub_amount_checked > 0 {
684 let recipient_spl = spl_token::state::Account::unpack(
685 &self
686 .input
687 .ctx
688 .accounts
689 .get_recipient_spl()
690 .as_ref()
691 .unwrap()
692 .data
693 .borrow(),
694 )?;
695 let sender_spl = spl_token::state::Account::unpack(
696 &self
697 .input
698 .ctx
699 .accounts
700 .get_sender_spl()
701 .as_ref()
702 .unwrap()
703 .data
704 .borrow(),
705 )?;
706
707 if self.mint_pubkey[1..] != hash(&recipient_spl.mint.to_bytes()).try_to_vec()?[1..] {
709 msg!(
710 "*self.mint_pubkey[..31] {:?}, {:?}, recipient_spl mint",
711 self.mint_pubkey[1..].to_vec(),
712 hash(&recipient_spl.mint.to_bytes()).try_to_vec()?[1..].to_vec()
713 );
714 return err!(VerifierSdkError::InconsistentMintProofSenderOrRecipient);
715 }
716
717 if self.is_shield_spl() {
719 self.shield_spl(pub_amount_checked, sender_spl, recipient_spl)?;
720 } else {
721 self.check_spl_pool_account_derivation(
722 &self
723 .input
724 .ctx
725 .accounts
726 .get_sender_spl()
727 .as_ref()
728 .unwrap()
729 .key(),
730 &sender_spl.mint,
731 )?;
732
733 unshield_spl_cpi(
735 self.input.ctx.program_id,
736 &self
737 .input
738 .ctx
739 .accounts
740 .get_program_merkle_tree()
741 .to_account_info(),
742 &self.input.ctx.accounts.get_authority().to_account_info(),
743 &self
744 .input
745 .ctx
746 .accounts
747 .get_sender_spl()
748 .as_ref()
749 .unwrap()
750 .to_account_info(),
751 &self
752 .input
753 .ctx
754 .accounts
755 .get_recipient_spl()
756 .as_ref()
757 .unwrap()
758 .to_account_info(),
759 &self
760 .input
761 .ctx
762 .accounts
763 .get_token_authority()
764 .as_ref()
765 .unwrap()
766 .to_account_info(),
767 &self
768 .input
769 .ctx
770 .accounts
771 .get_token_program()
772 .as_ref()
773 .unwrap()
774 .to_account_info(),
775 &self
776 .input
777 .ctx
778 .accounts
779 .get_registered_verifier_pda()
780 .to_account_info(),
781 pub_amount_checked,
782 )?;
783 }
784 msg!("transferred");
785 }
786
787 self.transferred_funds = true;
788 Ok(())
789 }
790
791 pub fn transfer_fee(&self) -> Result<()> {
793 if !self.verified_proof {
794 msg!("Tried to transfer fees without verifying the proof.");
795 return err!(VerifierSdkError::ProofNotVerified);
796 }
797
798 let (fee_amount_checked, relayer_fee) = self.check_amount(
800 self.input.relayer_fee,
801 change_endianness(&self.input.public_amount.sol),
802 )?;
803 msg!("fee amount {} ", fee_amount_checked);
804 if fee_amount_checked > 0 {
805 if self.is_shield_sol() {
806 msg!("is shield");
807 self.shield_sol(
808 fee_amount_checked,
809 &self
810 .input
811 .ctx
812 .accounts
813 .get_recipient_sol()
814 .as_ref()
815 .unwrap()
816 .to_account_info(),
817 )?;
818 } else {
819 msg!("is unshield");
820
821 self.check_sol_pool_account_derivation(
822 &self
823 .input
824 .ctx
825 .accounts
826 .get_sender_sol()
827 .as_ref()
828 .unwrap()
829 .key(),
830 *self
831 .input
832 .ctx
833 .accounts
834 .get_sender_sol()
835 .as_ref()
836 .unwrap()
837 .to_account_info()
838 .data
839 .try_borrow()
840 .unwrap(),
841 )?;
842 msg!("unshield sol cpi");
844 unshield_sol_cpi(
845 self.input.ctx.program_id,
846 &self
847 .input
848 .ctx
849 .accounts
850 .get_program_merkle_tree()
851 .to_account_info(),
852 &self.input.ctx.accounts.get_authority().to_account_info(),
853 &self
854 .input
855 .ctx
856 .accounts
857 .get_sender_sol()
858 .as_ref()
859 .unwrap()
860 .to_account_info(),
861 &self
862 .input
863 .ctx
864 .accounts
865 .get_recipient_sol()
866 .as_ref()
867 .unwrap()
868 .to_account_info(),
869 &self
870 .input
871 .ctx
872 .accounts
873 .get_registered_verifier_pda()
874 .to_account_info(),
875 fee_amount_checked,
876 )?;
877 msg!("unshielded sol for the user");
878 }
879 }
880 if !self.is_shield_sol() && relayer_fee > 0 {
881 unshield_sol_cpi(
883 self.input.ctx.program_id,
884 &self
885 .input
886 .ctx
887 .accounts
888 .get_program_merkle_tree()
889 .to_account_info(),
890 &self.input.ctx.accounts.get_authority().to_account_info(),
891 &self
892 .input
893 .ctx
894 .accounts
895 .get_sender_sol()
896 .as_ref()
897 .unwrap()
898 .to_account_info(),
899 &self
900 .input
901 .ctx
902 .accounts
903 .get_relayer_recipient_sol()
904 .as_ref()
905 .to_account_info(),
906 &self
907 .input
908 .ctx
909 .accounts
910 .get_registered_verifier_pda()
911 .to_account_info(),
912 relayer_fee,
913 )?;
914 }
915
916 Ok(())
917 }
918
919 fn shield_sol(&self, amount_checked: u64, recipient_sol: &AccountInfo) -> Result<()> {
921 self.check_sol_pool_account_derivation(
922 &recipient_sol.key(),
923 *recipient_sol.data.try_borrow().unwrap(),
924 )?;
925
926 let signing_address = self
927 .input
928 .ctx
929 .accounts
930 .get_signing_address()
931 .to_account_info();
932 let sender_sol = self
933 .input
934 .ctx
935 .accounts
936 .get_sender_sol()
937 .unwrap()
938 .to_account_info();
939
940 msg!("is shield");
941 let rent = <Rent as sysvar::Sysvar>::get()?;
942
943 create_and_check_pda(
944 self.input.ctx.program_id,
945 &signing_address,
946 &sender_sol,
947 &self
948 .input
949 .ctx
950 .accounts
951 .get_system_program()
952 .to_account_info(),
953 &rent,
954 &b"escrow"[..],
955 &Vec::new(),
956 0, amount_checked, false, )?;
960 close_account(
961 &self
962 .input
963 .ctx
964 .accounts
965 .get_sender_sol()
966 .as_ref()
967 .unwrap()
968 .to_account_info(),
969 recipient_sol,
970 )
971 }
972
973 fn shield_spl(
974 &self,
975 pub_amount_checked: u64,
976 sender_spl: spl_token::state::Account,
977 recipient_spl: spl_token::state::Account,
978 ) -> Result<()> {
979 self.check_spl_pool_account_derivation(
980 &self
981 .input
982 .ctx
983 .accounts
984 .get_recipient_spl()
985 .as_ref()
986 .unwrap()
987 .key(),
988 &recipient_spl.mint,
989 )?;
990
991 let signing_address = self
992 .input
993 .ctx
994 .accounts
995 .get_signing_address()
996 .to_account_info();
997
998 if sender_spl.owner.as_ref() != signing_address.key().as_ref() {
999 msg!(
1000 "sender_spl owned by: {}, expected signer: {}",
1001 sender_spl.owner,
1002 signing_address.key()
1003 );
1004 return err!(VerifierSdkError::InvalidSenderOrRecipient);
1005 }
1006
1007 let seed = light_merkle_tree_program::ID.to_bytes();
1008 let (_, bump) = anchor_lang::prelude::Pubkey::find_program_address(
1009 &[seed.as_ref()],
1010 self.input.ctx.program_id,
1011 );
1012 let bump = &[bump];
1013 let seeds = &[&[seed.as_slice(), bump][..]];
1014
1015 let accounts = Transfer {
1016 from: self
1017 .input
1018 .ctx
1019 .accounts
1020 .get_sender_spl()
1021 .as_ref()
1022 .unwrap()
1023 .to_account_info()
1024 .clone(),
1025 to: self
1026 .input
1027 .ctx
1028 .accounts
1029 .get_recipient_spl()
1030 .as_ref()
1031 .unwrap()
1032 .to_account_info()
1033 .clone(),
1034 authority: self
1035 .input
1036 .ctx
1037 .accounts
1038 .get_authority()
1039 .to_account_info()
1040 .clone(),
1041 };
1042
1043 let cpi_ctx = CpiContext::new_with_signer(
1044 self.input
1045 .ctx
1046 .accounts
1047 .get_token_program()
1048 .unwrap()
1049 .to_account_info()
1050 .clone(),
1051 accounts,
1052 seeds,
1053 );
1054 anchor_spl::token::transfer(cpi_ctx, pub_amount_checked)?;
1055
1056 Ok(())
1057 }
1058
1059 pub fn is_shield_spl(&self) -> bool {
1061 if self.input.public_amount.spl[24..] != [0u8; 8]
1062 && self.input.public_amount.spl[..24] == [0u8; 24]
1063 {
1064 return true;
1065 }
1066 false
1067 }
1068
1069 pub fn is_shield_sol(&self) -> bool {
1071 if self.input.public_amount.sol[24..] != [0u8; 8]
1072 && self.input.public_amount.sol[..24] == [0u8; 24]
1073 {
1074 return true;
1075 }
1076 false
1077 }
1078
1079 pub fn check_sol_pool_account_derivation(&self, pubkey: &Pubkey, data: &[u8]) -> Result<()> {
1080 let derived_pubkey = Pubkey::find_program_address(
1081 &[&[0u8; 32], self.input.pool_type, POOL_CONFIG_SEED],
1082 &LightMerkleTreeProgram::id(),
1083 );
1084 let mut cloned_data = data;
1085 light_merkle_tree_program::RegisteredAssetPool::try_deserialize(&mut cloned_data)?;
1086
1087 if derived_pubkey.0 != *pubkey {
1088 return err!(VerifierSdkError::InvalidSenderOrRecipient);
1089 }
1090 Ok(())
1091 }
1092
1093 pub fn check_spl_pool_account_derivation(&self, pubkey: &Pubkey, mint: &Pubkey) -> Result<()> {
1094 let derived_pubkey = Pubkey::find_program_address(
1095 &[&mint.to_bytes(), self.input.pool_type, POOL_SEED],
1096 &LightMerkleTreeProgram::id(),
1097 );
1098
1099 if derived_pubkey.0 != *pubkey {
1100 return err!(VerifierSdkError::InvalidSenderOrRecipient);
1101 }
1102 Ok(())
1103 }
1104
1105 pub fn check_completion(&self) -> Result<()> {
1106 if self.transferred_funds
1107 && self.verified_proof
1108 && self.inserted_leaves
1109 && self.inserted_nullifier
1110 {
1111 return Ok(());
1112 }
1113 msg!("verified_proof {}", self.verified_proof);
1114 msg!("inserted_leaves {}", self.inserted_leaves);
1115 msg!("transferred_funds {}", self.transferred_funds);
1116 err!(VerifierSdkError::TransactionIncomplete)
1117 }
1118
1119 #[allow(clippy::comparison_chain)]
1120 pub fn check_amount(&self, relayer_fee: u64, amount: [u8; 32]) -> Result<(u64, u64)> {
1121 let pub_amount = <BigInteger256 as FromBytes>::read(&amount[..]).unwrap();
1123 if pub_amount.0[0] > 0
1128 && pub_amount.0[1] == 0
1129 && pub_amount.0[2] == 0
1130 && pub_amount.0[3] == 0
1131 {
1132 if relayer_fee != 0 {
1133 msg!("relayer_fee {}", relayer_fee);
1134 return Err(VerifierSdkError::WrongPubAmount.into());
1135 }
1136 Ok((pub_amount.0[0], 0))
1137 } else if pub_amount.0[0] != 0 {
1138 let mut field = FrParameters::MODULUS;
1140 field.sub_noborrow(&pub_amount);
1141
1142 if field.0[1] != 0 || field.0[2] != 0 || field.0[3] != 0 {
1144 msg!("Public amount is larger than u64.");
1145 return Err(VerifierSdkError::WrongPubAmount.into());
1146 }
1147
1148 if field.0[0] < relayer_fee {
1149 msg!(
1150 "Unshield invalid relayer_fee: pub amount {} < {} fee",
1151 field.0[0],
1152 relayer_fee
1153 );
1154 return Err(VerifierSdkError::WrongPubAmount.into());
1155 }
1156
1157 Ok((field.0[0].saturating_sub(relayer_fee), relayer_fee))
1158 } else if pub_amount.0[0] == 0
1159 && pub_amount.0[1] == 0
1160 && pub_amount.0[2] == 0
1161 && pub_amount.0[3] == 0
1162 {
1163 Ok((0, 0))
1164 } else {
1165 Err(VerifierSdkError::WrongPubAmount.into())
1166 }
1167 }
1168}