Skip to content
Closed
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
4 changes: 4 additions & 0 deletions dlp-api/src/state/delegation_metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ pub enum UndelegationRequester {
/// Kept backward compatible with legacy `is_undelegatable = true`.
Validator,
/// The account owner program requested undelegation.
///
/// Encoded as byte `2`; legacy clients that deserialize this byte as a
/// bool may reject metadata that contains this variant.
// TODO (snawaz): edit the doc once all validators upgrade.
OwnerProgram,
}

Expand Down
42 changes: 32 additions & 10 deletions src/processor/fast/internal/undelegate_internal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,38 @@ pub(crate) fn process_auto_undelegation_if_requested(
system_program: &AccountView,
auto_accounts: Option<AutoUndelegationAccounts<'_>>,
) -> ProgramResult {
if requester != UndelegationRequester::OwnerProgram {
return Ok(());
}
let (request_accounts, auto_accounts) = match requester {
UndelegationRequester::None => return Ok(()),
UndelegationRequester::Validator => {
let Some(auto_accounts) = auto_accounts else {
// Backward compatibility: this is normally an error case
// because Finalize cannot process validator-requested
// undelegation without auto-undelegation accounts. Returning
// Ok here keeps older validators compatible: they do not pass
// these accounts and instead send standalone Undelegate after
// Finalize.
log!("WARN: validator-requested undelegation skipped; auto-undelegation accounts were not provided");
return Ok(());
};
(None, auto_accounts)
}
UndelegationRequester::OwnerProgram => {
let Some(auto_accounts) = auto_accounts else {
return Err(ProgramError::NotEnoughAccountKeys);
};
// For OwnerProgram requests, see UndelegationRequest::rent_payer
// for the rent-payer invariant. Validator requests do not use the
// request PDA.
(
Some((
auto_accounts.undelegation_request_account,
auto_accounts.rent_reimbursement,
)),
auto_accounts,
)
}
};

let auto_accounts =
auto_accounts.ok_or(ProgramError::NotEnoughAccountKeys)?;
process_undelegation(UndelegationAccounts {
validator,
delegated_account,
Expand All @@ -92,11 +118,7 @@ pub(crate) fn process_auto_undelegation_if_requested(
fees_vault: auto_accounts.fees_vault,
validator_fees_vault,
system_program,
// See UndelegationRequest::rent_payer for this invariant.
request_accounts: Some((
auto_accounts.undelegation_request_account,
auto_accounts.rent_reimbursement,
)),
request_accounts,
})
}

Expand Down
31 changes: 26 additions & 5 deletions tests/test_call_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ use dlp_api::{
},
};
use solana_program::{
hash::Hash, instruction::AccountMeta, native_token::LAMPORTS_PER_SOL,
hash::Hash,
instruction::{AccountMeta, Instruction},
native_token::LAMPORTS_PER_SOL,
rent::Rent,
};
use solana_program_test::{processor, read_file, BanksClient, ProgramTest};
Expand Down Expand Up @@ -328,6 +330,25 @@ async fn setup_program_test_env() -> (BanksClient, Keypair, Keypair, Hash) {
(banks, payer, validator, blockhash)
}

fn legacy_finalize_ix(
validator: Pubkey,
delegated_account: Pubkey,
owner_program: Pubkey,
rent_reimbursement: Pubkey,
) -> Instruction {
let mut ix = dlp_api::instruction_builder::finalize(
validator,
delegated_account,
owner_program,
rent_reimbursement,
);
// These tests exercise call_handler with the pre-auto-undelegation finalize
// layout. Validator-requested undelegation is skipped when auto accounts are
// absent, keeping standalone undelegate available for the following ix.
ix.accounts.truncate(8);
ix
}

/// Test call_handler in finalize context
#[tokio::test]
async fn test_finalize_call_handler() {
Expand All @@ -336,7 +357,7 @@ async fn test_finalize_call_handler() {
let (banks, payer, validator, blockhash) = setup_program_test_env().await;

let transfer_destination = Keypair::new();
let finalize_ix = dlp_api::instruction_builder::finalize(
let finalize_ix = legacy_finalize_ix(
validator.pubkey(),
DELEGATED_PDA_ID,
DELEGATED_PDA_OWNER_ID,
Expand Down Expand Up @@ -386,7 +407,7 @@ async fn test_undelegate_call_handler() {
let (banks, payer, validator, blockhash) = setup_program_test_env().await;

let transfer_destination = Keypair::new();
let finalize_ix = dlp_api::instruction_builder::finalize(
let finalize_ix = legacy_finalize_ix(
validator.pubkey(),
DELEGATED_PDA_ID,
DELEGATED_PDA_OWNER_ID,
Expand Down Expand Up @@ -455,7 +476,7 @@ async fn test_finalize_invalid_escrow_call_handler() {

// Submit the finalize with handler tx
let transfer_destination = Keypair::new();
let finalize_ix = dlp_api::instruction_builder::finalize(
let finalize_ix = legacy_finalize_ix(
authority.pubkey(),
DELEGATED_PDA_ID,
DELEGATED_PDA_OWNER_ID,
Expand Down Expand Up @@ -492,7 +513,7 @@ async fn test_undelegate_invalid_escow_call_handler() {

// Submit the finalize with handler tx
let destination = Keypair::new();
let finalize_ix = dlp_api::instruction_builder::finalize(
let finalize_ix = legacy_finalize_ix(
authority.pubkey(),
DELEGATED_PDA_ID,
DELEGATED_PDA_OWNER_ID,
Expand Down
31 changes: 26 additions & 5 deletions tests/test_call_handler_v2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ use dlp_api::{
},
};
use solana_program::{
hash::Hash, instruction::AccountMeta, native_token::LAMPORTS_PER_SOL,
hash::Hash,
instruction::{AccountMeta, Instruction},
native_token::LAMPORTS_PER_SOL,
rent::Rent,
};
use solana_program_test::{read_file, BanksClient, ProgramTest};
Expand Down Expand Up @@ -322,6 +324,25 @@ async fn setup_program_test_env() -> (BanksClient, Keypair, Keypair, Hash) {
(banks, payer, validator, blockhash)
}

fn legacy_finalize_ix(
validator: Pubkey,
delegated_account: Pubkey,
owner_program: Pubkey,
rent_reimbursement: Pubkey,
) -> Instruction {
let mut ix = dlp_api::instruction_builder::finalize(
validator,
delegated_account,
owner_program,
rent_reimbursement,
);
// These tests exercise call_handler_v2 with the pre-auto-undelegation
// finalize layout. Validator-requested undelegation is skipped when auto
// accounts are absent, keeping standalone undelegate available afterward.
ix.accounts.truncate(8);
ix
}

/// Test call_handler_v2 in finalize context
#[tokio::test]
async fn test_finalize_call_handler_v2() {
Expand All @@ -330,7 +351,7 @@ async fn test_finalize_call_handler_v2() {
let (banks, payer, validator, blockhash) = setup_program_test_env().await;

let transfer_destination = Keypair::new();
let finalize_ix = dlp_api::instruction_builder::finalize(
let finalize_ix = legacy_finalize_ix(
validator.pubkey(),
DELEGATED_PDA_ID,
DELEGATED_PDA_OWNER_ID,
Expand Down Expand Up @@ -381,7 +402,7 @@ async fn test_undelegate_call_handler_v2() {
let (banks, payer, validator, blockhash) = setup_program_test_env().await;

let transfer_destination = Keypair::new();
let finalize_ix = dlp_api::instruction_builder::finalize(
let finalize_ix = legacy_finalize_ix(
validator.pubkey(),
DELEGATED_PDA_ID,
DELEGATED_PDA_OWNER_ID,
Expand Down Expand Up @@ -449,7 +470,7 @@ async fn test_finalize_invalid_escrow_call_handler_v2() {

// Submit the finalize with handler tx
let transfer_destination = Keypair::new();
let finalize_ix = dlp_api::instruction_builder::finalize(
let finalize_ix = legacy_finalize_ix(
authority.pubkey(),
DELEGATED_PDA_ID,
DELEGATED_PDA_OWNER_ID,
Expand Down Expand Up @@ -487,7 +508,7 @@ async fn test_undelegate_invalid_escrow_call_handler_v2() {

// Submit the finalize with handler tx
let destination = Keypair::new();
let finalize_ix = dlp_api::instruction_builder::finalize(
let finalize_ix = legacy_finalize_ix(
authority.pubkey(),
DELEGATED_PDA_ID,
DELEGATED_PDA_OWNER_ID,
Expand Down
4 changes: 2 additions & 2 deletions tests/test_commit_finalize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ async fn run_test_commit_finalize(
authority.pubkey(),
&mut CommitFinalizeArgs {
commit_id: 1,
allow_undelegation: true.into(),
allow_undelegation: false.into(),
data_is_diff: data_is_diff.into(),
lamports: new_account_balance,
bumps: Default::default(),
Expand Down Expand Up @@ -119,7 +119,7 @@ async fn run_test_commit_finalize(

assert_eq!(
delegation_metadata.undelegation_requester,
dlp_api::state::UndelegationRequester::Validator
dlp_api::state::UndelegationRequester::None
);
}

Expand Down
4 changes: 2 additions & 2 deletions tests/test_commit_finalize_from_buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ async fn test_commit_finalize_from_buffer_perf() {
authority.pubkey(),
&mut CommitFinalizeArgs {
commit_id: 1,
allow_undelegation: true.into(),
allow_undelegation: false.into(),
data_is_diff: false.into(),
lamports: new_account_balance,
bumps: Default::default(),
Expand Down Expand Up @@ -97,7 +97,7 @@ async fn test_commit_finalize_from_buffer_perf() {

assert_eq!(
delegation_metadata.undelegation_requester,
dlp_api::state::UndelegationRequester::Validator
dlp_api::state::UndelegationRequester::None
);
}

Expand Down
23 changes: 19 additions & 4 deletions tests/test_lamports_settlement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -742,6 +742,7 @@ pub async fn test_commit_system_account_after_balance_decrease(
authority: &authority,
blockhash,
new_delegated_account_lamports,
allow_undelegation: also_undelegate,
delegated_account,
delegated_account_owner: owner_program,
})
Expand Down Expand Up @@ -799,6 +800,7 @@ async fn test_commit_system_account_after_balance_increase(
authority: &authority,
blockhash,
new_delegated_account_lamports,
allow_undelegation: also_undelegate,
delegated_account,
delegated_account_owner: owner_program,
})
Expand Down Expand Up @@ -854,6 +856,7 @@ async fn test_commit_system_account_after_balance_decrease_and_increase_mainchai
authority: &authority,
blockhash,
new_delegated_account_lamports,
allow_undelegation: also_undelegate,
delegated_account,
delegated_account_owner: owner_program,
})
Expand Down Expand Up @@ -912,6 +915,7 @@ async fn test_commit_system_account_after_balance_increase_and_increase_mainchai
authority: &authority,
blockhash,
new_delegated_account_lamports,
allow_undelegation: also_undelegate,
delegated_account,
delegated_account_owner: owner_program,
})
Expand Down Expand Up @@ -1049,12 +1053,17 @@ struct FinalizeNewStateArgs<'a> {
}

async fn finalize_new_state(args: FinalizeNewStateArgs<'_>) {
let ix = dlp_api::instruction_builder::finalize(
let mut ix = dlp_api::instruction_builder::finalize(
args.authority.pubkey(),
args.delegated_account,
args.owner_program,
args.authority.pubkey(),
);
// These lamports-settlement tests cover the original two-step
// finalize-then-undelegate flow. Keep finalize on the pre-auto-undelegation
// layout so `commit_undelegate_*` variants exercise the standalone
// undelegate instruction below.
ix.accounts.truncate(8);
let tx = Transaction::new_signed_with_payer(
&[ix],
Some(&args.authority.pubkey()),
Expand All @@ -1064,7 +1073,7 @@ async fn finalize_new_state(args: FinalizeNewStateArgs<'_>) {
let res = args.banks.process_transaction(tx).await;
assert!(res.is_ok());

// Assert that the account owner is still the delegation program
// Assert that the account owner is still the delegation program.
let pda_account = args
.banks
.get_account(args.delegated_account)
Expand All @@ -1079,6 +1088,7 @@ struct CommitNewStateArgs<'a> {
authority: &'a Keypair,
blockhash: Hash,
new_delegated_account_lamports: u64,
allow_undelegation: bool,
delegated_account: Pubkey,
delegated_account_owner: Pubkey,
}
Expand Down Expand Up @@ -1158,7 +1168,7 @@ async fn commit_new_state(args: CommitNewStateArgs<'_>) {
let commit_args = CommitStateArgs {
data: data.clone(),
nonce: 1,
allow_undelegation: true,
allow_undelegation: args.allow_undelegation,
lamports: args.new_delegated_account_lamports,
};

Expand Down Expand Up @@ -1231,9 +1241,14 @@ async fn commit_new_state(args: CommitNewStateArgs<'_>) {
&delegation_metadata_account.data,
)
.unwrap();
let expected_requester = if args.allow_undelegation {
dlp_api::state::UndelegationRequester::Validator
} else {
dlp_api::state::UndelegationRequester::None
};
assert_eq!(
delegation_metadata.undelegation_requester,
dlp_api::state::UndelegationRequester::Validator
expected_requester
);
}

Expand Down
Loading
Loading