Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 11 additions & 10 deletions js/compressed-token/tests/e2e/compress.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,8 @@ describe('compress', () => {
let stateTreeInfo: TreeInfo;
let tokenPoolInfo: TokenPoolInfo;

const maxBatchSize = 15;
// v2 trees have a limit of 10 leaves per insert
const maxBatchSize = 10;
const recipients = Array.from(
{ length: maxBatchSize },
() => Keypair.generate().publicKey,
Expand Down Expand Up @@ -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 (10 max for v2 trees) recipients with array of amounts and addresses', async () => {
const senderAtaBalanceBefore = await rpc.getTokenAccountBalance(bobAta);

const recipientCompressedTokenBalancesBefore = await Promise.all(
Expand All @@ -208,15 +209,15 @@ describe('compress', () => {
),
);

// compress to 11 recipients
// compress to 10 recipients (max for v2 trees)
await compress(
rpc,
payer,
mint,
amounts.slice(0, 11),
amounts.slice(0, 10),
bob,
bobAta,
recipients.slice(0, 11),
recipients.slice(0, 10),
stateTreeInfo,
tokenPoolInfo,
);
Expand All @@ -227,15 +228,15 @@ describe('compress', () => {
bn(senderAtaBalanceBefore.value.amount),
bobAta,
mint,
amounts.slice(0, 11),
recipients.slice(0, 11),
amounts.slice(0, 10),
recipients.slice(0, 10),
recipientCompressedTokenBalancesBefore.map(x => x.items),
);
}

const senderAtaBalanceAfter = await rpc.getTokenAccountBalance(bobAta);
const totalCompressed = amounts
.slice(0, 11)
.slice(0, 10)
.reduce((sum, amount) => sum.add(amount), bn(0));
expect(senderAtaBalanceAfter.value.amount).toEqual(
bn(senderAtaBalanceBefore.value.amount)
Expand All @@ -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,
),
Expand Down
8 changes: 4 additions & 4 deletions js/compressed-token/tests/e2e/mint-to.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,8 +135,8 @@ describe('mintTo', () => {
);
});

// const maxRecipients = 18;
const maxRecipients = 22;
// v2 trees have a limit of 10 leaves per insert
const maxRecipients = 10;
const recipients = Array.from(
{ length: maxRecipients },
() => Keypair.generate().publicKey,
Expand All @@ -156,7 +156,7 @@ describe('mintTo', () => {
tokenPoolInfo,
);

/// Mint to 10 recipients
/// Mint to 10 recipients (max for v2 trees)
const tx = await mintTo(
rpc,
payer,
Expand Down Expand Up @@ -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!;

Expand Down
8 changes: 7 additions & 1 deletion program-libs/batched-merkle-tree/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 = 120;

pub const DEFAULT_NUM_BATCHES: u64 = 2;

pub const TEST_DEFAULT_BATCH_SIZE: u64 = 50;
Expand All @@ -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_072;
pub const ADDRESS_BLOOM_FILTER_NUM_HASHES: u64 = 10;

#[deprecated(note = "Use DEFAULT_CPI_CONTEXT_ACCOUNT_SIZE_V2 instead")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
4 changes: 2 additions & 2 deletions program-tests/compressed-token-test/tests/mint/random.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
126 changes: 23 additions & 103 deletions program-tests/compressed-token-test/tests/transfer2/functional.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down Expand Up @@ -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,
})
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -601,14 +604,15 @@ fn test16_single_output_account() -> TestCase {
}
}

// Test 17: 10 output compressed accounts
// 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".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
Expand All @@ -620,105 +624,21 @@ 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,
is_delegate_transfer: false,
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,
}));
}
Expand Down
Loading