light_program_test/program_test/
light_program_test.rs1use std::fmt::{self, Debug, Formatter};
2
3use account_compression::{AddressMerkleTreeAccount, QueueAccount};
4use light_client::{
5 indexer::{AddressMerkleTreeAccounts, StateMerkleTreeAccounts},
6 rpc::{merkle_tree::MerkleTreeExt, RpcError},
7};
8use light_compressed_account::hash_to_bn254_field_size_be;
9use light_prover_client::prover::{spawn_prover, ProverConfig};
10use litesvm::LiteSVM;
11use solana_account::WritableAccount;
12use solana_sdk::signature::{Keypair, Signer};
13
14use crate::{
15 accounts::{
16 initialize::initialize_accounts, test_accounts::TestAccounts, test_keypairs::TestKeypairs,
17 },
18 indexer::TestIndexer,
19 program_test::TestRpc,
20 utils::setup_light_programs::setup_light_programs,
21 ProgramTestConfig,
22};
23
24pub struct LightProgramTest {
25 pub config: ProgramTestConfig,
26 pub context: LiteSVM,
27 pub indexer: Option<TestIndexer>,
28 pub test_accounts: TestAccounts,
29 pub payer: Keypair,
30 pub transaction_counter: usize,
31}
32
33impl LightProgramTest {
34 pub async fn new(config: ProgramTestConfig) -> Result<LightProgramTest, RpcError> {
52 let mut context = setup_light_programs(config.additional_programs.clone())?;
53 let payer = Keypair::new();
54 context
55 .airdrop(&payer.pubkey(), 100_000_000_000_000)
56 .expect("Payer airdrop failed.");
57 let mut context = Self {
58 context,
59 indexer: None,
60 test_accounts: TestAccounts::get_program_test_test_accounts(),
61 payer,
62 config: config.clone(),
63 transaction_counter: 0,
64 };
65 let keypairs = TestKeypairs::program_test_default();
66
67 context
68 .context
69 .airdrop(&keypairs.governance_authority.pubkey(), 100_000_000_000_000)
70 .expect("governance_authority airdrop failed.");
71 context
72 .context
73 .airdrop(&keypairs.forester.pubkey(), 10_000_000_000)
74 .expect("forester airdrop failed.");
75
76 if !config.skip_protocol_init {
77 let restore_logs = context.config.no_logs;
78 if context.config.skip_startup_logs {
79 context.config.no_logs = true;
80 }
81 initialize_accounts(&mut context, &config, &keypairs).await?;
82 if context.config.skip_startup_logs {
83 context.config.no_logs = restore_logs;
84 }
85 let batch_size = config
86 .v2_state_tree_config
87 .as_ref()
88 .map(|config| config.output_queue_batch_size as usize);
89 let test_accounts = context.test_accounts.clone();
90 context.add_indexer(&test_accounts, batch_size).await?;
91
92 {
94 let address_mt = context.test_accounts.v1_address_trees[0].merkle_tree;
95 let address_queue_pubkey = context.test_accounts.v1_address_trees[0].queue;
96 let mut account = context
97 .context
98 .get_account(&keypairs.address_merkle_tree.pubkey())
99 .unwrap();
100 let merkle_tree_account = bytemuck::from_bytes_mut::<AddressMerkleTreeAccount>(
101 &mut account.data_as_mut_slice()[8..AddressMerkleTreeAccount::LEN],
102 );
103 merkle_tree_account.metadata.associated_queue = address_queue_pubkey.into();
104 context.set_account(address_mt, account);
105
106 let mut account = context
107 .context
108 .get_account(&keypairs.address_merkle_tree_queue.pubkey())
109 .unwrap();
110 let queue_account = bytemuck::from_bytes_mut::<QueueAccount>(
111 &mut account.data_as_mut_slice()[8..QueueAccount::LEN],
112 );
113 queue_account.metadata.associated_merkle_tree = address_mt.into();
114 context.set_account(address_queue_pubkey, account);
115 }
116 }
117 {
119 let tree_account = context
120 .context
121 .get_account(&keypairs.state_merkle_tree.pubkey());
122 let queue_account = context
123 .context
124 .get_account(&keypairs.nullifier_queue.pubkey());
125 let cpi_account = context
126 .context
127 .get_account(&keypairs.cpi_context_account.pubkey());
128
129 if let (Some(tree_acc), Some(queue_acc), Some(cpi_acc)) =
130 (tree_account, queue_account, cpi_account)
131 {
132 for i in 0..context.test_accounts.v1_state_trees.len() {
133 let state_mt = context.test_accounts.v1_state_trees[i].merkle_tree;
134 let nullifier_queue_pubkey =
135 context.test_accounts.v1_state_trees[i].nullifier_queue;
136 let cpi_context_pubkey = context.test_accounts.v1_state_trees[i].cpi_context;
137
138 let mut tree_account_data = tree_acc.clone();
140 {
141 let merkle_tree_account = bytemuck::from_bytes_mut::<
142 account_compression::StateMerkleTreeAccount,
143 >(
144 &mut tree_account_data.data_as_mut_slice()
145 [8..account_compression::StateMerkleTreeAccount::LEN],
146 );
147 merkle_tree_account.metadata.associated_queue =
148 nullifier_queue_pubkey.into();
149 }
150 context.set_account(state_mt, tree_account_data);
151
152 let mut queue_account_data = queue_acc.clone();
154 {
155 let queue_account = bytemuck::from_bytes_mut::<QueueAccount>(
156 &mut queue_account_data.data_as_mut_slice()[8..QueueAccount::LEN],
157 );
158 queue_account.metadata.associated_merkle_tree = state_mt.into();
159 }
160 context.set_account(nullifier_queue_pubkey, queue_account_data);
161
162 let mut cpi_account_data = cpi_acc.clone();
164 {
165 let associated_merkle_tree_offset = 8 + 32; let associated_queue_offset = 8 + 32 + 32; cpi_account_data.data_as_mut_slice()
168 [associated_merkle_tree_offset..associated_merkle_tree_offset + 32]
169 .copy_from_slice(&state_mt.to_bytes());
170 cpi_account_data.data_as_mut_slice()
171 [associated_queue_offset..associated_queue_offset + 32]
172 .copy_from_slice(&nullifier_queue_pubkey.to_bytes());
173 }
174 context.set_account(cpi_context_pubkey, cpi_account_data);
175 }
176 }
177 }
178 {
179 let address_mt = context.test_accounts.v2_address_trees[0];
180 let account = context
181 .context
182 .get_account(&keypairs.batch_address_merkle_tree.pubkey());
183 if let Some(account) = account {
184 context.set_account(address_mt, account);
185 }
186 }
187 {
189 let tree_account = context
190 .context
191 .get_account(&keypairs.batched_state_merkle_tree.pubkey());
192 let queue_account = context
193 .context
194 .get_account(&keypairs.batched_output_queue.pubkey());
195 let cpi_account = context
196 .context
197 .get_account(&keypairs.batched_cpi_context.pubkey());
198
199 if let (Some(tree_acc), Some(queue_acc), Some(cpi_acc)) =
200 (tree_account, queue_account, cpi_account)
201 {
202 use light_batched_merkle_tree::{
203 merkle_tree::BatchedMerkleTreeAccount, queue::BatchedQueueAccount,
204 };
205
206 for i in 0..context.test_accounts.v2_state_trees.len() {
207 let merkle_tree_pubkey = context.test_accounts.v2_state_trees[i].merkle_tree;
208 let output_queue_pubkey = context.test_accounts.v2_state_trees[i].output_queue;
209 let cpi_context_pubkey = context.test_accounts.v2_state_trees[i].cpi_context;
210
211 let mut tree_account_data = tree_acc.clone();
213 {
214 let mut tree = BatchedMerkleTreeAccount::state_from_bytes(
215 tree_account_data.data_as_mut_slice(),
216 &merkle_tree_pubkey.into(),
217 )
218 .unwrap();
219 let metadata = tree.get_metadata_mut();
220 metadata.metadata.associated_queue = output_queue_pubkey.into();
221 metadata.hashed_pubkey =
222 hash_to_bn254_field_size_be(&merkle_tree_pubkey.to_bytes());
223 }
224 context.set_account(merkle_tree_pubkey, tree_account_data);
225
226 let mut queue_account_data = queue_acc.clone();
228 {
229 let mut queue = BatchedQueueAccount::output_from_bytes(
230 queue_account_data.data_as_mut_slice(),
231 )
232 .unwrap();
233 let metadata = queue.get_metadata_mut();
234 metadata.metadata.associated_merkle_tree = merkle_tree_pubkey.into();
235 metadata.hashed_merkle_tree_pubkey =
236 hash_to_bn254_field_size_be(&merkle_tree_pubkey.to_bytes());
237 metadata.hashed_queue_pubkey =
238 hash_to_bn254_field_size_be(&output_queue_pubkey.to_bytes());
239 }
240 context.set_account(output_queue_pubkey, queue_account_data);
241
242 let mut cpi_account_data = cpi_acc.clone();
244 {
245 let associated_merkle_tree_offset = 8 + 32; let associated_queue_offset = 8 + 32 + 32; cpi_account_data.data_as_mut_slice()
248 [associated_merkle_tree_offset..associated_merkle_tree_offset + 32]
249 .copy_from_slice(&merkle_tree_pubkey.to_bytes());
250 cpi_account_data.data_as_mut_slice()
251 [associated_queue_offset..associated_queue_offset + 32]
252 .copy_from_slice(&output_queue_pubkey.to_bytes());
253 }
254 context.set_account(cpi_context_pubkey, cpi_account_data);
255 }
256 }
257 }
258
259 context.transaction_counter = 0;
261 #[cfg(feature = "devenv")]
263 let prover_config = if config.prover_config.is_none() {
264 Some(ProverConfig::default())
265 } else {
266 config.prover_config
267 };
268 #[cfg(not(feature = "devenv"))]
269 let prover_config = if config.with_prover && config.prover_config.is_none() {
270 Some(ProverConfig::default())
271 } else {
272 config.prover_config
273 };
274 if let Some(ref prover_config) = prover_config {
275 spawn_prover(prover_config.clone()).await;
276 }
277 Ok(context)
278 }
279
280 pub fn indexer(&self) -> Result<&TestIndexer, RpcError> {
281 self.indexer.as_ref().ok_or(RpcError::IndexerNotInitialized)
282 }
283
284 pub fn indexer_mut(&mut self) -> Result<&mut TestIndexer, RpcError> {
285 self.indexer.as_mut().ok_or(RpcError::IndexerNotInitialized)
286 }
287
288 pub fn test_accounts(&self) -> &TestAccounts {
289 &self.test_accounts
290 }
291
292 pub fn get_state_merkle_tree_account(&self) -> StateMerkleTreeAccounts {
294 self.test_accounts.v1_state_trees[0]
295 }
296
297 pub fn get_address_merkle_tree(&self) -> AddressMerkleTreeAccounts {
298 self.test_accounts.v1_address_trees[0]
299 }
300
301 pub async fn add_indexer(
302 &mut self,
303 test_accounts: &TestAccounts,
304 batch_size: Option<usize>,
305 ) -> Result<(), RpcError> {
306 let indexer = TestIndexer::init_from_acounts(
307 &self.payer,
308 test_accounts,
309 batch_size.unwrap_or_default(),
310 )
311 .await;
312 self.indexer = Some(indexer);
313 Ok(())
314 }
315
316 pub fn clone_indexer(&self) -> Result<TestIndexer, RpcError> {
317 Ok((*self
318 .indexer
319 .as_ref()
320 .ok_or(RpcError::IndexerNotInitialized)?)
321 .clone())
322 }
323}
324
325impl MerkleTreeExt for LightProgramTest {}
326
327impl Debug for LightProgramTest {
328 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
329 f.debug_struct("LightProgramTest")
330 .field("context", &"ProgramTestContext")
331 .field("indexer", &self.indexer)
332 .field("test_accounts", &self.test_accounts)
333 .finish()
334 }
335}