From ba2ba03c4e095e84c3f58f116e3f82fc25fdfc6b Mon Sep 17 00:00:00 2001 From: ananas-block Date: Mon, 8 Dec 2025 19:52:15 +0000 Subject: [PATCH 1/7] feat: update mainnet batched tree values --- program-libs/batched-merkle-tree/src/constants.rs | 8 +++++++- .../src/initialize_address_tree.rs | 13 +++++++------ .../src/initialize_state_tree.rs | 6 +++--- .../src/processor/insert_into_queues.rs | 6 ++++++ 4 files changed, 23 insertions(+), 10 deletions(-) diff --git a/program-libs/batched-merkle-tree/src/constants.rs b/program-libs/batched-merkle-tree/src/constants.rs index 85e13857f8..9d5ef2a119 100644 --- a/program-libs/batched-merkle-tree/src/constants.rs +++ b/program-libs/batched-merkle-tree/src/constants.rs @@ -8,6 +8,8 @@ pub const DEFAULT_BATCH_STATE_TREE_HEIGHT: u32 = 32; pub const DEFAULT_BATCH_ROOT_HISTORY_LEN: u32 = 200; +pub const DEFAULT_ADDRESS_BATCH_ROOT_HISTORY_LEN: u32 = 100; + pub const DEFAULT_NUM_BATCHES: u64 = 2; pub const TEST_DEFAULT_BATCH_SIZE: u64 = 50; @@ -16,13 +18,17 @@ pub const TEST_DEFAULT_ZKP_BATCH_SIZE: u64 = 10; pub const DEFAULT_BATCH_SIZE: u64 = 15000; +pub const DEFAULT_ADDRESS_BATCH_SIZE: u64 = 30000; + pub const DEFAULT_ZKP_BATCH_SIZE: u64 = 500; pub const DEFAULT_ADDRESS_ZKP_BATCH_SIZE: u64 = 250; +// False positive probability 1.0E-12 for 15k elements. pub const STATE_BLOOM_FILTER_CAPACITY: u64 = 2_301_536; pub const STATE_BLOOM_FILTER_NUM_HASHES: u64 = 10; -pub const ADDRESS_BLOOM_FILTER_CAPACITY: u64 = 2_301_536; +// False positive probability 1.0E-12 for 30k elements. +pub const ADDRESS_BLOOM_FILTER_CAPACITY: u64 = 4_603_051; pub const ADDRESS_BLOOM_FILTER_NUM_HASHES: u64 = 10; #[deprecated(note = "Use DEFAULT_CPI_CONTEXT_ACCOUNT_SIZE_V2 instead")] diff --git a/program-libs/batched-merkle-tree/src/initialize_address_tree.rs b/program-libs/batched-merkle-tree/src/initialize_address_tree.rs index 8524cbd459..a2ced87f98 100644 --- a/program-libs/batched-merkle-tree/src/initialize_address_tree.rs +++ b/program-libs/batched-merkle-tree/src/initialize_address_tree.rs @@ -7,8 +7,9 @@ use light_merkle_tree_metadata::{ use crate::{ constants::{ + ADDRESS_BLOOM_FILTER_CAPACITY, ADDRESS_BLOOM_FILTER_NUM_HASHES, + DEFAULT_ADDRESS_BATCH_ROOT_HISTORY_LEN, DEFAULT_ADDRESS_BATCH_SIZE, DEFAULT_ADDRESS_ZKP_BATCH_SIZE, DEFAULT_BATCH_ADDRESS_TREE_HEIGHT, - DEFAULT_BATCH_ROOT_HISTORY_LEN, DEFAULT_BATCH_SIZE, }, errors::BatchedMerkleTreeError, merkle_tree::{get_merkle_tree_account_size, BatchedMerkleTreeAccount}, @@ -38,12 +39,12 @@ impl Default for InitAddressTreeAccountsInstructionData { index: 0, program_owner: None, forester: None, - bloom_filter_num_iters: 3, - input_queue_batch_size: DEFAULT_BATCH_SIZE, + bloom_filter_num_iters: ADDRESS_BLOOM_FILTER_NUM_HASHES, + input_queue_batch_size: DEFAULT_ADDRESS_BATCH_SIZE, input_queue_zkp_batch_size: DEFAULT_ADDRESS_ZKP_BATCH_SIZE, - height: 40, - root_history_capacity: DEFAULT_BATCH_ROOT_HISTORY_LEN, - bloom_filter_capacity: DEFAULT_BATCH_SIZE * 8, + height: DEFAULT_BATCH_ADDRESS_TREE_HEIGHT, + root_history_capacity: DEFAULT_ADDRESS_BATCH_ROOT_HISTORY_LEN, + bloom_filter_capacity: ADDRESS_BLOOM_FILTER_CAPACITY, network_fee: Some(10000), rollover_threshold: Some(95), close_threshold: None, diff --git a/program-libs/batched-merkle-tree/src/initialize_state_tree.rs b/program-libs/batched-merkle-tree/src/initialize_state_tree.rs index 4c5bf0a5b0..1dd32e98fd 100644 --- a/program-libs/batched-merkle-tree/src/initialize_state_tree.rs +++ b/program-libs/batched-merkle-tree/src/initialize_state_tree.rs @@ -9,7 +9,7 @@ use light_merkle_tree_metadata::{ use crate::{ constants::{ DEFAULT_BATCH_SIZE, DEFAULT_BATCH_STATE_TREE_HEIGHT, DEFAULT_CPI_CONTEXT_ACCOUNT_SIZE_V2, - DEFAULT_ZKP_BATCH_SIZE, + DEFAULT_ZKP_BATCH_SIZE, STATE_BLOOM_FILTER_CAPACITY, STATE_BLOOM_FILTER_NUM_HASHES, }, errors::BatchedMerkleTreeError, merkle_tree::{get_merkle_tree_account_size, BatchedMerkleTreeAccount}, @@ -48,14 +48,14 @@ impl Default for InitStateTreeAccountsInstructionData { program_owner: None, forester: None, additional_bytes: DEFAULT_CPI_CONTEXT_ACCOUNT_SIZE_V2, - bloom_filter_num_iters: 3, + bloom_filter_num_iters: STATE_BLOOM_FILTER_NUM_HASHES, input_queue_batch_size: DEFAULT_BATCH_SIZE, output_queue_batch_size: DEFAULT_BATCH_SIZE, input_queue_zkp_batch_size: DEFAULT_ZKP_BATCH_SIZE, output_queue_zkp_batch_size: DEFAULT_ZKP_BATCH_SIZE, height: DEFAULT_BATCH_STATE_TREE_HEIGHT, root_history_capacity: (DEFAULT_BATCH_SIZE / DEFAULT_ZKP_BATCH_SIZE * 2) as u32, - bloom_filter_capacity: DEFAULT_BATCH_SIZE * 8, + bloom_filter_capacity: STATE_BLOOM_FILTER_CAPACITY, network_fee: Some(5000), rollover_threshold: Some(95), close_threshold: None, diff --git a/programs/account-compression/src/processor/insert_into_queues.rs b/programs/account-compression/src/processor/insert_into_queues.rs index ea05002da7..cd26d63b22 100644 --- a/programs/account-compression/src/processor/insert_into_queues.rs +++ b/programs/account-compression/src/processor/insert_into_queues.rs @@ -14,6 +14,12 @@ pub fn process_insert_into_queues<'a, 'b, 'c: 'info, 'info>( ) -> Result<()> { let (inputs, _) = InsertIntoQueuesInstructionData::zero_copy_at(bytes.as_slice()) .map_err(ProgramError::from)?; + if inputs.addresses.len() > 8 { + return Err(AccountCompressionErrorCode::MaxAddressQueuesExceeded.into()); + } + if inputs.nullifiers.len() > 8 { + return Err(AccountCompressionErrorCode::MaxAddressQueuesExceeded.into()); + } let authority = ctx.accounts.authority.to_account_info(); // Checks accounts for every account in remaining accounts: // 1. program ownership From 6ce68d0a35b9c0e8dc73a159ee546f939b51714a Mon Sep 17 00:00:00 2001 From: ananas-block Date: Mon, 8 Dec 2025 19:53:33 +0000 Subject: [PATCH 2/7] feat: add limits for queue insertion --- programs/account-compression/src/errors.rs | 4 ++++ .../src/processor/insert_into_queues.rs | 12 ++++++++++-- .../src/processor/insert_leaves.rs | 7 +++++++ 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/programs/account-compression/src/errors.rs b/programs/account-compression/src/errors.rs index c39071adf0..d97ac867fe 100644 --- a/programs/account-compression/src/errors.rs +++ b/programs/account-compression/src/errors.rs @@ -66,4 +66,8 @@ pub enum AccountCompressionErrorCode { UnsupportedHeight, UnsupportedParameters, V1AccountMarkedAsProofByIndex, + #[msg("Too many addresses. The maximum is 8 per instruction.")] + TooManyAddresses, + #[msg("Too many nullifiers. The maximum is 8 per instruction.")] + TooManyNullifiers, } diff --git a/programs/account-compression/src/processor/insert_into_queues.rs b/programs/account-compression/src/processor/insert_into_queues.rs index cd26d63b22..720d75c4be 100644 --- a/programs/account-compression/src/processor/insert_into_queues.rs +++ b/programs/account-compression/src/processor/insert_into_queues.rs @@ -15,10 +15,18 @@ pub fn process_insert_into_queues<'a, 'b, 'c: 'info, 'info>( let (inputs, _) = InsertIntoQueuesInstructionData::zero_copy_at(bytes.as_slice()) .map_err(ProgramError::from)?; if inputs.addresses.len() > 8 { - return Err(AccountCompressionErrorCode::MaxAddressQueuesExceeded.into()); + msg!( + "Too many addresses: {}. The maximum is 8.", + inputs.addresses.len() + ); + return Err(AccountCompressionErrorCode::TooManyAddresses.into()); } if inputs.nullifiers.len() > 8 { - return Err(AccountCompressionErrorCode::MaxAddressQueuesExceeded.into()); + msg!( + "Too many nullifiers: {}. The maximum is 8.", + inputs.nullifiers.len() + ); + return Err(AccountCompressionErrorCode::TooManyNullifiers.into()); } let authority = ctx.accounts.authority.to_account_info(); // Checks accounts for every account in remaining accounts: diff --git a/programs/account-compression/src/processor/insert_leaves.rs b/programs/account-compression/src/processor/insert_leaves.rs index d86c4b8fae..5e1a1fe0f3 100644 --- a/programs/account-compression/src/processor/insert_leaves.rs +++ b/programs/account-compression/src/processor/insert_leaves.rs @@ -43,6 +43,13 @@ pub fn insert_leaves<'a, 'b, 'c: 'info, 'info>( // 4. append batch to Merkle tree or insert into output queue match account { AcpAccount::OutputQueue(queue) => { + if end.saturating_sub(start) > 8 { + msg!( + "Too many leaves: {}. The maximum for v2 trees is 8. There is no limit for v1 trees.", + end.saturating_sub(start) + ); + return Err(AccountCompressionErrorCode::TooManyLeaves.into()); + } for leaf in leaves[start..end].iter() { queue .insert_into_current_batch(&leaf.leaf, current_slot) From 855c0acdf5fc4b282f9e7bfb59712cd107adbb33 Mon Sep 17 00:00:00 2001 From: ananas-block Date: Mon, 8 Dec 2025 22:14:09 +0000 Subject: [PATCH 3/7] fix tests --- .../tests/e2e/compress.test.ts | 21 ++++++++++--------- js/compressed-token/tests/e2e/mint-to.test.ts | 12 +++++------ .../batched-merkle-tree/src/constants.rs | 4 ++-- .../tests/mint/random.rs | 4 ++-- .../compressed-token-test/tests/v1.rs | 13 +++++++----- 5 files changed, 29 insertions(+), 25 deletions(-) diff --git a/js/compressed-token/tests/e2e/compress.test.ts b/js/compressed-token/tests/e2e/compress.test.ts index 329f845063..b7f92dc7ad 100644 --- a/js/compressed-token/tests/e2e/compress.test.ts +++ b/js/compressed-token/tests/e2e/compress.test.ts @@ -101,7 +101,8 @@ describe('compress', () => { let stateTreeInfo: TreeInfo; let tokenPoolInfo: TokenPoolInfo; - const maxBatchSize = 15; + // v2 trees have a limit of 8 leaves per insert + const maxBatchSize = 8; const recipients = Array.from( { length: maxBatchSize }, () => Keypair.generate().publicKey, @@ -199,7 +200,7 @@ describe('compress', () => { const amounts = Array.from({ length: maxBatchSize }, (_, i) => bn(i + 1)); - it('should compress to multiple (11 max without LUT) recipients with array of amounts and addresses', async () => { + it('should compress to multiple (8 max for v2 trees) recipients with array of amounts and addresses', async () => { const senderAtaBalanceBefore = await rpc.getTokenAccountBalance(bobAta); const recipientCompressedTokenBalancesBefore = await Promise.all( @@ -208,15 +209,15 @@ describe('compress', () => { ), ); - // compress to 11 recipients + // compress to 8 recipients (max for v2 trees) await compress( rpc, payer, mint, - amounts.slice(0, 11), + amounts.slice(0, 8), bob, bobAta, - recipients.slice(0, 11), + recipients.slice(0, 8), stateTreeInfo, tokenPoolInfo, ); @@ -227,15 +228,15 @@ describe('compress', () => { bn(senderAtaBalanceBefore.value.amount), bobAta, mint, - amounts.slice(0, 11), - recipients.slice(0, 11), + amounts.slice(0, 8), + recipients.slice(0, 8), recipientCompressedTokenBalancesBefore.map(x => x.items), ); } const senderAtaBalanceAfter = await rpc.getTokenAccountBalance(bobAta); const totalCompressed = amounts - .slice(0, 11) + .slice(0, 8) .reduce((sum, amount) => sum.add(amount), bn(0)); expect(senderAtaBalanceAfter.value.amount).toEqual( bn(senderAtaBalanceBefore.value.amount) @@ -250,10 +251,10 @@ describe('compress', () => { rpc, payer, mint, - amounts.slice(0, 10), + amounts.slice(0, 5), bob, bobAta, - recipients.slice(0, 11), + recipients.slice(0, 6), stateTreeInfo, tokenPoolInfo, ), diff --git a/js/compressed-token/tests/e2e/mint-to.test.ts b/js/compressed-token/tests/e2e/mint-to.test.ts index 62c9adc3ed..0c2309385c 100644 --- a/js/compressed-token/tests/e2e/mint-to.test.ts +++ b/js/compressed-token/tests/e2e/mint-to.test.ts @@ -135,8 +135,8 @@ describe('mintTo', () => { ); }); - // const maxRecipients = 18; - const maxRecipients = 22; + // v2 trees have a limit of 8 leaves per insert + const maxRecipients = 8; const recipients = Array.from( { length: maxRecipients }, () => Keypair.generate().publicKey, @@ -156,14 +156,14 @@ describe('mintTo', () => { tokenPoolInfo, ); - /// Mint to 10 recipients + /// Mint to 8 recipients (max for v2 trees) const tx = await mintTo( rpc, payer, mint, - recipients.slice(0, 10), + recipients.slice(0, 8), mintAuthority, - amounts.slice(0, 10), + amounts.slice(0, 8), stateTreeInfo, tokenPoolInfo, ); @@ -185,7 +185,7 @@ describe('mintTo', () => { ); }); - it(`should mint to ${recipients.length} recipients optimized with LUT`, async () => { + it(`should mint to ${maxRecipients} recipients optimized with LUT`, async () => { const lookupTableAccount = (await rpc.getAddressLookupTable(lut)) .value!; diff --git a/program-libs/batched-merkle-tree/src/constants.rs b/program-libs/batched-merkle-tree/src/constants.rs index 9d5ef2a119..51eb85c20c 100644 --- a/program-libs/batched-merkle-tree/src/constants.rs +++ b/program-libs/batched-merkle-tree/src/constants.rs @@ -8,7 +8,7 @@ pub const DEFAULT_BATCH_STATE_TREE_HEIGHT: u32 = 32; pub const DEFAULT_BATCH_ROOT_HISTORY_LEN: u32 = 200; -pub const DEFAULT_ADDRESS_BATCH_ROOT_HISTORY_LEN: u32 = 100; +pub const DEFAULT_ADDRESS_BATCH_ROOT_HISTORY_LEN: u32 = 120; pub const DEFAULT_NUM_BATCHES: u64 = 2; @@ -28,7 +28,7 @@ pub const STATE_BLOOM_FILTER_CAPACITY: u64 = 2_301_536; pub const STATE_BLOOM_FILTER_NUM_HASHES: u64 = 10; // False positive probability 1.0E-12 for 30k elements. -pub const ADDRESS_BLOOM_FILTER_CAPACITY: u64 = 4_603_051; +pub const ADDRESS_BLOOM_FILTER_CAPACITY: u64 = 4_603_056; pub const ADDRESS_BLOOM_FILTER_NUM_HASHES: u64 = 10; #[deprecated(note = "Use DEFAULT_CPI_CONTEXT_ACCOUNT_SIZE_V2 instead")] diff --git a/program-tests/compressed-token-test/tests/mint/random.rs b/program-tests/compressed-token-test/tests/mint/random.rs index a08d85fb95..c5b84509cf 100644 --- a/program-tests/compressed-token-test/tests/mint/random.rs +++ b/program-tests/compressed-token-test/tests/mint/random.rs @@ -182,8 +182,8 @@ async fn test_random_mint_action() { match action_type { // 30% chance: MintToCompressed 0..=299 => { - // Random number of recipients (1-5), but respect the 29 total limit - let max_additional = (29 - total_recipients).min(5); + // Random number of recipients (1-5), but respect the 7 total limit + let max_additional = (7 - total_recipients).min(2); if max_additional > 0 { let num_recipients = rng.gen_range(1..=max_additional); let mut recipients = Vec::new(); diff --git a/program-tests/compressed-token-test/tests/v1.rs b/program-tests/compressed-token-test/tests/v1.rs index 0cfb486e02..917ef63276 100644 --- a/program-tests/compressed-token-test/tests/v1.rs +++ b/program-tests/compressed-token-test/tests/v1.rs @@ -5266,7 +5266,8 @@ async fn mint_with_batched_tree() { .unwrap(); let mint = create_mint_helper(&mut rpc, &payer).await; let amount = 10000u64; - let num_recipients = 30; + // v2 trees have a limit of 8 leaves per insert + let num_recipients = 8; mint_tokens_helper( &mut rpc, &mut test_indexer, @@ -5568,8 +5569,8 @@ async fn batch_compress_with_batched_tree() { ) .await .unwrap(); - // 1. Functional compress 1 to 26 recipients - for num_recipients in 1..=26 { + // 1. Functional compress 1 to 8 recipients (v2 trees have a limit of 8 leaves per insert) + for num_recipients in 1..=8 { let recipients = (0..num_recipients) .map(|_| Pubkey::new_unique()) .collect::>(); @@ -5634,7 +5635,8 @@ async fn batch_compress_with_batched_tree() { sum_amounts + pre_token_pool_balance ); } - for num_recipients in 1..=26 { + // v2 trees have a limit of 8 leaves per insert + for num_recipients in 1..=8 { let recipients = (0..num_recipients) .map(|_| Pubkey::new_unique()) .collect::>(); @@ -5702,7 +5704,8 @@ async fn batch_compress_with_batched_tree() { // 2. Failing unequal recipients amounts len { - let num_recipients = 26; + // Use 8 recipients (max for v2 trees) with mismatched amounts (7) + let num_recipients = 8; let recipients = (0..num_recipients) .map(|_| Pubkey::new_unique()) .collect::>(); From 6fbef702f0b812ebc3c7234df79287e8e75a994c Mon Sep 17 00:00:00 2001 From: ananas-block Date: Mon, 8 Dec 2025 23:06:36 +0000 Subject: [PATCH 4/7] fix --- program-libs/batched-merkle-tree/src/constants.rs | 2 +- .../tests/invoke_cpi_with_read_only.rs | 12 ++++++++---- .../src/processor/insert_into_queues.rs | 4 ++-- .../src/processor/insert_leaves.rs | 4 ++-- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/program-libs/batched-merkle-tree/src/constants.rs b/program-libs/batched-merkle-tree/src/constants.rs index 51eb85c20c..0ecc7df04b 100644 --- a/program-libs/batched-merkle-tree/src/constants.rs +++ b/program-libs/batched-merkle-tree/src/constants.rs @@ -28,7 +28,7 @@ pub const STATE_BLOOM_FILTER_CAPACITY: u64 = 2_301_536; pub const STATE_BLOOM_FILTER_NUM_HASHES: u64 = 10; // False positive probability 1.0E-12 for 30k elements. -pub const ADDRESS_BLOOM_FILTER_CAPACITY: u64 = 4_603_056; +pub const ADDRESS_BLOOM_FILTER_CAPACITY: u64 = 4_603_072; pub const ADDRESS_BLOOM_FILTER_NUM_HASHES: u64 = 10; #[deprecated(note = "Use DEFAULT_CPI_CONTEXT_ACCOUNT_SIZE_V2 instead")] diff --git a/program-tests/system-cpi-v2-test/tests/invoke_cpi_with_read_only.rs b/program-tests/system-cpi-v2-test/tests/invoke_cpi_with_read_only.rs index d9af38e708..f6d52b1d2e 100644 --- a/program-tests/system-cpi-v2-test/tests/invoke_cpi_with_read_only.rs +++ b/program-tests/system-cpi-v2-test/tests/invoke_cpi_with_read_only.rs @@ -92,7 +92,9 @@ async fn functional_read_only() { let payer = rpc.get_payer().insecure_clone(); let mut test_indexer = TestIndexer::init_from_acounts(&payer, &env, 0).await; // Create a bunch of outputs that we can use as inputs. - for _ in 0..5 { + // v2 trees have a limit of 8 leaves per insert, so we need more transactions + let (leaves_per_tx, num_setup_txs) = if batched { (8, 19) } else { (30, 5) }; + for _ in 0..num_setup_txs { let output_accounts = vec![ get_compressed_output_account( true, @@ -102,7 +104,7 @@ async fn functional_read_only() { env.v1_state_trees[0].merkle_tree } ); - 30 + leaves_per_tx ]; local_sdk::perform_test_transaction( &mut rpc, @@ -389,7 +391,9 @@ async fn functional_account_infos() { let payer = rpc.get_payer().insecure_clone(); let mut test_indexer = TestIndexer::init_from_acounts(&payer, &env, 0).await; // Create a bunch of outputs that we can use as inputs. - for _ in 0..5 { + // v2 trees have a limit of 8 leaves per insert, so we need more transactions + let (leaves_per_tx, num_setup_txs) = if batched { (8, 19) } else { (30, 5) }; + for _ in 0..num_setup_txs { let output_accounts = vec![ get_compressed_output_account( true, @@ -399,7 +403,7 @@ async fn functional_account_infos() { env.v1_state_trees[0].merkle_tree } ); - 30 + leaves_per_tx ]; local_sdk::perform_test_transaction( &mut rpc, diff --git a/programs/account-compression/src/processor/insert_into_queues.rs b/programs/account-compression/src/processor/insert_into_queues.rs index 720d75c4be..663d2bd2b0 100644 --- a/programs/account-compression/src/processor/insert_into_queues.rs +++ b/programs/account-compression/src/processor/insert_into_queues.rs @@ -21,9 +21,9 @@ pub fn process_insert_into_queues<'a, 'b, 'c: 'info, 'info>( ); return Err(AccountCompressionErrorCode::TooManyAddresses.into()); } - if inputs.nullifiers.len() > 8 { + if inputs.nullifiers.len() > 10 { msg!( - "Too many nullifiers: {}. The maximum is 8.", + "Too many nullifiers: {}. The maximum is 10.", inputs.nullifiers.len() ); return Err(AccountCompressionErrorCode::TooManyNullifiers.into()); diff --git a/programs/account-compression/src/processor/insert_leaves.rs b/programs/account-compression/src/processor/insert_leaves.rs index 5e1a1fe0f3..cb146c3620 100644 --- a/programs/account-compression/src/processor/insert_leaves.rs +++ b/programs/account-compression/src/processor/insert_leaves.rs @@ -43,9 +43,9 @@ pub fn insert_leaves<'a, 'b, 'c: 'info, 'info>( // 4. append batch to Merkle tree or insert into output queue match account { AcpAccount::OutputQueue(queue) => { - if end.saturating_sub(start) > 8 { + if end.saturating_sub(start) > 10 { msg!( - "Too many leaves: {}. The maximum for v2 trees is 8. There is no limit for v1 trees.", + "Too many leaves: {}. The maximum for v2 trees is 10. There is no limit for v1 trees.", end.saturating_sub(start) ); return Err(AccountCompressionErrorCode::TooManyLeaves.into()); From cce15e116c2ca5e5ed223f2511b25f47655dba43 Mon Sep 17 00:00:00 2001 From: ananas-block Date: Tue, 9 Dec 2025 00:13:16 +0000 Subject: [PATCH 5/7] fix test --- .../tests/transfer2/functional.rs | 122 +++--------------- programs/account-compression/src/errors.rs | 4 +- 2 files changed, 23 insertions(+), 103 deletions(-) diff --git a/program-tests/compressed-token-test/tests/transfer2/functional.rs b/program-tests/compressed-token-test/tests/transfer2/functional.rs index 2e894ad8d1..c868527bbb 100644 --- a/program-tests/compressed-token-test/tests/transfer2/functional.rs +++ b/program-tests/compressed-token-test/tests/transfer2/functional.rs @@ -129,11 +129,10 @@ async fn test_transfer2_functional() { test13_multiple_inputs_single_output(), test14_multiple_inputs_multiple_outputs(), test15_change_account_only(), - // Output Account Limits (16-19) + // Output Account Limits (16-17) + // Note: v2 trees have a limit of 10 leaves per insert, so tests 18-19 were removed test16_single_output_account(), test17_ten_output_accounts(), - test18_twenty_output_accounts(), - test19_maximum_output_accounts(), // Amount Edge Cases (20-25) test20_transfer_zero_tokens(), test21_transfer_one_token(), @@ -262,18 +261,20 @@ fn test3_basic_transfer_sha_flat() -> TestCase { } fn test4_basic_transfer_sha_flat_8() -> TestCase { + // Note: v2 trees have a limit of 8 leaves per insert, so we use 4 transfers + // (4 transfers * 2 outputs each = 8 leaves, which is the max for v2 trees) TestCase { - name: "8 transfers from different signers using ShaFlat (max input limit)".to_string(), - actions: (0..8) // MAX_INPUT_ACCOUNTS is 8 + name: "4 transfers from different signers using ShaFlat (max for v2 trees)".to_string(), + actions: (0..4) // v2 trees limit: 4 transfers * 2 outputs = 8 leaves .map(|i| { MetaTransfer2InstructionType::Transfer(MetaTransferInput { input_compressed_accounts: vec![300], // One account with 300 tokens amount: 100, // Partial transfer to avoid 0-amount change accounts is_delegate_transfer: false, token_data_version: TokenDataVersion::ShaFlat, - signer_index: i, // Each transfer from keypair 0-7 + signer_index: i, // Each transfer from keypair 0-3 delegate_index: None, // Not a delegate transfer - recipient_index: i + 8, // Transfer to keypair 8-15 (no overlap with signers) + recipient_index: i + 8, // Transfer to keypair 8-11 (no overlap with signers) change_amount: None, mint_index: 0, }) @@ -385,11 +386,13 @@ fn test10_basic_transfer_sha_flat_7_inputs() -> TestCase { } fn test11_basic_transfer_sha_flat_8_inputs() -> TestCase { + // Note: v2 trees have a limit of 10 leaves per insert, so we use 5 inputs + // (5 inputs * 2 outputs each = 10 leaves, which is the max for v2 trees) TestCase { - name: "8 transfers from different signers using ShaFlat (max input limit)".to_string(), + name: "5 transfers from different signers using ShaFlat (max for v2 trees)".to_string(), actions: vec![MetaTransfer2InstructionType::Transfer(MetaTransferInput { - input_compressed_accounts: vec![300, 300, 300, 300, 300, 300, 300, 300], // Eight accounts - amount: 2400, + input_compressed_accounts: vec![300, 300, 300, 300, 300], // Five accounts (max for v2) + amount: 1500, is_delegate_transfer: false, token_data_version: TokenDataVersion::ShaFlat, signer_index: 0, // Owner (keypair[0]) signs the transfer @@ -601,10 +604,11 @@ fn test16_single_output_account() -> TestCase { } } -// Test 17: 10 output compressed accounts +// Test 17: 10 output compressed accounts (max for v2 trees) fn test17_ten_output_accounts() -> TestCase { + // Note: v2 trees have a limit of 10 leaves per insert TestCase { - name: "10 output compressed accounts".to_string(), + name: "10 output compressed accounts (max for v2 trees)".to_string(), actions: { let mut actions = vec![]; // Create one large input account to split into 10 outputs @@ -620,12 +624,12 @@ fn test17_ten_output_accounts() -> TestCase { signer_index: 0, delegate_index: None, recipient_index: 1, - change_amount: Some(0), // Keep remaining as change + change_amount: Some(0), // No change account (amount = 0) mint_index: 0, })); - // 9 more transfers using the change from the first transfer - for i in 1..10 { + // 7 more transfers using the change from the first transfer (8 total outputs) + for i in 1..8 { actions.push(MetaTransfer2InstructionType::Transfer(MetaTransferInput { input_compressed_accounts: vec![], // Use change from previous amount: amount_per_output, @@ -633,92 +637,8 @@ fn test17_ten_output_accounts() -> TestCase { token_data_version: TokenDataVersion::ShaFlat, signer_index: 0, delegate_index: None, - recipient_index: i + 1, // Recipients 2-10 - change_amount: Some(0), - mint_index: 0, - })); - } - - actions - }, - } -} - -// Test 18: 20 output compressed accounts -fn test18_twenty_output_accounts() -> TestCase { - TestCase { - name: "20 output compressed accounts".to_string(), - actions: { - let mut actions = vec![]; - let total_amount = 2000u64; - let amount_per_output = 100u64; - - // First transfer with input account - actions.push(MetaTransfer2InstructionType::Transfer(MetaTransferInput { - input_compressed_accounts: vec![total_amount], - amount: amount_per_output, - is_delegate_transfer: false, - token_data_version: TokenDataVersion::ShaFlat, - signer_index: 0, - delegate_index: None, - recipient_index: 1, - change_amount: Some(0), - mint_index: 0, - })); - - // 19 more transfers using the change - for i in 1..20 { - actions.push(MetaTransfer2InstructionType::Transfer(MetaTransferInput { - input_compressed_accounts: vec![], - amount: amount_per_output, - is_delegate_transfer: false, - token_data_version: TokenDataVersion::ShaFlat, - signer_index: 0, - delegate_index: None, - recipient_index: i + 1, // Recipients 2-20 - change_amount: Some(0), - mint_index: 0, - })); - } - - actions - }, - } -} - -// Test 19: 35 output compressed accounts (maximum per instruction) -fn test19_maximum_output_accounts() -> TestCase { - TestCase { - name: "35 output compressed accounts (maximum)".to_string(), - actions: { - let mut actions = vec![]; - let total_amount = 2900u64; // 35 * 100 - let amount_per_output = 100u64; - - // First transfer with input account - actions.push(MetaTransfer2InstructionType::Transfer(MetaTransferInput { - input_compressed_accounts: vec![total_amount], - amount: amount_per_output, - is_delegate_transfer: false, - token_data_version: TokenDataVersion::ShaFlat, - signer_index: 0, - delegate_index: None, - recipient_index: 1, - change_amount: Some(0), - mint_index: 0, - })); - - // 34 more transfers to reach the maximum of 35 outputs - for i in 1..29 { - actions.push(MetaTransfer2InstructionType::Transfer(MetaTransferInput { - input_compressed_accounts: vec![], - amount: amount_per_output, - is_delegate_transfer: false, - token_data_version: TokenDataVersion::ShaFlat, - signer_index: 0, - delegate_index: None, - recipient_index: i + 1, // Recipients 2-35 - change_amount: Some(0), + recipient_index: i + 1, // Recipients 2-8 + change_amount: Some(0), // No change account mint_index: 0, })); } diff --git a/programs/account-compression/src/errors.rs b/programs/account-compression/src/errors.rs index d97ac867fe..fe8bdcc9a1 100644 --- a/programs/account-compression/src/errors.rs +++ b/programs/account-compression/src/errors.rs @@ -54,7 +54,7 @@ pub enum AccountCompressionErrorCode { #[msg("RolloverNotConfigured")] RolloverNotConfigured, StateMerkleTreeAccountDiscriminatorMismatch, - #[msg("The maximum number of leaves is 255")] + #[msg("The maximum number of leaves is v1 30, v2 10")] TooManyLeaves, TxHashUndefined, UnsupportedAdditionalBytes, @@ -68,6 +68,6 @@ pub enum AccountCompressionErrorCode { V1AccountMarkedAsProofByIndex, #[msg("Too many addresses. The maximum is 8 per instruction.")] TooManyAddresses, - #[msg("Too many nullifiers. The maximum is 8 per instruction.")] + #[msg("Too many nullifiers. The maximum is 10 per instruction.")] TooManyNullifiers, } From 5fea71647bfcf65c8effec5d3eedea9ad3c957b3 Mon Sep 17 00:00:00 2001 From: ananas-block Date: Tue, 9 Dec 2025 00:38:57 +0000 Subject: [PATCH 6/7] fix test --- .../tests/address_merkle_tree_tests.rs | 5 +++-- .../compressed-token-test/tests/transfer2/functional.rs | 8 ++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/program-tests/account-compression-test/tests/address_merkle_tree_tests.rs b/program-tests/account-compression-test/tests/address_merkle_tree_tests.rs index 1a67f70052..9a306a485c 100644 --- a/program-tests/account-compression-test/tests/address_merkle_tree_tests.rs +++ b/program-tests/account-compression-test/tests/address_merkle_tree_tests.rs @@ -1042,9 +1042,10 @@ async fn update_address_merkle_tree_wrap_around( .unwrap(); // Wrap around the indexed changelog with conflicting elements. + // Note: address insertions are limited to 8 per instruction let mut rng = thread_rng(); - for _ in (0..merkle_tree_config.address_changelog_size).step_by(10) { - let addresses: Vec<[u8; 32]> = (0..10) + for _ in (0..merkle_tree_config.address_changelog_size).step_by(8) { + let addresses: Vec<[u8; 32]> = (0..8) .map(|_| { Fr::rand(&mut rng) .into_bigint() diff --git a/program-tests/compressed-token-test/tests/transfer2/functional.rs b/program-tests/compressed-token-test/tests/transfer2/functional.rs index c868527bbb..174b1f4090 100644 --- a/program-tests/compressed-token-test/tests/transfer2/functional.rs +++ b/program-tests/compressed-token-test/tests/transfer2/functional.rs @@ -604,15 +604,15 @@ fn test16_single_output_account() -> TestCase { } } -// Test 17: 10 output compressed accounts (max for v2 trees) +// Test 17: 8 output compressed accounts (max for v2 trees) fn test17_ten_output_accounts() -> TestCase { // Note: v2 trees have a limit of 10 leaves per insert TestCase { - name: "10 output compressed accounts (max for v2 trees)".to_string(), + name: "8 output compressed accounts (max for v2 trees)".to_string(), actions: { let mut actions = vec![]; - // Create one large input account to split into 10 outputs - let total_amount = 1000u64; + // Create one large input account to split into 8 outputs + let total_amount = 800u64; let amount_per_output = 100u64; // First transfer with input account, creates change for subsequent transfers From 52b0a74cff851aac546f194c191f2951201c1154 Mon Sep 17 00:00:00 2001 From: ananas-block Date: Wed, 10 Dec 2025 16:10:06 +0000 Subject: [PATCH 7/7] fix: correct test values --- js/compressed-token/tests/e2e/compress.test.ts | 18 +++++++++--------- js/compressed-token/tests/e2e/mint-to.test.ts | 10 +++++----- programs/account-compression/src/errors.rs | 2 +- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/js/compressed-token/tests/e2e/compress.test.ts b/js/compressed-token/tests/e2e/compress.test.ts index b7f92dc7ad..7c6a1f175e 100644 --- a/js/compressed-token/tests/e2e/compress.test.ts +++ b/js/compressed-token/tests/e2e/compress.test.ts @@ -101,8 +101,8 @@ describe('compress', () => { let stateTreeInfo: TreeInfo; let tokenPoolInfo: TokenPoolInfo; - // v2 trees have a limit of 8 leaves per insert - const maxBatchSize = 8; + // v2 trees have a limit of 10 leaves per insert + const maxBatchSize = 10; const recipients = Array.from( { length: maxBatchSize }, () => Keypair.generate().publicKey, @@ -200,7 +200,7 @@ describe('compress', () => { const amounts = Array.from({ length: maxBatchSize }, (_, i) => bn(i + 1)); - it('should compress to multiple (8 max for v2 trees) recipients with array of amounts and addresses', async () => { + it('should compress to multiple (10 max for v2 trees) recipients with array of amounts and addresses', async () => { const senderAtaBalanceBefore = await rpc.getTokenAccountBalance(bobAta); const recipientCompressedTokenBalancesBefore = await Promise.all( @@ -209,15 +209,15 @@ describe('compress', () => { ), ); - // compress to 8 recipients (max for v2 trees) + // compress to 10 recipients (max for v2 trees) await compress( rpc, payer, mint, - amounts.slice(0, 8), + amounts.slice(0, 10), bob, bobAta, - recipients.slice(0, 8), + recipients.slice(0, 10), stateTreeInfo, tokenPoolInfo, ); @@ -228,15 +228,15 @@ describe('compress', () => { bn(senderAtaBalanceBefore.value.amount), bobAta, mint, - amounts.slice(0, 8), - recipients.slice(0, 8), + amounts.slice(0, 10), + recipients.slice(0, 10), recipientCompressedTokenBalancesBefore.map(x => x.items), ); } const senderAtaBalanceAfter = await rpc.getTokenAccountBalance(bobAta); const totalCompressed = amounts - .slice(0, 8) + .slice(0, 10) .reduce((sum, amount) => sum.add(amount), bn(0)); expect(senderAtaBalanceAfter.value.amount).toEqual( bn(senderAtaBalanceBefore.value.amount) diff --git a/js/compressed-token/tests/e2e/mint-to.test.ts b/js/compressed-token/tests/e2e/mint-to.test.ts index 0c2309385c..57e7b80483 100644 --- a/js/compressed-token/tests/e2e/mint-to.test.ts +++ b/js/compressed-token/tests/e2e/mint-to.test.ts @@ -135,8 +135,8 @@ describe('mintTo', () => { ); }); - // v2 trees have a limit of 8 leaves per insert - const maxRecipients = 8; + // v2 trees have a limit of 10 leaves per insert + const maxRecipients = 10; const recipients = Array.from( { length: maxRecipients }, () => Keypair.generate().publicKey, @@ -156,14 +156,14 @@ describe('mintTo', () => { tokenPoolInfo, ); - /// Mint to 8 recipients (max for v2 trees) + /// Mint to 10 recipients (max for v2 trees) const tx = await mintTo( rpc, payer, mint, - recipients.slice(0, 8), + recipients.slice(0, 10), mintAuthority, - amounts.slice(0, 8), + amounts.slice(0, 10), stateTreeInfo, tokenPoolInfo, ); diff --git a/programs/account-compression/src/errors.rs b/programs/account-compression/src/errors.rs index fe8bdcc9a1..30377e4d85 100644 --- a/programs/account-compression/src/errors.rs +++ b/programs/account-compression/src/errors.rs @@ -54,7 +54,7 @@ pub enum AccountCompressionErrorCode { #[msg("RolloverNotConfigured")] RolloverNotConfigured, StateMerkleTreeAccountDiscriminatorMismatch, - #[msg("The maximum number of leaves is v1 30, v2 10")] + #[msg("The maximum number of leaves is 30 for v1 and 10 for v2")] TooManyLeaves, TxHashUndefined, UnsupportedAdditionalBytes,