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 all 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"
32 changes: 26 additions & 6 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 All @@ -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);
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 All @@ -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<Keypair, BanksClientError> {
Expand All @@ -94,3 +107,10 @@ impl ProgramSimulator {
Ok(T::deserialize(&mut &account.data[8..])?)
}
}

pub fn into_transaction_error<T: Into<anchor_lang::prelude::Error>>(error: T) -> TransactionError {
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 All @@ -145,7 +183,7 @@ pub async fn setup_pyth_receiver(vaas: Vec<Vaa<&RawMessage>>) -> 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,
Expand Down
Loading
Loading