[Solana] More tests (#1263)

* Checkpoint

* Checkpoint

* Cleanup

* Checkpoint, debug

* Go

* Checkpoint

* Fix

* Add new error and test

* Cleanup

* Add another test

* Keep adding errors

* Another test

* Add comment

* More

* Do it

* Again

* Nice (#1265)

* Test governance

* Fix
This commit is contained in:
guibescos 2024-01-31 16:53:48 +00:00 committed by GitHub
parent 2ab72d994d
commit 1871ca1bf3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 1106 additions and 47 deletions

View File

@ -8,6 +8,7 @@ use {
messages::{
Message,
PriceFeedMessage,
TwapMessage,
},
wire::{
to_vec,
@ -110,24 +111,61 @@ pub fn create_dummy_price_feed_message(value: i64) -> Message {
Message::PriceFeedMessage(msg)
}
pub fn create_dummy_twap_message() -> Message {
let msg = TwapMessage {
feed_id: [0; 32],
cumulative_price: 0,
cumulative_conf: 0,
num_down_slots: 0,
exponent: 0,
publish_time: 0,
prev_publish_time: 0,
publish_slot: 0,
};
Message::TwapMessage(msg)
}
pub fn create_accumulator_message(
all_feeds: &[Message],
updates: &[Message],
corrupt_wormhole_message: bool,
corrupt_messages: bool,
) -> Vec<u8> {
let all_feeds_bytes: Vec<_> = all_feeds
let mut all_feeds_bytes: Vec<_> = all_feeds
.iter()
.map(|f| to_vec::<_, BigEndian>(f).unwrap())
.collect();
let mut updates_bytes: Vec<_> = updates
.iter()
.map(|f| to_vec::<_, BigEndian>(f).unwrap())
.collect();
if corrupt_messages {
all_feeds_bytes = all_feeds_bytes
.iter()
.map(|f| {
let mut f_copy = f.clone();
f_copy[0] = 255;
f_copy
})
.collect();
updates_bytes = updates_bytes
.iter()
.map(|f| {
let mut f_copy = f.clone();
f_copy[0] = 255;
f_copy
})
.collect();
}
let all_feeds_bytes_refs: Vec<_> = all_feeds_bytes.iter().map(|f| f.as_ref()).collect();
let tree = MerkleTree::<Keccak160>::new(all_feeds_bytes_refs.as_slice()).unwrap();
let mut price_updates: Vec<MerklePriceUpdate> = vec![];
for update in updates {
let proof = tree
.prove(&to_vec::<_, BigEndian>(update).unwrap())
.unwrap();
for update in updates_bytes {
let proof = tree.prove(&update).unwrap();
price_updates.push(MerklePriceUpdate {
message: PrefixedVec::from(to_vec::<_, BigEndian>(update).unwrap()),
message: PrefixedVec::from(update),
proof,
});
}

View File

@ -1155,7 +1155,7 @@ mod test {
let feed1 = create_dummy_price_feed_message(100);
let feed2 = create_dummy_price_feed_message(200);
let feed3 = create_dummy_price_feed_message(300);
let data = create_accumulator_message(&[feed1, feed2, feed3], &[feed1], false);
let data = create_accumulator_message(&[feed1, feed2, feed3], &[feed1], false, false);
check_sufficient_fee(&deps.as_ref(), &[data.into()])
}
@ -1246,13 +1246,13 @@ mod test {
let feed2 = create_dummy_price_feed_message(200);
let feed3 = create_dummy_price_feed_message(300);
let msg = create_accumulator_message(&[feed1, feed2, feed3], &[feed1, feed3], false);
let msg = create_accumulator_message(&[feed1, feed2, feed3], &[feed1, feed3], false, false);
assert_eq!(
get_update_fee_amount(&deps.as_ref(), &[msg.into()]).unwrap(),
200
);
let msg = create_accumulator_message(&[feed1, feed2, feed3], &[feed1], false);
let msg = create_accumulator_message(&[feed1, feed2, feed3], &[feed1], false, false);
assert_eq!(
get_update_fee_amount(&deps.as_ref(), &[msg.into()]).unwrap(),
100
@ -1262,6 +1262,7 @@ mod test {
&[feed1, feed2, feed3],
&[feed1, feed2, feed3, feed1, feed3],
false,
false,
);
assert_eq!(
get_update_fee_amount(&deps.as_ref(), &[msg.into()]).unwrap(),
@ -1270,7 +1271,12 @@ mod test {
let batch_msg =
create_batch_price_update_msg_from_attestations(vec![PriceAttestation::default()]);
let msg = create_accumulator_message(&[feed1, feed2, feed3], &[feed1, feed2, feed3], false);
let msg = create_accumulator_message(
&[feed1, feed2, feed3],
&[feed1, feed2, feed3],
false,
false,
);
assert_eq!(
get_update_fee_amount(&deps.as_ref(), &[msg.into(), batch_msg]).unwrap(),
400
@ -1287,7 +1293,7 @@ mod test {
let feed1 = create_dummy_price_feed_message(100);
let feed2 = create_dummy_price_feed_message(200);
let msg = create_accumulator_message(&[feed1, feed2], &[feed1], false);
let msg = create_accumulator_message(&[feed1, feed2], &[feed1], false, false);
let info = mock_info("123", &[]);
let result = update_price_feeds(deps.as_mut(), env, info, &[msg.into()]);
assert!(result.is_ok());
@ -1304,7 +1310,7 @@ mod test {
for i in 0..10000 {
all_feeds.push(create_dummy_price_feed_message(i));
}
let msg = create_accumulator_message(&all_feeds, &all_feeds[100..110], false);
let msg = create_accumulator_message(&all_feeds, &all_feeds[100..110], false, false);
let info = mock_info("123", &[]);
let result = update_price_feeds(deps.as_mut(), env, info, &[msg.into()]);
assert!(result.is_ok());
@ -1331,15 +1337,24 @@ mod test {
let mut feed1 = create_dummy_price_feed_message(100);
let mut feed2 = create_dummy_price_feed_message(200);
let mut feed3 = create_dummy_price_feed_message(300);
let msg = create_accumulator_message(&[feed1, feed2, feed3], &[feed1, feed2, feed3], false);
let msg = create_accumulator_message(
&[feed1, feed2, feed3],
&[feed1, feed2, feed3],
false,
false,
);
as_mut_price_feed(&mut feed1).publish_time += 1;
as_mut_price_feed(&mut feed2).publish_time += 1;
as_mut_price_feed(&mut feed3).publish_time += 1;
as_mut_price_feed(&mut feed1).price *= 2;
as_mut_price_feed(&mut feed2).price *= 2;
as_mut_price_feed(&mut feed3).price *= 2;
let msg2 =
create_accumulator_message(&[feed1, feed2, feed3], &[feed1, feed2, feed3], false);
let msg2 = create_accumulator_message(
&[feed1, feed2, feed3],
&[feed1, feed2, feed3],
false,
false,
);
let info = mock_info("123", &[]);
let result = update_price_feeds(deps.as_mut(), env, info, &[msg.into(), msg2.into()]);
@ -1360,7 +1375,12 @@ mod test {
let feed3 = create_dummy_price_feed_message(300);
as_mut_price_feed(&mut feed2).publish_time -= 1;
as_mut_price_feed(&mut feed2).price *= 2;
let msg = create_accumulator_message(&[feed1, feed2, feed3], &[feed1, feed2, feed3], false);
let msg = create_accumulator_message(
&[feed1, feed2, feed3],
&[feed1, feed2, feed3],
false,
false,
);
let info = mock_info("123", &[]);
let result = update_price_feeds(deps.as_mut(), env, info, &[msg.into()]);
@ -1380,9 +1400,10 @@ mod test {
let feed3 = create_dummy_price_feed_message(300);
as_mut_price_feed(&mut feed2).publish_time -= 1;
as_mut_price_feed(&mut feed2).price *= 2;
let msg = create_accumulator_message(&[feed1, feed2, feed3], &[feed1, feed3], false);
let msg = create_accumulator_message(&[feed1, feed2, feed3], &[feed1, feed3], false, false);
let msg2 = create_accumulator_message(&[feed1, feed2, feed3], &[feed2, feed3], false);
let msg2 =
create_accumulator_message(&[feed1, feed2, feed3], &[feed2, feed3], false, false);
let info = mock_info("123", &[]);
let result = update_price_feeds(deps.as_mut(), env, info, &[msg.into(), msg2.into()]);
@ -1399,7 +1420,7 @@ mod test {
.unwrap();
let feed1 = create_dummy_price_feed_message(100);
let mut msg = create_accumulator_message(&[feed1], &[feed1], false);
let mut msg = create_accumulator_message(&[feed1], &[feed1], false, false);
msg[4] = 3; // major version
let info = mock_info("123", &[]);
let result = update_price_feeds(deps.as_mut(), env, info, &[msg.into()]);
@ -1418,7 +1439,7 @@ mod test {
.unwrap();
let feed1 = create_dummy_price_feed_message(100);
let msg = create_accumulator_message(&[feed1], &[feed1], true);
let msg = create_accumulator_message(&[feed1], &[feed1], true, false);
let info = mock_info("123", &[]);
let result = update_price_feeds(deps.as_mut(), env, info, &[msg.into()]);
assert!(result.is_err());
@ -1446,7 +1467,7 @@ mod test {
prev_publish_time: 0,
publish_slot: 0,
});
let msg = create_accumulator_message(&[feed1], &[feed1], false);
let msg = create_accumulator_message(&[feed1], &[feed1], false, false);
let info = mock_info("123", &[]);
let result = update_price_feeds(deps.as_mut(), env, info, &[msg.into()]);
assert!(result.is_err());

View File

@ -863,7 +863,7 @@ async fn test_accumulator_updates() {
// Create a couple of test feeds.
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], false);
let message = create_accumulator_message(&[feed_1, feed_2], &[feed_1], false, false);
let message = hex::encode(message);
// Call the usual UpdatePriceFeed function.

View File

@ -106,6 +106,11 @@ impl ProgramSimulator {
Ok(T::deserialize(&mut &account.data[8..])?)
}
pub async fn get_balance(&mut self, pubkey: Pubkey) -> Result<u64, BanksClientError> {
let lamports = self.banks_client.get_balance(pubkey).await.unwrap();
Ok(lamports)
}
}
pub fn into_transaction_error<T: Into<anchor_lang::prelude::Error>>(error: T) -> TransactionError {

View File

@ -3,18 +3,21 @@ 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("An error occurred when deserializing the message")]
DeserializeMessageFailed,
#[msg("Received an invalid wormhole message")]
InvalidWormholeMessage,
#[msg("An error occurred when deserializing the message")]
DeserializeMessageFailed,
#[msg("Received an invalid price update")]
InvalidPriceUpdate,
#[msg("This type of message is not supported currently")]
UnsupportedMessageType,
#[msg("The tuple emitter chain, emitter doesn't match one of the valid data sources.")]
InvalidDataSource,
#[msg("Funds are insufficient to pay the receiving fee")]
InsufficientFunds,
// Price account permissions
#[msg("This signer can't write to price update account")]
WrongWriteAuthority,
// Wormhole contract encoded vaa error (from post_updates)
#[msg("The posted VAA account has the wrong owner.")]
WrongVaaOwner,

View File

@ -68,8 +68,8 @@ pub mod pyth_solana_receiver {
Ok(())
}
pub fn authorize_governance_authority_transfer(
ctx: Context<AuthorizeGovernanceAuthorityTransfer>,
pub fn accept_governance_authority_transfer(
ctx: Context<AcceptGovernanceAuthorityTransfer>,
) -> Result<()> {
let config = &mut ctx.accounts.config;
config.governance_authority = config.target_governance_authority.ok_or(error!(
@ -244,13 +244,13 @@ pub struct Governance<'info> {
}
#[derive(Accounts)]
pub struct AuthorizeGovernanceAuthorityTransfer<'info> {
pub struct AcceptGovernanceAuthorityTransfer<'info> {
#[account(constraint =
payer.key() == config.target_governance_authority.ok_or(error!(ReceiverError::NonexistentGovernanceAuthorityTransferRequest))? @
ReceiverError::TargetGovernanceAuthorityMismatch
)]
pub payer: Signer<'info>,
#[account(seeds = [CONFIG_SEED.as_ref()], bump)]
#[account(mut, seeds = [CONFIG_SEED.as_ref()], bump)]
pub config: Account<'info, Config>,
}
@ -269,7 +269,7 @@ pub struct PostUpdates<'info> {
pub treasury: AccountInfo<'info>,
/// The contraint is such that either the price_update_account is uninitialized or the payer is the write_authority.
/// Pubkey::default() is the SystemProgram on Solana and it can't sign so it's impossible that price_update_account.write_authority == Pubkey::default() once the account is initialized
#[account(init_if_needed, constraint = price_update_account.write_authority == Pubkey::default() || price_update_account.write_authority == payer.key(), payer =payer, space = PriceUpdateV1::LEN)]
#[account(init_if_needed, constraint = price_update_account.write_authority == Pubkey::default() || price_update_account.write_authority == payer.key() @ ReceiverError::WrongWriteAuthority , payer =payer, space = PriceUpdateV1::LEN)]
pub price_update_account: Account<'info, PriceUpdateV1>,
pub system_program: Program<'info, System>,
}
@ -290,7 +290,7 @@ pub struct PostUpdatesAtomic<'info> {
pub treasury: AccountInfo<'info>,
/// The contraint is such that either the price_update_account is uninitialized or the payer is the write_authority.
/// Pubkey::default() is the SystemProgram on Solana and it can't sign so it's impossible that price_update_account.write_authority == Pubkey::default() once the account is initialized
#[account(init_if_needed, constraint = price_update_account.write_authority == Pubkey::default() || price_update_account.write_authority == payer.key(), payer = payer, space = PriceUpdateV1::LEN)]
#[account(init_if_needed, constraint = price_update_account.write_authority == Pubkey::default() || price_update_account.write_authority == payer.key() @ ReceiverError::WrongWriteAuthority, payer = payer, space = PriceUpdateV1::LEN)]
pub price_update_account: Account<'info, PriceUpdateV1>,
pub system_program: Program<'info, System>,
}

View File

@ -2,7 +2,10 @@ use {
crate::{
accounts,
instruction,
state::config::Config,
state::config::{
Config,
DataSource,
},
PostUpdatesAtomicParams,
CONFIG_SEED,
ID,
@ -78,6 +81,13 @@ impl accounts::Governance {
}
}
impl accounts::AcceptGovernanceAuthorityTransfer {
pub fn populate(payer: Pubkey) -> Self {
let config = get_config_address();
accounts::AcceptGovernanceAuthorityTransfer { payer, config }
}
}
impl instruction::Initialize {
pub fn populate(payer: &Pubkey, initial_config: Config) -> Instruction {
Instruction {
@ -141,6 +151,85 @@ impl instruction::PostUpdatesAtomic {
}
impl instruction::SetDataSources {
pub fn populate(payer: Pubkey, data_sources: Vec<DataSource>) -> Instruction {
let governance_accounts = accounts::Governance::populate(payer).to_account_metas(None);
Instruction {
program_id: ID,
accounts: governance_accounts,
data: instruction::SetDataSources {
valid_data_sources: data_sources,
}
.data(),
}
}
}
impl instruction::SetFee {
pub fn populate(payer: Pubkey, fee: u64) -> Instruction {
let governance_accounts = accounts::Governance::populate(payer).to_account_metas(None);
Instruction {
program_id: ID,
accounts: governance_accounts,
data: instruction::SetFee {
single_update_fee_in_lamports: fee,
}
.data(),
}
}
}
impl instruction::SetWormholeAddress {
pub fn populate(payer: Pubkey, wormhole: Pubkey) -> Instruction {
let governance_accounts = accounts::Governance::populate(payer).to_account_metas(None);
Instruction {
program_id: ID,
accounts: governance_accounts,
data: instruction::SetWormholeAddress { wormhole }.data(),
}
}
}
impl instruction::SetMinimumSignatures {
pub fn populate(payer: Pubkey, minimum_signatures: u8) -> Instruction {
let governance_accounts = accounts::Governance::populate(payer).to_account_metas(None);
Instruction {
program_id: ID,
accounts: governance_accounts,
data: instruction::SetMinimumSignatures { minimum_signatures }.data(),
}
}
}
impl instruction::RequestGovernanceAuthorityTransfer {
pub fn populate(payer: Pubkey, target_governance_authority: Pubkey) -> Instruction {
let governance_accounts = accounts::Governance::populate(payer).to_account_metas(None);
Instruction {
program_id: ID,
accounts: governance_accounts,
data: instruction::RequestGovernanceAuthorityTransfer {
target_governance_authority,
}
.data(),
}
}
}
impl instruction::AcceptGovernanceAuthorityTransfer {
pub fn populate(payer: Pubkey) -> Instruction {
let governance_accounts =
accounts::AcceptGovernanceAuthorityTransfer::populate(payer).to_account_metas(None);
Instruction {
program_id: ID,
accounts: governance_accounts,
data: instruction::AcceptGovernanceAuthorityTransfer {}.data(),
}
}
}
pub fn get_treasury_address() -> Pubkey {
Pubkey::find_program_address(&[TREASURY_SEED.as_ref()], &ID).0
}

View File

@ -46,9 +46,9 @@ use {
pub const DEFAULT_GUARDIAN_SET_INDEX: u32 = 0;
pub const WRONG_GUARDIAN_SET_INDEX: u32 = 1;
pub fn default_receiver_config() -> Config {
pub fn default_receiver_config(governance_authority: Pubkey) -> Config {
Config {
governance_authority: Pubkey::new_unique(),
governance_authority,
target_governance_authority: None,
wormhole: BRIDGE_ID,
valid_data_sources: vec![DataSource {
@ -64,6 +64,7 @@ pub fn default_receiver_config() -> Config {
pub struct ProgramTestFixtures {
pub program_simulator: ProgramSimulator,
pub encoded_vaa_addresses: Vec<Pubkey>,
pub governance_authority: Keypair,
}
pub fn build_encoded_vaa_account_from_vaa(
@ -179,8 +180,9 @@ pub async fn setup_pyth_receiver(
let mut program_simulator = ProgramSimulator::start_from_program_test(program_test).await;
let initial_config = default_receiver_config();
let setup_keypair: Keypair = program_simulator.get_funded_keypair().await.unwrap();
let initial_config = default_receiver_config(setup_keypair.pubkey());
program_simulator
.process_ix_with_default_compute_limit(
@ -206,5 +208,21 @@ pub async fn setup_pyth_receiver(
ProgramTestFixtures {
program_simulator,
encoded_vaa_addresses,
governance_authority: setup_keypair,
}
}
pub async fn assert_treasury_balance(
program_simulator: &mut ProgramSimulator,
expected_balance: u64,
) {
let treasury_balance = program_simulator
.get_balance(get_treasury_address())
.await
.unwrap();
assert_eq!(
treasury_balance,
expected_balance + Rent::default().minimum_balance(0)
);
}

View File

@ -0,0 +1,406 @@
use {
crate::common::WrongSetupOption,
common::{
setup_pyth_receiver,
ProgramTestFixtures,
},
program_simulator::into_transaction_error,
pyth_solana_receiver::{
error::ReceiverError,
instruction::{
AcceptGovernanceAuthorityTransfer,
RequestGovernanceAuthorityTransfer,
SetDataSources,
SetFee,
SetMinimumSignatures,
SetWormholeAddress,
},
sdk::get_config_address,
state::config::{
Config,
DataSource,
},
},
pythnet_sdk::test_utils::SECONDARY_DATA_SOURCE,
solana_program::{
native_token::LAMPORTS_PER_SOL,
pubkey::Pubkey,
},
solana_sdk::signer::Signer,
};
mod common;
#[tokio::test]
async fn test_governance() {
let ProgramTestFixtures {
mut program_simulator,
encoded_vaa_addresses: _,
governance_authority,
} = setup_pyth_receiver(vec![], WrongSetupOption::None).await;
let new_governance_authority = program_simulator.get_funded_keypair().await.unwrap();
let initial_config = program_simulator
.get_anchor_account_data::<Config>(get_config_address())
.await
.unwrap();
let new_config = Config {
governance_authority: new_governance_authority.pubkey(),
target_governance_authority: None,
wormhole: Pubkey::new_unique(),
valid_data_sources: vec![DataSource {
chain: SECONDARY_DATA_SOURCE.chain.into(),
emitter: Pubkey::from(SECONDARY_DATA_SOURCE.address.0),
}],
single_update_fee_in_lamports: LAMPORTS_PER_SOL,
minimum_signatures: 20,
};
// this authority is not allowed to do anything
assert_eq!(
program_simulator
.process_ix_with_default_compute_limit(
SetDataSources::populate(
new_governance_authority.pubkey(),
new_config.valid_data_sources.clone()
),
&vec![&new_governance_authority],
None,
)
.await
.unwrap_err()
.unwrap(),
into_transaction_error(ReceiverError::GovernanceAuthorityMismatch)
);
assert_eq!(
program_simulator
.process_ix_with_default_compute_limit(
SetFee::populate(
new_governance_authority.pubkey(),
new_config.single_update_fee_in_lamports
),
&vec![&new_governance_authority],
None,
)
.await
.unwrap_err()
.unwrap(),
into_transaction_error(ReceiverError::GovernanceAuthorityMismatch)
);
assert_eq!(
program_simulator
.process_ix_with_default_compute_limit(
SetWormholeAddress::populate(
new_governance_authority.pubkey(),
new_config.wormhole
),
&vec![&new_governance_authority],
None,
)
.await
.unwrap_err()
.unwrap(),
into_transaction_error(ReceiverError::GovernanceAuthorityMismatch)
);
assert_eq!(
program_simulator
.process_ix_with_default_compute_limit(
SetMinimumSignatures::populate(
new_governance_authority.pubkey(),
new_config.minimum_signatures,
),
&vec![&new_governance_authority],
None,
)
.await
.unwrap_err()
.unwrap(),
into_transaction_error(ReceiverError::GovernanceAuthorityMismatch)
);
assert_eq!(
program_simulator
.process_ix_with_default_compute_limit(
RequestGovernanceAuthorityTransfer::populate(
new_governance_authority.pubkey(),
new_config.governance_authority,
),
&vec![&new_governance_authority],
None,
)
.await
.unwrap_err()
.unwrap(),
into_transaction_error(ReceiverError::GovernanceAuthorityMismatch)
);
assert_eq!(
program_simulator
.get_anchor_account_data::<Config>(get_config_address())
.await
.unwrap(),
initial_config
);
// Now start changing for real
program_simulator
.process_ix_with_default_compute_limit(
SetDataSources::populate(
governance_authority.pubkey(),
new_config.valid_data_sources.clone(),
),
&vec![&governance_authority],
None,
)
.await
.unwrap();
let mut current_config = program_simulator
.get_anchor_account_data::<Config>(get_config_address())
.await
.unwrap();
assert_eq!(
current_config.governance_authority,
initial_config.governance_authority
);
assert_eq!(current_config.target_governance_authority, None);
assert_eq!(current_config.wormhole, initial_config.wormhole);
assert_eq!(
current_config.valid_data_sources,
new_config.valid_data_sources
);
assert_eq!(
current_config.single_update_fee_in_lamports,
initial_config.single_update_fee_in_lamports
);
assert_eq!(
current_config.minimum_signatures,
initial_config.minimum_signatures
);
program_simulator
.process_ix_with_default_compute_limit(
SetFee::populate(
governance_authority.pubkey(),
new_config.single_update_fee_in_lamports,
),
&vec![&governance_authority],
None,
)
.await
.unwrap();
current_config = program_simulator
.get_anchor_account_data::<Config>(get_config_address())
.await
.unwrap();
assert_eq!(
current_config.governance_authority,
initial_config.governance_authority
);
assert_eq!(current_config.target_governance_authority, None);
assert_eq!(current_config.wormhole, initial_config.wormhole);
assert_eq!(
current_config.valid_data_sources,
new_config.valid_data_sources
);
assert_eq!(
current_config.single_update_fee_in_lamports,
new_config.single_update_fee_in_lamports
);
assert_eq!(
current_config.minimum_signatures,
initial_config.minimum_signatures
);
program_simulator
.process_ix_with_default_compute_limit(
SetWormholeAddress::populate(governance_authority.pubkey(), new_config.wormhole),
&vec![&governance_authority],
None,
)
.await
.unwrap();
current_config = program_simulator
.get_anchor_account_data::<Config>(get_config_address())
.await
.unwrap();
assert_eq!(
current_config.governance_authority,
initial_config.governance_authority
);
assert_eq!(current_config.target_governance_authority, None);
assert_eq!(current_config.wormhole, new_config.wormhole);
assert_eq!(
current_config.valid_data_sources,
new_config.valid_data_sources
);
assert_eq!(
current_config.single_update_fee_in_lamports,
new_config.single_update_fee_in_lamports
);
assert_eq!(
current_config.minimum_signatures,
initial_config.minimum_signatures
);
program_simulator
.process_ix_with_default_compute_limit(
SetMinimumSignatures::populate(
governance_authority.pubkey(),
new_config.minimum_signatures,
),
&vec![&governance_authority],
None,
)
.await
.unwrap();
current_config = program_simulator
.get_anchor_account_data::<Config>(get_config_address())
.await
.unwrap();
assert_eq!(
current_config.governance_authority,
initial_config.governance_authority
);
assert_eq!(current_config.target_governance_authority, None);
assert_eq!(current_config.wormhole, new_config.wormhole);
assert_eq!(
current_config.valid_data_sources,
new_config.valid_data_sources
);
assert_eq!(
current_config.single_update_fee_in_lamports,
new_config.single_update_fee_in_lamports
);
assert_eq!(
current_config.minimum_signatures,
new_config.minimum_signatures
);
// Target is not defined yet
assert_eq!(
program_simulator
.process_ix_with_default_compute_limit(
AcceptGovernanceAuthorityTransfer::populate(new_governance_authority.pubkey()),
&vec![&new_governance_authority],
None,
)
.await
.unwrap_err()
.unwrap(),
into_transaction_error(ReceiverError::NonexistentGovernanceAuthorityTransferRequest)
);
// Request transfer
program_simulator
.process_ix_with_default_compute_limit(
RequestGovernanceAuthorityTransfer::populate(
governance_authority.pubkey(),
new_governance_authority.pubkey(),
),
&vec![&governance_authority],
None,
)
.await
.unwrap();
current_config = program_simulator
.get_anchor_account_data::<Config>(get_config_address())
.await
.unwrap();
assert_eq!(
current_config.governance_authority,
initial_config.governance_authority
);
assert_eq!(
current_config.target_governance_authority,
Some(new_governance_authority.pubkey())
);
assert_eq!(current_config.wormhole, new_config.wormhole);
assert_eq!(
current_config.valid_data_sources,
new_config.valid_data_sources
);
assert_eq!(
current_config.single_update_fee_in_lamports,
new_config.single_update_fee_in_lamports
);
assert_eq!(
current_config.minimum_signatures,
new_config.minimum_signatures
);
let poster = program_simulator.get_funded_keypair().await.unwrap();
// Random guy can't accept
assert_eq!(
program_simulator
.process_ix_with_default_compute_limit(
AcceptGovernanceAuthorityTransfer::populate(poster.pubkey()),
&vec![&poster],
None,
)
.await
.unwrap_err()
.unwrap(),
into_transaction_error(ReceiverError::TargetGovernanceAuthorityMismatch)
);
// New authority can accept
program_simulator
.process_ix_with_default_compute_limit(
AcceptGovernanceAuthorityTransfer::populate(new_governance_authority.pubkey()),
&vec![&new_governance_authority],
None,
)
.await
.unwrap();
current_config = program_simulator
.get_anchor_account_data::<Config>(get_config_address())
.await
.unwrap();
assert_eq!(current_config, new_config);
// Now the new authority can do stuff
program_simulator
.process_ix_with_default_compute_limit(
SetFee::populate(new_governance_authority.pubkey(), 9),
&vec![&new_governance_authority],
None,
)
.await
.unwrap();
current_config = program_simulator
.get_anchor_account_data::<Config>(get_config_address())
.await
.unwrap();
assert_eq!(
current_config.governance_authority,
new_config.governance_authority
);
assert_eq!(current_config.target_governance_authority, None);
assert_eq!(current_config.wormhole, new_config.wormhole);
assert_eq!(
current_config.valid_data_sources,
new_config.valid_data_sources
);
assert_eq!(current_config.single_update_fee_in_lamports, 9);
assert_eq!(
current_config.minimum_signatures,
new_config.minimum_signatures
);
}

View File

@ -0,0 +1,460 @@
use {
crate::common::{
assert_treasury_balance,
WrongSetupOption,
DEFAULT_GUARDIAN_SET_INDEX,
},
common::{
setup_pyth_receiver,
ProgramTestFixtures,
},
program_simulator::into_transaction_error,
pyth_solana_receiver::{
error::ReceiverError,
instruction::{
PostUpdatesAtomic,
SetDataSources,
SetFee,
},
sdk::deserialize_accumulator_update_data,
state::{
config::DataSource,
price_update::{
PriceUpdateV1,
VerificationLevel,
},
},
},
pythnet_sdk::{
messages::Message,
test_utils::{
create_accumulator_message,
create_dummy_price_feed_message,
create_dummy_twap_message,
DEFAULT_DATA_SOURCE,
SECONDARY_DATA_SOURCE,
},
},
solana_program::{
native_token::LAMPORTS_PER_SOL,
pubkey::Pubkey,
},
solana_sdk::{
signature::Keypair,
signer::Signer,
},
wormhole_core_bridge_solana::ID as BRIDGE_ID,
};
mod common;
// This file is meant to test the errors that can be thrown by post_price_update_from_vaa
#[tokio::test]
async fn test_invalid_wormhole_message() {
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], true, false);
let (vaa, merkle_price_updates) = deserialize_accumulator_update_data(message).unwrap();
let ProgramTestFixtures {
mut program_simulator,
encoded_vaa_addresses: _,
governance_authority: _,
} = 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();
// corrupted wormhole message
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,
merkle_price_updates[0].clone(),
),
&vec![&poster, &price_update_keypair],
None,
)
.await
.unwrap_err()
.unwrap(),
into_transaction_error(ReceiverError::InvalidWormholeMessage)
);
}
#[tokio::test]
async fn test_invalid_update_message() {
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, true);
let (vaa, merkle_price_updates) = deserialize_accumulator_update_data(message).unwrap();
let ProgramTestFixtures {
mut program_simulator,
encoded_vaa_addresses: _,
governance_authority: _,
} = 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();
// corrupted wormhole message
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,
merkle_price_updates[0].clone(),
),
&vec![&poster, &price_update_keypair],
None,
)
.await
.unwrap_err()
.unwrap(),
into_transaction_error(ReceiverError::DeserializeMessageFailed)
);
}
#[tokio::test]
async fn test_post_price_update_from_vaa() {
let feed_1 = create_dummy_price_feed_message(100);
let feed_2 = create_dummy_price_feed_message(200);
let feed_3 = create_dummy_price_feed_message(300);
let twap_1 = create_dummy_twap_message();
let message = create_accumulator_message(
&[feed_1, feed_2, twap_1],
&[feed_1, feed_2, twap_1],
false,
false,
);
let (vaa, merkle_price_updates) = deserialize_accumulator_update_data(message).unwrap();
let message2 = create_accumulator_message(&[feed_2, feed_3], &[feed_3], false, false);
let (_, merkle_price_updates2) = deserialize_accumulator_update_data(message2).unwrap();
let ProgramTestFixtures {
mut program_simulator,
encoded_vaa_addresses: _,
governance_authority,
} = setup_pyth_receiver(
vec![serde_wormhole::from_slice(&vaa).unwrap()],
WrongSetupOption::None,
)
.await;
assert_treasury_balance(&mut program_simulator, 0).await;
let poster = program_simulator.get_funded_keypair().await.unwrap();
let price_update_keypair = Keypair::new();
// this update is not in the proof
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_updates2[0].clone(),
),
&vec![&poster, &price_update_keypair],
None,
)
.await
.unwrap_err()
.unwrap(),
into_transaction_error(ReceiverError::InvalidPriceUpdate)
);
// this update is a twap
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[2].clone(),
),
&vec![&poster, &price_update_keypair],
None,
)
.await
.unwrap_err()
.unwrap(),
into_transaction_error(ReceiverError::UnsupportedMessageType)
);
// change the data source
program_simulator
.process_ix_with_default_compute_limit(
SetDataSources::populate(
governance_authority.pubkey(),
vec![DataSource {
chain: SECONDARY_DATA_SOURCE.chain.into(),
emitter: Pubkey::from(DEFAULT_DATA_SOURCE.address.0),
}],
),
&vec![&governance_authority],
None,
)
.await
.unwrap();
// Now this should fail!
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::InvalidDataSource)
);
// change again, this time the emitter field
program_simulator
.process_ix_with_default_compute_limit(
SetDataSources::populate(
governance_authority.pubkey(),
vec![DataSource {
chain: DEFAULT_DATA_SOURCE.chain.into(),
emitter: Pubkey::from(SECONDARY_DATA_SOURCE.address.0),
}],
),
&vec![&governance_authority],
None,
)
.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::InvalidDataSource)
);
// change back
program_simulator
.process_ix_with_default_compute_limit(
SetDataSources::populate(
governance_authority.pubkey(),
vec![DataSource {
chain: DEFAULT_DATA_SOURCE.chain.into(),
emitter: Pubkey::from(DEFAULT_DATA_SOURCE.address.0),
}],
),
&vec![&governance_authority],
None,
)
.await
.unwrap();
// Now it works
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();
assert_treasury_balance(&mut program_simulator, 1).await;
let mut price_update_account = program_simulator
.get_anchor_account_data::<PriceUpdateV1>(price_update_keypair.pubkey())
.await
.unwrap();
assert_eq!(price_update_account.write_authority, poster.pubkey());
assert_eq!(
price_update_account.verification_level,
VerificationLevel::Partial(13)
);
assert_eq!(
Message::PriceFeedMessage(price_update_account.price_message),
feed_1
);
// Now change the fee!
program_simulator
.process_ix_with_default_compute_limit(
SetFee::populate(governance_authority.pubkey(), LAMPORTS_PER_SOL),
&vec![&governance_authority],
None,
)
.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[1].clone(),
),
&vec![&poster, &price_update_keypair],
None,
)
.await
.unwrap_err()
.unwrap(),
into_transaction_error(ReceiverError::InsufficientFunds)
);
assert_treasury_balance(&mut program_simulator, 1).await;
price_update_account = program_simulator
.get_anchor_account_data::<PriceUpdateV1>(price_update_keypair.pubkey())
.await
.unwrap();
assert_eq!(price_update_account.write_authority, poster.pubkey());
assert_eq!(
price_update_account.verification_level,
VerificationLevel::Partial(13)
);
assert_eq!(
Message::PriceFeedMessage(price_update_account.price_message),
feed_1
);
// Airdrop more
program_simulator
.airdrop(&poster.pubkey(), LAMPORTS_PER_SOL)
.await
.unwrap();
program_simulator
.process_ix_with_default_compute_limit(
SetFee::populate(governance_authority.pubkey(), LAMPORTS_PER_SOL),
&vec![&governance_authority],
None,
)
.await
.unwrap();
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[1].clone(),
),
&vec![&poster, &price_update_keypair],
None,
)
.await
.unwrap();
assert_treasury_balance(&mut program_simulator, LAMPORTS_PER_SOL + 1).await;
price_update_account = program_simulator
.get_anchor_account_data::<PriceUpdateV1>(price_update_keypair.pubkey())
.await
.unwrap();
assert_eq!(price_update_account.write_authority, poster.pubkey());
assert_eq!(
price_update_account.verification_level,
VerificationLevel::Partial(13)
);
assert_eq!(
Message::PriceFeedMessage(price_update_account.price_message),
feed_2
);
// poster_2 can't write to this price update account
let poster_2 = program_simulator.get_funded_keypair().await.unwrap();
assert_eq!(
program_simulator
.process_ix_with_default_compute_limit(
PostUpdatesAtomic::populate(
poster_2.pubkey(),
price_update_keypair.pubkey(),
BRIDGE_ID,
DEFAULT_GUARDIAN_SET_INDEX,
vaa.clone(),
merkle_price_updates[0].clone(),
),
&vec![&poster_2, &price_update_keypair],
None,
)
.await
.unwrap_err()
.unwrap(),
into_transaction_error(ReceiverError::WrongWriteAuthority)
);
}

View File

@ -1,5 +1,8 @@
use {
crate::common::WrongSetupOption,
crate::common::{
assert_treasury_balance,
WrongSetupOption,
},
common::{
setup_pyth_receiver,
ProgramTestFixtures,
@ -35,19 +38,22 @@ mod common;
async fn test_post_updates() {
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 message = create_accumulator_message(&[feed_1, feed_2], &[feed_1, feed_2], false, false);
let (vaa, merkle_price_updates) = deserialize_accumulator_update_data(message).unwrap();
let ProgramTestFixtures {
mut program_simulator,
encoded_vaa_addresses,
governance_authority: _,
} = setup_pyth_receiver(
vec![serde_wormhole::from_slice(&vaa).unwrap()],
WrongSetupOption::None,
)
.await;
assert_treasury_balance(&mut program_simulator, 0).await;
let poster = program_simulator.get_funded_keypair().await.unwrap();
let price_update_keypair = Keypair::new();
@ -66,6 +72,7 @@ async fn test_post_updates() {
.await
.unwrap();
assert_treasury_balance(&mut program_simulator, 1).await;
let mut price_update_account = program_simulator
.get_anchor_account_data::<PriceUpdateV1>(price_update_keypair.pubkey())
@ -97,6 +104,7 @@ async fn test_post_updates() {
.await
.unwrap();
assert_treasury_balance(&mut program_simulator, 2).await;
price_update_account = program_simulator
.get_anchor_account_data::<PriceUpdateV1>(price_update_keypair.pubkey())
@ -118,12 +126,13 @@ async fn test_post_updates() {
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 message = create_accumulator_message(&[feed_1, feed_2], &[feed_1, feed_2], false, false);
let (vaa, merkle_price_updates) = deserialize_accumulator_update_data(message).unwrap();
let ProgramTestFixtures {
mut program_simulator,
encoded_vaa_addresses: _,
governance_authority: _,
} = setup_pyth_receiver(
vec![serde_wormhole::from_slice(&vaa).unwrap()],
WrongSetupOption::None,
@ -156,12 +165,13 @@ async fn test_post_updates_wrong_encoded_vaa_owner() {
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 message = create_accumulator_message(&[feed_1, feed_2], &[feed_1, feed_2], false, false);
let (vaa, merkle_price_updates) = deserialize_accumulator_update_data(message).unwrap();
let ProgramTestFixtures {
mut program_simulator,
encoded_vaa_addresses,
governance_authority: _,
} = setup_pyth_receiver(
vec![serde_wormhole::from_slice(&vaa).unwrap()],
WrongSetupOption::UnverifiedEncodedVaa,

View File

@ -1,5 +1,6 @@
use {
crate::common::{
assert_treasury_balance,
WrongSetupOption,
DEFAULT_GUARDIAN_SET_INDEX,
},
@ -44,7 +45,7 @@ mod common;
async fn test_post_updates_atomic() {
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 message = create_accumulator_message(&[feed_1, feed_2], &[feed_1, feed_2], false, false);
let (vaa, merkle_price_updates) = deserialize_accumulator_update_data(message).unwrap();
let vaa = serde_wormhole::to_vec(&trim_vaa_signatures(
serde_wormhole::from_slice(&vaa).unwrap(),
@ -55,11 +56,14 @@ async fn test_post_updates_atomic() {
let ProgramTestFixtures {
mut program_simulator,
encoded_vaa_addresses: _,
governance_authority: _,
} = setup_pyth_receiver(vec![], WrongSetupOption::None).await;
let poster = program_simulator.get_funded_keypair().await.unwrap();
let price_update_keypair = Keypair::new();
assert_treasury_balance(&mut program_simulator, 0).await;
// post one update atomically
program_simulator
.process_ix_with_default_compute_limit(
@ -77,6 +81,7 @@ async fn test_post_updates_atomic() {
.await
.unwrap();
assert_treasury_balance(&mut program_simulator, 1).await;
let mut price_update_account = program_simulator
.get_anchor_account_data::<PriceUpdateV1>(price_update_keypair.pubkey())
@ -110,6 +115,7 @@ async fn test_post_updates_atomic() {
.await
.unwrap();
assert_treasury_balance(&mut program_simulator, 2).await;
price_update_account = program_simulator
.get_anchor_account_data::<PriceUpdateV1>(price_update_keypair.pubkey())
@ -131,12 +137,13 @@ async fn test_post_updates_atomic() {
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 message = create_accumulator_message(&[feed_1, feed_2], &[feed_1, feed_2], false, false);
let (vaa, merkle_price_updates) = deserialize_accumulator_update_data(message).unwrap();
let ProgramTestFixtures {
mut program_simulator,
encoded_vaa_addresses: _,
governance_authority: _,
} = setup_pyth_receiver(vec![], WrongSetupOption::None).await;
let poster = program_simulator.get_funded_keypair().await.unwrap();
@ -361,13 +368,14 @@ async fn test_post_updates_atomic_wrong_vaa() {
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 message = create_accumulator_message(&[feed_1, feed_2], &[feed_1, feed_2], false, 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: _,
governance_authority: _,
} = setup_pyth_receiver(vec![], WrongSetupOption::GuardianSetWrongIndex).await;
let poster: Keypair = program_simulator.get_funded_keypair().await.unwrap();
assert_eq!(
@ -394,6 +402,7 @@ async fn test_post_updates_atomic_wrong_setup() {
let ProgramTestFixtures {
mut program_simulator,
encoded_vaa_addresses: _,
governance_authority: _,
} = setup_pyth_receiver(vec![], WrongSetupOption::GuardianSetExpired).await;
let poster = program_simulator.get_funded_keypair().await.unwrap();
assert_eq!(