Skip to content

Action after finalization #75

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
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
15 changes: 15 additions & 0 deletions src/args/call_handler.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
use borsh::{BorshDeserialize, BorshSerialize};

#[derive(BorshSerialize, BorshDeserialize)]
pub enum Context {
Commit,
Undelegate,
Standalone,
}

#[derive(BorshSerialize, BorshDeserialize)]
pub struct CallHandlerArgs {
pub escrow_index: u8,
pub data: Vec<u8>,
pub context: Context
}
7 changes: 7 additions & 0 deletions src/args/finalize_with_handler.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
use borsh::{BorshDeserialize, BorshSerialize};

#[derive(BorshSerialize, BorshDeserialize)]
pub struct FinalizeWithHookArgs {
pub escrow_index: u8,
pub data: Vec<u8>,
}
4 changes: 4 additions & 0 deletions src/args/mod.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
mod call_handler;
mod commit_state;
mod delegate;
mod delegate_ephemeral_balance;
mod finalize_with_handler;
mod top_up_ephemeral_balance;
mod validator_claim_fees;
mod whitelist_validator_for_program;

pub use call_handler::*;
pub use commit_state::*;
pub use delegate::*;
pub use delegate_ephemeral_balance::*;
pub use finalize_with_handler::*;
pub use top_up_ephemeral_balance::*;
pub use validator_claim_fees::*;
pub use whitelist_validator_for_program::*;
9 changes: 9 additions & 0 deletions src/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,14 @@ pub const PROTOCOL_FEES_PERCENTAGE: u8 = 10;
/// The discriminator for the external undelegate instruction.
pub const EXTERNAL_UNDELEGATE_DISCRIMINATOR: [u8; 8] = [196, 28, 41, 206, 48, 37, 51, 167];

/// The discriminator for the external hook after finalization is complete
/// For anchor: corresponds to function/instruction name delegation_program_finalize_hook
pub const EXTERNAL_FINALIZE_WITH_HOOK_DISCRIMINATOR: [u8; 8] =
[74, 203, 100, 144, 173, 103, 210, 31];

/// The discriminator for the external hook after finalization is complete
/// For anchor: corresponds to function/instruction name delegation_program_call_handler
pub const EXTERNAL_CALL_HANDLER_DISCRIMINATOR: [u8; 8] = [157, 197, 228, 30, 0, 80, 121, 135];

/// The program ID of the delegation program.
pub const DELEGATION_PROGRAM_ID: Pubkey = crate::id();
6 changes: 6 additions & 0 deletions src/discriminator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ pub enum DlpDiscriminator {
CommitStateFromBuffer = 13,
/// See [crate::processor::process_close_validator_fees_vault] for docs.
CloseValidatorFeesVault = 14,
/// See [crate::processor::process_call_handler] for docs.
CallHandler = 15,
/// See [crate::processor::process_finalize_with_hook] for docs.
FinalizeWithHook = 16,
}

impl DlpDiscriminator {
Expand Down Expand Up @@ -60,6 +64,8 @@ impl TryFrom<[u8; 8]> for DlpDiscriminator {
0xc => Ok(DlpDiscriminator::ProtocolClaimFees),
0xd => Ok(DlpDiscriminator::CommitStateFromBuffer),
0xe => Ok(DlpDiscriminator::CloseValidatorFeesVault),
0xf => Ok(DlpDiscriminator::CallHandler),
// 0xf => Ok(DlpDiscriminator::FinalizeWithHook),
_ => Err(ProgramError::InvalidInstructionData),
}
}
Expand Down
40 changes: 40 additions & 0 deletions src/instruction_builder/call_handler.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
use crate::args::CallHandlerArgs;
use crate::discriminator::DlpDiscriminator;
use crate::pda::{ephemeral_balance_pda_from_payer, validator_fees_vault_pda_from_validator};
use borsh::to_vec;
use solana_program::instruction::Instruction;
use solana_program::{instruction::AccountMeta, pubkey::Pubkey};

/// Builds a finalize state instruction.
/// See [crate::processor::call_handler] for docs.
pub fn call_handler(
validator: Pubkey,
handler_program: Pubkey,
delegated_account: Pubkey, // TODO: rename
other_accounts: Vec<AccountMeta>,
args: CallHandlerArgs,
) -> Instruction {
let validator_fees_vault_pda = validator_fees_vault_pda_from_validator(&validator);

// handler accounts
let escrow_account = ephemeral_balance_pda_from_payer(&delegated_account, args.escrow_index);
let mut accounts = vec![
AccountMeta::new(validator, true),
AccountMeta::new(validator_fees_vault_pda, false),
AccountMeta::new_readonly(handler_program, false),
AccountMeta::new(delegated_account, false),
AccountMeta::new(escrow_account, false),
];
// append other accounts at the end
accounts.extend(other_accounts);

Instruction {
program_id: crate::id(),
accounts,
data: [
DlpDiscriminator::CallHandler.to_vec(),
to_vec(&args).unwrap(),
]
.concat(),
}
}
58 changes: 58 additions & 0 deletions src/instruction_builder/finalize_with_handler.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
use crate::args::FinalizeWithHookArgs;
use crate::discriminator::DlpDiscriminator;
use crate::pda::{
commit_record_pda_from_delegated_account, commit_state_pda_from_delegated_account,
delegation_metadata_pda_from_delegated_account, delegation_record_pda_from_delegated_account,
ephemeral_balance_pda_from_payer, validator_fees_vault_pda_from_validator,
};
use borsh::to_vec;
use solana_program::instruction::Instruction;
use solana_program::system_program;
use solana_program::{instruction::AccountMeta, pubkey::Pubkey};

/// Builds a finalize state instruction.
/// See [crate::processor::finalize_with_handler] for docs.
pub fn finalize_with_handler(
validator: Pubkey,
delegated_account: Pubkey,
other_accounts: Vec<AccountMeta>,
handler_program: Pubkey,
args: FinalizeWithHookArgs,
) -> Instruction {
// finalize accounts
let commit_state_pda = commit_state_pda_from_delegated_account(&delegated_account);
let commit_record_pda = commit_record_pda_from_delegated_account(&delegated_account);
let delegation_record_pda = delegation_record_pda_from_delegated_account(&delegated_account);
let delegation_metadata_pda =
delegation_metadata_pda_from_delegated_account(&delegated_account);
let validator_fees_vault_pda = validator_fees_vault_pda_from_validator(&validator);

// handler accounts
let escrow_account = ephemeral_balance_pda_from_payer(&delegated_account, args.escrow_index);
let mut accounts = vec![
// finalize accounts
AccountMeta::new(validator, true),
AccountMeta::new(delegated_account, false),
AccountMeta::new(commit_state_pda, false),
AccountMeta::new(commit_record_pda, false),
AccountMeta::new(delegation_record_pda, false),
AccountMeta::new(delegation_metadata_pda, false),
AccountMeta::new(validator_fees_vault_pda, false),
AccountMeta::new_readonly(system_program::id(), false),
// handler accounts
AccountMeta::new_readonly(handler_program, false),
AccountMeta::new(escrow_account, false),
];
// append other accounts at the end
accounts.extend(other_accounts);

Instruction {
program_id: crate::id(),
accounts,
data: [
DlpDiscriminator::FinalizeWithHook.to_vec(),
to_vec(&args).unwrap(),
]
.concat(),
}
}
5 changes: 4 additions & 1 deletion src/instruction_builder/mod.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
mod close_ephemeral_balance;
mod commit_state;

mod call_handler;
mod close_validator_fees_vault;
mod commit_state_from_buffer;
mod delegate;
mod delegate_ephemeral_balance;
mod finalize;
mod finalize_with_handler;
mod init_protocol_fees_vault;
mod init_validator_fees_vault;
mod protocol_claim_fees;
Expand All @@ -21,10 +22,12 @@ pub use commit_state_from_buffer::*;
pub use delegate::*;
pub use delegate_ephemeral_balance::*;
pub use finalize::*;
pub use finalize_with_handler::*;
pub use init_protocol_fees_vault::*;
pub use init_validator_fees_vault::*;
pub use protocol_claim_fees::*;
pub use top_up_ephemeral_balance::*;
pub use undelegate::*;
pub use validator_claim_fees::*;
pub use whitelist_validator_for_program::*;
pub use call_handler::*;
8 changes: 8 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#![allow(unexpected_cfgs)] // silence clippy for target_os solana and other solana program custom features

use crate::processor::process_call_handler;
use solana_program::{
account_info::AccountInfo, declare_id, entrypoint::ProgramResult, msg,
program_error::ProgramError, pubkey::Pubkey,
Expand Down Expand Up @@ -93,6 +94,13 @@ pub fn process_instruction(
discriminator::DlpDiscriminator::CloseValidatorFeesVault => {
processor::process_close_validator_fees_vault(program_id, accounts, data)?
}
discriminator::DlpDiscriminator::CallHandler => {
process_call_handler(program_id, accounts, data)?
}
discriminator::DlpDiscriminator::FinalizeWithHook => {
// processor::process_finalize_with_hook(program_id, accounts, data)?
unreachable!()
}
}
Ok(())
}
98 changes: 98 additions & 0 deletions src/processor/call_handler.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
use crate::args::{CallHandlerArgs};
use crate::consts::{
EXTERNAL_CALL_HANDLER_DISCRIMINATOR,
};
use crate::ephemeral_balance_seeds_from_payer;
use crate::processor::utils::loaders::{
load_initialized_validator_fees_vault, load_pda, load_signer,
};

use borsh::BorshDeserialize;
use solana_program::account_info::next_account_info;
use solana_program::account_info::AccountInfo;
use solana_program::entrypoint::ProgramResult;
use solana_program::instruction::{AccountMeta, Instruction};
use solana_program::msg;
use solana_program::program::invoke_signed;
use solana_program::program_error::ProgramError;
use solana_program::pubkey::Pubkey;

pub fn process_call_handler(
_program_id: &Pubkey,
accounts: &[AccountInfo],
data: &[u8],
) -> ProgramResult {
let args = CallHandlerArgs::try_from_slice(data)?;

let accounts_iter = &mut accounts.iter();
let validator = next_account_info(accounts_iter)?;
let validator_fees_vault = next_account_info(accounts_iter)?;

let handler_program = next_account_info(accounts_iter)?;
// TODO: rename, actee or something like that
let delegated_account = next_account_info(accounts_iter)?;
let escrow_account = next_account_info(accounts_iter)?;

// verify account is a signer
load_signer(validator, "validator")?;
// verify signer is a registered validator
load_initialized_validator_fees_vault(validator, validator_fees_vault, true)?;

// Check if destination prgram is executable
if !handler_program.executable {
msg!(
"{} program is not executable: destination program",
handler_program.key
);
return Err(ProgramError::InvalidAccountData);
}
// verify passed escrow_account derived from delegated_account
let escrow_seeds: &[&[u8]] =
ephemeral_balance_seeds_from_payer!(delegated_account.key, args.escrow_index);
let escrow_bump = load_pda(
escrow_account,
escrow_seeds,
&crate::id(),
true,
"ephemeral balance",
)?;

// deduce necessary accounts for CPI
let (accounts_meta, handler_accounts): (Vec<AccountMeta>, Vec<AccountInfo>) =
[delegated_account, escrow_account]
.into_iter()
.chain(accounts_iter)
// TODO: check if we can keep it, but set is_signer false to prevent draining
.filter(|account| account.key != validator.key)
.map(|account| {
(
AccountMeta {
pubkey: *account.key,
is_writable: account.is_writable,
is_signer: account.key == escrow_account.key,
},
account.clone(),
)
})
.collect();
msg!(
"Calling, accounts_meta.len: {}, handler_account.len: {}",
accounts_meta.len(),
handler_accounts.len()
);

let data = [EXTERNAL_CALL_HANDLER_DISCRIMINATOR.to_vec(), data.to_vec()].concat();
let handler_instruction = Instruction {
program_id: *handler_program.key,
data,
accounts: accounts_meta,
};
let bump_slice = &[escrow_bump];
let escrow_signer_seeds = [escrow_seeds, &[bump_slice]].concat();

invoke_signed(
&handler_instruction,
&handler_accounts,
&[&escrow_signer_seeds],
)
}
Loading