From f0eb327c6f55f7420e88233c40fd76e690199734 Mon Sep 17 00:00:00 2001 From: Jayant Krishnamurthy Date: Thu, 18 Aug 2022 15:00:18 -0700 Subject: [PATCH 01/16] deleting price accounts --- program/c/src/oracle/oracle.h | 7 +++ program/rust/src/processor.rs | 2 + program/rust/src/rust_oracle.rs | 51 ++++++++++++++++++++ program/rust/src/tests/mod.rs | 1 + program/rust/src/tests/test_del_price.rs | 61 ++++++++++++++++++++++++ program/rust/src/tests/test_utils.rs | 16 +++++++ program/rust/src/utils.rs | 9 ++++ 7 files changed, 147 insertions(+) create mode 100644 program/rust/src/tests/test_del_price.rs diff --git a/program/c/src/oracle/oracle.h b/program/c/src/oracle/oracle.h index 3db9b8b09..630bcd6bb 100644 --- a/program/c/src/oracle/oracle.h +++ b/program/c/src/oracle/oracle.h @@ -275,6 +275,13 @@ typedef enum { // key[1] price account [Signer writable] // key[2] system program [readable] e_cmd_resize_price_account, + + // deletes a price account + // key[0] funding account [signer writable] + // key[1] product account [signer writable] + // key[2] price account [signer writable] + // key[3] system program + e_cmd_del_price, } command_t; typedef struct cmd_hdr diff --git a/program/rust/src/processor.rs b/program/rust/src/processor.rs index 09343022f..409af20a2 100644 --- a/program/rust/src/processor.rs +++ b/program/rust/src/processor.rs @@ -29,6 +29,7 @@ use crate::rust_oracle::{ add_price, add_product, add_publisher, + del_price, del_publisher, init_mapping, init_price, @@ -77,6 +78,7 @@ pub fn process_instruction( command_t_e_cmd_add_product => add_product(program_id, accounts, instruction_data), command_t_e_cmd_upd_product => upd_product(program_id, accounts, instruction_data), command_t_e_cmd_set_min_pub => set_min_pub(program_id, accounts, instruction_data), + command_t_e_cmd_del_price => del_price(program_id, accounts, instruction_data), _ => Err(OracleError::UnrecognizedInstruction.into()), } } diff --git a/program/rust/src/rust_oracle.rs b/program/rust/src/rust_oracle.rs index 5dbcfe7f3..a33f7701e 100644 --- a/program/rust/src/rust_oracle.rs +++ b/program/rust/src/rust_oracle.rs @@ -68,6 +68,7 @@ use crate::utils::{ check_valid_writable_account, is_component_update, pubkey_assign, + pubkey_clear, pubkey_equal, pubkey_is_zero, pyth_assert, @@ -372,6 +373,56 @@ pub fn add_price( Ok(SUCCESS) } +/// Delete a price account. This function will remove the link between the price account and its +/// corresponding product account, then transfer any SOL in the price account to the funding +/// account. This function expects there to be only a single price account in the linked list of +/// price accounts for the given product. +/// +/// Warning: This function is dangerous and will break any programs that depend on the deleted +/// price account! +pub fn del_price( + program_id: &Pubkey, + accounts: &[AccountInfo], + instruction_data: &[u8], +) -> OracleResult { + let [funding_account, product_account, price_account, system_program_account] = match accounts { + [w, x, y, z] => Ok([w, x, y, z]), + _ => Err(ProgramError::InvalidArgument), + }?; + + check_valid_funding_account(funding_account)?; + check_valid_signable_account(program_id, product_account, PC_PROD_ACC_SIZE as usize)?; + check_valid_signable_account(program_id, price_account, size_of::())?; + pyth_assert( + check_id(system_program_account.key), + OracleError::InvalidSystemAccount.into(), + )?; + + let cmd_args = load::(&instruction_data)?; + let mut product_data = load_checked::(product_account, cmd_args.ver_)?; + let price_data = load_checked::(price_account, cmd_args.ver_)?; + pyth_assert( + pubkey_equal(&product_data.px_acc_, &price_account.key.to_bytes()), + ProgramError::InvalidArgument, + )?; + pyth_assert( + pubkey_is_zero(&price_data.next_), + ProgramError::InvalidArgument, + )?; + + pubkey_clear(&mut product_data.px_acc_); + + // Zero out the balance of the price account to delete it. + send_lamports( + price_account, + funding_account, + system_program_account, + price_account.lamports(), + )?; + + Ok(SUCCESS) +} + pub fn init_price( program_id: &Pubkey, accounts: &[AccountInfo], diff --git a/program/rust/src/tests/mod.rs b/program/rust/src/tests/mod.rs index 06009cd10..d7477c1f4 100644 --- a/program/rust/src/tests/mod.rs +++ b/program/rust/src/tests/mod.rs @@ -11,3 +11,4 @@ mod test_upd_price; mod test_upd_price_no_fail_on_error; mod test_upd_product; mod test_utils; +mod test_del_price; diff --git a/program/rust/src/tests/test_del_price.rs b/program/rust/src/tests/test_del_price.rs new file mode 100644 index 000000000..c3ac02c19 --- /dev/null +++ b/program/rust/src/tests/test_del_price.rs @@ -0,0 +1,61 @@ +use crate::deserialize::{ + initialize_pyth_account_checked, + load_checked, + load_mut, +}; +use crate::rust_oracle::{del_price, del_publisher}; +use crate::tests::test_utils::AccountSetup; +use bytemuck::bytes_of; +use solana_program::pubkey::Pubkey; + +use crate::c_oracle_header::{cmd_del_publisher, command_t_e_cmd_del_publisher, pc_price_comp_t, pc_price_info_t, pc_price_t, pc_pub_key_t, PythAccount, PC_STATUS_TRADING, PC_VERSION, cmd_hdr, command_t_e_cmd_del_price, pc_prod_t, pc_prod}; +use crate::utils::{ + pubkey_assign, + pubkey_equal, + pubkey_is_zero, +}; +use std::mem::size_of; +use solana_program::program_error::ProgramError; + +#[test] +fn test_del_price() { + let program_id = Pubkey::new_unique(); + let mut instruction_data = [0u8; size_of::()]; + let mut hdr = load_mut::(&mut instruction_data).unwrap(); + hdr.ver_ = PC_VERSION; + hdr.cmd_ = command_t_e_cmd_del_price as i32; + + let mut funding_setup = AccountSetup::new_funding(); + let funding_account = funding_setup.to_account_info(); + + let mut product_setup = AccountSetup::new::(&program_id); + let product_account = product_setup.to_account_info(); + initialize_pyth_account_checked::(&product_account, PC_VERSION).unwrap(); + + let mut price_setup = AccountSetup::new::(&program_id); + let price_account = price_setup.to_account_info(); + initialize_pyth_account_checked::(&price_account, PC_VERSION).unwrap(); + + let mut system_setup = AccountSetup::new_system_program(); + let system_account = system_setup.to_account_info(); + + // Try deleting a price account that isn't linked to the given product account + assert_eq!(del_price( + &program_id, + &[funding_account.clone(), product_account.clone(), price_account.clone(), system_account.clone()], + &instruction_data + ), Err(ProgramError::InvalidArgument)); + + // Same test with a random nonzero pubkey + { + let mut product_data = load_checked(&product_account, PC_VERSION)?; + pubkey_assign(&mut product_data.px_acc_, &Pubkey::new_unique().to_bytes()); + } + assert_eq!(del_price( + &program_id, + &[funding_account.clone(), product_account.clone(), price_account.clone(), system_account.clone()], + &instruction_data + ), Err(ProgramError::InvalidArgument)); + + // Note that we can't test success outside of the solana vm because of the system program. +} diff --git a/program/rust/src/tests/test_utils.rs b/program/rust/src/tests/test_utils.rs index ed04fc90a..0c141246a 100644 --- a/program/rust/src/tests/test_utils.rs +++ b/program/rust/src/tests/test_utils.rs @@ -78,6 +78,22 @@ impl AccountSetup { }; } + pub fn new_system_program() -> Self { + let key = system_program::id(); + let owner = sysvar::id(); + // Note: I have no clue what the right size is. This isn't going to work in reality anyway. + let size = clock::Clock::size_of(); + let balance = Rent::minimum_balance(&Rent::default(), size); + let data = [0u8; UPPER_BOUND_OF_ALL_ACCOUNT_SIZES]; + return AccountSetup { + key, + owner, + balance, + size, + data, + }; + } + pub fn to_account_info(&mut self) -> AccountInfo { return AccountInfo::new( &self.key, diff --git a/program/rust/src/utils.rs b/program/rust/src/utils.rs index b56effd88..c8bdad3cb 100644 --- a/program/rust/src/utils.rs +++ b/program/rust/src/utils.rs @@ -98,6 +98,15 @@ pub fn pubkey_assign(target: &mut pc_pub_key_t, source: &[u8]) { unsafe { target.k1_.copy_from_slice(source) } } +/// Set `target` to contain all-zeros. +pub fn pubkey_clear(target: &mut pc_pub_key_t) { + unsafe { + for i in 0..4 { + target.k8_[i] = 0 + } + } +} + pub fn pubkey_is_zero(key: &pc_pub_key_t) -> bool { return unsafe { key.k8_.iter().all(|x| *x == 0) }; } From 84945c40bbaf7cf673d912daea85f9254329d26c Mon Sep 17 00:00:00 2001 From: Jayant Krishnamurthy Date: Thu, 18 Aug 2022 15:01:56 -0700 Subject: [PATCH 02/16] ok --- program/rust/src/processor.rs | 1 + program/rust/src/tests/test_del_price.rs | 22 +++++++++------------- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/program/rust/src/processor.rs b/program/rust/src/processor.rs index 409af20a2..02c83767f 100644 --- a/program/rust/src/processor.rs +++ b/program/rust/src/processor.rs @@ -9,6 +9,7 @@ use crate::c_oracle_header::{ command_t_e_cmd_add_product, command_t_e_cmd_add_publisher, command_t_e_cmd_agg_price, + command_t_e_cmd_del_price, command_t_e_cmd_del_publisher, command_t_e_cmd_init_mapping, command_t_e_cmd_init_price, diff --git a/program/rust/src/tests/test_del_price.rs b/program/rust/src/tests/test_del_price.rs index c3ac02c19..0bd198164 100644 --- a/program/rust/src/tests/test_del_price.rs +++ b/program/rust/src/tests/test_del_price.rs @@ -1,21 +1,17 @@ +use std::mem::size_of; + +use solana_program::program_error::ProgramError; +use solana_program::pubkey::Pubkey; + +use crate::c_oracle_header::{cmd_hdr, command_t_e_cmd_del_price, pc_price_t, pc_prod_t, PC_VERSION}; use crate::deserialize::{ initialize_pyth_account_checked, load_checked, load_mut, }; -use crate::rust_oracle::{del_price, del_publisher}; +use crate::rust_oracle::del_price; use crate::tests::test_utils::AccountSetup; -use bytemuck::bytes_of; -use solana_program::pubkey::Pubkey; - -use crate::c_oracle_header::{cmd_del_publisher, command_t_e_cmd_del_publisher, pc_price_comp_t, pc_price_info_t, pc_price_t, pc_pub_key_t, PythAccount, PC_STATUS_TRADING, PC_VERSION, cmd_hdr, command_t_e_cmd_del_price, pc_prod_t, pc_prod}; -use crate::utils::{ - pubkey_assign, - pubkey_equal, - pubkey_is_zero, -}; -use std::mem::size_of; -use solana_program::program_error::ProgramError; +use crate::utils::pubkey_assign; #[test] fn test_del_price() { @@ -48,7 +44,7 @@ fn test_del_price() { // Same test with a random nonzero pubkey { - let mut product_data = load_checked(&product_account, PC_VERSION)?; + let mut product_data = load_checked::(&product_account, PC_VERSION).unwrap(); pubkey_assign(&mut product_data.px_acc_, &Pubkey::new_unique().to_bytes()); } assert_eq!(del_price( From 541a9bcf70c87fd9799cfd3e75b896eb53edc91a Mon Sep 17 00:00:00 2001 From: Jayant Krishnamurthy Date: Fri, 19 Aug 2022 14:21:08 -0700 Subject: [PATCH 03/16] fix build --- program/rust/src/rust_oracle.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/program/rust/src/rust_oracle.rs b/program/rust/src/rust_oracle.rs index 1bcc266a6..f150d3d01 100644 --- a/program/rust/src/rust_oracle.rs +++ b/program/rust/src/rust_oracle.rs @@ -383,7 +383,7 @@ pub fn del_price( program_id: &Pubkey, accounts: &[AccountInfo], instruction_data: &[u8], -) -> OracleResult { +) -> ProgramResult { let [funding_account, product_account, price_account, system_program_account] = match accounts { [w, x, y, z] => Ok([w, x, y, z]), _ => Err(ProgramError::InvalidArgument), @@ -419,7 +419,7 @@ pub fn del_price( price_account.lamports(), )?; - Ok(SUCCESS) + Ok(()) } pub fn init_price( From 680e02081bead4820541ed6df9438e36cfa0b266 Mon Sep 17 00:00:00 2001 From: Jayant Krishnamurthy Date: Fri, 19 Aug 2022 14:21:36 -0700 Subject: [PATCH 04/16] rut test --- program/rust/src/tests/test_del_price.rs | 108 ++++++++++++++--------- 1 file changed, 65 insertions(+), 43 deletions(-) diff --git a/program/rust/src/tests/test_del_price.rs b/program/rust/src/tests/test_del_price.rs index 0bd198164..6e0ffbe17 100644 --- a/program/rust/src/tests/test_del_price.rs +++ b/program/rust/src/tests/test_del_price.rs @@ -3,11 +3,17 @@ use std::mem::size_of; use solana_program::program_error::ProgramError; use solana_program::pubkey::Pubkey; -use crate::c_oracle_header::{cmd_hdr, command_t_e_cmd_del_price, pc_price_t, pc_prod_t, PC_VERSION}; +use crate::c_oracle_header::{ + cmd_hdr, + command_t_e_cmd_del_price, + pc_price_t, + pc_prod_t, + PC_VERSION, +}; use crate::deserialize::{ - initialize_pyth_account_checked, - load_checked, - load_mut, + initialize_pyth_account_checked, + load_checked, + load_mut, }; use crate::rust_oracle::del_price; use crate::tests::test_utils::AccountSetup; @@ -15,43 +21,59 @@ use crate::utils::pubkey_assign; #[test] fn test_del_price() { - let program_id = Pubkey::new_unique(); - let mut instruction_data = [0u8; size_of::()]; - let mut hdr = load_mut::(&mut instruction_data).unwrap(); - hdr.ver_ = PC_VERSION; - hdr.cmd_ = command_t_e_cmd_del_price as i32; - - let mut funding_setup = AccountSetup::new_funding(); - let funding_account = funding_setup.to_account_info(); - - let mut product_setup = AccountSetup::new::(&program_id); - let product_account = product_setup.to_account_info(); - initialize_pyth_account_checked::(&product_account, PC_VERSION).unwrap(); - - let mut price_setup = AccountSetup::new::(&program_id); - let price_account = price_setup.to_account_info(); - initialize_pyth_account_checked::(&price_account, PC_VERSION).unwrap(); - - let mut system_setup = AccountSetup::new_system_program(); - let system_account = system_setup.to_account_info(); - - // Try deleting a price account that isn't linked to the given product account - assert_eq!(del_price( - &program_id, - &[funding_account.clone(), product_account.clone(), price_account.clone(), system_account.clone()], - &instruction_data - ), Err(ProgramError::InvalidArgument)); - - // Same test with a random nonzero pubkey - { - let mut product_data = load_checked::(&product_account, PC_VERSION).unwrap(); - pubkey_assign(&mut product_data.px_acc_, &Pubkey::new_unique().to_bytes()); - } - assert_eq!(del_price( - &program_id, - &[funding_account.clone(), product_account.clone(), price_account.clone(), system_account.clone()], - &instruction_data - ), Err(ProgramError::InvalidArgument)); - - // Note that we can't test success outside of the solana vm because of the system program. + let program_id = Pubkey::new_unique(); + let mut instruction_data = [0u8; size_of::()]; + let mut hdr = load_mut::(&mut instruction_data).unwrap(); + hdr.ver_ = PC_VERSION; + hdr.cmd_ = command_t_e_cmd_del_price as i32; + + let mut funding_setup = AccountSetup::new_funding(); + let funding_account = funding_setup.to_account_info(); + + let mut product_setup = AccountSetup::new::(&program_id); + let product_account = product_setup.to_account_info(); + initialize_pyth_account_checked::(&product_account, PC_VERSION).unwrap(); + + let mut price_setup = AccountSetup::new::(&program_id); + let price_account = price_setup.to_account_info(); + initialize_pyth_account_checked::(&price_account, PC_VERSION).unwrap(); + + let mut system_setup = AccountSetup::new_system_program(); + let system_account = system_setup.to_account_info(); + + // Try deleting a price account that isn't linked to the given product account + assert_eq!( + del_price( + &program_id, + &[ + funding_account.clone(), + product_account.clone(), + price_account.clone(), + system_account.clone() + ], + &instruction_data + ), + Err(ProgramError::InvalidArgument) + ); + + // Same test with a random nonzero pubkey + { + let mut product_data = load_checked::(&product_account, PC_VERSION).unwrap(); + pubkey_assign(&mut product_data.px_acc_, &Pubkey::new_unique().to_bytes()); + } + assert_eq!( + del_price( + &program_id, + &[ + funding_account.clone(), + product_account.clone(), + price_account.clone(), + system_account.clone() + ], + &instruction_data + ), + Err(ProgramError::InvalidArgument) + ); + + // Note that we can't test success outside of the solana vm because of the system program. } From 7e7207ad817de437ffbb917e1f31fd422a20e729 Mon Sep 17 00:00:00 2001 From: Jayant Krishnamurthy Date: Fri, 19 Aug 2022 16:34:38 -0700 Subject: [PATCH 05/16] fix --- program/rust/src/rust_oracle.rs | 2 +- program/rust/src/tests/mod.rs | 2 +- program/rust/src/tests/test_utils.rs | 4 +++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/program/rust/src/rust_oracle.rs b/program/rust/src/rust_oracle.rs index f150d3d01..a98afcd1c 100644 --- a/program/rust/src/rust_oracle.rs +++ b/program/rust/src/rust_oracle.rs @@ -397,7 +397,7 @@ pub fn del_price( OracleError::InvalidSystemAccount.into(), )?; - let cmd_args = load::(&instruction_data)?; + let cmd_args = load::(instruction_data)?; let mut product_data = load_checked::(product_account, cmd_args.ver_)?; let price_data = load_checked::(price_account, cmd_args.ver_)?; pyth_assert( diff --git a/program/rust/src/tests/mod.rs b/program/rust/src/tests/mod.rs index 63fb39748..f4f486ba6 100644 --- a/program/rust/src/tests/mod.rs +++ b/program/rust/src/tests/mod.rs @@ -2,6 +2,7 @@ mod test_add_mapping; mod test_add_price; mod test_add_product; mod test_add_publisher; +mod test_del_price; mod test_del_publisher; mod test_init_mapping; mod test_init_price; @@ -12,4 +13,3 @@ mod test_upd_price; mod test_upd_price_no_fail_on_error; mod test_upd_product; mod test_utils; -mod test_del_price; diff --git a/program/rust/src/tests/test_utils.rs b/program/rust/src/tests/test_utils.rs index 0c141246a..3aa3b5cec 100644 --- a/program/rust/src/tests/test_utils.rs +++ b/program/rust/src/tests/test_utils.rs @@ -78,10 +78,12 @@ impl AccountSetup { }; } + /// Create a mock account for the system program. + /// This account is a barely passable version of the system program account -- + /// it has the correct id, but nothing else works. pub fn new_system_program() -> Self { let key = system_program::id(); let owner = sysvar::id(); - // Note: I have no clue what the right size is. This isn't going to work in reality anyway. let size = clock::Clock::size_of(); let balance = Rent::minimum_balance(&Rent::default(), size); let data = [0u8; UPPER_BOUND_OF_ALL_ACCOUNT_SIZES]; From 759481dde2d8359b714a1c581d01ca75c8858038 Mon Sep 17 00:00:00 2001 From: Jayant Krishnamurthy Date: Sat, 20 Aug 2022 08:41:24 -0700 Subject: [PATCH 06/16] trying to write a test --- program/rust/Cargo.toml | 5 +++ program/rust/src/tests/test_del_price.rs | 39 ++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/program/rust/Cargo.toml b/program/rust/Cargo.toml index e823ffe91..a368063ac 100644 --- a/program/rust/Cargo.toml +++ b/program/rust/Cargo.toml @@ -13,6 +13,11 @@ solana-program = "=1.10.29" bytemuck = "1.11.0" thiserror = "1.0" +[dev-dependencies] +solana-program-test = "=1.10.29" +solana-sdk = "=1.10.29" +tokio = "1.14.1" + [features] debug = [] diff --git a/program/rust/src/tests/test_del_price.rs b/program/rust/src/tests/test_del_price.rs index 6e0ffbe17..9ed590931 100644 --- a/program/rust/src/tests/test_del_price.rs +++ b/program/rust/src/tests/test_del_price.rs @@ -1,7 +1,18 @@ +use solana_program::instruction::{ + AccountMeta, + Instruction, +}; use std::mem::size_of; +use std::str::FromStr; use solana_program::program_error::ProgramError; use solana_program::pubkey::Pubkey; +use solana_program::sysvar; +use solana_program_test::{ + processor, + ProgramTest, +}; +use solana_sdk::transaction::Transaction; use crate::c_oracle_header::{ cmd_hdr, @@ -15,6 +26,7 @@ use crate::deserialize::{ load_checked, load_mut, }; +use crate::processor::process_instruction; use crate::rust_oracle::del_price; use crate::tests::test_utils::AccountSetup; use crate::utils::pubkey_assign; @@ -77,3 +89,30 @@ fn test_del_price() { // Note that we can't test success outside of the solana vm because of the system program. } + +#[tokio::test] +async fn test_sysvar() { + println!("This test!"); + let program_id = Pubkey::from_str("Pyth111111111111111111111111111111111111111").unwrap(); + let (mut banks_client, payer, recent_blockhash) = ProgramTest::new( + "spl_example_sysvar", + program_id, + processor!(process_instruction), + ) + .start() + .await; + + let mut transaction = Transaction::new_with_payer( + &[Instruction::new_with_bincode( + program_id, + &(), + vec![ + AccountMeta::new(sysvar::clock::id(), false), + AccountMeta::new(sysvar::rent::id(), false), + ], + )], + Some(&payer.pubkey()), + ); + transaction.sign(&[&payer], recent_blockhash); + banks_client.process_transaction(transaction).await.unwrap(); +} From c3c997e5c6fa31c6da60fafcf475124dba61a271 Mon Sep 17 00:00:00 2001 From: Jayant Krishnamurthy Date: Sat, 20 Aug 2022 09:17:48 -0700 Subject: [PATCH 07/16] trying to write tests --- program/rust/src/tests/mod.rs | 1 + program/rust/src/tests/test_del_price.rs | 58 ++++++++++++++++-------- program/rust/src/tests/test_tx_utils.rs | 8 ++++ program/rust/src/time_machine_types.rs | 4 +- 4 files changed, 50 insertions(+), 21 deletions(-) create mode 100644 program/rust/src/tests/test_tx_utils.rs diff --git a/program/rust/src/tests/mod.rs b/program/rust/src/tests/mod.rs index f4f486ba6..d494895aa 100644 --- a/program/rust/src/tests/mod.rs +++ b/program/rust/src/tests/mod.rs @@ -13,3 +13,4 @@ mod test_upd_price; mod test_upd_price_no_fail_on_error; mod test_upd_product; mod test_utils; +mod test_tx_utils; diff --git a/program/rust/src/tests/test_del_price.rs b/program/rust/src/tests/test_del_price.rs index 9ed590931..db807eac3 100644 --- a/program/rust/src/tests/test_del_price.rs +++ b/program/rust/src/tests/test_del_price.rs @@ -1,30 +1,22 @@ -use solana_program::instruction::{ - AccountMeta, - Instruction, -}; use std::mem::size_of; use std::str::FromStr; +use bytemuck::bytes_of; +use solana_program::system_instruction; use solana_program::program_error::ProgramError; use solana_program::pubkey::Pubkey; -use solana_program::sysvar; +use solana_program::rent::Rent; use solana_program_test::{ processor, ProgramTest, }; +use solana_sdk::signature::{Keypair, Signer}; use solana_sdk::transaction::Transaction; -use crate::c_oracle_header::{ - cmd_hdr, - command_t_e_cmd_del_price, - pc_price_t, - pc_prod_t, - PC_VERSION, -}; +use crate::c_oracle_header::{cmd_hdr, command_t_e_cmd_del_price, pc_map_table_t, pc_price_t, pc_prod_t, PC_VERSION}; use crate::deserialize::{ initialize_pyth_account_checked, load_checked, - load_mut, }; use crate::processor::process_instruction; use crate::rust_oracle::del_price; @@ -34,10 +26,8 @@ use crate::utils::pubkey_assign; #[test] fn test_del_price() { let program_id = Pubkey::new_unique(); - let mut instruction_data = [0u8; size_of::()]; - let mut hdr = load_mut::(&mut instruction_data).unwrap(); - hdr.ver_ = PC_VERSION; - hdr.cmd_ = command_t_e_cmd_del_price as i32; + let hdr = del_price_instruction(); + let instruction_data = bytes_of(&hdr); let mut funding_setup = AccountSetup::new_funding(); let funding_account = funding_setup.to_account_info(); @@ -92,16 +82,38 @@ fn test_del_price() { #[tokio::test] async fn test_sysvar() { - println!("This test!"); let program_id = Pubkey::from_str("Pyth111111111111111111111111111111111111111").unwrap(); let (mut banks_client, payer, recent_blockhash) = ProgramTest::new( - "spl_example_sysvar", + "pyth", program_id, processor!(process_instruction), ) .start() .await; + let hdr = del_price_instruction(); + let _instruction_data = bytes_of(&hdr); + + let mapping_keypair = Keypair::new(); + + let size = size_of::(); + let rent = Rent::minimum_balance(&Rent::default(), size); + let instruction = system_instruction::create_account(&payer.pubkey(), &mapping_keypair.pubkey(), rent, size as u64, &program_id); + let mut transaction = Transaction::new_with_payer( + &[instruction], + Some(&payer.pubkey()), + ); + transaction.sign(&[&payer, &mapping_keypair], recent_blockhash); + // transaction.sign(&[&mapping_keypair], recent_blockhash); + + + banks_client.process_transaction(transaction).await.unwrap(); + + let foo = banks_client.get_account(mapping_keypair.pubkey()).await.unwrap().unwrap(); + + assert_eq!(foo.data.len(), 7); + + /* let mut transaction = Transaction::new_with_payer( &[Instruction::new_with_bincode( program_id, @@ -115,4 +127,12 @@ async fn test_sysvar() { ); transaction.sign(&[&payer], recent_blockhash); banks_client.process_transaction(transaction).await.unwrap(); + */ } + +fn del_price_instruction() -> cmd_hdr { + cmd_hdr { + ver_: PC_VERSION, + cmd_: command_t_e_cmd_del_price as i32, + } +} \ No newline at end of file diff --git a/program/rust/src/tests/test_tx_utils.rs b/program/rust/src/tests/test_tx_utils.rs new file mode 100644 index 000000000..a5dd5aac0 --- /dev/null +++ b/program/rust/src/tests/test_tx_utils.rs @@ -0,0 +1,8 @@ +use solana_program_test::BanksClient; +use solana_sdk::signature::Keypair; + +struct PythSimulator { + mut banks_client: BanksClient, + payer: Keypair, + +} \ No newline at end of file diff --git a/program/rust/src/time_machine_types.rs b/program/rust/src/time_machine_types.rs index bccc1e0c3..854d3c5af 100644 --- a/program/rust/src/time_machine_types.rs +++ b/program/rust/src/time_machine_types.rs @@ -75,7 +75,7 @@ pub mod tests { fn c_time_machine_size_is_correct() { assert_eq!( size_of::(), - TIME_MACHINE_STRUCT_SIZE.try_into().unwrap(), + TIME_MACHINE_STRUCT_SIZE as usize, "expected TIME_MACHINE_STRUCT_SIZE ({}) in oracle.h to the same as the size of TimeMachineWrapper ({})", TIME_MACHINE_STRUCT_SIZE, size_of::() @@ -86,7 +86,7 @@ pub mod tests { fn c_price_account_size_is_correct() { assert_eq!( size_of::(), - PRICE_ACCOUNT_SIZE.try_into().unwrap(), + PRICE_ACCOUNT_SIZE as usize, "expected PRICE_ACCOUNT_SIZE ({}) in oracle.h to the same as the size of PriceAccountWrapper ({})", PRICE_ACCOUNT_SIZE, size_of::() From 299921a3bc1b6bc8599fdea83e56867742760672 Mon Sep 17 00:00:00 2001 From: Jayant Krishnamurthy Date: Sat, 20 Aug 2022 15:22:47 -0700 Subject: [PATCH 08/16] trying to write tests --- program/rust/src/tests/test_del_price.rs | 20 ++----- program/rust/src/tests/test_tx_utils.rs | 71 +++++++++++++++++++++++- 2 files changed, 75 insertions(+), 16 deletions(-) diff --git a/program/rust/src/tests/test_del_price.rs b/program/rust/src/tests/test_del_price.rs index db807eac3..f1b4db8ac 100644 --- a/program/rust/src/tests/test_del_price.rs +++ b/program/rust/src/tests/test_del_price.rs @@ -82,6 +82,8 @@ fn test_del_price() { #[tokio::test] async fn test_sysvar() { + + /* let program_id = Pubkey::from_str("Pyth111111111111111111111111111111111111111").unwrap(); let (mut banks_client, payer, recent_blockhash) = ProgramTest::new( "pyth", @@ -94,22 +96,12 @@ async fn test_sysvar() { let hdr = del_price_instruction(); let _instruction_data = bytes_of(&hdr); - let mapping_keypair = Keypair::new(); - - let size = size_of::(); - let rent = Rent::minimum_balance(&Rent::default(), size); - let instruction = system_instruction::create_account(&payer.pubkey(), &mapping_keypair.pubkey(), rent, size as u64, &program_id); - let mut transaction = Transaction::new_with_payer( - &[instruction], - Some(&payer.pubkey()), - ); - transaction.sign(&[&payer, &mapping_keypair], recent_blockhash); - // transaction.sign(&[&mapping_keypair], recent_blockhash); - +*/ - banks_client.process_transaction(transaction).await.unwrap(); + let sim = PythSimulator::new(); + let mapping_pubkey = sim.init_mapping(); - let foo = banks_client.get_account(mapping_keypair.pubkey()).await.unwrap().unwrap(); + let foo = banks_client.get_account(mapping_pubkey).await.unwrap().unwrap(); assert_eq!(foo.data.len(), 7); diff --git a/program/rust/src/tests/test_tx_utils.rs b/program/rust/src/tests/test_tx_utils.rs index a5dd5aac0..3f0b35aef 100644 --- a/program/rust/src/tests/test_tx_utils.rs +++ b/program/rust/src/tests/test_tx_utils.rs @@ -1,8 +1,75 @@ -use solana_program_test::BanksClient; + +use std::mem::size_of; +use std::str::FromStr; + +use bytemuck::bytes_of; +use solana_program::system_instruction; +use solana_program::program_error::ProgramError; +use solana_program::pubkey::Pubkey; +use solana_program::rent::Rent; +use solana_program_test::{ + processor, + ProgramTest, +}; +use crate::processor::process_instruction; +use solana_sdk::signature::{Keypair, Signer}; +use solana_sdk::transaction::Transaction; + +use crate::c_oracle_header::{cmd_hdr, command_t_e_cmd_del_price, pc_map_table_t, pc_price_t, pc_prod_t, PC_VERSION}; + +use std::str::FromStr; +use solana_program::account_info::Account; +use solana_program::hash::Hash; +use solana_program::pubkey::Pubkey; +use solana_program_test::{BanksClient, processor, ProgramTest}; use solana_sdk::signature::Keypair; +use solana_sdk::transaction::Transaction; +use crate::c_oracle_header::pc_map_table_t; struct PythSimulator { - mut banks_client: BanksClient, + program_id: Pubkey, + banks_client: BanksClient, payer: Keypair, + recent_blockhash: Hash +} + +impl PythSimulator { + pub async fn new() -> PythSimulator { + let program_id = Pubkey::new_unique(); + let (banks_client, payer, recent_blockhash) = ProgramTest::new( + "pyth", + program_id, + processor!(process_instruction), + ) + .start() + .await; + + PythSimulator { + program_id, + banks_client, + payer, + recent_blockhash + } + } + + pub async fn init_mapping(&mut self) -> Pubkey { + let mapping_keypair = Keypair::new(); + let size = size_of::(); + let rent = Rent::minimum_balance(&Rent::default(), size); + let instruction = system_instruction::create_account(&self.payer.pubkey(), &mapping_keypair.pubkey(), rent, size as u64, &self.program_id); + let mut transaction = Transaction::new_with_payer( + &[instruction], + Some(&self.payer.pubkey()), + ); + transaction.sign(&[&self.payer, &mapping_keypair], self.recent_blockhash); + // transaction.sign(&[&mapping_keypair], recent_blockhash); + + self.banks_client.process_transaction(transaction).await.unwrap(); + + mapping_keypair.pubkey() + } + pub async fn get_account(&self, key: Pubkey) -> Option { + self.banks_client.get_account(mapping_pubkey).await.unwrap() + } } \ No newline at end of file From ce840579ea13c6f76c6a2c3333193699af8c2a9a Mon Sep 17 00:00:00 2001 From: Jayant Krishnamurthy Date: Sat, 20 Aug 2022 16:38:36 -0700 Subject: [PATCH 09/16] it works --- program/rust/src/rust_oracle.rs | 37 ++-- program/rust/src/tests/mod.rs | 2 +- program/rust/src/tests/test_del_price.rs | 57 +++--- program/rust/src/tests/test_tx_utils.rs | 242 +++++++++++++++++------ 4 files changed, 224 insertions(+), 114 deletions(-) diff --git a/program/rust/src/rust_oracle.rs b/program/rust/src/rust_oracle.rs index a98afcd1c..2c0afe313 100644 --- a/program/rust/src/rust_oracle.rs +++ b/program/rust/src/rust_oracle.rs @@ -397,27 +397,28 @@ pub fn del_price( OracleError::InvalidSystemAccount.into(), )?; - let cmd_args = load::(instruction_data)?; - let mut product_data = load_checked::(product_account, cmd_args.ver_)?; - let price_data = load_checked::(price_account, cmd_args.ver_)?; - pyth_assert( - pubkey_equal(&product_data.px_acc_, &price_account.key.to_bytes()), - ProgramError::InvalidArgument, - )?; - pyth_assert( - pubkey_is_zero(&price_data.next_), - ProgramError::InvalidArgument, - )?; + { + let cmd_args = load::(instruction_data)?; + let mut product_data = load_checked::(product_account, cmd_args.ver_)?; + let price_data = load_checked::(price_account, cmd_args.ver_)?; + pyth_assert( + pubkey_equal(&product_data.px_acc_, &price_account.key.to_bytes()), + ProgramError::InvalidArgument, + )?; + pyth_assert( + pubkey_is_zero(&price_data.next_), + ProgramError::InvalidArgument, + )?; - pubkey_clear(&mut product_data.px_acc_); + pubkey_clear(&mut product_data.px_acc_); + } // Zero out the balance of the price account to delete it. - send_lamports( - price_account, - funding_account, - system_program_account, - price_account.lamports(), - )?; + // Note that you can't use the system program's transfer instruction to do this operation, as + // that instruction fails if the source account has any data. + let lamports = price_account.lamports(); + **price_account.lamports.borrow_mut() = 0; + **funding_account.lamports.borrow_mut() += lamports; Ok(()) } diff --git a/program/rust/src/tests/mod.rs b/program/rust/src/tests/mod.rs index d494895aa..f6ffe94aa 100644 --- a/program/rust/src/tests/mod.rs +++ b/program/rust/src/tests/mod.rs @@ -8,9 +8,9 @@ mod test_init_mapping; mod test_init_price; mod test_set_min_pub; mod test_sizes; +mod test_tx_utils; mod test_upd_aggregate; mod test_upd_price; mod test_upd_price_no_fail_on_error; mod test_upd_product; mod test_utils; -mod test_tx_utils; diff --git a/program/rust/src/tests/test_del_price.rs b/program/rust/src/tests/test_del_price.rs index f1b4db8ac..1df07b3cd 100644 --- a/program/rust/src/tests/test_del_price.rs +++ b/program/rust/src/tests/test_del_price.rs @@ -1,25 +1,21 @@ -use std::mem::size_of; -use std::str::FromStr; - use bytemuck::bytes_of; -use solana_program::system_instruction; use solana_program::program_error::ProgramError; use solana_program::pubkey::Pubkey; -use solana_program::rent::Rent; -use solana_program_test::{ - processor, - ProgramTest, +use solana_sdk::signer::Signer; + +use crate::c_oracle_header::{ + cmd_hdr, + command_t_e_cmd_del_price, + pc_price_t, + pc_prod_t, + PC_VERSION, }; -use solana_sdk::signature::{Keypair, Signer}; -use solana_sdk::transaction::Transaction; - -use crate::c_oracle_header::{cmd_hdr, command_t_e_cmd_del_price, pc_map_table_t, pc_price_t, pc_prod_t, PC_VERSION}; use crate::deserialize::{ initialize_pyth_account_checked, load_checked, }; -use crate::processor::process_instruction; use crate::rust_oracle::del_price; +use crate::tests::test_tx_utils::PythSimulator; use crate::tests::test_utils::AccountSetup; use crate::utils::pubkey_assign; @@ -81,29 +77,22 @@ fn test_del_price() { } #[tokio::test] -async fn test_sysvar() { - - /* - let program_id = Pubkey::from_str("Pyth111111111111111111111111111111111111111").unwrap(); - let (mut banks_client, payer, recent_blockhash) = ProgramTest::new( - "pyth", - program_id, - processor!(process_instruction), - ) - .start() - .await; - - let hdr = del_price_instruction(); - let _instruction_data = bytes_of(&hdr); - -*/ +async fn test_del_price_integration() { + let mut sim = PythSimulator::new().await; + let mapping_keypair = sim.init_mapping().await; + let product1 = sim.add_product(&mapping_keypair).await; + let product2 = sim.add_product(&mapping_keypair).await; + let price1 = sim.add_price(&product1, -8).await; + let price2 = sim.add_price(&product2, -8).await; - let sim = PythSimulator::new(); - let mapping_pubkey = sim.init_mapping(); + { + assert!(sim.get_account(price1.pubkey()).await.is_some()); + assert!(sim.get_account(price2.pubkey()).await.is_some()); + } - let foo = banks_client.get_account(mapping_pubkey).await.unwrap().unwrap(); + sim.del_price(&product1, &price1).await; - assert_eq!(foo.data.len(), 7); + assert!(sim.get_account(price1.pubkey()).await.is_none()); /* let mut transaction = Transaction::new_with_payer( @@ -127,4 +116,4 @@ fn del_price_instruction() -> cmd_hdr { ver_: PC_VERSION, cmd_: command_t_e_cmd_del_price as i32, } -} \ No newline at end of file +} diff --git a/program/rust/src/tests/test_tx_utils.rs b/program/rust/src/tests/test_tx_utils.rs index 3f0b35aef..8b7ef4b63 100644 --- a/program/rust/src/tests/test_tx_utils.rs +++ b/program/rust/src/tests/test_tx_utils.rs @@ -1,75 +1,195 @@ - use std::mem::size_of; -use std::str::FromStr; use bytemuck::bytes_of; -use solana_program::system_instruction; -use solana_program::program_error::ProgramError; +use solana_program::hash::Hash; +use solana_program::instruction::{ + AccountMeta, + Instruction, +}; use solana_program::pubkey::Pubkey; use solana_program::rent::Rent; +use solana_program::{system_instruction, system_program}; use solana_program_test::{ - processor, - ProgramTest, + BanksClient, + processor, + ProgramTest, +}; +use solana_sdk::account::Account; +use solana_sdk::signature::{ + Keypair, + Signer, }; -use crate::processor::process_instruction; -use solana_sdk::signature::{Keypair, Signer}; use solana_sdk::transaction::Transaction; -use crate::c_oracle_header::{cmd_hdr, command_t_e_cmd_del_price, pc_map_table_t, pc_price_t, pc_prod_t, PC_VERSION}; - -use std::str::FromStr; -use solana_program::account_info::Account; -use solana_program::hash::Hash; -use solana_program::pubkey::Pubkey; -use solana_program_test::{BanksClient, processor, ProgramTest}; -use solana_sdk::signature::Keypair; -use solana_sdk::transaction::Transaction; -use crate::c_oracle_header::pc_map_table_t; +use crate::c_oracle_header::{cmd_add_price_t, cmd_hdr_t, command_t_e_cmd_add_price, command_t_e_cmd_add_product, command_t_e_cmd_del_price, command_t_e_cmd_init_mapping, pc_map_table_t, pc_price_t, PC_PROD_ACC_SIZE, PC_PTYPE_PRICE, PC_VERSION}; +use crate::processor::process_instruction; -struct PythSimulator { - program_id: Pubkey, - banks_client: BanksClient, - payer: Keypair, - recent_blockhash: Hash +pub struct PythSimulator { + program_id: Pubkey, + banks_client: BanksClient, + payer: Keypair, + recent_blockhash: Hash, } impl PythSimulator { - pub async fn new() -> PythSimulator { - let program_id = Pubkey::new_unique(); - let (banks_client, payer, recent_blockhash) = ProgramTest::new( - "pyth", - program_id, - processor!(process_instruction), - ) - .start() - .await; - - PythSimulator { - program_id, - banks_client, - payer, - recent_blockhash + pub async fn new() -> PythSimulator { + let program_id = Pubkey::new_unique(); + let (banks_client, payer, recent_blockhash) = + ProgramTest::new("pyth", program_id, processor!(process_instruction)) + .start() + .await; + + PythSimulator { + program_id, + banks_client, + payer, + recent_blockhash, + } + } + + /// Create an account owned by the pyth program containing `size` bytes. + /// The account will be created with enough lamports to be rent-exempt. + pub async fn create_pyth_account(&mut self, size: usize) -> Keypair { + let keypair = Keypair::new(); + let rent = Rent::minimum_balance(&Rent::default(), size); + let instruction = system_instruction::create_account( + &self.payer.pubkey(), + &keypair.pubkey(), + rent, + size as u64, + &self.program_id, + ); + let mut transaction = + Transaction::new_with_payer(&[instruction], Some(&self.payer.pubkey())); + transaction.sign(&[&self.payer, &keypair], self.recent_blockhash); + self.banks_client + .process_transaction(transaction) + .await + .unwrap(); + + keypair } - } - - pub async fn init_mapping(&mut self) -> Pubkey { - let mapping_keypair = Keypair::new(); - let size = size_of::(); - let rent = Rent::minimum_balance(&Rent::default(), size); - let instruction = system_instruction::create_account(&self.payer.pubkey(), &mapping_keypair.pubkey(), rent, size as u64, &self.program_id); - let mut transaction = Transaction::new_with_payer( - &[instruction], - Some(&self.payer.pubkey()), - ); - transaction.sign(&[&self.payer, &mapping_keypair], self.recent_blockhash); - // transaction.sign(&[&mapping_keypair], recent_blockhash); - - self.banks_client.process_transaction(transaction).await.unwrap(); - - mapping_keypair.pubkey() - } - - pub async fn get_account(&self, key: Pubkey) -> Option { - self.banks_client.get_account(mapping_pubkey).await.unwrap() - } -} \ No newline at end of file + + pub async fn init_mapping(&mut self) -> Keypair { + let mapping_keypair = self.create_pyth_account(size_of::()).await; + + let cmd = cmd_hdr_t { + ver_: PC_VERSION, + cmd_: command_t_e_cmd_init_mapping as i32, + }; + let instruction = Instruction::new_with_bytes( + self.program_id, + bytes_of(&cmd), + vec![ + AccountMeta::new(self.payer.pubkey(), true), + AccountMeta::new(mapping_keypair.pubkey(), true), + ], + ); + + let mut transaction = + Transaction::new_with_payer(&[instruction], Some(&self.payer.pubkey())); + transaction.sign(&[&self.payer, &mapping_keypair], self.recent_blockhash); + self.banks_client + .process_transaction(transaction) + .await + .unwrap(); + + mapping_keypair + } + + pub async fn add_product(&mut self, mapping_keypair: &Keypair) -> Keypair { + let product_keypair = self.create_pyth_account(PC_PROD_ACC_SIZE as usize).await; + + let cmd = cmd_hdr_t { + ver_: PC_VERSION, + cmd_: command_t_e_cmd_add_product as i32, + }; + let instruction = Instruction::new_with_bytes( + self.program_id, + bytes_of(&cmd), + vec![ + AccountMeta::new(self.payer.pubkey(), true), + AccountMeta::new(mapping_keypair.pubkey(), true), + AccountMeta::new(product_keypair.pubkey(), true), + ], + ); + + let mut transaction = + Transaction::new_with_payer(&[instruction], Some(&self.payer.pubkey())); + transaction.sign( + &[&self.payer, &mapping_keypair, &product_keypair], + self.recent_blockhash, + ); + self.banks_client + .process_transaction(transaction) + .await + .unwrap(); + + product_keypair + } + + pub async fn add_price(&mut self, product_keypair: &Keypair, expo: i32) -> Keypair { + let price_keypair = self.create_pyth_account(size_of::()).await; + + let cmd = cmd_add_price_t { + ver_: PC_VERSION, + cmd_: command_t_e_cmd_add_price as i32, + expo_: expo, + ptype_: PC_PTYPE_PRICE, + }; + let instruction = Instruction::new_with_bytes( + self.program_id, + bytes_of(&cmd), + vec![ + AccountMeta::new(self.payer.pubkey(), true), + AccountMeta::new(product_keypair.pubkey(), true), + AccountMeta::new(price_keypair.pubkey(), true), + ], + ); + + let mut transaction = + Transaction::new_with_payer(&[instruction], Some(&self.payer.pubkey())); + transaction.sign( + &[&self.payer, &product_keypair, &price_keypair], + self.recent_blockhash, + ); + self.banks_client + .process_transaction(transaction) + .await + .unwrap(); + + price_keypair + } + + pub async fn del_price(&mut self, product_keypair: &Keypair, price_keypair: &Keypair) -> () { + let cmd = cmd_hdr_t { + ver_: PC_VERSION, + cmd_: command_t_e_cmd_del_price as i32, + }; + let instruction = Instruction::new_with_bytes( + self.program_id, + bytes_of(&cmd), + vec![ + AccountMeta::new(self.payer.pubkey(), true), + AccountMeta::new(product_keypair.pubkey(), true), + AccountMeta::new(price_keypair.pubkey(), true), + AccountMeta::new_readonly(system_program::id(), false), + ], + ); + + let mut transaction = + Transaction::new_with_payer(&[instruction], Some(&self.payer.pubkey())); + transaction.sign( + &[&self.payer, &product_keypair, &price_keypair], + self.recent_blockhash, + ); + self.banks_client + .process_transaction(transaction) + .await + .unwrap(); + } + + pub async fn get_account(&mut self, key: Pubkey) -> Option { + self.banks_client.get_account(key).await.unwrap() + } +} From 30d14f9e90a04f7f6329722382b9f96b071bbe9f Mon Sep 17 00:00:00 2001 From: Jayant Krishnamurthy Date: Sat, 20 Aug 2022 16:39:03 -0700 Subject: [PATCH 10/16] it works --- program/rust/src/rust_oracle.rs | 6 +----- program/rust/src/tests/test_tx_utils.rs | 1 - 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/program/rust/src/rust_oracle.rs b/program/rust/src/rust_oracle.rs index 2c0afe313..b879ebb79 100644 --- a/program/rust/src/rust_oracle.rs +++ b/program/rust/src/rust_oracle.rs @@ -384,7 +384,7 @@ pub fn del_price( accounts: &[AccountInfo], instruction_data: &[u8], ) -> ProgramResult { - let [funding_account, product_account, price_account, system_program_account] = match accounts { + let [funding_account, product_account, price_account] = match accounts { [w, x, y, z] => Ok([w, x, y, z]), _ => Err(ProgramError::InvalidArgument), }?; @@ -392,10 +392,6 @@ pub fn del_price( check_valid_funding_account(funding_account)?; check_valid_signable_account(program_id, product_account, PC_PROD_ACC_SIZE as usize)?; check_valid_signable_account(program_id, price_account, size_of::())?; - pyth_assert( - check_id(system_program_account.key), - OracleError::InvalidSystemAccount.into(), - )?; { let cmd_args = load::(instruction_data)?; diff --git a/program/rust/src/tests/test_tx_utils.rs b/program/rust/src/tests/test_tx_utils.rs index 8b7ef4b63..c06663b44 100644 --- a/program/rust/src/tests/test_tx_utils.rs +++ b/program/rust/src/tests/test_tx_utils.rs @@ -173,7 +173,6 @@ impl PythSimulator { AccountMeta::new(self.payer.pubkey(), true), AccountMeta::new(product_keypair.pubkey(), true), AccountMeta::new(price_keypair.pubkey(), true), - AccountMeta::new_readonly(system_program::id(), false), ], ); From f89520c27df71c04bb6504cc1fc8288feb528cc6 Mon Sep 17 00:00:00 2001 From: Jayant Krishnamurthy Date: Sat, 20 Aug 2022 16:53:53 -0700 Subject: [PATCH 11/16] better comments --- program/rust/src/rust_oracle.rs | 14 +++--- program/rust/src/tests/test_del_price.rs | 31 +++++--------- program/rust/src/tests/test_tx_utils.rs | 54 +++++++++++++++++++----- 3 files changed, 60 insertions(+), 39 deletions(-) diff --git a/program/rust/src/rust_oracle.rs b/program/rust/src/rust_oracle.rs index b879ebb79..1105715a6 100644 --- a/program/rust/src/rust_oracle.rs +++ b/program/rust/src/rust_oracle.rs @@ -10,6 +10,7 @@ use bytemuck::{ use solana_program::account_info::AccountInfo; use solana_program::clock::Clock; use solana_program::entrypoint::ProgramResult; +use solana_program::program::invoke; use solana_program::program_error::ProgramError; use solana_program::program_memory::{ sol_memcpy, @@ -17,14 +18,9 @@ use solana_program::program_memory::{ }; use solana_program::pubkey::Pubkey; use solana_program::rent::Rent; -use solana_program::sysvar::Sysvar; - - -use crate::time_machine_types::PriceAccountWrapper; -use solana_program::program::invoke; use solana_program::system_instruction::transfer; use solana_program::system_program::check_id; - +use solana_program::sysvar::Sysvar; use crate::c_oracle_header::{ cmd_add_price_t, @@ -57,8 +53,7 @@ use crate::deserialize::{ load_account_as_mut, load_checked, }; -use crate::OracleError; - +use crate::time_machine_types::PriceAccountWrapper; use crate::utils::{ check_exponent_range, check_valid_fresh_account, @@ -74,6 +69,7 @@ use crate::utils::{ read_pc_str_t, try_convert, }; +use crate::OracleError; const PRICE_T_SIZE: usize = size_of::(); const PRICE_ACCOUNT_SIZE: usize = size_of::(); @@ -385,7 +381,7 @@ pub fn del_price( instruction_data: &[u8], ) -> ProgramResult { let [funding_account, product_account, price_account] = match accounts { - [w, x, y, z] => Ok([w, x, y, z]), + [w, x, y] => Ok([w, x, y]), _ => Err(ProgramError::InvalidArgument), }?; diff --git a/program/rust/src/tests/test_del_price.rs b/program/rust/src/tests/test_del_price.rs index 1df07b3cd..c24773ff6 100644 --- a/program/rust/src/tests/test_del_price.rs +++ b/program/rust/src/tests/test_del_price.rs @@ -17,7 +17,10 @@ use crate::deserialize::{ use crate::rust_oracle::del_price; use crate::tests::test_tx_utils::PythSimulator; use crate::tests::test_utils::AccountSetup; -use crate::utils::pubkey_assign; +use crate::utils::{ + pubkey_assign, + pubkey_is_zero, +}; #[test] fn test_del_price() { @@ -85,30 +88,18 @@ async fn test_del_price_integration() { let price1 = sim.add_price(&product1, -8).await; let price2 = sim.add_price(&product2, -8).await; - { - assert!(sim.get_account(price1.pubkey()).await.is_some()); - assert!(sim.get_account(price2.pubkey()).await.is_some()); - } + assert!(sim.get_account(price1.pubkey()).await.is_some()); + assert!(sim.get_account(price2.pubkey()).await.is_some()); sim.del_price(&product1, &price1).await; assert!(sim.get_account(price1.pubkey()).await.is_none()); - /* - let mut transaction = Transaction::new_with_payer( - &[Instruction::new_with_bincode( - program_id, - &(), - vec![ - AccountMeta::new(sysvar::clock::id(), false), - AccountMeta::new(sysvar::rent::id(), false), - ], - )], - Some(&payer.pubkey()), - ); - transaction.sign(&[&payer], recent_blockhash); - banks_client.process_transaction(transaction).await.unwrap(); - */ + let product1_data = sim + .get_account_data_as::(product1.pubkey()) + .await + .unwrap(); + assert!(pubkey_is_zero(&product1_data.px_acc_)); } fn del_price_instruction() -> cmd_hdr { diff --git a/program/rust/src/tests/test_tx_utils.rs b/program/rust/src/tests/test_tx_utils.rs index c06663b44..8c7bf04f5 100644 --- a/program/rust/src/tests/test_tx_utils.rs +++ b/program/rust/src/tests/test_tx_utils.rs @@ -1,6 +1,9 @@ use std::mem::size_of; -use bytemuck::bytes_of; +use bytemuck::{ + bytes_of, + Pod, +}; use solana_program::hash::Hash; use solana_program::instruction::{ AccountMeta, @@ -8,10 +11,10 @@ use solana_program::instruction::{ }; use solana_program::pubkey::Pubkey; use solana_program::rent::Rent; -use solana_program::{system_instruction, system_program}; +use solana_program::system_instruction; use solana_program_test::{ - BanksClient, processor, + BanksClient, ProgramTest, }; use solana_sdk::account::Account; @@ -21,7 +24,20 @@ use solana_sdk::signature::{ }; use solana_sdk::transaction::Transaction; -use crate::c_oracle_header::{cmd_add_price_t, cmd_hdr_t, command_t_e_cmd_add_price, command_t_e_cmd_add_product, command_t_e_cmd_del_price, command_t_e_cmd_init_mapping, pc_map_table_t, pc_price_t, PC_PROD_ACC_SIZE, PC_PTYPE_PRICE, PC_VERSION}; +use crate::c_oracle_header::{ + cmd_add_price_t, + cmd_hdr_t, + command_t_e_cmd_add_price, + command_t_e_cmd_add_product, + command_t_e_cmd_del_price, + command_t_e_cmd_init_mapping, + pc_map_table_t, + pc_price_t, + PC_PROD_ACC_SIZE, + PC_PTYPE_PRICE, + PC_VERSION, +}; +use crate::deserialize::load; use crate::processor::process_instruction; pub struct PythSimulator { @@ -70,6 +86,8 @@ impl PythSimulator { keypair } + /// Initialize a mapping account (using the init_mapping instruction), returning the keypair + /// associated with the newly-created account. pub async fn init_mapping(&mut self) -> Keypair { let mapping_keypair = self.create_pyth_account(size_of::()).await; @@ -97,6 +115,8 @@ impl PythSimulator { mapping_keypair } + /// Initialize a product account and add it to an existing mapping account (using the + /// add_product instruction). Returns the keypair associated with the newly-created account. pub async fn add_product(&mut self, mapping_keypair: &Keypair) -> Keypair { let product_keypair = self.create_pyth_account(PC_PROD_ACC_SIZE as usize).await; @@ -128,6 +148,8 @@ impl PythSimulator { product_keypair } + /// Initialize a price account and add it to an existing product account (using the add_price + /// instruction). Returns the keypair associated with the newly-created account. pub async fn add_price(&mut self, product_keypair: &Keypair, expo: i32) -> Keypair { let price_keypair = self.create_pyth_account(size_of::()).await; @@ -161,10 +183,11 @@ impl PythSimulator { price_keypair } + /// Delete a price account from an existing product account (using the del_price instruction). pub async fn del_price(&mut self, product_keypair: &Keypair, price_keypair: &Keypair) -> () { let cmd = cmd_hdr_t { - ver_: PC_VERSION, - cmd_: command_t_e_cmd_del_price as i32, + ver_: PC_VERSION, + cmd_: command_t_e_cmd_del_price as i32, }; let instruction = Instruction::new_with_bytes( self.program_id, @@ -177,18 +200,29 @@ impl PythSimulator { ); let mut transaction = - Transaction::new_with_payer(&[instruction], Some(&self.payer.pubkey())); + Transaction::new_with_payer(&[instruction], Some(&self.payer.pubkey())); transaction.sign( &[&self.payer, &product_keypair, &price_keypair], self.recent_blockhash, ); self.banks_client - .process_transaction(transaction) - .await - .unwrap(); + .process_transaction(transaction) + .await + .unwrap(); } + /// Get the account at `key`. Returns `None` if no such account exists. pub async fn get_account(&mut self, key: Pubkey) -> Option { self.banks_client.get_account(key).await.unwrap() } + + /// Get the content of an account as a value of type `T`. This function returns a copy of the + /// account data -- you cannot mutate the result to mutate the on-chain account data. + /// Returns None if the account does not exist. Panics if the account data cannot be read as a + /// `T`. + pub async fn get_account_data_as(&mut self, key: Pubkey) -> Option { + self.get_account(key) + .await + .map(|x| load::(&x.data).unwrap().clone()) + } } From a832748579644f93fc2b140b7e3d24e02b3469ad Mon Sep 17 00:00:00 2001 From: Jayant Krishnamurthy Date: Sat, 20 Aug 2022 17:02:42 -0700 Subject: [PATCH 12/16] integration tests --- program/rust/src/tests/test_del_price.rs | 20 +++++++++----- program/rust/src/tests/test_tx_utils.rs | 34 ++++++++++++++---------- 2 files changed, 33 insertions(+), 21 deletions(-) diff --git a/program/rust/src/tests/test_del_price.rs b/program/rust/src/tests/test_del_price.rs index c24773ff6..c4f5eecda 100644 --- a/program/rust/src/tests/test_del_price.rs +++ b/program/rust/src/tests/test_del_price.rs @@ -82,17 +82,23 @@ fn test_del_price() { #[tokio::test] async fn test_del_price_integration() { let mut sim = PythSimulator::new().await; - let mapping_keypair = sim.init_mapping().await; - let product1 = sim.add_product(&mapping_keypair).await; - let product2 = sim.add_product(&mapping_keypair).await; - let price1 = sim.add_price(&product1, -8).await; - let price2 = sim.add_price(&product2, -8).await; + let mapping_keypair = sim.init_mapping().await.unwrap(); + let product1 = sim.add_product(&mapping_keypair).await.unwrap(); + let product2 = sim.add_product(&mapping_keypair).await.unwrap(); + let product3 = sim.add_product(&mapping_keypair).await.unwrap(); + let price1 = sim.add_price(&product1, -8).await.unwrap(); + let price2_1 = sim.add_price(&product2, -8).await.unwrap(); + let price2_2 = sim.add_price(&product2, -8).await.unwrap(); assert!(sim.get_account(price1.pubkey()).await.is_some()); - assert!(sim.get_account(price2.pubkey()).await.is_some()); + assert!(sim.get_account(price2_1.pubkey()).await.is_some()); - sim.del_price(&product1, &price1).await; + assert!(sim.del_price(&product2, &price1).await.is_err()); + assert!(sim.del_price(&product2, &price2_1).await.is_err()); + assert!(sim.del_price(&product1, &price2_2).await.is_err()); + assert!(sim.del_price(&product3, &price2_2).await.is_err()); + sim.del_price(&product1, &price1).await.unwrap(); assert!(sim.get_account(price1.pubkey()).await.is_none()); let product1_data = sim diff --git a/program/rust/src/tests/test_tx_utils.rs b/program/rust/src/tests/test_tx_utils.rs index 8c7bf04f5..61de5a432 100644 --- a/program/rust/src/tests/test_tx_utils.rs +++ b/program/rust/src/tests/test_tx_utils.rs @@ -15,6 +15,7 @@ use solana_program::system_instruction; use solana_program_test::{ processor, BanksClient, + BanksClientError, ProgramTest, }; use solana_sdk::account::Account; @@ -88,7 +89,7 @@ impl PythSimulator { /// Initialize a mapping account (using the init_mapping instruction), returning the keypair /// associated with the newly-created account. - pub async fn init_mapping(&mut self) -> Keypair { + pub async fn init_mapping(&mut self) -> Result { let mapping_keypair = self.create_pyth_account(size_of::()).await; let cmd = cmd_hdr_t { @@ -110,14 +111,15 @@ impl PythSimulator { self.banks_client .process_transaction(transaction) .await - .unwrap(); - - mapping_keypair + .map(|_| mapping_keypair) } /// Initialize a product account and add it to an existing mapping account (using the /// add_product instruction). Returns the keypair associated with the newly-created account. - pub async fn add_product(&mut self, mapping_keypair: &Keypair) -> Keypair { + pub async fn add_product( + &mut self, + mapping_keypair: &Keypair, + ) -> Result { let product_keypair = self.create_pyth_account(PC_PROD_ACC_SIZE as usize).await; let cmd = cmd_hdr_t { @@ -143,14 +145,16 @@ impl PythSimulator { self.banks_client .process_transaction(transaction) .await - .unwrap(); - - product_keypair + .map(|_| product_keypair) } /// Initialize a price account and add it to an existing product account (using the add_price /// instruction). Returns the keypair associated with the newly-created account. - pub async fn add_price(&mut self, product_keypair: &Keypair, expo: i32) -> Keypair { + pub async fn add_price( + &mut self, + product_keypair: &Keypair, + expo: i32, + ) -> Result { let price_keypair = self.create_pyth_account(size_of::()).await; let cmd = cmd_add_price_t { @@ -178,13 +182,15 @@ impl PythSimulator { self.banks_client .process_transaction(transaction) .await - .unwrap(); - - price_keypair + .map(|_| price_keypair) } /// Delete a price account from an existing product account (using the del_price instruction). - pub async fn del_price(&mut self, product_keypair: &Keypair, price_keypair: &Keypair) -> () { + pub async fn del_price( + &mut self, + product_keypair: &Keypair, + price_keypair: &Keypair, + ) -> Result<(), BanksClientError> { let cmd = cmd_hdr_t { ver_: PC_VERSION, cmd_: command_t_e_cmd_del_price as i32, @@ -208,7 +214,7 @@ impl PythSimulator { self.banks_client .process_transaction(transaction) .await - .unwrap(); + .map(|_| ()) } /// Get the account at `key`. Returns `None` if no such account exists. From 80b1b257fc54bf4eb830a8afcd0919c18e3f4dd9 Mon Sep 17 00:00:00 2001 From: Jayant Krishnamurthy Date: Sat, 20 Aug 2022 18:44:56 -0700 Subject: [PATCH 13/16] jeez --- program/c/src/oracle/oracle.h | 1 - program/rust/src/rust_oracle.rs | 8 +-- program/rust/src/tests/test_del_price.rs | 20 +++++- program/rust/src/tests/test_tx_utils.rs | 80 +++++++++++------------- program/rust/src/utils.rs | 9 --- 5 files changed, 61 insertions(+), 57 deletions(-) diff --git a/program/c/src/oracle/oracle.h b/program/c/src/oracle/oracle.h index c9009a4b1..320d1e84e 100644 --- a/program/c/src/oracle/oracle.h +++ b/program/c/src/oracle/oracle.h @@ -274,7 +274,6 @@ typedef enum { // key[0] funding account [signer writable] // key[1] product account [signer writable] // key[2] price account [signer writable] - // key[3] system program e_cmd_del_price, } command_t; diff --git a/program/rust/src/rust_oracle.rs b/program/rust/src/rust_oracle.rs index 1105715a6..3bf306b1e 100644 --- a/program/rust/src/rust_oracle.rs +++ b/program/rust/src/rust_oracle.rs @@ -62,7 +62,6 @@ use crate::utils::{ check_valid_writable_account, is_component_update, pubkey_assign, - pubkey_clear, pubkey_equal, pubkey_is_zero, pyth_assert, @@ -370,7 +369,7 @@ pub fn add_price( /// Delete a price account. This function will remove the link between the price account and its /// corresponding product account, then transfer any SOL in the price account to the funding -/// account. This function expects there to be only a single price account in the linked list of +/// account. This function can only delete the first price account in the linked list of /// price accounts for the given product. /// /// Warning: This function is dangerous and will break any programs that depend on the deleted @@ -397,12 +396,13 @@ pub fn del_price( pubkey_equal(&product_data.px_acc_, &price_account.key.to_bytes()), ProgramError::InvalidArgument, )?; + pyth_assert( - pubkey_is_zero(&price_data.next_), + pubkey_equal(&price_data.prod_, &product_account.key.to_bytes()), ProgramError::InvalidArgument, )?; - pubkey_clear(&mut product_data.px_acc_); + pubkey_assign(&mut product_data.px_acc_, bytes_of(&price_data.next_)); } // Zero out the balance of the price account to delete it. diff --git a/program/rust/src/tests/test_del_price.rs b/program/rust/src/tests/test_del_price.rs index c4f5eecda..7ec02055e 100644 --- a/program/rust/src/tests/test_del_price.rs +++ b/program/rust/src/tests/test_del_price.rs @@ -94,8 +94,9 @@ async fn test_del_price_integration() { assert!(sim.get_account(price2_1.pubkey()).await.is_some()); assert!(sim.del_price(&product2, &price1).await.is_err()); - assert!(sim.del_price(&product2, &price2_1).await.is_err()); + assert!(sim.del_price(&product1, &price2_1).await.is_err()); assert!(sim.del_price(&product1, &price2_2).await.is_err()); + assert!(sim.del_price(&product3, &price2_1).await.is_err()); assert!(sim.del_price(&product3, &price2_2).await.is_err()); sim.del_price(&product1, &price1).await.unwrap(); @@ -106,6 +107,23 @@ async fn test_del_price_integration() { .await .unwrap(); assert!(pubkey_is_zero(&product1_data.px_acc_)); + + + // price2_1 is the 2nd item in the linked list since price2_2 got added after t. + assert!(sim.del_price(&product2, &price2_1).await.is_err()); + // Can delete the accounts in the opposite order though + assert!(sim.del_price(&product2, &price2_2).await.is_ok()); + assert!(sim.del_price(&product2, &price2_1).await.is_ok()); + + assert!(sim.get_account(price2_2.pubkey()).await.is_none()); + assert!(sim.get_account(price2_1.pubkey()).await.is_none()); + + let product2_data = sim + .get_account_data_as::(product2.pubkey()) + .await + .unwrap(); + + assert!(pubkey_is_zero(&product2_data.px_acc_)); } fn del_price_instruction() -> cmd_hdr { diff --git a/program/rust/src/tests/test_tx_utils.rs b/program/rust/src/tests/test_tx_utils.rs index 61de5a432..b38bceacd 100644 --- a/program/rust/src/tests/test_tx_utils.rs +++ b/program/rust/src/tests/test_tx_utils.rs @@ -17,6 +17,7 @@ use solana_program_test::{ BanksClient, BanksClientError, ProgramTest, + ProgramTestBanksClientExt, }; use solana_sdk::account::Account; use solana_sdk::signature::{ @@ -42,10 +43,13 @@ use crate::deserialize::load; use crate::processor::process_instruction; pub struct PythSimulator { - program_id: Pubkey, - banks_client: BanksClient, - payer: Keypair, - recent_blockhash: Hash, + program_id: Pubkey, + banks_client: BanksClient, + payer: Keypair, + /// Hash used to submit the last transaction. The hash must be advanced for each new + /// transaction; otherwise, replayed transactions in different states can return stale + /// results. + last_blockhash: Hash, } impl PythSimulator { @@ -60,10 +64,33 @@ impl PythSimulator { program_id, banks_client, payer, - recent_blockhash, + last_blockhash: recent_blockhash, } } + /// Process a transaction containing `instruction` signed by `signers`. + /// The transaction is assumed to require `self.payer` to pay for and sign the transaction. + async fn process_ix( + &mut self, + instruction: Instruction, + signers: &Vec<&Keypair>, + ) -> Result<(), BanksClientError> { + let mut transaction = + Transaction::new_with_payer(&[instruction], Some(&self.payer.pubkey())); + + let blockhash = self + .banks_client + .get_new_latest_blockhash(&self.last_blockhash) + .await + .unwrap(); + self.last_blockhash = blockhash; + + transaction.partial_sign(&[&self.payer], self.last_blockhash); + transaction.partial_sign(signers, self.last_blockhash); + + self.banks_client.process_transaction(transaction).await + } + /// Create an account owned by the pyth program containing `size` bytes. /// The account will be created with enough lamports to be rent-exempt. pub async fn create_pyth_account(&mut self, size: usize) -> Keypair { @@ -76,13 +103,8 @@ impl PythSimulator { size as u64, &self.program_id, ); - let mut transaction = - Transaction::new_with_payer(&[instruction], Some(&self.payer.pubkey())); - transaction.sign(&[&self.payer, &keypair], self.recent_blockhash); - self.banks_client - .process_transaction(transaction) - .await - .unwrap(); + + self.process_ix(instruction, &vec![&keypair]).await.unwrap(); keypair } @@ -105,11 +127,7 @@ impl PythSimulator { ], ); - let mut transaction = - Transaction::new_with_payer(&[instruction], Some(&self.payer.pubkey())); - transaction.sign(&[&self.payer, &mapping_keypair], self.recent_blockhash); - self.banks_client - .process_transaction(transaction) + self.process_ix(instruction, &vec![&mapping_keypair]) .await .map(|_| mapping_keypair) } @@ -136,14 +154,7 @@ impl PythSimulator { ], ); - let mut transaction = - Transaction::new_with_payer(&[instruction], Some(&self.payer.pubkey())); - transaction.sign( - &[&self.payer, &mapping_keypair, &product_keypair], - self.recent_blockhash, - ); - self.banks_client - .process_transaction(transaction) + self.process_ix(instruction, &vec![&mapping_keypair, &product_keypair]) .await .map(|_| product_keypair) } @@ -173,14 +184,7 @@ impl PythSimulator { ], ); - let mut transaction = - Transaction::new_with_payer(&[instruction], Some(&self.payer.pubkey())); - transaction.sign( - &[&self.payer, &product_keypair, &price_keypair], - self.recent_blockhash, - ); - self.banks_client - .process_transaction(transaction) + self.process_ix(instruction, &vec![&product_keypair, &price_keypair]) .await .map(|_| price_keypair) } @@ -205,16 +209,8 @@ impl PythSimulator { ], ); - let mut transaction = - Transaction::new_with_payer(&[instruction], Some(&self.payer.pubkey())); - transaction.sign( - &[&self.payer, &product_keypair, &price_keypair], - self.recent_blockhash, - ); - self.banks_client - .process_transaction(transaction) + self.process_ix(instruction, &vec![&product_keypair, &price_keypair]) .await - .map(|_| ()) } /// Get the account at `key`. Returns `None` if no such account exists. diff --git a/program/rust/src/utils.rs b/program/rust/src/utils.rs index c8bdad3cb..b56effd88 100644 --- a/program/rust/src/utils.rs +++ b/program/rust/src/utils.rs @@ -98,15 +98,6 @@ pub fn pubkey_assign(target: &mut pc_pub_key_t, source: &[u8]) { unsafe { target.k1_.copy_from_slice(source) } } -/// Set `target` to contain all-zeros. -pub fn pubkey_clear(target: &mut pc_pub_key_t) { - unsafe { - for i in 0..4 { - target.k8_[i] = 0 - } - } -} - pub fn pubkey_is_zero(key: &pc_pub_key_t) -> bool { return unsafe { key.k8_.iter().all(|x| *x == 0) }; } From 40ab15cdf542d3266007a0b80b49a98e95e385fc Mon Sep 17 00:00:00 2001 From: Jayant Krishnamurthy Date: Sat, 20 Aug 2022 18:48:19 -0700 Subject: [PATCH 14/16] fix this --- program/rust/src/tests/mod.rs | 2 +- .../{test_tx_utils.rs => pyth_simulator.rs} | 0 program/rust/src/tests/test_del_price.rs | 90 +------------------ program/rust/src/tests/test_utils.rs | 18 ---- 4 files changed, 5 insertions(+), 105 deletions(-) rename program/rust/src/tests/{test_tx_utils.rs => pyth_simulator.rs} (100%) diff --git a/program/rust/src/tests/mod.rs b/program/rust/src/tests/mod.rs index f6ffe94aa..164ebcf80 100644 --- a/program/rust/src/tests/mod.rs +++ b/program/rust/src/tests/mod.rs @@ -1,3 +1,4 @@ +mod pyth_simulator; mod test_add_mapping; mod test_add_price; mod test_add_product; @@ -8,7 +9,6 @@ mod test_init_mapping; mod test_init_price; mod test_set_min_pub; mod test_sizes; -mod test_tx_utils; mod test_upd_aggregate; mod test_upd_price; mod test_upd_price_no_fail_on_error; diff --git a/program/rust/src/tests/test_tx_utils.rs b/program/rust/src/tests/pyth_simulator.rs similarity index 100% rename from program/rust/src/tests/test_tx_utils.rs rename to program/rust/src/tests/pyth_simulator.rs diff --git a/program/rust/src/tests/test_del_price.rs b/program/rust/src/tests/test_del_price.rs index 7ec02055e..8f2e66153 100644 --- a/program/rust/src/tests/test_del_price.rs +++ b/program/rust/src/tests/test_del_price.rs @@ -1,86 +1,11 @@ -use bytemuck::bytes_of; -use solana_program::program_error::ProgramError; -use solana_program::pubkey::Pubkey; use solana_sdk::signer::Signer; -use crate::c_oracle_header::{ - cmd_hdr, - command_t_e_cmd_del_price, - pc_price_t, - pc_prod_t, - PC_VERSION, -}; -use crate::deserialize::{ - initialize_pyth_account_checked, - load_checked, -}; -use crate::rust_oracle::del_price; -use crate::tests::test_tx_utils::PythSimulator; -use crate::tests::test_utils::AccountSetup; -use crate::utils::{ - pubkey_assign, - pubkey_is_zero, -}; - -#[test] -fn test_del_price() { - let program_id = Pubkey::new_unique(); - let hdr = del_price_instruction(); - let instruction_data = bytes_of(&hdr); - - let mut funding_setup = AccountSetup::new_funding(); - let funding_account = funding_setup.to_account_info(); - - let mut product_setup = AccountSetup::new::(&program_id); - let product_account = product_setup.to_account_info(); - initialize_pyth_account_checked::(&product_account, PC_VERSION).unwrap(); - - let mut price_setup = AccountSetup::new::(&program_id); - let price_account = price_setup.to_account_info(); - initialize_pyth_account_checked::(&price_account, PC_VERSION).unwrap(); - - let mut system_setup = AccountSetup::new_system_program(); - let system_account = system_setup.to_account_info(); - - // Try deleting a price account that isn't linked to the given product account - assert_eq!( - del_price( - &program_id, - &[ - funding_account.clone(), - product_account.clone(), - price_account.clone(), - system_account.clone() - ], - &instruction_data - ), - Err(ProgramError::InvalidArgument) - ); - - // Same test with a random nonzero pubkey - { - let mut product_data = load_checked::(&product_account, PC_VERSION).unwrap(); - pubkey_assign(&mut product_data.px_acc_, &Pubkey::new_unique().to_bytes()); - } - assert_eq!( - del_price( - &program_id, - &[ - funding_account.clone(), - product_account.clone(), - price_account.clone(), - system_account.clone() - ], - &instruction_data - ), - Err(ProgramError::InvalidArgument) - ); - - // Note that we can't test success outside of the solana vm because of the system program. -} +use crate::c_oracle_header::pc_prod_t; +use crate::tests::pyth_simulator::PythSimulator; +use crate::utils::pubkey_is_zero; #[tokio::test] -async fn test_del_price_integration() { +async fn test_del_price() { let mut sim = PythSimulator::new().await; let mapping_keypair = sim.init_mapping().await.unwrap(); let product1 = sim.add_product(&mapping_keypair).await.unwrap(); @@ -125,10 +50,3 @@ async fn test_del_price_integration() { assert!(pubkey_is_zero(&product2_data.px_acc_)); } - -fn del_price_instruction() -> cmd_hdr { - cmd_hdr { - ver_: PC_VERSION, - cmd_: command_t_e_cmd_del_price as i32, - } -} diff --git a/program/rust/src/tests/test_utils.rs b/program/rust/src/tests/test_utils.rs index 3aa3b5cec..ed04fc90a 100644 --- a/program/rust/src/tests/test_utils.rs +++ b/program/rust/src/tests/test_utils.rs @@ -78,24 +78,6 @@ impl AccountSetup { }; } - /// Create a mock account for the system program. - /// This account is a barely passable version of the system program account -- - /// it has the correct id, but nothing else works. - pub fn new_system_program() -> Self { - let key = system_program::id(); - let owner = sysvar::id(); - let size = clock::Clock::size_of(); - let balance = Rent::minimum_balance(&Rent::default(), size); - let data = [0u8; UPPER_BOUND_OF_ALL_ACCOUNT_SIZES]; - return AccountSetup { - key, - owner, - balance, - size, - data, - }; - } - pub fn to_account_info(&mut self) -> AccountInfo { return AccountInfo::new( &self.key, From da4b44cb01db62dfa1dd1853e3360e3dbb39a7a3 Mon Sep 17 00:00:00 2001 From: Jayant Krishnamurthy Date: Sat, 20 Aug 2022 18:50:31 -0700 Subject: [PATCH 15/16] doc --- program/rust/src/tests/pyth_simulator.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/program/rust/src/tests/pyth_simulator.rs b/program/rust/src/tests/pyth_simulator.rs index b38bceacd..3ea0d7790 100644 --- a/program/rust/src/tests/pyth_simulator.rs +++ b/program/rust/src/tests/pyth_simulator.rs @@ -42,6 +42,8 @@ use crate::c_oracle_header::{ use crate::deserialize::load; use crate::processor::process_instruction; +/// Simulator for the state of the pyth program on Solana. You can run solana transactions against +/// this struct to test how pyth instructions execute in the Solana runtime. pub struct PythSimulator { program_id: Pubkey, banks_client: BanksClient, From 0fe84e53362b059947ea45ddb4be4885b0823517 Mon Sep 17 00:00:00 2001 From: Jayant Krishnamurthy Date: Sun, 21 Aug 2022 14:35:31 -0700 Subject: [PATCH 16/16] ok weird that this name matters --- program/rust/src/tests/pyth_simulator.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/rust/src/tests/pyth_simulator.rs b/program/rust/src/tests/pyth_simulator.rs index 3ea0d7790..194c9336f 100644 --- a/program/rust/src/tests/pyth_simulator.rs +++ b/program/rust/src/tests/pyth_simulator.rs @@ -58,7 +58,7 @@ impl PythSimulator { pub async fn new() -> PythSimulator { let program_id = Pubkey::new_unique(); let (banks_client, payer, recent_blockhash) = - ProgramTest::new("pyth", program_id, processor!(process_instruction)) + ProgramTest::new("pyth_oracle", program_id, processor!(process_instruction)) .start() .await;