diff --git a/target_chains/solana/Cargo.lock b/target_chains/solana/Cargo.lock index 402f6f20c2..3ce3971cd7 100644 --- a/target_chains/solana/Cargo.lock +++ b/target_chains/solana/Cargo.lock @@ -2981,6 +2981,7 @@ dependencies = [ name = "program-simulator" version = "0.1.0" dependencies = [ + "anchor-lang", "bincode", "borsh 0.10.3", "solana-client", diff --git a/target_chains/solana/program_simulator/Cargo.toml b/target_chains/solana/program_simulator/Cargo.toml index 58c10329fa..44d0832e4e 100644 --- a/target_chains/solana/program_simulator/Cargo.toml +++ b/target_chains/solana/program_simulator/Cargo.toml @@ -15,3 +15,4 @@ solana-program-test = "1.16.20" solana-program = "1.16.20" bincode = "1.3.3" borsh = "0.10.3" +anchor-lang = "0.28.0" diff --git a/target_chains/solana/program_simulator/src/lib.rs b/target_chains/solana/program_simulator/src/lib.rs index e0594dff3e..b592384b7a 100644 --- a/target_chains/solana/program_simulator/src/lib.rs +++ b/target_chains/solana/program_simulator/src/lib.rs @@ -2,8 +2,12 @@ use { borsh::BorshDeserialize, solana_program::{ hash::Hash, - instruction::Instruction, + instruction::{ + Instruction, + InstructionError, + }, native_token::LAMPORTS_PER_SOL, + program_error::ProgramError, pubkey::Pubkey, system_instruction, }, @@ -14,11 +18,15 @@ use { ProgramTestBanksClientExt, }, solana_sdk::{ + compute_budget, signature::{ Keypair, Signer, }, - transaction::Transaction, + transaction::{ + Transaction, + TransactionError, + }, }, }; @@ -43,15 +51,19 @@ impl ProgramSimulator { /// Process a transaction containing `instruction` signed by `signers`. /// `payer` is used to pay for and sign the transaction. - pub async fn process_ix( + pub async fn process_ix_with_default_compute_limit( &mut self, instruction: Instruction, signers: &Vec<&Keypair>, payer: Option<&Keypair>, ) -> Result<(), BanksClientError> { + let compute_units_ixs = + compute_budget::ComputeBudgetInstruction::set_compute_unit_limit(2000000); let actual_payer = payer.unwrap_or(&self.genesis_keypair); - let mut transaction = - Transaction::new_with_payer(&[instruction], Some(&actual_payer.pubkey())); + let mut transaction = Transaction::new_with_payer( + &[instruction, compute_units_ixs], + Some(&actual_payer.pubkey()), + ); let blockhash = self .banks_client @@ -71,7 +83,8 @@ impl ProgramSimulator { let instruction = system_instruction::transfer(&self.genesis_keypair.pubkey(), to, lamports); - self.process_ix(instruction, &vec![], None).await + self.process_ix_with_default_compute_limit(instruction, &vec![], None) + .await } pub async fn get_funded_keypair(&mut self) -> Result { @@ -94,3 +107,10 @@ impl ProgramSimulator { Ok(T::deserialize(&mut &account.data[8..])?) } } + +pub fn into_transaction_error>(error: T) -> TransactionError { + TransactionError::InstructionError( + 0, + InstructionError::try_from(u64::from(ProgramError::from(error.into()))).unwrap(), + ) +} diff --git a/target_chains/solana/programs/pyth-solana-receiver/src/error.rs b/target_chains/solana/programs/pyth-solana-receiver/src/error.rs index 80b93eed22..ff8b5fa036 100644 --- a/target_chains/solana/programs/pyth-solana-receiver/src/error.rs +++ b/target_chains/solana/programs/pyth-solana-receiver/src/error.rs @@ -2,16 +2,9 @@ use anchor_lang::prelude::*; #[error_code] pub enum ReceiverError { + // Pyth payload errors #[msg("The tuple emitter chain, emitter doesn't match one of the valid data sources.")] InvalidDataSource, - #[msg("The posted VAA account has the wrong owner.")] - WrongVaaOwner, - #[msg("The posted VAA has wrong magic number.")] - PostedVaaHeaderWrongMagicNumber, - #[msg("An error occurred when deserializing the VAA.")] - DeserializeVaaFailed, - #[msg("An error occurred when deserializing the updates.")] - DeserializeUpdateFailed, #[msg("An error occurred when deserializing the message")] DeserializeMessageFailed, #[msg("Received an invalid wormhole message")] @@ -20,27 +13,39 @@ pub enum ReceiverError { InvalidPriceUpdate, #[msg("This type of message is not supported currently")] UnsupportedMessageType, - #[msg("The signer is not authorized to perform this governance action")] - GovernanceAuthorityMismatch, - #[msg("The signer is not authorized to accept the governance authority")] - TargetGovernanceAuthorityMismatch, - #[msg("The governance authority needs to request a transfer first")] - NonexistentGovernanceAuthorityTransferRequest, #[msg("Funds are insufficient to pay the receiving fee")] InsufficientFunds, + // Wormhole contract encoded vaa error (from post_updates) + #[msg("The posted VAA account has the wrong owner.")] + WrongVaaOwner, + // Wormhole signatures verification errors (from post_updates_atomic) + #[msg("An error occurred when deserializing the VAA.")] + DeserializeVaaFailed, #[msg("The number of guardian signatures is below the minimum")] InsufficientGuardianSignatures, - #[msg("The Guardian Set account doesn't match the PDA derivation")] - InvalidGuardianSetPda, - // Wormhole errors #[msg("Invalid VAA version")] InvalidVaaVersion, #[msg("Guardian set version in the VAA doesn't match the guardian set passed")] GuardianSetMismatch, + #[msg("Guardian signature indices must be increasing")] + InvalidGuardianOrder, #[msg("Guardian index exceeds the number of guardians in the set")] InvalidGuardianIndex, #[msg("A VAA signature is invalid")] InvalidSignature, #[msg("The recovered guardian public key doesn't match the guardian set")] InvalidGuardianKeyRecovery, + #[msg("The guardian set account is owned by the wrong program")] + WrongGuardianSetOwner, + #[msg("The Guardian Set account doesn't match the PDA derivation")] + InvalidGuardianSetPda, + #[msg("The Guardian Set is expired")] + GuardianSetExpired, + // Governance errors + #[msg("The signer is not authorized to perform this governance action")] + GovernanceAuthorityMismatch, + #[msg("The signer is not authorized to accept the governance authority")] + TargetGovernanceAuthorityMismatch, + #[msg("The governance authority needs to request a transfer first")] + NonexistentGovernanceAuthorityTransferRequest, } diff --git a/target_chains/solana/programs/pyth-solana-receiver/src/lib.rs b/target_chains/solana/programs/pyth-solana-receiver/src/lib.rs index 3f97c263c2..5e030ac7d7 100644 --- a/target_chains/solana/programs/pyth-solana-receiver/src/lib.rs +++ b/target_chains/solana/programs/pyth-solana-receiver/src/lib.rs @@ -143,7 +143,7 @@ pub mod pyth_solana_receiver { // We do not allow for non-increasing guardian signature indices. let index = usize::from(sig.guardian_index()); if let Some(last_index) = last_guardian_index { - require!(index > last_index, ReceiverError::InvalidGuardianIndex); + require!(index > last_index, ReceiverError::InvalidGuardianOrder); } // Does this guardian index exist in this guardian set? @@ -258,7 +258,7 @@ pub struct AuthorizeGovernanceAuthorityTransfer<'info> { pub struct PostUpdates<'info> { #[account(mut)] pub payer: Signer<'info>, - #[account(owner = config.wormhole)] + #[account(owner = config.wormhole @ ReceiverError::WrongVaaOwner)] /// CHECK: We aren't deserializing the VAA here but later with VaaAccount::load, which is the recommended way pub encoded_vaa: AccountInfo<'info>, #[account(seeds = [CONFIG_SEED.as_ref()], bump)] @@ -281,7 +281,7 @@ pub struct PostUpdatesAtomic<'info> { /// CHECK: We can't use AccountVariant:: here because its owner is hardcoded as the "official" Wormhole program and we want to get the wormhole address from the config. /// Instead we do the same steps in deserialize_guardian_set_checked. #[account( - owner = config.wormhole)] + owner = config.wormhole @ ReceiverError::WrongGuardianSetOwner)] pub guardian_set: AccountInfo<'info>, #[account(seeds = [CONFIG_SEED.as_ref()], bump)] pub config: Account<'info, Config>, @@ -324,6 +324,12 @@ fn deserialize_guardian_set_checked( ReceiverError::InvalidGuardianSetPda ); + let timestamp = Clock::get().map(Into::into)?; + require!( + guardian_set.inner().is_active(×tamp), + ReceiverError::GuardianSetExpired + ); + Ok(guardian_set) } diff --git a/target_chains/solana/programs/pyth-solana-receiver/tests/common/mod.rs b/target_chains/solana/programs/pyth-solana-receiver/tests/common/mod.rs index 631158c812..968374dd46 100644 --- a/target_chains/solana/programs/pyth-solana-receiver/tests/common/mod.rs +++ b/target_chains/solana/programs/pyth-solana-receiver/tests/common/mod.rs @@ -44,6 +44,7 @@ use { }; pub const DEFAULT_GUARDIAN_SET_INDEX: u32 = 0; +pub const WRONG_GUARDIAN_SET_INDEX: u32 = 1; pub fn default_receiver_config() -> Config { Config { @@ -65,11 +66,20 @@ pub struct ProgramTestFixtures { pub encoded_vaa_addresses: Vec, } -pub fn build_encoded_vaa_account_from_vaa(vaa: Vaa<&RawMessage>) -> Account { +pub fn build_encoded_vaa_account_from_vaa( + vaa: Vaa<&RawMessage>, + wrong_setup_option: WrongSetupOption, +) -> Account { let encoded_vaa_data = ( ::DISCRIMINATOR, Header { - status: ProcessingStatus::Verified, + status: { + if matches!(wrong_setup_option, WrongSetupOption::UnverifiedEncodedVaa) { + ProcessingStatus::Writing + } else { + ProcessingStatus::Verified + } + }, write_authority: Pubkey::new_unique(), version: 1, }, @@ -87,9 +97,15 @@ pub fn build_encoded_vaa_account_from_vaa(vaa: Vaa<&RawMessage>) -> Account { } } -pub fn build_guardian_set_account() -> Account { +pub fn build_guardian_set_account(wrong_setup_option: WrongSetupOption) -> Account { let guardian_set = GuardianSet { - index: DEFAULT_GUARDIAN_SET_INDEX, + index: { + if matches!(wrong_setup_option, WrongSetupOption::GuardianSetWrongIndex) { + WRONG_GUARDIAN_SET_INDEX + } else { + DEFAULT_GUARDIAN_SET_INDEX + } + }, keys: dummy_guardians() .iter() .map(|x| { @@ -101,7 +117,14 @@ pub fn build_guardian_set_account() -> Account { }) .collect::>(), creation_time: 0.into(), - expiration_time: 0.into(), + expiration_time: { + if matches!(wrong_setup_option, WrongSetupOption::GuardianSetExpired) { + 1 + } else { + 0 + } + } + .into(), }; let guardian_set_data = ( @@ -119,12 +142,24 @@ pub fn build_guardian_set_account() -> Account { rent_epoch: 0, } } + +#[derive(Copy, Clone)] +pub enum WrongSetupOption { + None, + GuardianSetExpired, + GuardianSetWrongIndex, + UnverifiedEncodedVaa, +} + /** * Setup to test the Pyth Receiver. The return values are a tuple composed of : * - The program simulator, which is used to send transactions * - The pubkeys of the encoded VAA accounts corresponding to the VAAs passed as argument, these accounts are prepopulated and can be used to test post_updates */ -pub async fn setup_pyth_receiver(vaas: Vec>) -> ProgramTestFixtures { +pub async fn setup_pyth_receiver( + vaas: Vec>, + wrong_setup_option: WrongSetupOption, +) -> ProgramTestFixtures { let mut program_test = ProgramTest::default(); program_test.add_program("pyth_solana_receiver", ID, None); @@ -132,11 +167,14 @@ pub async fn setup_pyth_receiver(vaas: Vec>) -> ProgramTestFixt for vaa in vaas { let encoded_vaa_address = Pubkey::new_unique(); encoded_vaa_addresses.push(encoded_vaa_address); - program_test.add_account(encoded_vaa_address, build_encoded_vaa_account_from_vaa(vaa)); + program_test.add_account( + encoded_vaa_address, + build_encoded_vaa_account_from_vaa(vaa, wrong_setup_option), + ); } program_test.add_account( get_guardian_set_address(BRIDGE_ID, DEFAULT_GUARDIAN_SET_INDEX), - build_guardian_set_account(), + build_guardian_set_account(wrong_setup_option), ); let mut program_simulator = ProgramSimulator::start_from_program_test(program_test).await; @@ -145,7 +183,7 @@ pub async fn setup_pyth_receiver(vaas: Vec>) -> ProgramTestFixt let setup_keypair: Keypair = program_simulator.get_funded_keypair().await.unwrap(); program_simulator - .process_ix( + .process_ix_with_default_compute_limit( Initialize::populate(&setup_keypair.pubkey(), initial_config.clone()), &vec![&setup_keypair], None, diff --git a/target_chains/solana/programs/pyth-solana-receiver/tests/test_post_updates.rs b/target_chains/solana/programs/pyth-solana-receiver/tests/test_post_updates.rs index fd22e4773c..b3b0761ec7 100644 --- a/target_chains/solana/programs/pyth-solana-receiver/tests/test_post_updates.rs +++ b/target_chains/solana/programs/pyth-solana-receiver/tests/test_post_updates.rs @@ -1,9 +1,12 @@ use { + crate::common::WrongSetupOption, common::{ setup_pyth_receiver, ProgramTestFixtures, }, + program_simulator::into_transaction_error, pyth_solana_receiver::{ + error::ReceiverError, instruction::PostUpdates, sdk::deserialize_accumulator_update_data, state::price_update::{ @@ -18,6 +21,7 @@ use { create_dummy_price_feed_message, }, }, + solana_program::pubkey::Pubkey, solana_sdk::{ signature::Keypair, signer::Signer, @@ -38,14 +42,18 @@ async fn test_post_updates() { let ProgramTestFixtures { mut program_simulator, encoded_vaa_addresses, - } = setup_pyth_receiver(vec![serde_wormhole::from_slice(&vaa).unwrap()]).await; + } = setup_pyth_receiver( + vec![serde_wormhole::from_slice(&vaa).unwrap()], + WrongSetupOption::None, + ) + .await; let poster = program_simulator.get_funded_keypair().await.unwrap(); let price_update_keypair = Keypair::new(); // post one update program_simulator - .process_ix( + .process_ix_with_default_compute_limit( PostUpdates::populate( poster.pubkey(), encoded_vaa_addresses[0], @@ -76,7 +84,7 @@ async fn test_post_updates() { // post another update to the same account program_simulator - .process_ix( + .process_ix_with_default_compute_limit( PostUpdates::populate( poster.pubkey(), encoded_vaa_addresses[0], @@ -105,3 +113,79 @@ async fn test_post_updates() { feed_2 ); } + +#[tokio::test] +async fn test_post_updates_wrong_encoded_vaa_owner() { + let feed_1 = create_dummy_price_feed_message(100); + let feed_2 = create_dummy_price_feed_message(200); + let message = create_accumulator_message(&[feed_1, feed_2], &[feed_1, feed_2], false); + let (vaa, merkle_price_updates) = deserialize_accumulator_update_data(message).unwrap(); + + let ProgramTestFixtures { + mut program_simulator, + encoded_vaa_addresses: _, + } = setup_pyth_receiver( + vec![serde_wormhole::from_slice(&vaa).unwrap()], + WrongSetupOption::None, + ) + .await; + + let poster = program_simulator.get_funded_keypair().await.unwrap(); + let price_update_keypair = Keypair::new(); + + assert_eq!( + program_simulator + .process_ix_with_default_compute_limit( + PostUpdates::populate( + poster.pubkey(), + Pubkey::new_unique(), // Random pubkey instead of the encoded VAA address + price_update_keypair.pubkey(), + merkle_price_updates[0].clone(), + ), + &vec![&poster, &price_update_keypair], + None, + ) + .await + .unwrap_err() + .unwrap(), + into_transaction_error(ReceiverError::WrongVaaOwner) + ); +} + +#[tokio::test] +async fn test_post_updates_wrong_setup() { + let feed_1 = create_dummy_price_feed_message(100); + let feed_2 = create_dummy_price_feed_message(200); + let message = create_accumulator_message(&[feed_1, feed_2], &[feed_1, feed_2], false); + let (vaa, merkle_price_updates) = deserialize_accumulator_update_data(message).unwrap(); + + let ProgramTestFixtures { + mut program_simulator, + encoded_vaa_addresses, + } = setup_pyth_receiver( + vec![serde_wormhole::from_slice(&vaa).unwrap()], + WrongSetupOption::UnverifiedEncodedVaa, + ) + .await; + + let poster = program_simulator.get_funded_keypair().await.unwrap(); + let price_update_keypair = Keypair::new(); + + assert_eq!( + program_simulator + .process_ix_with_default_compute_limit( + PostUpdates::populate( + poster.pubkey(), + encoded_vaa_addresses[0], + price_update_keypair.pubkey(), + merkle_price_updates[0].clone(), + ), + &vec![&poster, &price_update_keypair], + None, + ) + .await + .unwrap_err() + .unwrap(), + into_transaction_error(wormhole_core_bridge_solana::error::CoreBridgeError::UnverifiedVaa) + ); +} diff --git a/target_chains/solana/programs/pyth-solana-receiver/tests/test_post_updates_atomic.rs b/target_chains/solana/programs/pyth-solana-receiver/tests/test_post_updates_atomic.rs index 972692ffe4..a464e7ef9f 100644 --- a/target_chains/solana/programs/pyth-solana-receiver/tests/test_post_updates_atomic.rs +++ b/target_chains/solana/programs/pyth-solana-receiver/tests/test_post_updates_atomic.rs @@ -1,12 +1,20 @@ use { - crate::common::DEFAULT_GUARDIAN_SET_INDEX, + crate::common::{ + WrongSetupOption, + DEFAULT_GUARDIAN_SET_INDEX, + }, common::{ setup_pyth_receiver, ProgramTestFixtures, }, + program_simulator::into_transaction_error, pyth_solana_receiver::{ + error::ReceiverError, instruction::PostUpdatesAtomic, - sdk::deserialize_accumulator_update_data, + sdk::{ + deserialize_accumulator_update_data, + get_guardian_set_address, + }, state::price_update::{ PriceUpdateV1, VerificationLevel, @@ -20,11 +28,13 @@ use { trim_vaa_signatures, }, }, + serde_wormhole::RawMessage, solana_sdk::{ signature::Keypair, signer::Signer, }, wormhole_core_bridge_solana::ID as BRIDGE_ID, + wormhole_sdk::Vaa, }; mod common; @@ -45,14 +55,14 @@ async fn test_post_updates_atomic() { let ProgramTestFixtures { mut program_simulator, encoded_vaa_addresses: _, - } = setup_pyth_receiver(vec![]).await; + } = setup_pyth_receiver(vec![], WrongSetupOption::None).await; let poster = program_simulator.get_funded_keypair().await.unwrap(); let price_update_keypair = Keypair::new(); // post one update atomically program_simulator - .process_ix( + .process_ix_with_default_compute_limit( PostUpdatesAtomic::populate( poster.pubkey(), price_update_keypair.pubkey(), @@ -85,7 +95,7 @@ async fn test_post_updates_atomic() { // post another update to the same account program_simulator - .process_ix( + .process_ix_with_default_compute_limit( PostUpdatesAtomic::populate( poster.pubkey(), price_update_keypair.pubkey(), @@ -116,3 +126,293 @@ async fn test_post_updates_atomic() { feed_2 ); } + +#[tokio::test] +async fn test_post_updates_atomic_wrong_vaa() { + let feed_1 = create_dummy_price_feed_message(100); + let feed_2 = create_dummy_price_feed_message(200); + let message = create_accumulator_message(&[feed_1, feed_2], &[feed_1, feed_2], false); + let (vaa, merkle_price_updates) = deserialize_accumulator_update_data(message).unwrap(); + + let ProgramTestFixtures { + mut program_simulator, + encoded_vaa_addresses: _, + } = setup_pyth_receiver(vec![], WrongSetupOption::None).await; + + let poster = program_simulator.get_funded_keypair().await.unwrap(); + let price_update_keypair = Keypair::new(); + + let mut vaa_buffer_copy: Vec = vaa.clone(); + // Mess up with the length of signatures + vaa_buffer_copy[5] = 255; + assert_eq!( + program_simulator + .process_ix_with_default_compute_limit( + PostUpdatesAtomic::populate( + poster.pubkey(), + price_update_keypair.pubkey(), + BRIDGE_ID, + DEFAULT_GUARDIAN_SET_INDEX, + vaa_buffer_copy, + merkle_price_updates[0].clone(), + ), + &vec![&poster, &price_update_keypair], + None, + ) + .await + .unwrap_err() + .unwrap(), + into_transaction_error(ReceiverError::DeserializeVaaFailed) + ); + + let vaa_wrong_num_signatures = serde_wormhole::to_vec(&trim_vaa_signatures( + serde_wormhole::from_slice(&vaa).unwrap(), + 4, + )) + .unwrap(); + assert_eq!( + program_simulator + .process_ix_with_default_compute_limit( + PostUpdatesAtomic::populate( + poster.pubkey(), + price_update_keypair.pubkey(), + BRIDGE_ID, + DEFAULT_GUARDIAN_SET_INDEX, + vaa_wrong_num_signatures.clone(), + merkle_price_updates[0].clone(), + ), + &vec![&poster, &price_update_keypair], + None, + ) + .await + .unwrap_err() + .unwrap(), + into_transaction_error(ReceiverError::InsufficientGuardianSignatures) + ); + + + let mut vaa_copy: Vaa<&RawMessage> = serde_wormhole::from_slice(&vaa).unwrap(); + vaa_copy.version = 0; + + assert_eq!( + program_simulator + .process_ix_with_default_compute_limit( + PostUpdatesAtomic::populate( + poster.pubkey(), + price_update_keypair.pubkey(), + BRIDGE_ID, + DEFAULT_GUARDIAN_SET_INDEX, + serde_wormhole::to_vec(&vaa_copy).unwrap(), + merkle_price_updates[0].clone(), + ), + &vec![&poster, &price_update_keypair], + None, + ) + .await + .unwrap_err() + .unwrap(), + into_transaction_error(ReceiverError::InvalidVaaVersion) + ); + + let mut vaa_copy: Vaa<&RawMessage> = serde_wormhole::from_slice(&vaa).unwrap(); + vaa_copy.guardian_set_index = 1; + + assert_eq!( + program_simulator + .process_ix_with_default_compute_limit( + PostUpdatesAtomic::populate( + poster.pubkey(), + price_update_keypair.pubkey(), + BRIDGE_ID, + DEFAULT_GUARDIAN_SET_INDEX, + serde_wormhole::to_vec(&vaa_copy).unwrap(), + merkle_price_updates[0].clone(), + ), + &vec![&poster, &price_update_keypair], + None, + ) + .await + .unwrap_err() + .unwrap(), + into_transaction_error(ReceiverError::GuardianSetMismatch) + ); + + let mut vaa_copy: Vaa<&RawMessage> = serde_wormhole::from_slice(&vaa).unwrap(); + vaa_copy.signatures[0] = vaa_copy.signatures[1]; + + assert_eq!( + program_simulator + .process_ix_with_default_compute_limit( + PostUpdatesAtomic::populate( + poster.pubkey(), + price_update_keypair.pubkey(), + BRIDGE_ID, + DEFAULT_GUARDIAN_SET_INDEX, + serde_wormhole::to_vec(&vaa_copy).unwrap(), + merkle_price_updates[0].clone(), + ), + &vec![&poster, &price_update_keypair], + None, + ) + .await + .unwrap_err() + .unwrap(), + into_transaction_error(ReceiverError::InvalidGuardianOrder) + ); + + + let mut vaa_copy: Vaa<&RawMessage> = serde_wormhole::from_slice(&vaa).unwrap(); + vaa_copy.signatures[0].index = 20; + + assert_eq!( + program_simulator + .process_ix_with_default_compute_limit( + PostUpdatesAtomic::populate( + poster.pubkey(), + price_update_keypair.pubkey(), + BRIDGE_ID, + DEFAULT_GUARDIAN_SET_INDEX, + serde_wormhole::to_vec(&vaa_copy).unwrap(), + merkle_price_updates[0].clone(), + ), + &vec![&poster, &price_update_keypair], + None, + ) + .await + .unwrap_err() + .unwrap(), + into_transaction_error(ReceiverError::InvalidGuardianIndex) + ); + + let mut vaa_copy: Vaa<&RawMessage> = serde_wormhole::from_slice(&vaa).unwrap(); + vaa_copy.signatures[0].signature[64] = 5; + + assert_eq!( + program_simulator + .process_ix_with_default_compute_limit( + PostUpdatesAtomic::populate( + poster.pubkey(), + price_update_keypair.pubkey(), + BRIDGE_ID, + DEFAULT_GUARDIAN_SET_INDEX, + serde_wormhole::to_vec(&vaa_copy).unwrap(), + merkle_price_updates[0].clone(), + ), + &vec![&poster, &price_update_keypair], + None, + ) + .await + .unwrap_err() + .unwrap(), + into_transaction_error(ReceiverError::InvalidSignature) + ); + + + let mut vaa_copy: Vaa<&RawMessage> = serde_wormhole::from_slice(&vaa).unwrap(); + vaa_copy.signatures[0].signature = vaa_copy.signatures[1].signature; + + assert_eq!( + program_simulator + .process_ix_with_default_compute_limit( + PostUpdatesAtomic::populate( + poster.pubkey(), + price_update_keypair.pubkey(), + BRIDGE_ID, + DEFAULT_GUARDIAN_SET_INDEX, + serde_wormhole::to_vec(&vaa_copy).unwrap(), + merkle_price_updates[0].clone(), + ), + &vec![&poster, &price_update_keypair], + None, + ) + .await + .unwrap_err() + .unwrap(), + into_transaction_error(ReceiverError::InvalidGuardianKeyRecovery) + ); + + let mut wrong_instruction = PostUpdatesAtomic::populate( + poster.pubkey(), + price_update_keypair.pubkey(), + BRIDGE_ID, + DEFAULT_GUARDIAN_SET_INDEX, + vaa.clone(), + merkle_price_updates[0].clone(), + ); + + let wrong_guardian_set = get_guardian_set_address(BRIDGE_ID, 1); + wrong_instruction.accounts[1].pubkey = wrong_guardian_set; + assert_eq!( + program_simulator + .process_ix_with_default_compute_limit( + wrong_instruction, + &vec![&poster, &price_update_keypair], + None, + ) + .await + .unwrap_err() + .unwrap(), + into_transaction_error(ReceiverError::WrongGuardianSetOwner) + ); +} + + +#[tokio::test] +async fn test_post_updates_atomic_wrong_setup() { + let feed_1 = create_dummy_price_feed_message(100); + let feed_2 = create_dummy_price_feed_message(200); + let message = create_accumulator_message(&[feed_1, feed_2], &[feed_1, feed_2], false); + let (vaa, merkle_price_updates) = deserialize_accumulator_update_data(message).unwrap(); + let price_update_keypair = Keypair::new(); + + let ProgramTestFixtures { + mut program_simulator, + encoded_vaa_addresses: _, + } = setup_pyth_receiver(vec![], WrongSetupOption::GuardianSetWrongIndex).await; + let poster: Keypair = program_simulator.get_funded_keypair().await.unwrap(); + assert_eq!( + program_simulator + .process_ix_with_default_compute_limit( + PostUpdatesAtomic::populate( + poster.pubkey(), + price_update_keypair.pubkey(), + BRIDGE_ID, + DEFAULT_GUARDIAN_SET_INDEX, + vaa.clone(), + merkle_price_updates[0].clone(), + ), + &vec![&poster, &price_update_keypair], + None, + ) + .await + .unwrap_err() + .unwrap(), + into_transaction_error(ReceiverError::InvalidGuardianSetPda) + ); + + + let ProgramTestFixtures { + mut program_simulator, + encoded_vaa_addresses: _, + } = setup_pyth_receiver(vec![], WrongSetupOption::GuardianSetExpired).await; + let poster = program_simulator.get_funded_keypair().await.unwrap(); + assert_eq!( + program_simulator + .process_ix_with_default_compute_limit( + PostUpdatesAtomic::populate( + poster.pubkey(), + price_update_keypair.pubkey(), + BRIDGE_ID, + DEFAULT_GUARDIAN_SET_INDEX, + vaa.clone(), + merkle_price_updates[0].clone(), + ), + &vec![&poster, &price_update_keypair], + None, + ) + .await + .unwrap_err() + .unwrap(), + into_transaction_error(ReceiverError::GuardianSetExpired) + ); +}