Skip to main content

light_verifier_sdk/
light_transaction.rs

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    /// Program ID of the verifier program.
45    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    // Client input.
61    pub input: TransactionInput<'a, 'b, 'c, 'info, NR_CHECKED_INPUTS, NR_LEAVES, NR_NULLIFIERS, A>,
62    // State of transaction.
63    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    /// Transact is a wrapper function which computes the integrity hash, checks the root,
165    /// verifies the zero knowledge proof, inserts leaves, inserts nullifiers, transfers funds and fees.
166    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        // Initialize the vector of leaves
182        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    /// Calls the Merkle tree program via CPI to insert event leaves.
269    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    /// Verifies a Goth16 zero knowledge proof over the bn254 curve.
304    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        // self.verified_proof = true;
363        // Ok(())
364        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    /// Computes the integrity hash of the transaction. This hash is an input to the ZKP, and
389    /// ensures that the relayer cannot change parameters of the internal or unshield transaction.
390    /// H(recipient_spl||recipient_sol||signer||relayer_fee||encrypted_utxos).
391    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        // msg!("message_hash: {:?}", message_hash.to_vec());
423        // msg!("recipient_spl: {:?}", recipient_spl.to_vec());
424        // msg!(
425        //     "recipient_sol: {:?}",
426        //     self.accounts
427        //         .unwrap()
428        //         .recipient_sol
429        //         .as_ref()
430        //         .unwrap()
431        //         .key()
432        //         .to_bytes()
433        //         .to_vec()
434        // );
435        // msg!(
436        //     "signing_address: {:?}",
437        //     self.accounts
438        //         .unwrap()
439        //         .signing_address
440        //         .key()
441        //         .to_bytes()
442        //         .to_vec()
443        // );
444        // msg!("relayer_fee: {:?}", self.relayer_fee.to_le_bytes().to_vec());
445        // msg!("relayer_fee {}", self.relayer_fee);
446        // msg!("integrity_hash inputs.len(): {}", input.len());
447        // msg!("encrypted_utxos: {:?}", self.encrypted_utxos);
448
449        self.tx_integrity_hash = truncate_to_circuit(&tx_integrity_hash.to_bytes());
450        self.computed_tx_integrity_hash = true;
451        Ok(())
452    }
453
454    /// Fetches the root according to an index from the passed-in Merkle tree.
455    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    /// Fetches the token mint from passed in sender_spl account. If the sender_spl account is not a
464    /// token account, native mint is assumed.
465    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                        // Omits the last byte for the mint pubkey bytes to fit into the bn254 field.
471                        // msg!(
472                        //     "{:?}",
473                        //     [vec![0u8], sender_mint.mint.to_bytes()[..31].to_vec()].concat()
474                        // );
475                        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    /// Checks the expected number of remaning accounts:
506    ///
507    /// * Nullifier and leaf accounts (mandatory).
508    /// * Merkle tree accounts (optional).
509    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 // Only nullifiers and leaves.
513            // Nullifiers, leaves and next Merkle trees (transaction, event).
514            && 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    /// Calls the Merkle tree program via cpi to insert transaction leaves.
529    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        // check merkle tree
538        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            // check account integrities
546            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    /// Returns a Transaction Merkle Tree which should be used for the current
580    /// transaction. It might be either:
581    ///
582    /// * Transaction Merkle Tree provided in the context.
583    /// * A new Transaction Merkle Tree provided as a remaining account, which
584    ///   usually is the case when we reached the switch threshold (255_000).
585    ///   We pass a new Transaction Merkle Tree as remaining account, because
586    ///   Anchor does not support passing two accounts of the same type in the
587    ///   same instruction. We need a second Transacton Merkle Tree account for
588    ///   the UTXOs we want to spend in the transaction are in the old
589    ///   Transaction Merkle Tree. Since the old Merkle Tree is almost full we
590    ///   need to insert the new utxo commitments into the new Transaction
591    ///   Merkle Tree.
592    fn get_transaction_merkle_tree(&self) -> Result<AccountInfo<'info>> {
593        // Index of a new Transaction Merkle Tree in remaining accounts.
594        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    /// Calls merkle tree via cpi to insert nullifiers.
634    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    /// Transfers user funds either to or from a merkle tree liquidity pool.
671    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        // check mintPubkey
679        let (pub_amount_checked, _) =
680            self.check_amount(0, change_endianness(&self.input.public_amount.spl))?;
681
682        // Only transfer if pub amount is greater than zero otherwise recipient_spl and sender_spl accounts are not checked
683        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            // check mint
708            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            // is a token shield or unshield
718            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                // shield_spl_cpi
734                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    /// Transfers the relayer fee  to or from a merkle tree liquidity pool.
792    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        // check that it is the native token pool
799        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                // Unshield sol for the user
843                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            // pays the relayer fee
882            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    /// Creates and closes an account such that shielded sol is part of the transaction fees.
920    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,              //bytes
957            amount_checked, //lamports
958            false,          //rent_exempt
959        )?;
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    /// Checks whether a transaction is a shield by inspecting the public amount.
1060    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    /// Checks whether a transaction is a deposit by inspecting the public amount.
1070    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        // pub_amount is the public amount included in public inputs for proof verification
1122        let pub_amount = <BigInteger256 as FromBytes>::read(&amount[..]).unwrap();
1123        // Big integers are stored in 4 u64 limbs, if the number is <= U64::max() and encoded in little endian,
1124        // only the first limb is greater than 0.
1125        // Amounts in shielded accounts are limited to 64bit therefore an unshield will always be greater
1126        // than one U64::max().
1127        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            // calculate ext_amount from pubAmount:
1139            let mut field = FrParameters::MODULUS;
1140            field.sub_noborrow(&pub_amount);
1141
1142            // field.0[0] is the positive value
1143            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}