light_sdk/
merkle_context.rs

1use std::collections::HashMap;
2
3use anchor_lang::prelude::{AccountMeta, AnchorDeserialize, AnchorSerialize, Pubkey};
4
5/// Collection of remaining accounts which are sent to the program.
6#[derive(Default)]
7pub struct RemainingAccounts {
8    next_index: u8,
9    map: HashMap<Pubkey, u8>,
10}
11
12impl RemainingAccounts {
13    /// Returns the index of the provided `pubkey` in the collection.
14    ///
15    /// If the provided `pubkey` is not a part of the collection, it gets
16    /// inserted with a `next_index`.
17    ///
18    /// If the privided `pubkey` already exists in the collection, its already
19    /// existing index is returned.
20    pub fn insert_or_get(&mut self, pubkey: Pubkey) -> u8 {
21        *self.map.entry(pubkey).or_insert_with(|| {
22            let index = self.next_index;
23            self.next_index += 1;
24            index
25        })
26    }
27
28    /// Converts the collection of accounts to a vector of
29    /// [`AccountMeta`](solana_sdk::instruction::AccountMeta), which can be used
30    /// as remaining accounts in instructions or CPI calls.
31    pub fn to_account_metas(&self) -> Vec<AccountMeta> {
32        let mut remaining_accounts = self
33            .map
34            .iter()
35            .map(|(k, i)| {
36                (
37                    AccountMeta {
38                        pubkey: *k,
39                        is_signer: false,
40                        is_writable: true,
41                    },
42                    *i as usize,
43                )
44            })
45            .collect::<Vec<(AccountMeta, usize)>>();
46        // hash maps are not sorted so we need to sort manually and collect into a vector again
47        remaining_accounts.sort_by(|a, b| a.1.cmp(&b.1));
48        let remaining_accounts = remaining_accounts
49            .iter()
50            .map(|(k, _)| k.clone())
51            .collect::<Vec<AccountMeta>>();
52        remaining_accounts
53    }
54}
55
56#[derive(Debug, Clone, Copy, AnchorSerialize, AnchorDeserialize, PartialEq, Default)]
57pub struct QueueIndex {
58    /// Id of queue in queue account.
59    pub queue_id: u8,
60    /// Index of compressed account hash in queue.
61    pub index: u16,
62}
63
64#[derive(Debug, Clone, Copy, AnchorSerialize, AnchorDeserialize, PartialEq, Default)]
65pub struct MerkleContext {
66    pub merkle_tree_pubkey: Pubkey,
67    pub nullifier_queue_pubkey: Pubkey,
68    pub leaf_index: u32,
69    /// Index of leaf in queue. Placeholder of batched Merkle tree updates
70    /// currently unimplemented.
71    pub queue_index: Option<QueueIndex>,
72}
73
74#[derive(Debug, Clone, Copy, AnchorSerialize, AnchorDeserialize, PartialEq, Default)]
75pub struct PackedMerkleContext {
76    pub merkle_tree_pubkey_index: u8,
77    pub nullifier_queue_pubkey_index: u8,
78    pub leaf_index: u32,
79    /// Index of leaf in queue. Placeholder of batched Merkle tree updates
80    /// currently unimplemented.
81    pub queue_index: Option<QueueIndex>,
82}
83
84pub fn pack_merkle_contexts(
85    merkle_contexts: &[MerkleContext],
86    remaining_accounts: &mut RemainingAccounts,
87) -> Vec<PackedMerkleContext> {
88    merkle_contexts
89        .iter()
90        .map(|x| {
91            let merkle_tree_pubkey_index = remaining_accounts.insert_or_get(x.merkle_tree_pubkey);
92            let nullifier_queue_pubkey_index =
93                remaining_accounts.insert_or_get(x.nullifier_queue_pubkey);
94            PackedMerkleContext {
95                merkle_tree_pubkey_index,
96                nullifier_queue_pubkey_index,
97                leaf_index: x.leaf_index,
98                queue_index: x.queue_index,
99            }
100        })
101        .collect::<Vec<_>>()
102}
103
104pub fn pack_merkle_context(
105    merkle_context: MerkleContext,
106    remaining_accounts: &mut RemainingAccounts,
107) -> PackedMerkleContext {
108    pack_merkle_contexts(&[merkle_context], remaining_accounts)[0]
109}
110
111/// Context which contains the accounts necessary for emitting the output
112/// compressed account.
113///
114/// The difference between `MerkleOutputContext` and `MerkleContext` is that
115/// the former can be used only for creating new accounts and therefore does
116/// not contain:
117///
118/// - nullifier queue (because the output accout is just being created)
119/// - `leaf_index` (because it does not exist yet)
120#[derive(Debug, Clone, Copy, AnchorSerialize, AnchorDeserialize, PartialEq, Default)]
121pub struct MerkleOutputContext {
122    pub merkle_tree_pubkey: Pubkey,
123}
124
125/// Context which contains the indices of accounts necessary for emitting the
126/// output compressed account.
127///
128/// The difference between `MerkleOutputContext` and `MerkleContext` is that
129/// the former can be used only for creating new accounts and therefore does
130/// not contain:
131///
132/// - nullifier queue (because the output accout is just being created)
133/// - `leaf_index` (because it does not exist yet)
134#[derive(Debug, Clone, Copy, AnchorSerialize, AnchorDeserialize, PartialEq, Default)]
135pub struct PackedMerkleOutputContext {
136    pub merkle_tree_pubkey_index: u8,
137}
138
139/// Returns a vector of [`PackedMerkleOutputContext`] and fills up `remaining_accounts`
140/// based on the given `merkle_contexts`.
141pub fn pack_merkle_output_contexts(
142    merkle_contexts: &[MerkleOutputContext],
143    remaining_accounts: &mut RemainingAccounts,
144) -> Vec<PackedMerkleOutputContext> {
145    merkle_contexts
146        .iter()
147        .map(|x| {
148            let merkle_tree_pubkey_index = remaining_accounts.insert_or_get(x.merkle_tree_pubkey);
149            PackedMerkleOutputContext {
150                merkle_tree_pubkey_index,
151            }
152        })
153        .collect::<Vec<_>>()
154}
155
156/// Returns a [`PackedMerkleOutputContext`] and fills up `remaining_accounts` based
157/// on the given `merkle_output_context`.
158pub fn pack_merkle_output_context(
159    merkle_output_context: MerkleOutputContext,
160    remaining_accounts: &mut RemainingAccounts,
161) -> PackedMerkleOutputContext {
162    pack_merkle_output_contexts(&[merkle_output_context], remaining_accounts)[0]
163}
164
165#[derive(Debug, Clone, Copy, AnchorSerialize, AnchorDeserialize, PartialEq, Default)]
166pub struct AddressMerkleContext {
167    pub address_merkle_tree_pubkey: Pubkey,
168    pub address_queue_pubkey: Pubkey,
169}
170
171#[derive(Debug, Clone, Copy, AnchorSerialize, AnchorDeserialize, PartialEq, Default)]
172pub struct PackedAddressMerkleContext {
173    pub address_merkle_tree_pubkey_index: u8,
174    pub address_queue_pubkey_index: u8,
175}
176
177/// Returns a vector of [`PackedAddressMerkleContext`] and fills up
178/// `remaining_accounts` based on the given `merkle_contexts`.
179pub fn pack_address_merkle_contexts(
180    address_merkle_contexts: &[AddressMerkleContext],
181    remaining_accounts: &mut RemainingAccounts,
182) -> Vec<PackedAddressMerkleContext> {
183    address_merkle_contexts
184        .iter()
185        .map(|x| {
186            let address_merkle_tree_pubkey_index =
187                remaining_accounts.insert_or_get(x.address_merkle_tree_pubkey);
188            let address_queue_pubkey_index =
189                remaining_accounts.insert_or_get(x.address_queue_pubkey);
190            PackedAddressMerkleContext {
191                address_merkle_tree_pubkey_index,
192                address_queue_pubkey_index,
193            }
194        })
195        .collect::<Vec<_>>()
196}
197
198/// Returns a [`PackedAddressMerkleContext`] and fills up `remaining_accounts`
199/// based on the given `merkle_context`.
200pub fn pack_address_merkle_context(
201    address_merkle_context: AddressMerkleContext,
202    remaining_accounts: &mut RemainingAccounts,
203) -> PackedAddressMerkleContext {
204    pack_address_merkle_contexts(&[address_merkle_context], remaining_accounts)[0]
205}