From cbd1db2a15d3e6d23d053abf53c60b446975b17b Mon Sep 17 00:00:00 2001 From: ananas-block Date: Mon, 8 Dec 2025 15:42:32 +0000 Subject: [PATCH 1/7] fix(registry): add InvalidTokenAccountData error and prevent pausing deprecated config --- .../src/compressible/compressed_token/compress_and_close.rs | 2 +- programs/registry/src/errors.rs | 2 ++ programs/registry/src/lib.rs | 5 +++++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/programs/registry/src/compressible/compressed_token/compress_and_close.rs b/programs/registry/src/compressible/compressed_token/compress_and_close.rs index bfe608e15c..5b63dec098 100644 --- a/programs/registry/src/compressible/compressed_token/compress_and_close.rs +++ b/programs/registry/src/compressible/compressed_token/compress_and_close.rs @@ -88,7 +88,7 @@ pub fn compress_and_close_ctoken_accounts_with_indices<'info>( let amount = CToken::amount_from_slice(&account_data).map_err(|e| { anchor_lang::prelude::msg!("Failed to read amount from CToken: {:?}", e); - RegistryError::InvalidSigner + RegistryError::InvalidTokenAccountData })?; // Create one output account per compression operation diff --git a/programs/registry/src/errors.rs b/programs/registry/src/errors.rs index 6d3fca8cf1..3d4c2565c9 100644 --- a/programs/registry/src/errors.rs +++ b/programs/registry/src/errors.rs @@ -34,4 +34,6 @@ pub enum RegistryError { ProgramOwnerUndefined, #[msg("Invalid config state for this operation")] InvalidConfigState, + #[msg("Failed to deserialize token account data")] + InvalidTokenAccountData, } diff --git a/programs/registry/src/lib.rs b/programs/registry/src/lib.rs index e56bc87fec..983dd32d26 100644 --- a/programs/registry/src/lib.rs +++ b/programs/registry/src/lib.rs @@ -733,6 +733,11 @@ pub mod light_registry { /// 2. claiming from rent sponsor /// 3. witdrawal from rent sponsor pub fn pause_compressible_config(ctx: Context) -> Result<()> { + // Cannot pause a deprecated config + require!( + ctx.accounts.compressible_config.state != 2, + RegistryError::InvalidConfigState + ); ctx.accounts.compressible_config.state = 0; Ok(()) } From fbb43c23fa5b185d66270206100776c15fc5083b Mon Sep 17 00:00:00 2001 From: ananas-block Date: Mon, 8 Dec 2025 15:53:59 +0000 Subject: [PATCH 2/7] fix(registry): replace InvalidSigner with specific error variants --- programs/registry/src/compressible/compress_and_close.rs | 2 +- .../src/compressible/compressed_token/compress_and_close.rs | 4 ++-- programs/registry/src/errors.rs | 6 ++++++ 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/programs/registry/src/compressible/compress_and_close.rs b/programs/registry/src/compressible/compress_and_close.rs index 1a73394706..534865160b 100644 --- a/programs/registry/src/compressible/compress_and_close.rs +++ b/programs/registry/src/compressible/compress_and_close.rs @@ -44,7 +44,7 @@ pub fn process_compress_and_close<'c: 'info, 'info>( .map_err(ProgramError::from)?; // Validate indices - require!(!indices.is_empty(), RegistryError::InvalidSigner); + require!(!indices.is_empty(), RegistryError::EmptyIndices); let fee_payer = ctx.accounts.authority.to_account_info(); diff --git a/programs/registry/src/compressible/compressed_token/compress_and_close.rs b/programs/registry/src/compressible/compressed_token/compress_and_close.rs index 5b63dec098..915c23a1b4 100644 --- a/programs/registry/src/compressible/compressed_token/compress_and_close.rs +++ b/programs/registry/src/compressible/compressed_token/compress_and_close.rs @@ -84,7 +84,7 @@ pub fn compress_and_close_ctoken_accounts_with_indices<'info>( let account_data = source_account .try_borrow_data() - .map_err(|_| RegistryError::InvalidSigner)?; + .map_err(|_| RegistryError::BorrowAccountDataFailed)?; let amount = CToken::amount_from_slice(&account_data).map_err(|e| { anchor_lang::prelude::msg!("Failed to read amount from CToken: {:?}", e); @@ -143,7 +143,7 @@ pub fn compress_and_close_ctoken_accounts_with_indices<'info>( // Serialize instruction data let serialized = instruction_data .try_to_vec() - .map_err(|_| RegistryError::InvalidSigner)?; + .map_err(|_| RegistryError::SerializationFailed)?; // Build instruction data with discriminator let mut data = Vec::with_capacity(1 + serialized.len()); diff --git a/programs/registry/src/errors.rs b/programs/registry/src/errors.rs index 3d4c2565c9..7c445d2ca3 100644 --- a/programs/registry/src/errors.rs +++ b/programs/registry/src/errors.rs @@ -36,4 +36,10 @@ pub enum RegistryError { InvalidConfigState, #[msg("Failed to deserialize token account data")] InvalidTokenAccountData, + #[msg("Indices array cannot be empty")] + EmptyIndices, + #[msg("Failed to borrow account data")] + BorrowAccountDataFailed, + #[msg("Failed to serialize instruction data")] + SerializationFailed, } From 945d3cc71b2aecd214c492935b7d62511dd8bc71 Mon Sep 17 00:00:00 2001 From: ananas-block Date: Mon, 8 Dec 2025 16:37:06 +0000 Subject: [PATCH 3/7] chore: move top up tests into separate file --- .../compressible/tests/compression_info.rs | 261 ---------------- program-libs/compressible/tests/top_up.rs | 278 ++++++++++++++++++ 2 files changed, 278 insertions(+), 261 deletions(-) create mode 100644 program-libs/compressible/tests/top_up.rs diff --git a/program-libs/compressible/tests/compression_info.rs b/program-libs/compressible/tests/compression_info.rs index 3f83487749..2ccf10619e 100644 --- a/program-libs/compressible/tests/compression_info.rs +++ b/program-libs/compressible/tests/compression_info.rs @@ -330,264 +330,3 @@ fn test_get_last_paid_epoch() { ); } } - -#[test] -fn test_calculate_top_up_lamports() { - let rent_exemption_lamports = get_rent_exemption_lamports(TEST_BYTES); - let lamports_per_write = 5000u32; - - #[derive(Debug)] - struct TestCase { - name: &'static str, - current_slot: u64, - current_lamports: u64, - last_claimed_slot: u64, - lamports_per_write: u32, - expected_top_up: u64, - description: &'static str, - } - - let test_cases = vec![ - // ============================================================ - // PATH 1: COMPRESSIBLE CASES (lamports_per_write + rent_deficit) - // ============================================================ - TestCase { - name: "instant compressibility - account created with only rent exemption + compression cost", - current_slot: 0, - current_lamports: rent_exemption_lamports + FULL_COMPRESSION_COSTS, - last_claimed_slot: 0, - lamports_per_write, - expected_top_up: lamports_per_write as u64 + RENT_PER_EPOCH + FULL_COMPRESSION_COSTS, - description: "Epoch 0: available_balance=0, required_epochs=1, deficit includes 1 epoch + compression_cost", - }, - TestCase { - name: "partial epoch rent in epoch 0", - current_slot: 100, - current_lamports: rent_exemption_lamports + FULL_COMPRESSION_COSTS + (RENT_PER_EPOCH / 2), - last_claimed_slot: 0, - lamports_per_write, - expected_top_up: lamports_per_write as u64 + (RENT_PER_EPOCH / 2) + FULL_COMPRESSION_COSTS, - description: "Epoch 0: available_balance=194 (0.5 epochs), required=388 (1 epoch), compressible with deficit of 0.5 epoch", - }, - TestCase { - name: "epoch boundary crossing - becomes compressible", - current_slot: SLOTS_PER_EPOCH + 1, - current_lamports: rent_exemption_lamports + FULL_COMPRESSION_COSTS + RENT_PER_EPOCH, - last_claimed_slot: 0, - lamports_per_write, - expected_top_up: lamports_per_write as u64 + RENT_PER_EPOCH + FULL_COMPRESSION_COSTS, - description: "Epoch 1: available_balance=388 (1 epoch), required_epochs=2 (epochs 1+2), deficit=1 epoch + compression_cost", - }, - TestCase { - name: "many epochs behind (10 epochs)", - current_slot: SLOTS_PER_EPOCH * 10, - current_lamports: rent_exemption_lamports + FULL_COMPRESSION_COSTS + RENT_PER_EPOCH, - last_claimed_slot: 0, - lamports_per_write, - expected_top_up: lamports_per_write as u64 + (RENT_PER_EPOCH * 10) + FULL_COMPRESSION_COSTS, - description: "Epoch 10: available_balance=388 (1 epoch), required_epochs=11 (epochs 0-10 + next), deficit=10 epochs + compression_cost", - }, - TestCase { - name: "extreme epoch gap - 10,000 epochs behind", - current_slot: SLOTS_PER_EPOCH * 10_000, - current_lamports: rent_exemption_lamports + FULL_COMPRESSION_COSTS, - last_claimed_slot: 0, - lamports_per_write, - expected_top_up: lamports_per_write as u64 + (RENT_PER_EPOCH * 10_001) + FULL_COMPRESSION_COSTS, - description: "Epoch 10,000: available_balance=0, required_epochs=10,001, deficit includes all 10,001 epochs", - }, - TestCase { - name: "one lamport short of required rent", - current_slot: SLOTS_PER_EPOCH, - current_lamports: rent_exemption_lamports + FULL_COMPRESSION_COSTS + (RENT_PER_EPOCH * 2) - 1, - last_claimed_slot: 0, - lamports_per_write, - expected_top_up: lamports_per_write as u64 + 1 + FULL_COMPRESSION_COSTS, - description: "Epoch 1: available_balance=775 (1.997 epochs), required=776 (2 epochs), compressible with 1 lamport deficit", - }, - // ============================================================ - // PATH 2: NOT COMPRESSIBLE, NEEDS TOP-UP (lamports_per_write) - // ============================================================ - TestCase { - name: "exact boundary - lagging claim requires top-up", - current_slot: SLOTS_PER_EPOCH, - current_lamports: rent_exemption_lamports + FULL_COMPRESSION_COSTS + (RENT_PER_EPOCH * 2), - last_claimed_slot: 0, - lamports_per_write, - expected_top_up: lamports_per_write as u64, - description: "Epoch 1: last_claimed=epoch 0, funded through epoch 1, epochs_funded_ahead=1 < max=2", - }, - TestCase { - name: "exactly 1 epoch funded (max is 2)", - current_slot: 0, - current_lamports: rent_exemption_lamports + FULL_COMPRESSION_COSTS + RENT_PER_EPOCH, - last_claimed_slot: 0, - lamports_per_write, - expected_top_up: lamports_per_write as u64, - description: "Epoch 0: not compressible, epochs_funded_ahead=1 < max_funded_epochs=2, needs write top-up only", - }, - TestCase { - name: "1.5 epochs funded (rounds down to 1)", - current_slot: 0, - current_lamports: rent_exemption_lamports + FULL_COMPRESSION_COSTS + (RENT_PER_EPOCH * 3 / 2), - last_claimed_slot: 0, - lamports_per_write, - expected_top_up: lamports_per_write as u64, - description: "Epoch 0: not compressible, epochs_funded_ahead=582/388=1 (rounds down) < max_funded_epochs=2", - }, - TestCase { - name: "fractional epoch - 1.99 epochs rounds down", - current_slot: 0, - current_lamports: rent_exemption_lamports + FULL_COMPRESSION_COSTS + (RENT_PER_EPOCH * 2) - 1, - last_claimed_slot: 0, - lamports_per_write, - expected_top_up: lamports_per_write as u64, - description: "Epoch 0: not compressible, epochs_funded_ahead=775/388=1 (rounds down) < max_funded_epochs=2", - }, - TestCase { - name: "epoch boundary with 1 epoch funded", - current_slot: SLOTS_PER_EPOCH - 1, - current_lamports: rent_exemption_lamports + FULL_COMPRESSION_COSTS + RENT_PER_EPOCH, - last_claimed_slot: 0, - lamports_per_write, - expected_top_up: lamports_per_write as u64, - description: "Last slot of epoch 0: not compressible, epochs_funded_ahead=1 < max_funded_epochs=2", - }, - TestCase { - name: "account created in later epoch with 1 epoch rent", - current_slot: SLOTS_PER_EPOCH * 5, - current_lamports: rent_exemption_lamports + FULL_COMPRESSION_COSTS + RENT_PER_EPOCH, - last_claimed_slot: SLOTS_PER_EPOCH * 5, - lamports_per_write, - expected_top_up: lamports_per_write as u64, - description: "Epoch 5: created same epoch, not compressible, epochs_funded_ahead=1 < max_funded_epochs=2", - }, - // ============================================================ - // PATH 3: WELL-FUNDED (0 lamports) - // ============================================================ - TestCase { - name: "2 epochs funded - needs top-up (only 1 ahead)", - current_slot: 0, - current_lamports: rent_exemption_lamports + FULL_COMPRESSION_COSTS + (RENT_PER_EPOCH * 2), - last_claimed_slot: 0, - lamports_per_write, - expected_top_up: lamports_per_write as u64, - description: "Epoch 0: funded through epoch 1, epochs_funded_ahead=1 < max=2, needs top-up", - }, - TestCase { - name: "3 epochs when max is 2 - exactly at target", - current_slot: 0, - current_lamports: rent_exemption_lamports + FULL_COMPRESSION_COSTS + (RENT_PER_EPOCH * 3), - last_claimed_slot: 0, - lamports_per_write, - expected_top_up: 0, - description: "Epoch 0: funded through epoch 2, epochs_funded_ahead=2 >= max_funded_epochs=2", - }, - TestCase { - name: "2 epochs at epoch 1 boundary - needs top-up", - current_slot: SLOTS_PER_EPOCH, - current_lamports: rent_exemption_lamports + FULL_COMPRESSION_COSTS + (RENT_PER_EPOCH * 2), - last_claimed_slot: SLOTS_PER_EPOCH, - lamports_per_write, - expected_top_up: lamports_per_write as u64, - description: "Epoch 1: funded through epoch 2, epochs_funded_ahead=1 < max_funded_epochs=2", - }, - // ============================================================ - // EDGE CASES - // ============================================================ - TestCase { - name: "zero lamports_per_write - compressible case", - current_slot: 0, - current_lamports: rent_exemption_lamports + FULL_COMPRESSION_COSTS, - last_claimed_slot: 0, - lamports_per_write: 0, - expected_top_up: RENT_PER_EPOCH + FULL_COMPRESSION_COSTS, - description: "Zero write fee + compressible state: top_up = 0 + deficit (rent + compression_cost)", - }, - TestCase { - name: "zero lamports_per_write - needs top-up but write fee is 0", - current_slot: 0, - current_lamports: rent_exemption_lamports + FULL_COMPRESSION_COSTS + (RENT_PER_EPOCH * 2), - last_claimed_slot: 0, - lamports_per_write: 0, - expected_top_up: 0, - description: "Zero write fee: epochs_funded_ahead=1 < max=2, but lamports_per_write=0 so top_up=0", - }, - TestCase { - name: "large lamports_per_write", - current_slot: 0, - current_lamports: rent_exemption_lamports + FULL_COMPRESSION_COSTS + RENT_PER_EPOCH, - last_claimed_slot: 0, - lamports_per_write: 1_000_000, - expected_top_up: 1_000_000, - description: "Large write fee (1M): not compressible, epochs_funded_ahead=1 < max_funded_epochs=2", - }, - TestCase { - name: "underflow protection - zero available balance", - current_slot: 0, - current_lamports: rent_exemption_lamports, // NOTE: Invalid state - missing compression_cost, but tests saturating_sub behavior - last_claimed_slot: 0, - lamports_per_write, - expected_top_up: lamports_per_write as u64 + RENT_PER_EPOCH + FULL_COMPRESSION_COSTS, - description: "Invalid state: current_lamports < rent_exemption+compression_cost, saturating_sub → available_balance=0", - }, - // ============================================================ - // LAGGING CLAIMS - verifies arrears don't count as "funded ahead" - // ============================================================ - TestCase { - name: "lagging claim - 5 epochs funded but only 1 ahead due to arrears", - current_slot: SLOTS_PER_EPOCH * 3, // epoch 3 - current_lamports: rent_exemption_lamports + FULL_COMPRESSION_COSTS + (RENT_PER_EPOCH * 5), - last_claimed_slot: 0, // epoch 0 - lagging 3 epochs behind - lamports_per_write, - expected_top_up: lamports_per_write as u64, - description: "Epoch 3: 5 epochs funded from epoch 0 covers through epoch 4. epochs_funded_ahead = 4 - 3 = 1 < max=2, needs top-up", - }, - TestCase { - name: "lagging claim - 6 epochs funded, exactly 2 ahead", - current_slot: SLOTS_PER_EPOCH * 3, // epoch 3 - current_lamports: rent_exemption_lamports + FULL_COMPRESSION_COSTS + (RENT_PER_EPOCH * 6), - last_claimed_slot: 0, // epoch 0 - lagging 3 epochs behind - lamports_per_write, - expected_top_up: 0, - description: "Epoch 3: 6 epochs funded from epoch 0 covers through epoch 5. epochs_funded_ahead = 5 - 3 = 2 >= max=2, no top-up", - }, - TestCase { - name: "lagging claim - just covers arrears plus current+next, no buffer", - current_slot: SLOTS_PER_EPOCH * 3, // epoch 3 - current_lamports: rent_exemption_lamports + FULL_COMPRESSION_COSTS + (RENT_PER_EPOCH * 4), - last_claimed_slot: 0, // epoch 0 - lamports_per_write, - expected_top_up: lamports_per_write as u64, - description: "Epoch 3: 4 epochs funded (0,1,2,3) covers through epoch 3. epochs_funded_ahead = 3 - 3 = 0 < max=2, needs top-up", - }, - ]; - - for test_case in test_cases { - let extension = CompressionInfo { - account_version: 3, - config_account_version: 1, - compression_authority: [0u8; 32], - rent_sponsor: [0u8; 32], - last_claimed_slot: test_case.last_claimed_slot, - lamports_per_write: test_case.lamports_per_write, - compress_to_pubkey: 0, - rent_config: test_rent_config(), - }; - - let top_up = extension - .calculate_top_up_lamports( - TEST_BYTES, - test_case.current_slot, - test_case.current_lamports, - rent_exemption_lamports, - ) - .unwrap(); - - assert_eq!( - top_up, test_case.expected_top_up, - "\nTest '{}' failed:\n Description: {}\n Expected: {}\n Got: {}\n Test case: {:?}", - test_case.name, test_case.description, test_case.expected_top_up, top_up, test_case - ); - } -} diff --git a/program-libs/compressible/tests/top_up.rs b/program-libs/compressible/tests/top_up.rs new file mode 100644 index 0000000000..1d25ea0afd --- /dev/null +++ b/program-libs/compressible/tests/top_up.rs @@ -0,0 +1,278 @@ +#![cfg(test)] +use light_compressible::{ + compression_info::CompressionInfo, + rent::{RentConfig, COMPRESSION_COST, COMPRESSION_INCENTIVE, SLOTS_PER_EPOCH}, +}; + +const TEST_BYTES: u64 = 260; +const RENT_PER_EPOCH: u64 = 260 + 128; +const FULL_COMPRESSION_COSTS: u64 = (COMPRESSION_COST + COMPRESSION_INCENTIVE) as u64; + +fn test_rent_config() -> RentConfig { + RentConfig::default() +} + +pub fn get_rent_exemption_lamports(_num_bytes: u64) -> u64 { + 2700480 +} + +#[test] +fn test_calculate_top_up_lamports() { + let rent_exemption_lamports = get_rent_exemption_lamports(TEST_BYTES); + let lamports_per_write = 5000u32; + + #[derive(Debug)] + struct TestCase { + name: &'static str, + current_slot: u64, + current_lamports: u64, + last_claimed_slot: u64, + lamports_per_write: u32, + expected_top_up: u64, + description: &'static str, + } + + let test_cases = vec![ + // ============================================================ + // PATH 1: COMPRESSIBLE CASES (lamports_per_write + rent_deficit) + // ============================================================ + TestCase { + name: "instant compressibility - account created with only rent exemption + compression cost", + current_slot: 0, + current_lamports: rent_exemption_lamports + FULL_COMPRESSION_COSTS, + last_claimed_slot: 0, + lamports_per_write, + expected_top_up: lamports_per_write as u64 + RENT_PER_EPOCH + FULL_COMPRESSION_COSTS, + description: "Epoch 0: available_balance=0, required_epochs=1, deficit includes 1 epoch + compression_cost", + }, + TestCase { + name: "partial epoch rent in epoch 0", + current_slot: 100, + current_lamports: rent_exemption_lamports + FULL_COMPRESSION_COSTS + (RENT_PER_EPOCH / 2), + last_claimed_slot: 0, + lamports_per_write, + expected_top_up: lamports_per_write as u64 + (RENT_PER_EPOCH / 2) + FULL_COMPRESSION_COSTS, + description: "Epoch 0: available_balance=194 (0.5 epochs), required=388 (1 epoch), compressible with deficit of 0.5 epoch", + }, + TestCase { + name: "epoch boundary crossing - becomes compressible", + current_slot: SLOTS_PER_EPOCH + 1, + current_lamports: rent_exemption_lamports + FULL_COMPRESSION_COSTS + RENT_PER_EPOCH, + last_claimed_slot: 0, + lamports_per_write, + expected_top_up: lamports_per_write as u64 + RENT_PER_EPOCH + FULL_COMPRESSION_COSTS, + description: "Epoch 1: available_balance=388 (1 epoch), required_epochs=2 (epochs 1+2), deficit=1 epoch + compression_cost", + }, + TestCase { + name: "many epochs behind (10 epochs)", + current_slot: SLOTS_PER_EPOCH * 10, + current_lamports: rent_exemption_lamports + FULL_COMPRESSION_COSTS + RENT_PER_EPOCH, + last_claimed_slot: 0, + lamports_per_write, + expected_top_up: lamports_per_write as u64 + (RENT_PER_EPOCH * 10) + FULL_COMPRESSION_COSTS, + description: "Epoch 10: available_balance=388 (1 epoch), required_epochs=11 (epochs 0-10 + next), deficit=10 epochs + compression_cost", + }, + TestCase { + name: "extreme epoch gap - 10,000 epochs behind", + current_slot: SLOTS_PER_EPOCH * 10_000, + current_lamports: rent_exemption_lamports + FULL_COMPRESSION_COSTS, + last_claimed_slot: 0, + lamports_per_write, + expected_top_up: lamports_per_write as u64 + (RENT_PER_EPOCH * 10_001) + FULL_COMPRESSION_COSTS, + description: "Epoch 10,000: available_balance=0, required_epochs=10,001, deficit includes all 10,001 epochs", + }, + TestCase { + name: "one lamport short of required rent", + current_slot: SLOTS_PER_EPOCH, + current_lamports: rent_exemption_lamports + FULL_COMPRESSION_COSTS + (RENT_PER_EPOCH * 2) - 1, + last_claimed_slot: 0, + lamports_per_write, + expected_top_up: lamports_per_write as u64 + 1 + FULL_COMPRESSION_COSTS, + description: "Epoch 1: available_balance=775 (1.997 epochs), required=776 (2 epochs), compressible with 1 lamport deficit", + }, + // ============================================================ + // PATH 2: NOT COMPRESSIBLE, NEEDS TOP-UP (lamports_per_write) + // ============================================================ + TestCase { + name: "exact boundary - lagging claim requires top-up", + current_slot: SLOTS_PER_EPOCH, + current_lamports: rent_exemption_lamports + FULL_COMPRESSION_COSTS + (RENT_PER_EPOCH * 2), + last_claimed_slot: 0, + lamports_per_write, + expected_top_up: lamports_per_write as u64, + description: "Epoch 1: last_claimed=epoch 0, funded through epoch 1, epochs_funded_ahead=1 < max=2", + }, + TestCase { + name: "exactly 1 epoch funded (max is 2)", + current_slot: 0, + current_lamports: rent_exemption_lamports + FULL_COMPRESSION_COSTS + RENT_PER_EPOCH, + last_claimed_slot: 0, + lamports_per_write, + expected_top_up: lamports_per_write as u64, + description: "Epoch 0: not compressible, epochs_funded_ahead=1 < max_funded_epochs=2, needs write top-up only", + }, + TestCase { + name: "1.5 epochs funded (rounds down to 1)", + current_slot: 0, + current_lamports: rent_exemption_lamports + FULL_COMPRESSION_COSTS + (RENT_PER_EPOCH * 3 / 2), + last_claimed_slot: 0, + lamports_per_write, + expected_top_up: lamports_per_write as u64, + description: "Epoch 0: not compressible, epochs_funded_ahead=582/388=1 (rounds down) < max_funded_epochs=2", + }, + TestCase { + name: "fractional epoch - 1.99 epochs rounds down", + current_slot: 0, + current_lamports: rent_exemption_lamports + FULL_COMPRESSION_COSTS + (RENT_PER_EPOCH * 2) - 1, + last_claimed_slot: 0, + lamports_per_write, + expected_top_up: lamports_per_write as u64, + description: "Epoch 0: not compressible, epochs_funded_ahead=775/388=1 (rounds down) < max_funded_epochs=2", + }, + TestCase { + name: "epoch boundary with 1 epoch funded", + current_slot: SLOTS_PER_EPOCH - 1, + current_lamports: rent_exemption_lamports + FULL_COMPRESSION_COSTS + RENT_PER_EPOCH, + last_claimed_slot: 0, + lamports_per_write, + expected_top_up: lamports_per_write as u64, + description: "Last slot of epoch 0: not compressible, epochs_funded_ahead=1 < max_funded_epochs=2", + }, + TestCase { + name: "account created in later epoch with 1 epoch rent", + current_slot: SLOTS_PER_EPOCH * 5, + current_lamports: rent_exemption_lamports + FULL_COMPRESSION_COSTS + RENT_PER_EPOCH, + last_claimed_slot: SLOTS_PER_EPOCH * 5, + lamports_per_write, + expected_top_up: lamports_per_write as u64, + description: "Epoch 5: created same epoch, not compressible, epochs_funded_ahead=1 < max_funded_epochs=2", + }, + // ============================================================ + // PATH 3: WELL-FUNDED (0 lamports) + // ============================================================ + TestCase { + name: "2 epochs funded - needs top-up (only 1 ahead)", + current_slot: 0, + current_lamports: rent_exemption_lamports + FULL_COMPRESSION_COSTS + (RENT_PER_EPOCH * 2), + last_claimed_slot: 0, + lamports_per_write, + expected_top_up: lamports_per_write as u64, + description: "Epoch 0: funded through epoch 1, epochs_funded_ahead=1 < max=2, needs top-up", + }, + TestCase { + name: "3 epochs when max is 2 - exactly at target", + current_slot: 0, + current_lamports: rent_exemption_lamports + FULL_COMPRESSION_COSTS + (RENT_PER_EPOCH * 3), + last_claimed_slot: 0, + lamports_per_write, + expected_top_up: 0, + description: "Epoch 0: funded through epoch 2, epochs_funded_ahead=2 >= max_funded_epochs=2", + }, + TestCase { + name: "2 epochs at epoch 1 boundary - needs top-up", + current_slot: SLOTS_PER_EPOCH, + current_lamports: rent_exemption_lamports + FULL_COMPRESSION_COSTS + (RENT_PER_EPOCH * 2), + last_claimed_slot: SLOTS_PER_EPOCH, + lamports_per_write, + expected_top_up: lamports_per_write as u64, + description: "Epoch 1: funded through epoch 2, epochs_funded_ahead=1 < max_funded_epochs=2", + }, + // ============================================================ + // EDGE CASES + // ============================================================ + TestCase { + name: "zero lamports_per_write - compressible case", + current_slot: 0, + current_lamports: rent_exemption_lamports + FULL_COMPRESSION_COSTS, + last_claimed_slot: 0, + lamports_per_write: 0, + expected_top_up: RENT_PER_EPOCH + FULL_COMPRESSION_COSTS, + description: "Zero write fee + compressible state: top_up = 0 + deficit (rent + compression_cost)", + }, + TestCase { + name: "zero lamports_per_write - needs top-up but write fee is 0", + current_slot: 0, + current_lamports: rent_exemption_lamports + FULL_COMPRESSION_COSTS + (RENT_PER_EPOCH * 2), + last_claimed_slot: 0, + lamports_per_write: 0, + expected_top_up: 0, + description: "Zero write fee: epochs_funded_ahead=1 < max=2, but lamports_per_write=0 so top_up=0", + }, + TestCase { + name: "large lamports_per_write", + current_slot: 0, + current_lamports: rent_exemption_lamports + FULL_COMPRESSION_COSTS + RENT_PER_EPOCH, + last_claimed_slot: 0, + lamports_per_write: 1_000_000, + expected_top_up: 1_000_000, + description: "Large write fee (1M): not compressible, epochs_funded_ahead=1 < max_funded_epochs=2", + }, + TestCase { + name: "underflow protection - zero available balance", + current_slot: 0, + current_lamports: rent_exemption_lamports, // NOTE: Invalid state - missing compression_cost, but tests saturating_sub behavior + last_claimed_slot: 0, + lamports_per_write, + expected_top_up: lamports_per_write as u64 + RENT_PER_EPOCH + FULL_COMPRESSION_COSTS, + description: "Invalid state: current_lamports < rent_exemption+compression_cost, saturating_sub → available_balance=0", + }, + // ============================================================ + // LAGGING CLAIMS - verifies arrears don't count as "funded ahead" + // ============================================================ + TestCase { + name: "lagging claim - 5 epochs funded but only 1 ahead due to arrears", + current_slot: SLOTS_PER_EPOCH * 3, // epoch 3 + current_lamports: rent_exemption_lamports + FULL_COMPRESSION_COSTS + (RENT_PER_EPOCH * 5), + last_claimed_slot: 0, // epoch 0 - lagging 3 epochs behind + lamports_per_write, + expected_top_up: lamports_per_write as u64, + description: "Epoch 3: 5 epochs funded from epoch 0 covers through epoch 4. epochs_funded_ahead = 4 - 3 = 1 < max=2, needs top-up", + }, + TestCase { + name: "lagging claim - 6 epochs funded, exactly 2 ahead", + current_slot: SLOTS_PER_EPOCH * 3, // epoch 3 + current_lamports: rent_exemption_lamports + FULL_COMPRESSION_COSTS + (RENT_PER_EPOCH * 6), + last_claimed_slot: 0, // epoch 0 - lagging 3 epochs behind + lamports_per_write, + expected_top_up: 0, + description: "Epoch 3: 6 epochs funded from epoch 0 covers through epoch 5. epochs_funded_ahead = 5 - 3 = 2 >= max=2, no top-up", + }, + TestCase { + name: "lagging claim - just covers arrears plus current+next, no buffer", + current_slot: SLOTS_PER_EPOCH * 3, // epoch 3 + current_lamports: rent_exemption_lamports + FULL_COMPRESSION_COSTS + (RENT_PER_EPOCH * 4), + last_claimed_slot: 0, // epoch 0 + lamports_per_write, + expected_top_up: lamports_per_write as u64, + description: "Epoch 3: 4 epochs funded (0,1,2,3) covers through epoch 3. epochs_funded_ahead = 3 - 3 = 0 < max=2, needs top-up", + }, + ]; + + for test_case in test_cases { + let extension = CompressionInfo { + account_version: 3, + config_account_version: 1, + compression_authority: [0u8; 32], + rent_sponsor: [0u8; 32], + last_claimed_slot: test_case.last_claimed_slot, + lamports_per_write: test_case.lamports_per_write, + compress_to_pubkey: 0, + rent_config: test_rent_config(), + }; + + let top_up = extension + .calculate_top_up_lamports( + TEST_BYTES, + test_case.current_slot, + test_case.current_lamports, + rent_exemption_lamports, + ) + .unwrap(); + + assert_eq!( + top_up, test_case.expected_top_up, + "\nTest '{}' failed:\n Description: {}\n Expected: {}\n Got: {}\n Test case: {:?}", + test_case.name, test_case.description, test_case.expected_top_up, top_up, test_case + ); + } +} From 566c8eece2e5d48b4dc5c920943d14c79ad63d34 Mon Sep 17 00:00:00 2001 From: ananas-block Date: Mon, 8 Dec 2025 16:53:13 +0000 Subject: [PATCH 4/7] test(compressible): add default scenario test for 16-epoch rent with 2-epoch top-up --- program-libs/compressible/tests/top_up.rs | 123 ++++++++++++++++++++-- 1 file changed, 112 insertions(+), 11 deletions(-) diff --git a/program-libs/compressible/tests/top_up.rs b/program-libs/compressible/tests/top_up.rs index 1d25ea0afd..1d32105652 100644 --- a/program-libs/compressible/tests/top_up.rs +++ b/program-libs/compressible/tests/top_up.rs @@ -16,22 +16,22 @@ pub fn get_rent_exemption_lamports(_num_bytes: u64) -> u64 { 2700480 } +#[derive(Debug)] +struct TestCase { + name: &'static str, + current_slot: u64, + current_lamports: u64, + last_claimed_slot: u64, + lamports_per_write: u32, + expected_top_up: u64, + description: &'static str, +} + #[test] fn test_calculate_top_up_lamports() { let rent_exemption_lamports = get_rent_exemption_lamports(TEST_BYTES); let lamports_per_write = 5000u32; - #[derive(Debug)] - struct TestCase { - name: &'static str, - current_slot: u64, - current_lamports: u64, - last_claimed_slot: u64, - lamports_per_write: u32, - expected_top_up: u64, - description: &'static str, - } - let test_cases = vec![ // ============================================================ // PATH 1: COMPRESSIBLE CASES (lamports_per_write + rent_deficit) @@ -276,3 +276,104 @@ fn test_calculate_top_up_lamports() { ); } } + +#[test] +fn test_default_scenario_16_epochs_with_2_epoch_topup() { + let rent_exemption_lamports = get_rent_exemption_lamports(TEST_BYTES); + + // Random starting slot (epoch 5) + let start_slot = SLOTS_PER_EPOCH * 5 + 1234; + + let lamports_per_write = 388u32 * 2; + let initial_rent_epochs = 16u64; // Create with 16 epochs rent + let initial_lamports = + rent_exemption_lamports + FULL_COMPRESSION_COSTS + (RENT_PER_EPOCH * initial_rent_epochs); + + let extension = CompressionInfo { + account_version: 3, + config_account_version: 1, + compression_authority: [0u8; 32], + rent_sponsor: [0u8; 32], + last_claimed_slot: start_slot, + lamports_per_write, + compress_to_pubkey: 0, + rent_config: test_rent_config(), // max_funded_epochs = 2 + }; + + // Test each epoch from 0 to 13 - should NOT require top-up + for epoch_offset in 0..=13 { + let current_slot = start_slot + (SLOTS_PER_EPOCH * epoch_offset); + + let top_up = extension + .calculate_top_up_lamports( + TEST_BYTES, + current_slot, + initial_lamports, + rent_exemption_lamports, + ) + .unwrap(); + + assert_eq!( + top_up, + 0, + "Epoch offset {} should NOT require top-up (still {} epochs ahead)", + epoch_offset, + 15 - epoch_offset + ); + } + + // Epoch 14 - should require top-up (only 1 epoch funded ahead < max_funded_epochs=2) + let epoch_14_slot = start_slot + (SLOTS_PER_EPOCH * 14); + let top_up = extension + .calculate_top_up_lamports( + TEST_BYTES, + epoch_14_slot, + initial_lamports, + rent_exemption_lamports, + ) + .unwrap(); + + assert_eq!( + top_up, lamports_per_write as u64, + "Epoch 14 should require top-up (only 1 epoch funded ahead)" + ); + + // After write in epoch 14, lamports increase by top_up (lamports_per_write = 2 epochs rent) + let lamports_after_write = initial_lamports + top_up; + + // Epochs 14 and 15 should NOT require top-up after the write + for epoch_offset in 14..=15 { + let current_slot = start_slot + (SLOTS_PER_EPOCH * epoch_offset); + + let top_up = extension + .calculate_top_up_lamports( + TEST_BYTES, + current_slot, + lamports_after_write, + rent_exemption_lamports, + ) + .unwrap(); + + assert_eq!( + top_up, 0, + "Epoch offset {} should NOT require top-up after write (funded for 2 more epochs)", + epoch_offset + ); + } + + // Epoch 16 - should require top-up again + let epoch_16_slot = start_slot + (SLOTS_PER_EPOCH * 16); + let top_up = extension + .calculate_top_up_lamports( + TEST_BYTES, + epoch_16_slot, + lamports_after_write, + rent_exemption_lamports, + ) + .unwrap(); + + assert_eq!( + top_up, lamports_per_write as u64, + "Epoch 16 should require top-up again" + ); +} From b6bd5cb0dbe49688944aa8783324410d40bb0bbc Mon Sep 17 00:00:00 2001 From: ananas-block Date: Mon, 8 Dec 2025 18:12:42 +0000 Subject: [PATCH 5/7] make functions private --- program-libs/compressible/src/rent/account_rent.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/program-libs/compressible/src/rent/account_rent.rs b/program-libs/compressible/src/rent/account_rent.rs index f24a0601ce..92723a3cf8 100644 --- a/program-libs/compressible/src/rent/account_rent.rs +++ b/program-libs/compressible/src/rent/account_rent.rs @@ -25,7 +25,7 @@ impl AccountRentState { /// # Returns /// The lamports available for rent payments, or 0 if insufficient balance #[inline(always)] - pub fn get_available_rent_balance( + fn get_available_rent_balance( &self, rent_exemption_lamports: u64, compression_cost: u64, @@ -49,7 +49,7 @@ impl AccountRentState { /// # Returns /// The number of epochs requiring rent payment #[inline(always)] - pub fn get_required_epochs(&self) -> u64 { + fn get_required_epochs(&self) -> u64 { let last_completed_epoch = slot_to_epoch(self.current_slot); let last_claimed_epoch = slot_to_epoch(self.last_claimed_slot); @@ -144,7 +144,7 @@ impl AccountRentState { /// /// # Returns /// The amount of unused lamports - pub fn get_unused_lamports( + fn get_unused_lamports( &self, config: &impl RentConfigTrait, rent_exemption_lamports: u64, @@ -193,7 +193,7 @@ pub struct CloseDistribution { /// First epoch is 0. #[inline(always)] -pub fn slot_to_epoch(slot: u64) -> u64 { +fn slot_to_epoch(slot: u64) -> u64 { slot / SLOTS_PER_EPOCH } From ead004366c73809457fde4df6d10b8125fd9386e Mon Sep 17 00:00:00 2001 From: ananas-block Date: Mon, 8 Dec 2025 18:13:06 +0000 Subject: [PATCH 6/7] test: add randomized consistency test for rent functions --- Cargo.lock | 1 + program-libs/compressible/Cargo.toml | 3 + .../compressible/tests/consistency.rs | 242 ++++++++++++++++++ .../registry-test/tests/compressible.rs | 6 +- 4 files changed, 250 insertions(+), 2 deletions(-) create mode 100644 program-libs/compressible/tests/consistency.rs diff --git a/Cargo.lock b/Cargo.lock index f724f98662..a1c44cb394 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3715,6 +3715,7 @@ dependencies = [ "light-zero-copy", "pinocchio", "pinocchio-pubkey", + "rand 0.8.5", "solana-msg 2.2.1", "solana-program-error 2.2.2", "solana-pubkey 2.4.0", diff --git a/program-libs/compressible/Cargo.toml b/program-libs/compressible/Cargo.toml index 9b9161a661..92c24b8d8c 100644 --- a/program-libs/compressible/Cargo.toml +++ b/program-libs/compressible/Cargo.toml @@ -35,6 +35,9 @@ light-compressed-account = { workspace= true } aligned-sized = { workspace= true } solana-sysvar = {workspace = true, optional = true} +[dev-dependencies] +rand = { workspace = true } + [lints.rust.unexpected_cfgs] level = "allow" check-cfg = [ diff --git a/program-libs/compressible/tests/consistency.rs b/program-libs/compressible/tests/consistency.rs new file mode 100644 index 0000000000..f244a1fbbb --- /dev/null +++ b/program-libs/compressible/tests/consistency.rs @@ -0,0 +1,242 @@ +#![cfg(test)] +use rand::{rngs::StdRng, thread_rng, Rng, SeedableRng}; + +use light_compressible::{ + compression_info::CompressionInfo, + rent::{ + get_last_funded_epoch, AccountRentState, RentConfig, COMPRESSION_COST, + COMPRESSION_INCENTIVE, SLOTS_PER_EPOCH, + }, +}; + +const TEST_BYTES: u64 = 260; +const RENT_PER_EPOCH: u64 = 260 + 128; +const FULL_COMPRESSION_COSTS: u64 = (COMPRESSION_COST + COMPRESSION_INCENTIVE) as u64; + +fn test_rent_config() -> RentConfig { + RentConfig::default() +} + +pub fn get_rent_exemption_lamports(_num_bytes: u64) -> u64 { + 2700480 +} + +#[derive(Debug, Clone, PartialEq, Eq)] +struct RentState { + completed_epochs: u64, + is_compressible: bool, + claimable: Option, + epochs_ahead: u64, + close_dist_to_sponsor: u64, + close_dist_to_user: u64, + last_funded: u64, + top_up: u64, +} + +impl RentState { + fn from_account_rent_state( + state: &AccountRentState, + compression_info: &CompressionInfo, + rent_config: &RentConfig, + rent_exemption_lamports: u64, + ) -> Self { + let completed_epochs = state.get_completed_epochs(); + let is_compressible = state + .is_compressible(rent_config, rent_exemption_lamports) + .is_some(); + let claimable = state.calculate_claimable_rent(rent_config, rent_exemption_lamports); + let epochs_ahead = state.epochs_funded_ahead(rent_config, rent_exemption_lamports); + let close_dist = state.calculate_close_distribution(rent_config, rent_exemption_lamports); + let last_funded = get_last_funded_epoch( + state.num_bytes, + state.current_lamports, + state.last_claimed_slot, + rent_config, + rent_exemption_lamports, + ); + let top_up = compression_info + .calculate_top_up_lamports( + state.num_bytes, + state.current_slot, + state.current_lamports, + rent_exemption_lamports, + ) + .unwrap(); + + Self { + completed_epochs, + is_compressible, + claimable, + epochs_ahead, + close_dist_to_sponsor: close_dist.to_rent_sponsor, + close_dist_to_user: close_dist.to_user, + last_funded, + top_up, + } + } + + fn expected( + current_slot: u64, + current_lamports: u64, + last_claimed_slot: u64, + rent_exemption_lamports: u64, + lamports_per_write: u32, + ) -> Self { + let current_epoch = current_slot / SLOTS_PER_EPOCH; + let last_claimed_epoch = last_claimed_slot / SLOTS_PER_EPOCH; + + let available_rent = current_lamports - rent_exemption_lamports - FULL_COMPRESSION_COSTS; + let total_epochs_fundable = available_rent / RENT_PER_EPOCH; + let required_epochs = current_epoch - last_claimed_epoch + 1; + let epochs_ahead = total_epochs_fundable.saturating_sub(required_epochs); + let completed_epochs = current_epoch - last_claimed_epoch; + + let last_funded = if total_epochs_fundable > 0 { + last_claimed_epoch + total_epochs_fundable - 1 + } else { + last_claimed_epoch.saturating_sub(1) + }; + + let rent_needed = required_epochs * RENT_PER_EPOCH; + let is_compressible = available_rent < rent_needed; + + let claimable = if is_compressible { + None + } else { + Some(completed_epochs * RENT_PER_EPOCH) + }; + + // Calculate close distribution + let rent_due = required_epochs * RENT_PER_EPOCH; + let unutilized = available_rent.saturating_sub(rent_due); + let close_dist_to_user = unutilized; + let close_dist_to_sponsor = current_lamports - unutilized; + + // Top-up logic: max_funded_epochs = 2 + let top_up = if is_compressible { + let deficit = rent_needed - available_rent + FULL_COMPRESSION_COSTS; + lamports_per_write as u64 + deficit + } else if epochs_ahead >= 2 { + 0 + } else { + lamports_per_write as u64 + }; + + Self { + completed_epochs, + is_compressible, + claimable, + epochs_ahead, + close_dist_to_sponsor, + close_dist_to_user, + last_funded, + top_up, + } + } +} + +#[test] +fn test_consistency_16_epochs_progression() { + let rent_exemption_lamports = get_rent_exemption_lamports(TEST_BYTES); + let rent_config = test_rent_config(); + + // Random starting slot (epoch 5) + let start_slot = SLOTS_PER_EPOCH * 5 + 1234; + + // lamports_per_write = 2 epochs rent (matching top_up test) + let lamports_per_write = (RENT_PER_EPOCH * 2) as u32; + let initial_rent_epochs = 16u64; + let initial_lamports = + rent_exemption_lamports + FULL_COMPRESSION_COSTS + (RENT_PER_EPOCH * initial_rent_epochs); + + // Tracker variables - accumulate state across epochs + let mut total_rent_claimed: u64 = 0; + let mut total_top_up: u64 = 0; + let mut current_lamports = initial_lamports; + let mut current_slot = start_slot; + let mut last_claimed_slot = start_slot; + let mut epochs_funded_ahead = initial_rent_epochs - 1; + let seed = thread_rng().gen(); + println!("Seed: {}", seed); + // Continue with 1000 random slot increments (1-10 epochs) + let mut rng = StdRng::seed_from_u64(seed); + + for _ in 0..1_000_000 { + // Always advance by at least 1 slot, up to epochs_funded_ahead epochs + let max_slots = (epochs_funded_ahead.max(1)) * SLOTS_PER_EPOCH; + let slot_increment = rng.gen_range(0..max_slots); + current_slot += slot_increment; + let epoch_offset = current_slot / SLOTS_PER_EPOCH; + let state = AccountRentState { + num_bytes: TEST_BYTES, + current_slot, + current_lamports, + last_claimed_slot, + }; + + // CompressionInfo for top-up calculation (recreate with current last_claimed_slot) + let compression_info = CompressionInfo { + account_version: 3, + config_account_version: 1, + compression_authority: [0u8; 32], + rent_sponsor: [0u8; 32], + last_claimed_slot, + lamports_per_write, + compress_to_pubkey: 0, + rent_config: test_rent_config(), + }; + + let actual = RentState::from_account_rent_state( + &state, + &compression_info, + &rent_config, + rent_exemption_lamports, + ); + + let expected = RentState::expected( + current_slot, + current_lamports, + last_claimed_slot, + rent_exemption_lamports, + lamports_per_write, + ); + + assert_eq!(actual, expected, "Epoch offset {}", epoch_offset); + + // Simulate rent claim if claimable - update trackers + if let Some(rent_to_claim) = actual.claimable { + if rent_to_claim > 0 { + total_rent_claimed += rent_to_claim; + current_lamports -= rent_to_claim; + last_claimed_slot = current_slot; + } + } + + // Print state for debugging + println!( + "Epoch {}: slot={}, lamports={}, completed={}, compressible={}, claimable={:?}, ahead={}, last_funded={}, top_up={}, total_claimed={}", + epoch_offset, + current_slot, + current_lamports, + actual.completed_epochs, + actual.is_compressible, + actual.claimable, + actual.epochs_ahead, + actual.last_funded, + actual.top_up, + total_rent_claimed + ); + // Always add top-up (simulates write operation) + total_top_up += actual.top_up; + current_lamports += actual.top_up; + + epochs_funded_ahead = actual.epochs_ahead; + } + + // Verify balance equation: current = initial - claimed + top_ups + assert_eq!( + current_lamports, + initial_lamports + total_top_up - total_rent_claimed, + "Final lamports mismatch" + ); +} diff --git a/program-tests/registry-test/tests/compressible.rs b/program-tests/registry-test/tests/compressible.rs index c7f882fce2..8bb744ebf8 100644 --- a/program-tests/registry-test/tests/compressible.rs +++ b/program-tests/registry-test/tests/compressible.rs @@ -31,7 +31,9 @@ use solana_sdk::{ signer::Signer, transaction::Transaction, }; - +fn slot_to_epoch(slot: u64) -> u64 { + slot / SLOTS_PER_EPOCH +} /// Withdraw funds from the compressed token pool via the registry program /// This function invokes the registry program's withdraw_funding_pool instruction, /// which then CPIs to the compressed token program with the compression_authority PDA as signer. @@ -1169,7 +1171,7 @@ async fn assert_not_compressible( )) })?; - let current_epoch = light_compressible::rent::slot_to_epoch(current_slot); + let current_epoch = slot_to_epoch(current_slot); assert!( last_funded_epoch >= current_epoch, From 24322da0778e3fd489871e6c5d5d0b1378604949 Mon Sep 17 00:00:00 2001 From: ananas-block Date: Mon, 8 Dec 2025 18:17:34 +0000 Subject: [PATCH 7/7] chore: replace magic numbers with enum --- program-libs/compressible/tests/consistency.rs | 3 +-- programs/registry/src/lib.rs | 12 ++++++------ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/program-libs/compressible/tests/consistency.rs b/program-libs/compressible/tests/consistency.rs index f244a1fbbb..a31d3babba 100644 --- a/program-libs/compressible/tests/consistency.rs +++ b/program-libs/compressible/tests/consistency.rs @@ -1,6 +1,4 @@ #![cfg(test)] -use rand::{rngs::StdRng, thread_rng, Rng, SeedableRng}; - use light_compressible::{ compression_info::CompressionInfo, rent::{ @@ -8,6 +6,7 @@ use light_compressible::{ COMPRESSION_INCENTIVE, SLOTS_PER_EPOCH, }, }; +use rand::{rngs::StdRng, thread_rng, Rng, SeedableRng}; const TEST_BYTES: u64 = 260; const RENT_PER_EPOCH: u64 = 260 + 128; diff --git a/programs/registry/src/lib.rs b/programs/registry/src/lib.rs index 983dd32d26..78c164a37b 100644 --- a/programs/registry/src/lib.rs +++ b/programs/registry/src/lib.rs @@ -62,7 +62,7 @@ declare_id!("Lighton6oQpVkeewmo2mcPTQQp7kYHr4fWpAgJyEmDX"); pub mod light_registry { use constants::DEFAULT_WORK_V1; - use light_compressible::config::CompressibleConfig; + use light_compressible::config::{CompressibleConfig, CompressibleConfigState}; use super::*; @@ -735,10 +735,10 @@ pub mod light_registry { pub fn pause_compressible_config(ctx: Context) -> Result<()> { // Cannot pause a deprecated config require!( - ctx.accounts.compressible_config.state != 2, + ctx.accounts.compressible_config.state != CompressibleConfigState::Deprecated as u8, RegistryError::InvalidConfigState ); - ctx.accounts.compressible_config.state = 0; + ctx.accounts.compressible_config.state = CompressibleConfigState::Inactive as u8; Ok(()) } @@ -746,10 +746,10 @@ pub mod light_registry { /// Only paused configs (state=0) can be unpaused. pub fn unpause_compressible_config(ctx: Context) -> Result<()> { require!( - ctx.accounts.compressible_config.state == 0, + ctx.accounts.compressible_config.state == CompressibleConfigState::Inactive as u8, RegistryError::InvalidConfigState ); - ctx.accounts.compressible_config.state = 1; + ctx.accounts.compressible_config.state = CompressibleConfigState::Active as u8; Ok(()) } @@ -757,7 +757,7 @@ pub mod light_registry { /// Deprecate means no new ctoken accounts can be created with this config. /// Other operations are functional. pub fn deprecate_compressible_config(ctx: Context) -> Result<()> { - ctx.accounts.compressible_config.state = 2; + ctx.accounts.compressible_config.state = CompressibleConfigState::Deprecated as u8; Ok(()) }