From 9e1ce6a3912c39be094de8652f92c61805a80bf7 Mon Sep 17 00:00:00 2001 From: Pavel Strakhov Date: Fri, 6 Sep 2024 11:13:20 +0100 Subject: [PATCH] refactor(target_chains/solana): parse data before passing to instruction handlers, remove clones, fix feature --- .../programs/pyth-price-publisher/Cargo.toml | 7 +- .../pyth-price-publisher/src/accounts.rs | 6 +- .../src/accounts/buffer.rs | 24 +++++-- .../src/accounts/config.rs | 16 +++-- .../src/accounts/errors.rs | 6 +- .../src/accounts/publisher_config.rs | 18 +++-- .../pyth-price-publisher/src/instruction.rs | 30 +++++--- .../pyth-price-publisher/src/processor.rs | 52 ++++++++++---- .../src/processor/initialize.rs | 44 ++++++++---- .../src/processor/initialize_publisher.rs | 57 ++++++++++----- .../src/processor/submit_prices.rs | 36 ++++++---- .../pyth-price-publisher/src/validate.rs | 71 ++++++++++--------- 12 files changed, 246 insertions(+), 121 deletions(-) diff --git a/target_chains/solana/programs/pyth-price-publisher/Cargo.toml b/target_chains/solana/programs/pyth-price-publisher/Cargo.toml index 262dece6a2..24cd8ef0ee 100644 --- a/target_chains/solana/programs/pyth-price-publisher/Cargo.toml +++ b/target_chains/solana/programs/pyth-price-publisher/Cargo.toml @@ -6,14 +6,17 @@ edition = "2021" [lib] crate-type = ["cdylib", "lib"] +[features] +solana-program = ["dep:solana-program", "cc", "jobserver"] + [dependencies] bytemuck = { version = "1.13.0", features = ["derive"] } solana-program = { version = "=1.14.17", optional = true } thiserror = "1.0.40" # Select older packages which are compatible with solana's Rust toolchain -cc = "=1.0.67" -jobserver = "=0.1.20" +cc = { version = "=1.0.67", optional = true } +jobserver = { version = "=0.1.20", optional = true } [dev-dependencies] solana-program-test = "=1.14.17" diff --git a/target_chains/solana/programs/pyth-price-publisher/src/accounts.rs b/target_chains/solana/programs/pyth-price-publisher/src/accounts.rs index b76e39fdf6..0d70a47480 100644 --- a/target_chains/solana/programs/pyth-price-publisher/src/accounts.rs +++ b/target_chains/solana/programs/pyth-price-publisher/src/accounts.rs @@ -1,5 +1,7 @@ -use bytemuck::from_bytes; -use std::mem::size_of; +use { + bytemuck::from_bytes, + std::mem::size_of, +}; pub mod buffer; pub mod config; diff --git a/target_chains/solana/programs/pyth-price-publisher/src/accounts/buffer.rs b/target_chains/solana/programs/pyth-price-publisher/src/accounts/buffer.rs index 24adb9a85a..163c9a1fd0 100644 --- a/target_chains/solana/programs/pyth-price-publisher/src/accounts/buffer.rs +++ b/target_chains/solana/programs/pyth-price-publisher/src/accounts/buffer.rs @@ -1,6 +1,16 @@ use { - super::errors::{ExtendError, PublisherPriceError, ReadAccountError}, - bytemuck::{cast_slice, from_bytes, from_bytes_mut, Pod, Zeroable}, + super::errors::{ + ExtendError, + PublisherPriceError, + ReadAccountError, + }, + bytemuck::{ + cast_slice, + from_bytes, + from_bytes_mut, + Pod, + Zeroable, + }, std::mem::size_of, }; @@ -18,12 +28,12 @@ const FORMAT: u32 = 2848712303; #[repr(C, packed)] pub struct BufferHeader { /// Account magic to avoid account confusion. - pub format: u32, + pub format: u32, /// The publisher this buffer is associated with. - pub publisher: [u8; 32], + pub publisher: [u8; 32], /// The slot corresponding to all prices currently stored in the account. /// Determined by the clock value when `SubmitPrices` is called. - pub slot: u64, + pub slot: u64, /// The number of prices currently stored in the account. pub num_prices: u32, } @@ -45,8 +55,8 @@ pub struct BufferedPrice { // 4 high bits: trading status // 28 low bits: feed index pub trading_status_and_feed_index: u32, - pub price: i64, - pub confidence: u64, + pub price: i64, + pub confidence: u64, } impl BufferedPrice { diff --git a/target_chains/solana/programs/pyth-price-publisher/src/accounts/config.rs b/target_chains/solana/programs/pyth-price-publisher/src/accounts/config.rs index 859c6d1d80..2e5aab9f89 100644 --- a/target_chains/solana/programs/pyth-price-publisher/src/accounts/config.rs +++ b/target_chains/solana/programs/pyth-price-publisher/src/accounts/config.rs @@ -1,7 +1,13 @@ -use bytemuck::{from_bytes, from_bytes_mut, Pod, Zeroable}; -use std::mem::size_of; - -use super::errors::ReadAccountError; +use { + super::errors::ReadAccountError, + bytemuck::{ + from_bytes, + from_bytes_mut, + Pod, + Zeroable, + }, + std::mem::size_of, +}; /// Account Magic to avoid Account Confusiong const FORMAT: u32 = 1505352794; @@ -15,7 +21,7 @@ pub fn format_matches(data: &[u8]) -> bool { #[repr(C, packed)] pub struct Config { /// Account magic to avoid account confusion. - pub format: u32, + pub format: u32, /// The signature of the authority account will be required to execute /// `InitializePublisher` instruction. pub authority: [u8; 32], diff --git a/target_chains/solana/programs/pyth-price-publisher/src/accounts/errors.rs b/target_chains/solana/programs/pyth-price-publisher/src/accounts/errors.rs index 80524276ed..e5691bdabb 100644 --- a/target_chains/solana/programs/pyth-price-publisher/src/accounts/errors.rs +++ b/target_chains/solana/programs/pyth-price-publisher/src/accounts/errors.rs @@ -26,8 +26,10 @@ pub enum ExtendError { #[cfg(feature = "solana-program")] mod convert { - use super::*; - use solana_program::program_error::ProgramError; + use { + super::*, + solana_program::program_error::ProgramError, + }; impl From for ProgramError { fn from(value: ReadAccountError) -> Self { diff --git a/target_chains/solana/programs/pyth-price-publisher/src/accounts/publisher_config.rs b/target_chains/solana/programs/pyth-price-publisher/src/accounts/publisher_config.rs index 4bd99aefdd..9e36247e58 100644 --- a/target_chains/solana/programs/pyth-price-publisher/src/accounts/publisher_config.rs +++ b/target_chains/solana/programs/pyth-price-publisher/src/accounts/publisher_config.rs @@ -1,7 +1,13 @@ -use bytemuck::{from_bytes, from_bytes_mut, Pod, Zeroable}; -use std::mem::size_of; - -use super::errors::ReadAccountError; +use { + super::errors::ReadAccountError, + bytemuck::{ + from_bytes, + from_bytes_mut, + Pod, + Zeroable, + }, + std::mem::size_of, +}; /// Account Magic to avoid Account Confusiong const FORMAT: u32 = 2258188348; @@ -15,10 +21,10 @@ pub fn format_matches(data: &[u8]) -> bool { #[repr(C, packed)] pub struct PublisherConfig { /// Account magic to avoid account confusion. - pub format: u32, + pub format: u32, /// The publisher this config is associated with. /// Always matches the pubkey used to derive the PDA pubkey. - pub publisher: [u8; 32], + pub publisher: [u8; 32], /// The publisher's buffer account. pub buffer_account: [u8; 32], } diff --git a/target_chains/solana/programs/pyth-price-publisher/src/instruction.rs b/target_chains/solana/programs/pyth-price-publisher/src/instruction.rs index 446468e406..285e991e63 100644 --- a/target_chains/solana/programs/pyth-price-publisher/src/instruction.rs +++ b/target_chains/solana/programs/pyth-price-publisher/src/instruction.rs @@ -1,4 +1,7 @@ -use bytemuck::{Pod, Zeroable}; +use bytemuck::{ + Pod, + Zeroable, +}; /// Seed used to derive the config account. pub const CONFIG_SEED: &str = "CONFIG"; @@ -27,13 +30,20 @@ pub enum Instruction { #[cfg(feature = "solana-program")] impl Instruction { - pub fn parse(input: &[u8]) -> Result { - match input.first() { - Some(0) => Ok(Instruction::Initialize), - Some(1) => Ok(Instruction::SubmitPrices), - Some(2) => Ok(Instruction::InitializePublisher), - _ => Err(solana_program::program_error::ProgramError::InvalidInstructionData), + pub fn parse( + input: &[u8], + ) -> Result<(Instruction, &[u8]), solana_program::program_error::ProgramError> { + if input.is_empty() { + return Err(solana_program::program_error::ProgramError::InvalidInstructionData); } + let payload = &input[1..]; + let instruction = match input[0] { + 0 => Instruction::Initialize, + 1 => Instruction::SubmitPrices, + 2 => Instruction::InitializePublisher, + _ => return Err(solana_program::program_error::ProgramError::InvalidInstructionData), + }; + Ok((instruction, payload)) } } @@ -44,18 +54,18 @@ pub struct InitializeArgs { pub config_bump: u8, /// The signature of the authority account will be required to execute /// `InitializePublisher` instruction. - pub authority: [u8; 32], + pub authority: [u8; 32], } #[derive(Debug, Clone, Copy, Zeroable, Pod)] #[repr(C, packed)] pub struct InitializePublisherArgs { /// PDA bump of the config account. - pub config_bump: u8, + pub config_bump: u8, /// PDA bump of the publisher config account. pub publisher_config_bump: u8, /// The publisher to be initialized. - pub publisher: [u8; 32], + pub publisher: [u8; 32], } #[derive(Debug, Clone, Copy, Zeroable, Pod)] diff --git a/target_chains/solana/programs/pyth-price-publisher/src/processor.rs b/target_chains/solana/programs/pyth-price-publisher/src/processor.rs index 7806677b08..ad2f706559 100644 --- a/target_chains/solana/programs/pyth-price-publisher/src/processor.rs +++ b/target_chains/solana/programs/pyth-price-publisher/src/processor.rs @@ -2,13 +2,26 @@ mod initialize; mod initialize_publisher; mod submit_prices; -use crate::instruction::Instruction; -use solana_program::{ - account_info::AccountInfo, entrypoint::ProgramResult, program_error::ProgramError, - pubkey::Pubkey, -}; use { - initialize::initialize, initialize_publisher::initialize_publisher, + crate::{ + ensure, + instruction::{ + InitializeArgs, + InitializePublisherArgs, + Instruction, + SubmitPricesArgsHeader, + }, + }, + bytemuck::try_from_bytes, + initialize::initialize, + initialize_publisher::initialize_publisher, + solana_program::{ + account_info::AccountInfo, + entrypoint::ProgramResult, + program_error::ProgramError, + pubkey::Pubkey, + }, + std::mem::size_of, submit_prices::submit_prices, }; @@ -18,12 +31,27 @@ pub fn process_instruction( accounts: &[AccountInfo], data: &[u8], ) -> ProgramResult { - match Instruction::parse(data) { - Ok(Instruction::Initialize) => initialize(program_id, accounts, &data[1..]), - Ok(Instruction::SubmitPrices) => submit_prices(program_id, accounts, &data[1..]), - Ok(Instruction::InitializePublisher) => { - initialize_publisher(program_id, accounts, &data[1..]) + let (instruction, payload) = Instruction::parse(data)?; + match instruction { + Instruction::Initialize => { + let args: &InitializeArgs = + try_from_bytes(payload).map_err(|_| ProgramError::InvalidInstructionData)?; + initialize(program_id, accounts, args) + } + Instruction::SubmitPrices => { + ensure!( + ProgramError::InvalidInstructionData, + payload.len() >= size_of::() + ); + let (args_data, prices_data) = payload.split_at(size_of::()); + let args: &SubmitPricesArgsHeader = + try_from_bytes(args_data).map_err(|_| ProgramError::InvalidInstructionData)?; + submit_prices(program_id, accounts, args, prices_data) + } + Instruction::InitializePublisher => { + let args: &InitializePublisherArgs = + try_from_bytes(payload).map_err(|_| ProgramError::InvalidInstructionData)?; + initialize_publisher(program_id, accounts, args) } - _ => Err(ProgramError::InvalidInstructionData), } } diff --git a/target_chains/solana/programs/pyth-price-publisher/src/processor/initialize.rs b/target_chains/solana/programs/pyth-price-publisher/src/processor/initialize.rs index 52b40f769b..4aef549086 100644 --- a/target_chains/solana/programs/pyth-price-publisher/src/processor/initialize.rs +++ b/target_chains/solana/programs/pyth-price-publisher/src/processor/initialize.rs @@ -1,23 +1,34 @@ use { crate::{ accounts, - instruction::{InitializeArgs, CONFIG_SEED}, - validate::{validate_config, validate_payer, validate_system}, + instruction::{ + InitializeArgs, + CONFIG_SEED, + }, + validate::{ + validate_config, + validate_payer, + validate_system, + }, }, - bytemuck::try_from_bytes, solana_program::{ - account_info::AccountInfo, entrypoint::ProgramResult, program::invoke_signed, - program_error::ProgramError, pubkey::Pubkey, rent::Rent, system_instruction, + account_info::AccountInfo, + entrypoint::ProgramResult, + program::invoke_signed, + pubkey::Pubkey, + rent::Rent, + system_instruction, sysvar::Sysvar, }, }; // Creates a config account that stores the authority pubkey. // The authority is allowed to modify publisher configs. -pub fn initialize(program_id: &Pubkey, accounts: &[AccountInfo], data: &[u8]) -> ProgramResult { - let args: &InitializeArgs = - try_from_bytes(data).map_err(|_| ProgramError::InvalidInstructionData)?; - +pub fn initialize( + program_id: &Pubkey, + accounts: &[AccountInfo], + args: &InitializeArgs, +) -> ProgramResult { let mut accounts = accounts.iter(); let payer = validate_payer(accounts.next())?; let config = validate_config(accounts.next(), args.config_bump, program_id, true)?; @@ -47,14 +58,23 @@ pub fn initialize(program_id: &Pubkey, accounts: &[AccountInfo], data: &[u8]) -> #[cfg(test)] mod tests { use { - crate::{accounts, instruction::CONFIG_SEED}, + crate::{ + accounts, + instruction::CONFIG_SEED, + }, solana_program::{ - instruction::{AccountMeta, Instruction}, + instruction::{ + AccountMeta, + Instruction, + }, pubkey::Pubkey, system_program, }, solana_program_test::*, - solana_sdk::{signature::Signer, transaction::Transaction}, + solana_sdk::{ + signature::Signer, + transaction::Transaction, + }, }; #[tokio::test] diff --git a/target_chains/solana/programs/pyth-price-publisher/src/processor/initialize_publisher.rs b/target_chains/solana/programs/pyth-price-publisher/src/processor/initialize_publisher.rs index 19f95eab86..f5abefda9d 100644 --- a/target_chains/solana/programs/pyth-price-publisher/src/processor/initialize_publisher.rs +++ b/target_chains/solana/programs/pyth-price-publisher/src/processor/initialize_publisher.rs @@ -1,17 +1,30 @@ use { crate::{ - accounts::{buffer, publisher_config}, + accounts::{ + buffer, + publisher_config, + }, ensure, - instruction::{InitializePublisherArgs, PUBLISHER_CONFIG_SEED}, + instruction::{ + InitializePublisherArgs, + PUBLISHER_CONFIG_SEED, + }, validate::{ - validate_authority, validate_buffer, validate_config, validate_publisher_config, + validate_authority, + validate_buffer, + validate_config, + validate_publisher_config, validate_system, }, }, - bytemuck::try_from_bytes, solana_program::{ - account_info::AccountInfo, entrypoint::ProgramResult, program::invoke_signed, - program_error::ProgramError, pubkey::Pubkey, rent::Rent, system_instruction, + account_info::AccountInfo, + entrypoint::ProgramResult, + program::invoke_signed, + program_error::ProgramError, + pubkey::Pubkey, + rent::Rent, + system_instruction, sysvar::Sysvar, }, }; @@ -21,15 +34,12 @@ use { pub fn initialize_publisher( program_id: &Pubkey, accounts: &[AccountInfo], - data: &[u8], + args: &InitializePublisherArgs, ) -> ProgramResult { - let args: &InitializePublisherArgs = - try_from_bytes(data).map_err(|_| ProgramError::InvalidInstructionData)?; - let mut accounts = accounts.iter(); let first_account = accounts.next(); let config = validate_config(accounts.next(), args.config_bump, program_id, false)?; - let authority = validate_authority(first_account, &config)?; + let authority = validate_authority(first_account, config)?; let publisher_config = validate_publisher_config( accounts.next(), args.publisher_config_bump, @@ -86,20 +96,35 @@ mod tests { crate::{ accounts::{ self, - buffer::{BufferHeader, BufferedPrice}, + buffer::{ + BufferHeader, + BufferedPrice, + }, + }, + instruction::{ + CONFIG_SEED, + PUBLISHER_CONFIG_SEED, }, - instruction::{CONFIG_SEED, PUBLISHER_CONFIG_SEED}, }, - bytemuck::{bytes_of, cast_slice}, + bytemuck::{ + bytes_of, + cast_slice, + }, solana_program::{ - instruction::{AccountMeta, Instruction}, + instruction::{ + AccountMeta, + Instruction, + }, pubkey::Pubkey, system_program, }, solana_program_test::*, solana_sdk::{ rent::Rent, - signature::{Keypair, Signer}, + signature::{ + Keypair, + Signer, + }, transaction::Transaction, }, }; diff --git a/target_chains/solana/programs/pyth-price-publisher/src/processor/submit_prices.rs b/target_chains/solana/programs/pyth-price-publisher/src/processor/submit_prices.rs index 6efde524e6..e246162ccc 100644 --- a/target_chains/solana/programs/pyth-price-publisher/src/processor/submit_prices.rs +++ b/target_chains/solana/programs/pyth-price-publisher/src/processor/submit_prices.rs @@ -1,31 +1,37 @@ use { crate::{ - accounts::{buffer, publisher_config}, + accounts::{ + buffer, + publisher_config, + }, ensure, instruction::SubmitPricesArgsHeader, - validate::{validate_buffer, validate_publisher, validate_publisher_config}, + validate::{ + validate_buffer, + validate_publisher, + validate_publisher_config, + }, }, - bytemuck::try_from_bytes, solana_program::{ - account_info::AccountInfo, clock::Clock, entrypoint::ProgramResult, - program_error::ProgramError, pubkey::Pubkey, sysvar::Sysvar, + account_info::AccountInfo, + clock::Clock, + entrypoint::ProgramResult, + program_error::ProgramError, + pubkey::Pubkey, + sysvar::Sysvar, }, - std::mem::size_of, }; /// Append the new pricing information provided by the publisher to /// its buffer account. The buffer account will be read and applied by the validator /// to read at the end of the slot. /// If there are old prices in the account, they will be removed before adding new data. -pub fn submit_prices(program_id: &Pubkey, accounts: &[AccountInfo], data: &[u8]) -> ProgramResult { - ensure!( - ProgramError::InvalidInstructionData, - data.len() >= size_of::() - ); - let (args_data, prices_data) = data.split_at(size_of::()); - let args: &SubmitPricesArgsHeader = - try_from_bytes(args_data).map_err(|_| ProgramError::InvalidInstructionData)?; - +pub fn submit_prices( + program_id: &Pubkey, + accounts: &[AccountInfo], + args: &SubmitPricesArgsHeader, + prices_data: &[u8], +) -> ProgramResult { let mut accounts = accounts.iter(); let publisher = validate_publisher(accounts.next())?; let publisher_config = validate_publisher_config( diff --git a/target_chains/solana/programs/pyth-price-publisher/src/validate.rs b/target_chains/solana/programs/pyth-price-publisher/src/validate.rs index e246e4f245..ceb43eab4f 100644 --- a/target_chains/solana/programs/pyth-price-publisher/src/validate.rs +++ b/target_chains/solana/programs/pyth-price-publisher/src/validate.rs @@ -1,27 +1,34 @@ use { crate::{ - accounts, ensure, - instruction::{CONFIG_SEED, PUBLISHER_CONFIG_SEED}, + accounts, + ensure, + instruction::{ + CONFIG_SEED, + PUBLISHER_CONFIG_SEED, + }, }, solana_program::{ - account_info::AccountInfo, program_error::ProgramError, program_memory::sol_memcmp, - pubkey::Pubkey, system_program, + account_info::AccountInfo, + program_error::ProgramError, + program_memory::sol_memcmp, + pubkey::Pubkey, + system_program, }, }; -pub fn validate_publisher<'a>( - account: Option<&AccountInfo<'a>>, -) -> Result, ProgramError> { - let publisher = account.cloned().ok_or(ProgramError::NotEnoughAccountKeys)?; +pub fn validate_publisher<'a, 'b>( + account: Option<&'b AccountInfo<'a>>, +) -> Result<&'b AccountInfo<'a>, ProgramError> { + let publisher = account.ok_or(ProgramError::NotEnoughAccountKeys)?; ensure!(ProgramError::MissingRequiredSignature, publisher.is_signer); ensure!(ProgramError::InvalidArgument, publisher.is_writable); Ok(publisher) } -pub fn validate_system<'a>( - account: Option<&AccountInfo<'a>>, -) -> Result, ProgramError> { - let system = account.cloned().ok_or(ProgramError::NotEnoughAccountKeys)?; +pub fn validate_system<'a, 'b>( + account: Option<&'b AccountInfo<'a>>, +) -> Result<&'b AccountInfo<'a>, ProgramError> { + let system = account.ok_or(ProgramError::NotEnoughAccountKeys)?; ensure!( ProgramError::InvalidArgument, pubkey_eq(system.key, &system_program::id()) @@ -29,10 +36,10 @@ pub fn validate_system<'a>( Ok(system) } -pub fn validate_payer<'a>( - account: Option<&AccountInfo<'a>>, -) -> Result, ProgramError> { - let payer = account.cloned().ok_or(ProgramError::NotEnoughAccountKeys)?; +pub fn validate_payer<'a, 'b>( + account: Option<&'b AccountInfo<'a>>, +) -> Result<&'b AccountInfo<'a>, ProgramError> { + let payer = account.ok_or(ProgramError::NotEnoughAccountKeys)?; ensure!(ProgramError::MissingRequiredSignature, payer.is_signer); ensure!(ProgramError::InvalidArgument, payer.is_writable); Ok(payer) @@ -42,13 +49,13 @@ fn pubkey_eq(a: &Pubkey, b: &Pubkey) -> bool { sol_memcmp(a.as_ref(), b.as_ref(), 32) == 0 } -pub fn validate_config<'a>( - account: Option<&AccountInfo<'a>>, +pub fn validate_config<'a, 'b>( + account: Option<&'b AccountInfo<'a>>, bump: u8, program_id: &Pubkey, require_writable: bool, -) -> Result, ProgramError> { - let config = account.cloned().ok_or(ProgramError::NotEnoughAccountKeys)?; +) -> Result<&'b AccountInfo<'a>, ProgramError> { + let config = account.ok_or(ProgramError::NotEnoughAccountKeys)?; let config_pda = Pubkey::create_program_address(&[CONFIG_SEED.as_bytes(), &[bump]], program_id) .map_err(|_| ProgramError::InvalidInstructionData)?; ensure!( @@ -61,11 +68,11 @@ pub fn validate_config<'a>( Ok(config) } -pub fn validate_authority<'a>( - account: Option<&AccountInfo<'a>>, +pub fn validate_authority<'a, 'b>( + account: Option<&'b AccountInfo<'a>>, config: &AccountInfo<'a>, -) -> Result, ProgramError> { - let authority = account.cloned().ok_or(ProgramError::NotEnoughAccountKeys)?; +) -> Result<&'b AccountInfo<'a>, ProgramError> { + let authority = account.ok_or(ProgramError::NotEnoughAccountKeys)?; ensure!(ProgramError::MissingRequiredSignature, authority.is_signer); ensure!(ProgramError::InvalidArgument, authority.is_writable); let config_data = config.data.borrow(); @@ -77,14 +84,14 @@ pub fn validate_authority<'a>( Ok(authority) } -pub fn validate_publisher_config<'a>( - account: Option<&AccountInfo<'a>>, +pub fn validate_publisher_config<'a, 'b>( + account: Option<&'b AccountInfo<'a>>, bump: u8, publisher: &Pubkey, program_id: &Pubkey, require_writable: bool, -) -> Result, ProgramError> { - let publisher_config = account.cloned().ok_or(ProgramError::NotEnoughAccountKeys)?; +) -> Result<&'b AccountInfo<'a>, ProgramError> { + let publisher_config = account.ok_or(ProgramError::NotEnoughAccountKeys)?; let publisher_config_pda = Pubkey::create_program_address( &[ PUBLISHER_CONFIG_SEED.as_bytes(), @@ -104,11 +111,11 @@ pub fn validate_publisher_config<'a>( Ok(publisher_config) } -pub fn validate_buffer<'a>( - account: Option<&AccountInfo<'a>>, +pub fn validate_buffer<'a, 'b>( + account: Option<&'b AccountInfo<'a>>, program_id: &Pubkey, -) -> Result, ProgramError> { - let buffer = account.cloned().ok_or(ProgramError::NotEnoughAccountKeys)?; +) -> Result<&'b AccountInfo<'a>, ProgramError> { + let buffer = account.ok_or(ProgramError::NotEnoughAccountKeys)?; ensure!(ProgramError::InvalidArgument, buffer.is_writable); ensure!(ProgramError::IllegalOwner, buffer.owner == program_id); Ok(buffer)