Skip to content
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

[solana] Add wrong vaa tests #1262

Merged
merged 15 commits into from
Jan 31, 2024
Merged
Show file tree
Hide file tree
Changes from 12 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
1 change: 1 addition & 0 deletions target_chains/solana/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions target_chains/solana/program_simulator/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
27 changes: 23 additions & 4 deletions target_chains/solana/program_simulator/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
Expand All @@ -14,11 +18,15 @@ use {
ProgramTestBanksClientExt,
},
solana_sdk::{
compute_budget,
signature::{
Keypair,
Signer,
},
transaction::Transaction,
transaction::{
Transaction,
TransactionError,
},
},
};

Expand Down Expand Up @@ -49,9 +57,13 @@ impl ProgramSimulator {
signers: &Vec<&Keypair>,
payer: Option<&Keypair>,
) -> Result<(), BanksClientError> {
let compute_units_ixs =
compute_budget::ComputeBudgetInstruction::set_compute_unit_limit(2000000);
guibescos marked this conversation as resolved.
Show resolved Hide resolved
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
Expand Down Expand Up @@ -94,3 +106,10 @@ impl ProgramSimulator {
Ok(T::deserialize(&mut &account.data[8..])?)
}
}

pub fn into_transation_error<T: Into<anchor_lang::prelude::Error>>(error: T) -> TransactionError {
guibescos marked this conversation as resolved.
Show resolved Hide resolved
TransactionError::InstructionError(
0,
InstructionError::try_from(u64::from(ProgramError::from(error.into()))).unwrap(),
)
}
39 changes: 22 additions & 17 deletions target_chains/solana/programs/pyth-solana-receiver/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,9 @@ use anchor_lang::prelude::*;

#[error_code]
pub enum ReceiverError {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reordered the errors to match the order of the tests

// 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")]
Expand All @@ -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,
}
12 changes: 9 additions & 3 deletions target_chains/solana/programs/pyth-solana-receiver/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changing this error because the same error code was used in another location

}

// Does this guardian index exist in this guardian set?
Expand Down Expand Up @@ -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)]
Expand All @@ -281,7 +281,7 @@ pub struct PostUpdatesAtomic<'info> {
/// CHECK: We can't use AccountVariant::<GuardianSet> 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>,
Expand Down Expand Up @@ -324,6 +324,12 @@ fn deserialize_guardian_set_checked(
ReceiverError::InvalidGuardianSetPda
);

let timestamp = Clock::get().map(Into::into)?;
require!(
guardian_set.inner().is_active(&timestamp),
ReceiverError::GuardianSetExpired
);

guibescos marked this conversation as resolved.
Show resolved Hide resolved
Ok(guardian_set)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -65,11 +66,20 @@ pub struct ProgramTestFixtures {
pub encoded_vaa_addresses: Vec<Pubkey>,
}

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 = (
<EncodedVaa as anchor_lang::Discriminator>::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,
},
Expand All @@ -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| {
Expand All @@ -101,7 +117,14 @@ pub fn build_guardian_set_account() -> Account {
})
.collect::<Vec<[u8; 20]>>(),
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 = (
Expand All @@ -119,24 +142,39 @@ 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<Vaa<&RawMessage>>) -> ProgramTestFixtures {
pub async fn setup_pyth_receiver(
vaas: Vec<Vaa<&RawMessage>>,
wrong_setup_option: WrongSetupOption,
) -> ProgramTestFixtures {
let mut program_test = ProgramTest::default();
program_test.add_program("pyth_solana_receiver", ID, None);

let mut encoded_vaa_addresses: Vec<Pubkey> = vec![];
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;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
use {
crate::common::WrongSetupOption,
common::{
setup_pyth_receiver,
ProgramTestFixtures,
},
program_simulator::into_transation_error,
pyth_solana_receiver::{
error::ReceiverError,
instruction::PostUpdates,
sdk::deserialize_accumulator_update_data,
state::price_update::{
Expand All @@ -18,6 +21,7 @@ use {
create_dummy_price_feed_message,
},
},
solana_program::pubkey::Pubkey,
solana_sdk::{
signature::Keypair,
signer::Signer,
Expand All @@ -38,7 +42,11 @@ 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();
Expand Down Expand Up @@ -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(
PostUpdates::populate(
poster.pubkey(),
Pubkey::new_unique(),
price_update_keypair.pubkey(),
merkle_price_updates[0].clone(),
),
&vec![&poster, &price_update_keypair],
None,
)
.await
.unwrap_err()
.unwrap(),
into_transation_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(
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_transation_error(wormhole_core_bridge_solana::error::CoreBridgeError::UnverifiedVaa)
);
}
Loading
Loading