light_test_utils/
test_forester.rs

1use crate::test_env::NOOP_PROGRAM_ID;
2use account_compression::instruction::UpdateAddressMerkleTree;
3use account_compression::state::QueueAccount;
4use account_compression::utils::constants::{
5    ADDRESS_MERKLE_TREE_HEIGHT, ADDRESS_MERKLE_TREE_ROOTS,
6};
7use account_compression::{instruction::InsertAddresses, StateMerkleTreeAccount, ID};
8use account_compression::{AddressMerkleTreeAccount, SAFETY_MARGIN};
9use anchor_lang::system_program;
10use anchor_lang::{InstructionData, ToAccountMetas};
11use light_client::rpc::errors::RpcError;
12use light_client::rpc::RpcConnection;
13use light_concurrent_merkle_tree::event::MerkleTreeEvent;
14use light_hasher::Poseidon;
15use light_indexed_merkle_tree::copy::IndexedMerkleTreeCopy;
16
17use forester_utils::indexer::{AddressMerkleTreeBundle, StateMerkleTreeBundle};
18use forester_utils::{get_concurrent_merkle_tree, get_hash_set, get_indexed_merkle_tree};
19use light_registry::account_compression_cpi::sdk::{
20    create_nullify_instruction, create_update_address_merkle_tree_instruction,
21    CreateNullifyInstructionInputs, UpdateAddressMerkleTreeInstructionInputs,
22};
23use light_registry::utils::get_forester_epoch_pda_from_authority;
24use light_registry::{ForesterEpochPda, RegisterForester};
25use light_utils::bigint::bigint_to_be_bytes_array;
26use log::debug;
27use solana_sdk::signature::Signature;
28use solana_sdk::{
29    instruction::{AccountMeta, Instruction},
30    pubkey::Pubkey,
31    signature::{Keypair, Signer},
32    transaction::Transaction,
33};
34use thiserror::Error;
35// doesn't keep its own Merkle tree but gets it from the indexer
36// can also get all the state and Address Merkle trees from the indexer
37// the lightweight version is just a function
38// we should have a random option that shuffles the order in which to nullify transactions
39// we should have a parameters how many to nullify
40// in the test we should nullify everything once the queue is 60% full
41
42/// Check compressed_accounts in the queue array which are not nullified yet
43/// Iterate over these compressed_accounts and nullify them
44///
45/// Checks:
46/// 1. Value in hashset is marked
47/// 2. State tree root is updated
48/// 3. TODO: add event is emitted (after rebase)
49///     optional: assert that the Merkle tree doesn't change except the updated leaf
50pub async fn nullify_compressed_accounts<R: RpcConnection>(
51    rpc: &mut R,
52    forester: &Keypair,
53    state_tree_bundle: &mut StateMerkleTreeBundle,
54    epoch: u64,
55    is_metadata_forester: bool,
56) -> Result<(), RpcError> {
57    let nullifier_queue = unsafe {
58        get_hash_set::<QueueAccount, R>(rpc, state_tree_bundle.accounts.nullifier_queue).await
59    };
60    let pre_forester_counter = if is_metadata_forester {
61        0
62    } else {
63        rpc.get_anchor_account::<ForesterEpochPda>(
64            &get_forester_epoch_pda_from_authority(&forester.pubkey(), epoch).0,
65        )
66        .await
67        .unwrap()
68        .unwrap()
69        .work_counter
70    };
71    let onchain_merkle_tree =
72        get_concurrent_merkle_tree::<StateMerkleTreeAccount, R, Poseidon, 26>(
73            rpc,
74            state_tree_bundle.accounts.merkle_tree,
75        )
76        .await;
77    assert_eq!(
78        onchain_merkle_tree.root(),
79        state_tree_bundle.merkle_tree.root()
80    );
81    let pre_root = onchain_merkle_tree.root();
82    let change_log_index = onchain_merkle_tree.changelog_index() as u64;
83
84    let mut compressed_account_to_nullify = Vec::new();
85
86    let first = nullifier_queue.first_no_seq().unwrap();
87
88    for i in 0..nullifier_queue.get_capacity() {
89        let bucket = nullifier_queue.get_bucket(i).unwrap();
90        if let Some(bucket) = bucket {
91            if bucket.sequence_number.is_none() {
92                debug!("element to nullify: {:?}", bucket.value_bytes());
93                let leaf_index: usize = state_tree_bundle
94                    .merkle_tree
95                    .get_leaf_index(&bucket.value_bytes())
96                    .unwrap();
97                debug!("leaf_index: {:?}", leaf_index);
98                compressed_account_to_nullify.push((i, bucket.value_bytes()));
99            }
100        }
101    }
102
103    debug!(
104        "nullifying {:?} accounts ",
105        compressed_account_to_nullify.len()
106    );
107
108    for (i, (index_in_nullifier_queue, compressed_account)) in
109        compressed_account_to_nullify.iter().enumerate()
110    {
111        let leaf_index: usize = state_tree_bundle
112            .merkle_tree
113            .get_leaf_index(compressed_account)
114            .unwrap();
115        debug!("nullifying leaf: {:?}", leaf_index);
116
117        let proof: Vec<[u8; 32]> = state_tree_bundle
118            .merkle_tree
119            .get_proof_of_leaf(leaf_index, false)
120            .unwrap()
121            .to_array::<16>()
122            .unwrap()
123            .to_vec();
124        let ix = create_nullify_instruction(
125            CreateNullifyInstructionInputs {
126                authority: forester.pubkey(),
127                nullifier_queue: state_tree_bundle.accounts.nullifier_queue,
128                merkle_tree: state_tree_bundle.accounts.merkle_tree,
129                change_log_indices: vec![change_log_index],
130                leaves_queue_indices: vec![*index_in_nullifier_queue as u16],
131                indices: vec![leaf_index as u64],
132                proofs: vec![proof],
133                derivation: forester.pubkey(),
134                is_metadata_forester,
135            },
136            epoch,
137        );
138        let instructions = [ix];
139
140        let event = rpc
141            .create_and_send_transaction_with_event::<MerkleTreeEvent>(
142                &instructions,
143                &forester.pubkey(),
144                &[forester],
145                None,
146            )
147            .await?
148            .unwrap();
149
150        match event.0 {
151            MerkleTreeEvent::V2(event) => {
152                assert_eq!(event.id, state_tree_bundle.accounts.merkle_tree.to_bytes());
153                assert_eq!(
154                    event.seq,
155                    onchain_merkle_tree.sequence_number() as u64 + 1 + i as u64
156                );
157                assert_eq!(event.nullified_leaves_indices.len(), 1);
158                assert_eq!(event.nullified_leaves_indices[0], leaf_index as u64);
159            }
160            _ => {
161                panic!("Wrong event type.");
162            }
163        }
164
165        assert_value_is_marked_in_queue(
166            rpc,
167            state_tree_bundle,
168            index_in_nullifier_queue,
169            compressed_account,
170        )
171        .await;
172    }
173
174    let num_nullified = compressed_account_to_nullify.len() as u64;
175    // Locally nullify all leaves
176    for (_, compressed_account) in compressed_account_to_nullify.iter() {
177        let leaf_index = state_tree_bundle
178            .merkle_tree
179            .get_leaf_index(compressed_account)
180            .unwrap();
181        debug!("locally nullifying leaf_index {}", leaf_index);
182        debug!("compressed_account {:?}", compressed_account);
183        debug!(
184            "merkle tree pubkey {:?}",
185            state_tree_bundle.accounts.merkle_tree
186        );
187
188        state_tree_bundle
189            .merkle_tree
190            .update(&[0u8; 32], leaf_index)
191            .unwrap();
192    }
193    let onchain_merkle_tree =
194        get_concurrent_merkle_tree::<StateMerkleTreeAccount, R, Poseidon, 26>(
195            rpc,
196            state_tree_bundle.accounts.merkle_tree,
197        )
198        .await;
199    assert_eq!(
200        onchain_merkle_tree.root(),
201        state_tree_bundle.merkle_tree.root()
202    );
203    if !is_metadata_forester {
204        assert_forester_counter(
205            rpc,
206            &get_forester_epoch_pda_from_authority(&forester.pubkey(), epoch).0,
207            pre_forester_counter,
208            num_nullified,
209        )
210        .await
211        .unwrap();
212    }
213    // SAFEGUARD: check that the root changed if there was at least one element to nullify
214    if first.is_some() {
215        assert_ne!(pre_root, onchain_merkle_tree.root());
216    }
217    Ok(())
218}
219
220async fn assert_value_is_marked_in_queue<'a, R: RpcConnection>(
221    rpc: &mut R,
222    state_tree_bundle: &mut StateMerkleTreeBundle,
223    index_in_nullifier_queue: &usize,
224    compressed_account: &[u8; 32],
225) {
226    let nullifier_queue = unsafe {
227        get_hash_set::<QueueAccount, R>(rpc, state_tree_bundle.accounts.nullifier_queue).await
228    };
229    let array_element = nullifier_queue
230        .get_bucket(*index_in_nullifier_queue)
231        .unwrap()
232        .unwrap();
233    assert_eq!(&array_element.value_bytes(), compressed_account);
234    let onchain_merkle_tree =
235        get_concurrent_merkle_tree::<StateMerkleTreeAccount, R, Poseidon, 26>(
236            rpc,
237            state_tree_bundle.accounts.merkle_tree,
238        )
239        .await;
240    assert_eq!(
241        array_element.sequence_number(),
242        Some(
243            onchain_merkle_tree.sequence_number()
244                + onchain_merkle_tree.roots.capacity()
245                + SAFETY_MARGIN as usize
246        )
247    );
248}
249
250pub async fn assert_forester_counter<R: RpcConnection>(
251    rpc: &mut R,
252    pubkey: &Pubkey,
253    pre: u64,
254    num_nullified: u64,
255) -> Result<(), RpcError> {
256    let account = rpc
257        .get_anchor_account::<ForesterEpochPda>(pubkey)
258        .await?
259        .unwrap();
260    if account.work_counter != pre + num_nullified {
261        debug!("account.work_counter: {}", account.work_counter);
262        debug!("pre: {}", pre);
263        debug!("num_nullified: {}", num_nullified);
264        debug!("forester pubkey: {:?}", pubkey);
265        return Err(RpcError::CustomError(
266            "ForesterEpochPda counter not updated correctly".to_string(),
267        ));
268    }
269    Ok(())
270}
271
272#[derive(Error, Debug)]
273pub enum RelayerUpdateError {
274    #[error("Error in relayer update")]
275    RpcError,
276}
277/// Mocks the address insert logic of a forester.
278/// Gets addresses from the AddressQueue and inserts them into the AddressMerkleTree.
279///
280/// Checks:
281/// 1. Element has been marked correctly
282/// 2. Merkle tree has been updated correctly
283///
284/// TODO: Event has been emitted, event doesn't exist yet
285pub async fn empty_address_queue_test<R: RpcConnection>(
286    forester: &Keypair,
287    rpc: &mut R,
288    address_tree_bundle: &mut AddressMerkleTreeBundle,
289    signer_is_owner: bool,
290    epoch: u64,
291    is_metadata_forester: bool,
292) -> Result<(), RelayerUpdateError> {
293    let address_merkle_tree_pubkey = address_tree_bundle.accounts.merkle_tree;
294    let address_queue_pubkey = address_tree_bundle.accounts.queue;
295    let initial_merkle_tree_state = address_tree_bundle.merkle_tree.clone();
296    let initial_indexed_array_state = address_tree_bundle.indexed_array.clone();
297    let relayer_merkle_tree = &mut address_tree_bundle.merkle_tree;
298    let relayer_indexing_array = &mut address_tree_bundle.indexed_array;
299    let mut update_errors: Vec<RpcError> = Vec::new();
300    let address_merkle_tree =
301        get_indexed_merkle_tree::<AddressMerkleTreeAccount, R, Poseidon, usize, 26, 16>(
302            rpc,
303            address_merkle_tree_pubkey,
304        )
305        .await;
306    let indexed_changelog_index = address_merkle_tree.indexed_changelog_index() as u16;
307    let changelog_index = address_merkle_tree.changelog_index() as u16;
308    let mut counter = 0;
309    loop {
310        let pre_forester_counter = if !signer_is_owner {
311            rpc.get_anchor_account::<ForesterEpochPda>(
312                &get_forester_epoch_pda_from_authority(&forester.pubkey(), epoch).0,
313            )
314            .await
315            .map_err(|e| RelayerUpdateError::RpcError)?
316            .unwrap()
317            .work_counter
318        } else {
319            0
320        };
321        let address_merkle_tree =
322            get_indexed_merkle_tree::<AddressMerkleTreeAccount, R, Poseidon, usize, 26, 16>(
323                rpc,
324                address_merkle_tree_pubkey,
325            )
326            .await;
327        assert_eq!(relayer_merkle_tree.root(), address_merkle_tree.root());
328        let address_queue =
329            unsafe { get_hash_set::<QueueAccount, R>(rpc, address_queue_pubkey).await };
330
331        let address = address_queue.first_no_seq().unwrap();
332
333        if address.is_none() {
334            break;
335        }
336        let (address, address_hashset_index) = address.unwrap();
337        // Create new element from the dequeued value.
338        let (old_low_address, old_low_address_next_value) = initial_indexed_array_state
339            .find_low_element_for_nonexistent(&address.value_biguint())
340            .unwrap();
341        let address_bundle = initial_indexed_array_state
342            .new_element_with_low_element_index(old_low_address.index, &address.value_biguint())
343            .unwrap();
344
345        // Get the Merkle proof for updating low element.
346        let low_address_proof = initial_merkle_tree_state
347            .get_proof_of_leaf(old_low_address.index, false)
348            .unwrap();
349
350        let old_sequence_number = address_merkle_tree.sequence_number();
351        let old_root = address_merkle_tree.root();
352        // Update on-chain tree.
353        let update_successful = match update_merkle_tree(
354            rpc,
355            forester,
356            address_queue_pubkey,
357            address_merkle_tree_pubkey,
358            address_hashset_index,
359            old_low_address.index as u64,
360            bigint_to_be_bytes_array(&old_low_address.value).unwrap(),
361            old_low_address.next_index as u64,
362            bigint_to_be_bytes_array(&old_low_address_next_value).unwrap(),
363            low_address_proof.to_array().unwrap(),
364            Some(changelog_index),
365            Some(indexed_changelog_index),
366            signer_is_owner,
367            epoch,
368            is_metadata_forester,
369        )
370        .await
371        {
372            Ok(event) => {
373                let event = event.unwrap();
374                match event.0 {
375                    MerkleTreeEvent::V3(event) => {
376                        // Only assert for the first update since the other updates might be patched
377                        // the asserts are likely to fail
378                        if counter == 0 {
379                            assert_eq!(event.id, address_merkle_tree_pubkey.to_bytes());
380                            assert_eq!(event.seq, old_sequence_number as u64 + 1);
381                            assert_eq!(event.updates.len(), 1);
382                            let event = &event.updates[0];
383                            assert_eq!(
384                                event.new_low_element.index, address_bundle.new_low_element.index,
385                                "Empty Address Queue Test: invalid new_low_element.index"
386                            );
387                            assert_eq!(
388                                event.new_low_element.next_index,
389                                address_bundle.new_low_element.next_index,
390                                "Empty Address Queue Test: invalid new_low_element.next_index"
391                            );
392                            assert_eq!(
393                                event.new_low_element.value,
394                                bigint_to_be_bytes_array::<32>(
395                                    &address_bundle.new_low_element.value
396                                )
397                                .unwrap(),
398                                "Empty Address Queue Test: invalid new_low_element.value"
399                            );
400                            assert_eq!(
401                                event.new_low_element.next_value,
402                                bigint_to_be_bytes_array::<32>(&address_bundle.new_element.value)
403                                    .unwrap(),
404                                "Empty Address Queue Test: invalid new_low_element.next_value"
405                            );
406                            let leaf_hash = address_bundle
407                                .new_low_element
408                                .hash::<Poseidon>(&address_bundle.new_element.value)
409                                .unwrap();
410                            assert_eq!(
411                                event.new_low_element_hash, leaf_hash,
412                                "Empty Address Queue Test: invalid new_low_element_hash"
413                            );
414                            let leaf_hash = address_bundle
415                                .new_element
416                                .hash::<Poseidon>(&address_bundle.new_element_next_value)
417                                .unwrap();
418                            assert_eq!(
419                                event.new_high_element_hash, leaf_hash,
420                                "Empty Address Queue Test: invalid new_high_element_hash"
421                            );
422                            assert_eq!(
423                                event.new_high_element.index, address_bundle.new_element.index,
424                                "Empty Address Queue Test: invalid new_high_element.index"
425                            );
426                            assert_eq!(
427                                event.new_high_element.next_index,
428                                address_bundle.new_element.next_index,
429                                "Empty Address Queue Test: invalid new_high_element.next_index"
430                            );
431                            assert_eq!(
432                                event.new_high_element.value,
433                                bigint_to_be_bytes_array::<32>(&address_bundle.new_element.value)
434                                    .unwrap(),
435                                "Empty Address Queue Test: invalid new_high_element.value"
436                            );
437                            assert_eq!(
438                                event.new_high_element.next_value,
439                                bigint_to_be_bytes_array::<32>(
440                                    &address_bundle.new_element_next_value
441                                )
442                                .unwrap(),
443                                "Empty Address Queue Test: invalid new_high_element.next_value"
444                            );
445                        }
446                    }
447                    _ => {
448                        panic!("Wrong event type.");
449                    }
450                }
451                counter += 1;
452                true
453            }
454            Err(e) => {
455                update_errors.push(e);
456                break;
457            }
458        };
459
460        if update_successful {
461            if !signer_is_owner {
462                assert_forester_counter(
463                    rpc,
464                    &get_forester_epoch_pda_from_authority(&forester.pubkey(), epoch).0,
465                    pre_forester_counter,
466                    1,
467                )
468                .await
469                .unwrap();
470            }
471            let merkle_tree =
472                get_indexed_merkle_tree::<AddressMerkleTreeAccount, R, Poseidon, usize, 26, 16>(
473                    rpc,
474                    address_merkle_tree_pubkey,
475                )
476                .await;
477
478            let (old_low_address, _) = relayer_indexing_array
479                .find_low_element_for_nonexistent(&address.value_biguint())
480                .unwrap();
481            let address_bundle = relayer_indexing_array
482                .new_element_with_low_element_index(old_low_address.index, &address.value_biguint())
483                .unwrap();
484            let address_queue =
485                unsafe { get_hash_set::<QueueAccount, R>(rpc, address_queue_pubkey).await };
486
487            assert_eq!(
488                address_queue
489                    .get_bucket(address_hashset_index as usize)
490                    .unwrap()
491                    .unwrap()
492                    .sequence_number()
493                    .unwrap(),
494                old_sequence_number + address_queue.sequence_threshold + 2 // We are doing two Merkle tree operations
495            );
496
497            relayer_merkle_tree
498                .update(
499                    &address_bundle.new_low_element,
500                    &address_bundle.new_element,
501                    &address_bundle.new_element_next_value,
502                )
503                .unwrap();
504            relayer_indexing_array
505                .append_with_low_element_index(
506                    address_bundle.new_low_element.index,
507                    &address_bundle.new_element.value,
508                )
509                .unwrap();
510            assert_eq!(merkle_tree.sequence_number(), old_sequence_number + 2);
511            assert_ne!(old_root, merkle_tree.root(), "Root did not change.");
512            assert_eq!(
513                relayer_merkle_tree.root(),
514                merkle_tree.root(),
515                "Root off-chain onchain inconsistent."
516            );
517
518            let changelog_entry = merkle_tree
519                .changelog
520                .get(merkle_tree.changelog_index())
521                .unwrap();
522            let path = relayer_merkle_tree
523                .get_path_of_leaf(merkle_tree.current_index(), true)
524                .unwrap();
525            for i in 0..ADDRESS_MERKLE_TREE_HEIGHT as usize {
526                let changelog_node = changelog_entry.path[i].unwrap();
527                let path_node = path[i];
528                assert_eq!(changelog_node, path_node);
529            }
530
531            let indexed_changelog_entry = merkle_tree
532                .indexed_changelog
533                .get(merkle_tree.indexed_changelog_index())
534                .unwrap();
535            let proof = relayer_merkle_tree
536                .get_proof_of_leaf(merkle_tree.current_index(), false)
537                .unwrap();
538            assert_eq!(
539                address_bundle.new_element,
540                indexed_changelog_entry.element.into(),
541            );
542            assert_eq!(indexed_changelog_entry.proof.as_slice(), proof.as_slice());
543            assert_eq!(
544                indexed_changelog_entry.changelog_index,
545                merkle_tree.changelog_index()
546            );
547        }
548    }
549
550    if update_errors.is_empty() {
551        Ok(())
552    } else {
553        panic!("Errors: {:?}", update_errors);
554    }
555}
556
557#[allow(clippy::too_many_arguments)]
558pub async fn update_merkle_tree<R: RpcConnection>(
559    rpc: &mut R,
560    forester: &Keypair,
561    address_queue_pubkey: Pubkey,
562    address_merkle_tree_pubkey: Pubkey,
563    value: u16,
564    low_address_index: u64,
565    low_address_value: [u8; 32],
566    low_address_next_index: u64,
567    low_address_next_value: [u8; 32],
568    low_address_proof: [[u8; 32]; 16],
569    changelog_index: Option<u16>,
570    indexed_changelog_index: Option<u16>,
571    signer_is_owner: bool,
572    epoch: u64,
573    is_metadata_forester: bool,
574) -> Result<Option<(MerkleTreeEvent, Signature, u64)>, RpcError> {
575    let changelog_index = match changelog_index {
576        Some(changelog_index) => changelog_index,
577        None => {
578            let address_merkle_tree =
579                get_indexed_merkle_tree::<AddressMerkleTreeAccount, R, Poseidon, usize, 26, 16>(
580                    rpc,
581                    address_merkle_tree_pubkey,
582                )
583                .await;
584
585            address_merkle_tree.changelog_index() as u16
586        }
587    };
588    let indexed_changelog_index = match indexed_changelog_index {
589        Some(indexed_changelog_index) => indexed_changelog_index,
590        None => {
591            let address_merkle_tree =
592                get_indexed_merkle_tree::<AddressMerkleTreeAccount, R, Poseidon, usize, 26, 16>(
593                    rpc,
594                    address_merkle_tree_pubkey,
595                )
596                .await;
597
598            address_merkle_tree.indexed_changelog_index() as u16
599        }
600    };
601    let update_ix = if !signer_is_owner {
602        create_update_address_merkle_tree_instruction(
603            UpdateAddressMerkleTreeInstructionInputs {
604                authority: forester.pubkey(),
605                address_merkle_tree: address_merkle_tree_pubkey,
606                address_queue: address_queue_pubkey,
607                changelog_index,
608                indexed_changelog_index,
609                value,
610                low_address_index,
611                low_address_value,
612                low_address_next_index,
613                low_address_next_value,
614                low_address_proof,
615                is_metadata_forester,
616            },
617            epoch,
618        )
619    } else {
620        let instruction_data = UpdateAddressMerkleTree {
621            changelog_index,
622            indexed_changelog_index,
623            value,
624            low_address_index,
625            low_address_value,
626            low_address_next_index,
627            low_address_next_value,
628            low_address_proof,
629        };
630        Instruction {
631            program_id: ID,
632            accounts: vec![
633                AccountMeta::new(forester.pubkey(), true),
634                AccountMeta::new(ID, false),
635                AccountMeta::new(address_queue_pubkey, false),
636                AccountMeta::new(address_merkle_tree_pubkey, false),
637                AccountMeta::new(NOOP_PROGRAM_ID, false),
638            ],
639            data: instruction_data.data(),
640        }
641    };
642
643    rpc.create_and_send_transaction_with_event::<MerkleTreeEvent>(
644        &[update_ix],
645        &forester.pubkey(),
646        &[forester],
647        None,
648    )
649    .await
650}
651
652pub async fn insert_addresses<R: RpcConnection>(
653    context: &mut R,
654    address_queue_pubkey: Pubkey,
655    address_merkle_tree_pubkey: Pubkey,
656    addresses: Vec<[u8; 32]>,
657) -> Result<Signature, RpcError> {
658    let num_addresses = addresses.len();
659    let instruction_data = InsertAddresses { addresses };
660    let accounts = account_compression::accounts::InsertIntoQueues {
661        fee_payer: context.get_payer().pubkey(),
662        authority: context.get_payer().pubkey(),
663        registered_program_pda: None,
664        system_program: system_program::ID,
665    };
666    let insert_ix = Instruction {
667        program_id: ID,
668        accounts: [
669            accounts.to_account_metas(Some(true)),
670            vec![
671                vec![
672                    AccountMeta::new(address_queue_pubkey, false),
673                    AccountMeta::new(address_merkle_tree_pubkey, false)
674                ];
675                num_addresses
676            ]
677            .iter()
678            .flat_map(|x| x.to_vec())
679            .collect::<Vec<AccountMeta>>(),
680        ]
681        .concat(),
682        data: instruction_data.data(),
683    };
684    let latest_blockhash = context.get_latest_blockhash().await.unwrap();
685    let transaction = Transaction::new_signed_with_payer(
686        &[insert_ix],
687        Some(&context.get_payer().pubkey()),
688        &[&context.get_payer()],
689        latest_blockhash,
690    );
691    context.process_transaction(transaction).await
692}