-
Notifications
You must be signed in to change notification settings - Fork 9
feat: call handler v2 #143
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
Changes from all commits
3f25618
b1306e7
2cb4910
d310023
1c31417
f5f7bdc
1cbb24b
887a1b0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| use crate::args::CallHandlerArgs; | ||
| use crate::discriminator::DlpDiscriminator; | ||
| use crate::pda::{ephemeral_balance_pda_from_payer, validator_fees_vault_pda_from_validator}; | ||
| use crate::{total_size_budget, AccountSizeClass, DLP_PROGRAM_DATA_SIZE_CLASS}; | ||
| use borsh::to_vec; | ||
| use solana_program::instruction::Instruction; | ||
| use solana_program::{instruction::AccountMeta, pubkey::Pubkey}; | ||
|
|
||
| /// Builds a call handler v2 instruction. | ||
| /// See [crate::processor::call_handler_v2] for docs. | ||
| pub fn call_handler_v2( | ||
| validator: Pubkey, | ||
| destination_program: Pubkey, | ||
| source_program: Pubkey, | ||
| escrow_authority: Pubkey, | ||
| 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(&escrow_authority, args.escrow_index); | ||
| let mut accounts = vec![ | ||
| AccountMeta::new(validator, true), | ||
| AccountMeta::new(validator_fees_vault_pda, false), | ||
| AccountMeta::new_readonly(destination_program, false), | ||
| AccountMeta::new_readonly(source_program, false), | ||
| AccountMeta::new(escrow_authority, false), | ||
| AccountMeta::new(escrow_account, false), | ||
| ]; | ||
| // append other accounts at the end | ||
| accounts.extend(other_accounts); | ||
|
|
||
| Instruction { | ||
| program_id: crate::id(), | ||
| accounts, | ||
| data: [ | ||
| DlpDiscriminator::CallHandlerV2.to_vec(), | ||
| to_vec(&args).unwrap(), | ||
| ] | ||
| .concat(), | ||
| } | ||
| } | ||
|
|
||
| /// | ||
| /// Returns accounts-data-size budget for call_handler_v2 instruction. | ||
| /// | ||
| /// This value can be used with ComputeBudgetInstruction::SetLoadedAccountsDataSizeLimit | ||
| /// | ||
| pub fn call_handler_v2_size_budget( | ||
| destination_program: AccountSizeClass, | ||
| source_program: AccountSizeClass, | ||
| other_accounts: u32, | ||
| ) -> u32 { | ||
| total_size_budget(&[ | ||
| DLP_PROGRAM_DATA_SIZE_CLASS, | ||
| AccountSizeClass::Tiny, // validator | ||
| AccountSizeClass::Tiny, // validator_fees_vault_pda | ||
| destination_program, | ||
| source_program, | ||
| AccountSizeClass::Tiny, // escrow_authority | ||
| AccountSizeClass::Tiny, // escrow_account | ||
| ]) + other_accounts | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,110 @@ | ||
| use crate::args::CallHandlerArgs; | ||
| use crate::ephemeral_balance_seeds_from_payer; | ||
| use crate::error::{INVALID_ESCROW_OWNER, INVALID_ESCROW_PDA}; | ||
| use crate::processor::utils::loaders::{ | ||
| load_initialized_validator_fees_vault, load_owned_pda, load_pda, load_signer, | ||
| }; | ||
|
|
||
| use borsh::BorshDeserialize; | ||
| use solana_program::account_info::AccountInfo; | ||
| use solana_program::entrypoint::ProgramResult; | ||
| use solana_program::instruction::{AccountMeta, Instruction}; | ||
| use solana_program::program::invoke_signed; | ||
| use solana_program::program_error::ProgramError; | ||
| use solana_program::pubkey::Pubkey; | ||
| use solana_program::system_program; | ||
|
|
||
| /// Calls a handler on user specified program | ||
| /// | ||
| /// Accounts: | ||
| /// 0: `[signer]` validator | ||
| /// 1: `[]` validator fee vault to verify its registration | ||
| /// 2: `[]` destination program of an action | ||
| /// 3: `[]` source program of an action | ||
| /// 4: `[]` escrow authority account which created escrow account | ||
| /// 5: `[writable]` non delegated escrow PDA created from escrow_authority (index 4) | ||
| /// 6: `[readonly/writable]` other accounts needed for action | ||
| /// 7: `[readonly/writable]` other accounts needed for action | ||
| /// 8: ... | ||
| /// | ||
| /// Requirements: | ||
| /// | ||
| /// - escrow account initialized | ||
| /// - escrow account not delegated | ||
| /// - validator as a caller | ||
| /// | ||
| /// Steps: | ||
| /// 1. Verify that signer is a valid registered validator | ||
| /// 2. Verify escrow pda exists and not delegated | ||
| /// 3. Invoke signed on behalf of escrow pda user specified action | ||
| /// | ||
| /// Usage: | ||
| /// | ||
| /// This instruction is meant to be called via CPI with the owning program signing for the | ||
| /// delegated account. | ||
| pub fn process_call_handler_v2( | ||
| _program_id: &Pubkey, | ||
| accounts: &[AccountInfo], | ||
| data: &[u8], | ||
| ) -> ProgramResult { | ||
| const OTHER_ACCOUNTS_OFFSET: usize = 6; | ||
|
|
||
| let ( | ||
| [validator, validator_fees_vault, destination_program, source_program, escrow_authority_account, escrow_account], | ||
| other_accounts, | ||
| ) = accounts.split_at(OTHER_ACCOUNTS_OFFSET) | ||
| else { | ||
| return Err(ProgramError::NotEnoughAccountKeys); | ||
| }; | ||
|
|
||
| let args = CallHandlerArgs::try_from_slice(data)?; | ||
|
|
||
| // 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)?; | ||
|
|
||
| // verify passed escrow_account derived from escrow authority | ||
| let escrow_seeds: &[&[u8]] = | ||
| ephemeral_balance_seeds_from_payer!(escrow_authority_account.key, args.escrow_index); | ||
| let escrow_bump = load_pda( | ||
| escrow_account, | ||
| escrow_seeds, | ||
| &crate::id(), | ||
| true, | ||
| INVALID_ESCROW_PDA, | ||
| )?; | ||
| load_owned_pda(escrow_account, &system_program::id(), INVALID_ESCROW_OWNER)?; | ||
|
Comment on lines
+67
to
+77
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
A prior code review on this PR observed that 🛡️ Proposed fix // verify passed escrow_account derived from escrow authority
+ if !source_program.executable {
+ return Err(ProgramError::InvalidAccountData);
+ }
let escrow_seeds: &[&[u8]] =
ephemeral_balance_seeds_from_payer!(escrow_authority_account.key, args.escrow_index);🤖 Prompt for AI Agents |
||
|
|
||
| // deduce necessary accounts for CPI | ||
| let (accounts_meta, handler_accounts): (Vec<AccountMeta>, Vec<AccountInfo>) = other_accounts | ||
| .iter() | ||
| .chain([source_program, escrow_authority_account, escrow_account]) | ||
GabrielePicco marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| .filter(|account| account.key != validator.key) | ||
| .map(|account| { | ||
| ( | ||
| // We enable only escrow to be a signer | ||
| AccountMeta { | ||
| pubkey: *account.key, | ||
| is_writable: account.is_writable, | ||
| is_signer: account.key == escrow_account.key, | ||
| }, | ||
| account.clone(), | ||
| ) | ||
| }) | ||
| .collect(); | ||
taco-paco marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| let handler_instruction = Instruction { | ||
| program_id: *destination_program.key, | ||
| data: args.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], | ||
| ) | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.