diff --git a/target_chains/solana/programs/pyth-price-store/src/processor/initialize_publisher.rs b/target_chains/solana/programs/pyth-price-store/src/processor/initialize_publisher.rs index d0c3c5a624..0a1aacf1f1 100644 --- a/target_chains/solana/programs/pyth-price-store/src/processor/initialize_publisher.rs +++ b/target_chains/solana/programs/pyth-price-store/src/processor/initialize_publisher.rs @@ -13,7 +13,7 @@ use { validate_authority, validate_buffer, validate_config, - validate_publisher_config, + validate_publisher_config_for_init, validate_system, }, }, @@ -51,12 +51,11 @@ pub fn initialize_publisher( 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 publisher_config = validate_publisher_config( + let publisher_config = validate_publisher_config_for_init( accounts.next(), args.publisher_config_bump, &args.publisher.into(), program_id, - true, )?; let buffer = validate_buffer(accounts.next(), program_id)?; let system = validate_system(accounts.next())?; diff --git a/target_chains/solana/programs/pyth-price-store/src/processor/submit_prices.rs b/target_chains/solana/programs/pyth-price-store/src/processor/submit_prices.rs index 7abc53fd9b..5f521a78d5 100644 --- a/target_chains/solana/programs/pyth-price-store/src/processor/submit_prices.rs +++ b/target_chains/solana/programs/pyth-price-store/src/processor/submit_prices.rs @@ -9,7 +9,7 @@ use { validate::{ validate_buffer, validate_publisher, - validate_publisher_config, + validate_publisher_config_for_access, }, }, solana_program::{ @@ -39,17 +39,22 @@ pub fn submit_prices( ) -> ProgramResult { let mut accounts = accounts.iter(); let publisher = validate_publisher(accounts.next())?; - let publisher_config = validate_publisher_config( + let publisher_config = validate_publisher_config_for_access( accounts.next(), args.publisher_config_bump, publisher.key, program_id, - false, )?; let buffer = validate_buffer(accounts.next(), program_id)?; let publisher_config_data = publisher_config.data.borrow(); let publisher_config = publisher_config::read(*publisher_config_data)?; + // Required to ensure that `find_program_address` returned the same account as + // `create_program_address` in `initialize_publisher`. + ensure!( + ProgramError::InvalidArgument, + sol_memcmp(&publisher.key.to_bytes(), &publisher_config.publisher, 32) == 0 + ); ensure!( ProgramError::InvalidArgument, sol_memcmp(&buffer.key.to_bytes(), &publisher_config.buffer_account, 32) == 0 diff --git a/target_chains/solana/programs/pyth-price-store/src/validate.rs b/target_chains/solana/programs/pyth-price-store/src/validate.rs index 613c939b6e..74e6682a13 100644 --- a/target_chains/solana/programs/pyth-price-store/src/validate.rs +++ b/target_chains/solana/programs/pyth-price-store/src/validate.rs @@ -57,8 +57,9 @@ pub fn validate_config<'a, 'b>( require_writable: bool, ) -> 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)?; + let (config_pda, expected_bump) = + Pubkey::find_program_address(&[CONFIG_SEED.as_bytes()], program_id); + ensure!(ProgramError::InvalidInstructionData, bump == expected_bump); ensure!( ProgramError::InvalidArgument, pubkey_eq(config.key, &config_pda) @@ -85,14 +86,17 @@ pub fn validate_authority<'a, 'b>( Ok(authority) } -pub fn validate_publisher_config<'a, 'b>( +pub fn validate_publisher_config_for_access<'a, 'b>( account: Option<&'b AccountInfo<'a>>, bump: u8, publisher: &Pubkey, program_id: &Pubkey, - require_writable: bool, ) -> Result<&'b AccountInfo<'a>, ProgramError> { let publisher_config = account.ok_or(ProgramError::NotEnoughAccountKeys)?; + // We use `create_program_address` to make the `submit_prices` instruction cheaper. + // `find_program_address` is used in `initialize_publisher`, so we'll always have + // only one publisher config per publisher. As long as we check the publisher key + // stored in the account in `submit_prices`, it should be safe. let publisher_config_pda = Pubkey::create_program_address( &[ PUBLISHER_CONFIG_SEED.as_bytes(), @@ -102,9 +106,28 @@ pub fn validate_publisher_config<'a, 'b>( program_id, ) .map_err(|_| ProgramError::InvalidInstructionData)?; - if require_writable { - ensure!(ProgramError::InvalidArgument, publisher_config.is_writable); - } + ensure!( + ProgramError::MissingRequiredSignature, + pubkey_eq(publisher_config.key, &publisher_config_pda) + ); + Ok(publisher_config) +} + +pub fn validate_publisher_config_for_init<'a, 'b>( + account: Option<&'b AccountInfo<'a>>, + bump: u8, + publisher: &Pubkey, + program_id: &Pubkey, +) -> Result<&'b AccountInfo<'a>, ProgramError> { + let publisher_config = account.ok_or(ProgramError::NotEnoughAccountKeys)?; + // We use `find_program_address` to guarantee that only one publisher_config + // is created per publisher. + let (publisher_config_pda, expected_bump) = Pubkey::find_program_address( + &[PUBLISHER_CONFIG_SEED.as_bytes(), &publisher.to_bytes()], + program_id, + ); + ensure!(ProgramError::InvalidInstructionData, bump == expected_bump); + ensure!(ProgramError::InvalidArgument, publisher_config.is_writable); ensure!( ProgramError::MissingRequiredSignature, pubkey_eq(publisher_config.key, &publisher_config_pda)