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
265 changes: 262 additions & 3 deletions program-tests/registry-test/tests/compressible.rs
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,8 @@ async fn pause_compressible_config<R: Rpc>(
fee_payer: payer.pubkey(),
update_authority: update_authority.pubkey(),
compressible_config,
system_program: solana_sdk::system_program::id(),
new_update_authority: None,
new_withdrawal_authority: None,
};

let instruction = Instruction {
Expand Down Expand Up @@ -354,7 +355,8 @@ async fn unpause_compressible_config<R: Rpc>(
fee_payer: payer.pubkey(),
update_authority: update_authority.pubkey(),
compressible_config,
system_program: solana_sdk::system_program::id(),
new_update_authority: None,
new_withdrawal_authority: None,
};

let instruction = Instruction {
Expand Down Expand Up @@ -388,7 +390,8 @@ async fn deprecate_compressible_config<R: Rpc>(
fee_payer: payer.pubkey(),
update_authority: update_authority.pubkey(),
compressible_config,
system_program: solana_sdk::system_program::id(),
new_update_authority: None,
new_withdrawal_authority: None,
};

let instruction = Instruction {
Expand All @@ -408,6 +411,51 @@ async fn deprecate_compressible_config<R: Rpc>(
rpc.process_transaction(transaction).await
}

/// Helper function to update compressible config authorities
async fn update_compressible_config_authorities<R: Rpc>(
rpc: &mut R,
update_authority: &Keypair,
new_update_authority: Option<&Keypair>,
new_withdrawal_authority: Option<&Keypair>,
payer: &Keypair,
) -> Result<Signature, RpcError> {
let compressible_config = CompressibleConfig::ctoken_v1_config_pda();

let accounts = UpdateCompressibleConfigAccounts {
fee_payer: payer.pubkey(),
update_authority: update_authority.pubkey(),
compressible_config,
new_update_authority: new_update_authority.map(|k| k.pubkey()),
new_withdrawal_authority: new_withdrawal_authority.map(|k| k.pubkey()),
};

let instruction = Instruction {
program_id: light_registry::ID,
accounts: accounts.to_account_metas(Some(true)),
data: light_registry::instruction::UpdateCompressibleConfig {}.data(),
};

let (blockhash, _) = rpc.get_latest_blockhash().await.unwrap();

// Collect signers
let mut signers: Vec<&Keypair> = vec![payer, update_authority];
if let Some(new_auth) = new_update_authority {
signers.push(new_auth);
}
if let Some(new_auth) = new_withdrawal_authority {
signers.push(new_auth);
}

let transaction = Transaction::new_signed_with_payer(
&[instruction],
Some(&payer.pubkey()),
&signers,
blockhash,
);

rpc.process_transaction(transaction).await
}

#[tokio::test]
async fn test_pause_compressible_config_with_valid_authority() -> Result<(), RpcError> {
let mut rpc = LightProgramTest::new(ProgramTestConfig::new_v2(false, None))
Expand Down Expand Up @@ -800,3 +848,214 @@ async fn test_deprecate_compressible_config_with_invalid_authority() -> Result<(

Ok(())
}

#[tokio::test]
async fn test_update_compressible_config_update_authority() -> Result<(), RpcError> {
let mut rpc = LightProgramTest::new(ProgramTestConfig::new_v2(false, None))
.await
.unwrap();
let payer = rpc.get_payer().insecure_clone();

// Create new update authority
let new_update_authority = Keypair::new();
airdrop_lamports(&mut rpc, &new_update_authority.pubkey(), 1_000_000)
.await
.unwrap();

// Update the update_authority
update_compressible_config_authorities(
&mut rpc,
&payer, // current update_authority
Some(&new_update_authority),
None,
&payer,
)
.await
.unwrap();

// Verify the update_authority was updated
let compressible_config_pda = CompressibleConfig::ctoken_v1_config_pda();
let account_data = rpc
.get_account(compressible_config_pda)
.await?
.expect("CompressibleConfig account should exist");

let config = CompressibleConfig::try_from_slice(&account_data.data[8..])
.expect("Failed to deserialize CompressibleConfig");

assert_eq!(
config.update_authority,
new_update_authority.pubkey(),
"Update authority should be updated"
);

Ok(())
}

#[tokio::test]
async fn test_update_compressible_config_withdrawal_authority() -> Result<(), RpcError> {
let mut rpc = LightProgramTest::new(ProgramTestConfig::new_v2(false, None))
.await
.unwrap();
let payer = rpc.get_payer().insecure_clone();

// Store original withdrawal authority
let compressible_config_pda = CompressibleConfig::ctoken_v1_config_pda();
let account_data_before = rpc
.get_account(compressible_config_pda)
.await?
.expect("CompressibleConfig account should exist");
let config_before = CompressibleConfig::try_from_slice(&account_data_before.data[8..])
.expect("Failed to deserialize CompressibleConfig");
let original_withdrawal_authority = config_before.withdrawal_authority;

// Create new withdrawal authority
let new_withdrawal_authority = Keypair::new();
airdrop_lamports(&mut rpc, &new_withdrawal_authority.pubkey(), 1_000_000)
.await
.unwrap();

// Update the withdrawal_authority
update_compressible_config_authorities(
&mut rpc,
&payer, // current update_authority
None,
Some(&new_withdrawal_authority),
&payer,
)
.await
.unwrap();

// Verify the withdrawal_authority was updated
let account_data = rpc
.get_account(compressible_config_pda)
.await?
.expect("CompressibleConfig account should exist");

let config = CompressibleConfig::try_from_slice(&account_data.data[8..])
.expect("Failed to deserialize CompressibleConfig");

assert_eq!(
config.withdrawal_authority,
new_withdrawal_authority.pubkey(),
"Withdrawal authority should be updated"
);
assert_eq!(
config.update_authority,
payer.pubkey(),
"Update authority should remain unchanged"
);
assert_ne!(
config.withdrawal_authority, original_withdrawal_authority,
"Withdrawal authority should be different from original"
);

Ok(())
}

#[tokio::test]
async fn test_update_compressible_config_both_authorities() -> Result<(), RpcError> {
let mut rpc = LightProgramTest::new(ProgramTestConfig::new_v2(false, None))
.await
.unwrap();
let payer = rpc.get_payer().insecure_clone();

// Create new authorities
let new_update_authority = Keypair::new();
let new_withdrawal_authority = Keypair::new();
airdrop_lamports(&mut rpc, &new_update_authority.pubkey(), 1_000_000)
.await
.unwrap();
airdrop_lamports(&mut rpc, &new_withdrawal_authority.pubkey(), 1_000_000)
.await
.unwrap();

// Update both authorities
update_compressible_config_authorities(
&mut rpc,
&payer, // current update_authority
Some(&new_update_authority),
Some(&new_withdrawal_authority),
&payer,
)
.await
.unwrap();

// Verify both authorities were updated
let compressible_config_pda = CompressibleConfig::ctoken_v1_config_pda();
let account_data = rpc
.get_account(compressible_config_pda)
.await?
.expect("CompressibleConfig account should exist");

let config = CompressibleConfig::try_from_slice(&account_data.data[8..])
.expect("Failed to deserialize CompressibleConfig");

assert_eq!(
config.update_authority,
new_update_authority.pubkey(),
"Update authority should be updated"
);
assert_eq!(
config.withdrawal_authority,
new_withdrawal_authority.pubkey(),
"Withdrawal authority should be updated"
);

Ok(())
}

#[tokio::test]
async fn test_update_compressible_config_invalid_authority() -> Result<(), RpcError> {
let mut rpc = LightProgramTest::new(ProgramTestConfig::new_v2(false, None))
.await
.unwrap();
let payer = rpc.get_payer().insecure_clone();

// Create wrong authority keypair
let wrong_authority = Keypair::new();
airdrop_lamports(&mut rpc, &wrong_authority.pubkey(), 1_000_000)
.await
.unwrap();

// Create new update authority
let new_update_authority = Keypair::new();
airdrop_lamports(&mut rpc, &new_update_authority.pubkey(), 1_000_000)
.await
.unwrap();

// Try to update with wrong authority
let result = update_compressible_config_authorities(
&mut rpc,
&wrong_authority, // wrong update_authority
Some(&new_update_authority),
None,
&payer,
)
.await;

assert_rpc_error(
result,
0,
anchor_lang::prelude::ErrorCode::ConstraintHasOne.into(),
)
.unwrap();

// Verify the update_authority was NOT updated
let compressible_config_pda = CompressibleConfig::ctoken_v1_config_pda();
let account_data = rpc
.get_account(compressible_config_pda)
.await?
.expect("CompressibleConfig account should exist");

let config = CompressibleConfig::try_from_slice(&account_data.data[8..])
.expect("Failed to deserialize CompressibleConfig");

assert_eq!(
config.update_authority,
payer.pubkey(),
"Update authority should remain unchanged"
);

Ok(())
}
6 changes: 5 additions & 1 deletion programs/registry/src/compressible/update_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,9 @@ pub struct UpdateCompressibleConfig<'info> {
)]
pub compressible_config: Account<'info, CompressibleConfig>,

pub system_program: Program<'info, System>,
/// New update authority (optional). Must be a signer to reduce risk of updating with a wrong authority.
pub new_update_authority: Option<Signer<'info>>,

/// New withdrawal authority (optional). Must be a signer to reduce risk of updating with a wrong authority.
pub new_withdrawal_authority: Option<Signer<'info>>,
}
24 changes: 7 additions & 17 deletions programs/registry/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,7 @@ use light_batched_merkle_tree::{
initialize_state_tree::InitStateTreeAccountsInstructionData,
merkle_tree::BatchedMerkleTreeAccount, queue::BatchedQueueAccount,
};
use light_compressible::registry_instructions::{
CreateCompressibleConfig as CreateCompressibleConfigData,
CreateConfigCounter as CreateConfigCounterData,
};
use light_compressible::registry_instructions::CreateCompressibleConfig as CreateCompressibleConfigData;
use protocol_config::state::ProtocolConfig;
pub use selection::forester::*;

Expand Down Expand Up @@ -691,10 +688,7 @@ pub mod light_registry {
}

/// Creates the config counter PDA
pub fn create_config_counter(
ctx: Context<CreateConfigCounter>,
_params: CreateConfigCounterData,
) -> Result<()> {
pub fn create_config_counter(ctx: Context<CreateConfigCounter>) -> Result<()> {
ctx.accounts.config_counter.counter += 1;
Ok(())
}
Expand All @@ -718,19 +712,15 @@ pub mod light_registry {
}

/// Updates an existing compressible config
pub fn update_compressible_config(
ctx: Context<UpdateCompressibleConfig>,
new_update_authority: Option<Pubkey>,
new_withdrawal_authority: Option<Pubkey>,
) -> Result<()> {
pub fn update_compressible_config(ctx: Context<UpdateCompressibleConfig>) -> Result<()> {
// Update the update_authority if provided
if let Some(authority) = new_update_authority {
ctx.accounts.compressible_config.update_authority = authority;
if let Some(authority) = ctx.accounts.new_update_authority.as_ref() {
ctx.accounts.compressible_config.update_authority = authority.key();
}

// Update the withdrawal_authority if provided
if let Some(authority) = new_withdrawal_authority {
ctx.accounts.compressible_config.withdrawal_authority = authority;
if let Some(authority) = ctx.accounts.new_withdrawal_authority.as_ref() {
ctx.accounts.compressible_config.withdrawal_authority = authority.key();
}

Ok(())
Expand Down