From d936c13bb353b187960677d159c441e9583283b4 Mon Sep 17 00:00:00 2001 From: Jayant Krishnamurthy Date: Fri, 5 Aug 2022 13:18:22 -0700 Subject: [PATCH 1/7] initial version --- program/rust/src/rust_oracle.rs | 60 +++++++++++-- program/rust/src/test_oracle.rs | 155 ++++++++++++++++++++++++++++---- 2 files changed, 192 insertions(+), 23 deletions(-) diff --git a/program/rust/src/rust_oracle.rs b/program/rust/src/rust_oracle.rs index b47be8aee..f3e14b89d 100644 --- a/program/rust/src/rust_oracle.rs +++ b/program/rust/src/rust_oracle.rs @@ -20,14 +20,7 @@ use solana_program::program_memory::sol_memset; use solana_program::pubkey::Pubkey; use solana_program::rent::Rent; -use crate::c_oracle_header::{ - cmd_hdr_t, - pc_acc, - pc_map_table_t, - PC_ACCTYPE_MAPPING, - PC_MAGIC, - PC_MAP_TABLE_SIZE, -}; +use crate::c_oracle_header::{cmd_hdr_t, pc_acc, pc_map_table_t, PC_ACCTYPE_MAPPING, PC_MAGIC, PC_MAP_TABLE_SIZE, PC_PROD_ACC_SIZE, pc_prod_t, PC_ACCTYPE_PRODUCT, pc_pub_key_t}; use crate::error::OracleResult; use crate::utils::pyth_assert; @@ -120,6 +113,42 @@ pub fn add_mapping( Ok(SUCCESS) } +pub fn add_product( + program_id: &Pubkey, + accounts: &[AccountInfo], + instruction_data: &[u8], +) -> OracleResult { + let [_funding_account, tail_mapping_account, new_product_account] = match accounts { + [x, y, z] + if valid_funding_account(x) + && valid_signable_account(program_id, y, size_of::()) + && valid_signable_account(program_id, z, PC_PROD_ACC_SIZE as usize) + && valid_fresh_account(z) => + { + Ok([x, y, z]) + } + _ => Err(ProgramError::InvalidArgument), + }?; + + let hdr = load::(instruction_data)?; + let mut mapping_data = load_mapping_account_mut(tail_mapping_account, hdr.ver_)?; + // The mapping account must have free space to add the product account + pyth_assert( + mapping_data.num_ < PC_MAP_TABLE_SIZE, + ProgramError::InvalidArgument, + )?; + + initialize_product_account(new_product_account, hdr.ver_)?; + + unsafe { + mapping_data.prod_[mapping_data.num_].k1_.copy_from_slice(&new_product_account.key.to_bytes()) + } + mapping_data.num_ += 1; + mapping_data.size_ = size_of::() - size_of_val(&mapping_data.prod_) + mapping_data.num_ * size_of::(); + + Ok(SUCCESS) +} + fn valid_funding_account(account: &AccountInfo) -> bool { account.is_signer && account.is_writable } @@ -218,3 +247,18 @@ fn initialize_mapping_account(account: &AccountInfo, version: u32) -> Result<(), Ok(()) } + +/// Initialize account as a new product account. This function will zero out any existing data in +/// the account. +fn initialize_product_account(account: &AccountInfo, version: u32) -> Result<(), ProgramError> { + clear_account(account)?; + + let mut prod_account = load_account_as_mut::(account)?; + prod_account.magic_ = PC_MAGIC; + prod_account.ver_ = version; + prod_account.type_ = PC_ACCTYPE_PRODUCT; + prod_account.size_ = size_of::() as u32; + + Ok(()) +} + diff --git a/program/rust/src/test_oracle.rs b/program/rust/src/test_oracle.rs index 0e313cc49..e8839c0f9 100644 --- a/program/rust/src/test_oracle.rs +++ b/program/rust/src/test_oracle.rs @@ -1,20 +1,9 @@ #[cfg(test)] mod test { - use crate::c_oracle_header::{ - cmd_hdr_t, - command_t_e_cmd_init_mapping, - pc_map_table_t, - PC_ACCTYPE_MAPPING, - PC_MAGIC, - PC_VERSION, - }; - use crate::rust_oracle::{ - clear_account, - init_mapping, - load_account_as, - }; - use bytemuck::bytes_of; - use solana_program::account_info::AccountInfo; + use crate::c_oracle_header::{cmd_hdr_t, command_t_e_cmd_add_product, command_t_e_cmd_init_mapping, pc_map_table_t, PC_ACCTYPE_MAPPING, PC_MAGIC, PC_VERSION, pc_prod_t, PC_PROD_ACC_SIZE, PC_ACCTYPE_PRODUCT}; + use crate::rust_oracle::{add_product, clear_account, init_mapping, load_account_as, load_mapping_account_mut}; + use bytemuck::{bytes_of, Zeroable}; + use solana_program::account_info::{Account, AccountInfo}; use solana_program::clock::Epoch; use solana_program::native_token::LAMPORTS_PER_SOL; use solana_program::program_error::ProgramError; @@ -179,4 +168,140 @@ mod test { ) .is_ok()); } + + fn fresh_funding_account<'a>() -> AccountInfo<'a> { + let system_program = system_program::id(); + + let mut funding_balance = LAMPORTS_PER_SOL.clone(); + AccountInfo::new( + &Pubkey::new_unique(), + true, + true, + &mut funding_balance, + &mut [], + &system_program, + false, + Epoch::default() + ) + } + + /* + macro_rules! zero_account { + ($size:expr) => { + AccountInfo::new( + &Pubkey::new_unique(), + true, + true, + &mut Rent::minimum_balance(&Rent::default(), size as usize), + &mut [0u8; size], + &program_id, + false, + Epoch::default(), + ); + } + } + */ + + #[test] + fn test_add_product() { + let hdr = cmd_hdr_t { + ver_: PC_VERSION, + cmd_: command_t_e_cmd_add_product as i32, + }; + let instruction_data = bytes_of::(&hdr); + + let program_id = Pubkey::new_unique(); + let mkey = Pubkey::new_unique(); + let product_key_1 = Pubkey::new_unique(); + let product_key_2 = Pubkey::new_unique(); + + let mut funding_account = fresh_funding_account(); + let pkey = funding_account.key; + + let mut mapping_balance = + Rent::minimum_balance(&Rent::default(), size_of::()); + let mut mapping_data: pc_map_table_t = pc_map_table_t::zeroed(); + mapping_data.magic_ = PC_MAGIC; + mapping_data.ver_ = PC_VERSION; + mapping_data.type_ = PC_ACCTYPE_MAPPING; + + let mapping_account = AccountInfo::new( + &mkey, + true, + true, + &mut mapping_balance, + &mut bytemuck::bytes_of_mut(&mut mapping_data), + &program_id, + false, + Epoch::default(), + ); + + let mut prod_raw_data = [0u8; PC_PROD_ACC_SIZE as usize]; + let product_account = AccountInfo::new( + &product_key_1, + true, + true, + &mut Rent::minimum_balance(&Rent::default(), PC_PROD_ACC_SIZE as usize), + &mut prod_raw_data, + &program_id, + false, + Epoch::default(), + ); + + let mut prod_raw_data_2 = [0u8; PC_PROD_ACC_SIZE as usize]; + let product_account_2 = AccountInfo::new( + &product_key_2, + true, + true, + &mut Rent::minimum_balance(&Rent::default(), PC_PROD_ACC_SIZE as usize), + &mut prod_raw_data_2, + &program_id, + false, + Epoch::default(), + ); + + assert!(add_product(&program_id, &[funding_account.clone(), mapping_account.clone(), product_account.clone()], instruction_data).is_ok()); + + { + let product_data = load_account_as::(&product_account).unwrap(); + let mapping_data = load_mapping_account_mut(&mapping_account).unwrap(); + + assert_eq!(product_data.magic_, PC_MAGIC); + assert_eq!(product_data.ver_, PC_VERSION); + assert_eq!(product_data.type_, PC_ACCTYPE_PRODUCT); + assert_eq!(product_data.size_, size_of::); + assert_eq!(mapping_data.num_, 1); + assert_eq!(mapping_data.prod_[0].k1_, product_account.key().to_bytes()); + } + + assert!(add_product(&program_id, &[funding_account.clone(), mapping_account.clone(), product_account_2.clone()], instruction_data).is_ok()); + { + let mapping_data = load_mapping_account_mut(&mapping_account).unwrap(); + assert_eq!(mapping_data.num_, 2); + assert_eq!(mapping_data.prod_[1].k1_, product_account_2.key().to_bytes()); + } + + /* + // invalid acc size + acc[2].data_len = 1; + cr_assert( ERROR_INVALID_ARGUMENT== dispatch( &prm, acc ) ); + acc[2].data_len = PC_PROD_ACC_SIZE; + + // test fill up of mapping table + sol_memset( mptr, 0, sizeof( pc_map_table_t ) ); + mptr->magic_ = PC_MAGIC; + mptr->ver_ = PC_VERSION; + mptr->type_ = PC_ACCTYPE_MAPPING; + for( unsigned i = 0;; ++i ) { + sol_memset( sptr, 0, PC_PROD_ACC_SIZE ); + uint64_t rc = dispatch( &prm, acc ); + if ( rc != SUCCESS ) { + cr_assert( i == ( unsigned )(PC_MAP_TABLE_SIZE) ); + break; + } + cr_assert( mptr->num_ == i + 1 ); + cr_assert( rc == SUCCESS ); + } + */ + } } From a7f866448732a47622b7151e3b86d918d0b6acf4 Mon Sep 17 00:00:00 2001 From: Jayant Krishnamurthy Date: Fri, 5 Aug 2022 13:30:02 -0700 Subject: [PATCH 2/7] cleaning up stuff --- program/rust/src/processor.rs | 10 ++----- program/rust/src/rust_oracle.rs | 11 ++++---- program/rust/src/test_oracle.rs | 49 ++++++++++++++++----------------- 3 files changed, 33 insertions(+), 37 deletions(-) diff --git a/program/rust/src/processor.rs b/program/rust/src/processor.rs index 2d8c967dc..fffd5e611 100644 --- a/program/rust/src/processor.rs +++ b/program/rust/src/processor.rs @@ -10,6 +10,7 @@ use crate::c_oracle_header::{ cmd_hdr, command_t_e_cmd_add_mapping, command_t_e_cmd_add_price, + command_t_e_cmd_add_product, command_t_e_cmd_agg_price, command_t_e_cmd_init_mapping, command_t_e_cmd_upd_account_version, @@ -21,13 +22,7 @@ use crate::error::{ OracleError, OracleResult, }; -use crate::rust_oracle::{ - add_mapping, - add_price, - init_mapping, - update_price, - update_version, -}; +use crate::rust_oracle::{add_mapping, add_price, add_product, init_mapping, update_price, update_version}; ///dispatch to the right instruction in the oracle pub fn process_instruction( @@ -63,6 +58,7 @@ pub fn process_instruction( command_t_e_cmd_add_price => add_price(program_id, accounts, instruction_data), command_t_e_cmd_init_mapping => init_mapping(program_id, accounts, instruction_data), command_t_e_cmd_add_mapping => add_mapping(program_id, accounts, instruction_data), + command_t_e_cmd_add_product => add_product(program_id, accounts, instruction_data), _ => c_entrypoint_wrapper(input), } } diff --git a/program/rust/src/rust_oracle.rs b/program/rust/src/rust_oracle.rs index dcb2bbbf6..8a32f13cf 100644 --- a/program/rust/src/rust_oracle.rs +++ b/program/rust/src/rust_oracle.rs @@ -200,14 +200,15 @@ pub fn add_product( initialize_product_account(new_product_account, hdr.ver_)?; + let current_index = mapping_data.num_ as usize; unsafe { - mapping_data.prod_[mapping_data.num_] + mapping_data.prod_[current_index] .k1_ .copy_from_slice(&new_product_account.key.to_bytes()) } mapping_data.num_ += 1; - mapping_data.size_ = size_of::() - size_of_val(&mapping_data.prod_) - + mapping_data.num_ * size_of::(); + mapping_data.size_ = (size_of::() - size_of_val(&mapping_data.prod_)) as u32 + + mapping_data.num_ * (size_of::() as u32); Ok(SUCCESS) } @@ -283,7 +284,7 @@ pub fn load_account_as_mut<'a, T: Pod>( /// Mutably borrow the data in `account` as a mapping account, validating that the account /// is properly formatted. Any mutations to the returned value will be reflected in the /// account data. Use this to read already-initialized accounts. -fn load_mapping_account_mut<'a>( +pub fn load_mapping_account_mut<'a>( account: &'a AccountInfo, expected_version: u32, ) -> Result, ProgramError> { @@ -331,7 +332,7 @@ fn initialize_product_account(account: &AccountInfo, version: u32) -> Result<(), /// Mutably borrow the data in `account` as a product account, validating that the account /// is properly formatted. Any mutations to the returned value will be reflected in the /// account data. Use this to read already-initialized accounts. -fn load_product_account_mut<'a>( +pub fn load_product_account_mut<'a>( account: &'a AccountInfo, expected_version: u32, ) -> Result, ProgramError> { diff --git a/program/rust/src/test_oracle.rs b/program/rust/src/test_oracle.rs index d2324c9b7..5285f82f6 100644 --- a/program/rust/src/test_oracle.rs +++ b/program/rust/src/test_oracle.rs @@ -1,17 +1,33 @@ #[cfg(test)] mod test { + use std::cell::RefCell; + use std::mem::size_of; + use std::rc::Rc; + + use bytemuck::{ + bytes_of, + Zeroable, + }; + use solana_program::account_info::AccountInfo; + use solana_program::clock::Epoch; + use solana_program::native_token::LAMPORTS_PER_SOL; + use solana_program::program_error::ProgramError; + use solana_program::pubkey::Pubkey; + use solana_program::rent::Rent; + use solana_program::system_program; + use crate::c_oracle_header::{ cmd_hdr_t, command_t_e_cmd_add_product, command_t_e_cmd_init_mapping, - pc_map_table_t, - pc_prod_t, PC_ACCTYPE_MAPPING, PC_ACCTYPE_PRODUCT, PC_MAGIC, + pc_map_table_t, PC_PROD_ACC_SIZE, + pc_prod_t, PC_VERSION, - }; + }; use crate::rust_oracle::{ add_product, clear_account, @@ -19,23 +35,6 @@ mod test { load_account_as, load_mapping_account_mut, }; - use bytemuck::{ - bytes_of, - Zeroable, - }; - use solana_program::account_info::{ - Account, - AccountInfo, - }; - use solana_program::clock::Epoch; - use solana_program::native_token::LAMPORTS_PER_SOL; - use solana_program::program_error::ProgramError; - use solana_program::pubkey::Pubkey; - use solana_program::rent::Rent; - use solana_program::system_program; - use std::cell::RefCell; - use std::mem::size_of; - use std::rc::Rc; #[test] fn test_init_mapping() { @@ -296,14 +295,14 @@ mod test { { let product_data = load_account_as::(&product_account).unwrap(); - let mapping_data = load_mapping_account_mut(&mapping_account).unwrap(); + let mapping_data = load_mapping_account_mut(&mapping_account, PC_VERSION).unwrap(); assert_eq!(product_data.magic_, PC_MAGIC); assert_eq!(product_data.ver_, PC_VERSION); assert_eq!(product_data.type_, PC_ACCTYPE_PRODUCT); - assert_eq!(product_data.size_, size_of::); + assert_eq!(product_data.size_, size_of::() as u32); assert_eq!(mapping_data.num_, 1); - assert_eq!(mapping_data.prod_[0].k1_, product_account.key().to_bytes()); + assert_eq!(mapping_data.prod_[0].k1_, product_account.key.to_bytes()); } assert!(add_product( @@ -317,11 +316,11 @@ mod test { ) .is_ok()); { - let mapping_data = load_mapping_account_mut(&mapping_account).unwrap(); + let mapping_data = load_mapping_account_mut(&mapping_account, PC_VERSION).unwrap(); assert_eq!(mapping_data.num_, 2); assert_eq!( mapping_data.prod_[1].k1_, - product_account_2.key().to_bytes() + product_account_2.key.to_bytes() ); } From 171700d855725115b22406d23aed756948dd9950 Mon Sep 17 00:00:00 2001 From: Jayant Krishnamurthy Date: Fri, 5 Aug 2022 17:58:47 -0700 Subject: [PATCH 3/7] add tests --- program/rust/src/processor.rs | 9 +- program/rust/src/rust_oracle.rs | 6 +- program/rust/src/test_oracle.rs | 147 +++++++++++++++++++++----------- 3 files changed, 110 insertions(+), 52 deletions(-) diff --git a/program/rust/src/processor.rs b/program/rust/src/processor.rs index fffd5e611..03200448f 100644 --- a/program/rust/src/processor.rs +++ b/program/rust/src/processor.rs @@ -22,7 +22,14 @@ use crate::error::{ OracleError, OracleResult, }; -use crate::rust_oracle::{add_mapping, add_price, add_product, init_mapping, update_price, update_version}; +use crate::rust_oracle::{ + add_mapping, + add_price, + add_product, + init_mapping, + update_price, + update_version, +}; ///dispatch to the right instruction in the oracle pub fn process_instruction( diff --git a/program/rust/src/rust_oracle.rs b/program/rust/src/rust_oracle.rs index 8a32f13cf..7a69d6be5 100644 --- a/program/rust/src/rust_oracle.rs +++ b/program/rust/src/rust_oracle.rs @@ -200,7 +200,7 @@ pub fn add_product( initialize_product_account(new_product_account, hdr.ver_)?; - let current_index = mapping_data.num_ as usize; + let current_index = mapping_data.num_ as usize; unsafe { mapping_data.prod_[current_index] .k1_ @@ -302,7 +302,7 @@ pub fn load_mapping_account_mut<'a>( /// Initialize account as a new mapping account. This function will zero out any existing data in /// the account. -fn initialize_mapping_account(account: &AccountInfo, version: u32) -> Result<(), ProgramError> { +pub fn initialize_mapping_account(account: &AccountInfo, version: u32) -> Result<(), ProgramError> { clear_account(account)?; let mut mapping_account = load_account_as_mut::(account)?; @@ -317,7 +317,7 @@ fn initialize_mapping_account(account: &AccountInfo, version: u32) -> Result<(), /// Initialize account as a new product account. This function will zero out any existing data in /// the account. -fn initialize_product_account(account: &AccountInfo, version: u32) -> Result<(), ProgramError> { +pub fn initialize_product_account(account: &AccountInfo, version: u32) -> Result<(), ProgramError> { clear_account(account)?; let mut prod_account = load_account_as_mut::(account)?; diff --git a/program/rust/src/test_oracle.rs b/program/rust/src/test_oracle.rs index 5285f82f6..b54fe617f 100644 --- a/program/rust/src/test_oracle.rs +++ b/program/rust/src/test_oracle.rs @@ -20,18 +20,21 @@ mod test { cmd_hdr_t, command_t_e_cmd_add_product, command_t_e_cmd_init_mapping, + pc_map_table_t, + pc_prod_t, + pc_pub_key_t, PC_ACCTYPE_MAPPING, PC_ACCTYPE_PRODUCT, PC_MAGIC, - pc_map_table_t, + PC_MAP_TABLE_SIZE, PC_PROD_ACC_SIZE, - pc_prod_t, PC_VERSION, - }; + }; use crate::rust_oracle::{ add_product, clear_account, init_mapping, + initialize_mapping_account, load_account_as, load_mapping_account_mut, }; @@ -191,22 +194,6 @@ mod test { .is_ok()); } - fn fresh_funding_account<'a>() -> AccountInfo<'a> { - let system_program = system_program::id(); - - let mut funding_balance = LAMPORTS_PER_SOL.clone(); - AccountInfo::new( - &Pubkey::new_unique(), - true, - true, - &mut funding_balance, - &mut [], - &system_program, - false, - Epoch::default(), - ) - } - /* macro_rules! zero_account { ($size:expr) => { @@ -233,12 +220,23 @@ mod test { let instruction_data = bytes_of::(&hdr); let program_id = Pubkey::new_unique(); + let funding_key = Pubkey::new_unique(); let mkey = Pubkey::new_unique(); let product_key_1 = Pubkey::new_unique(); let product_key_2 = Pubkey::new_unique(); - let mut funding_account = fresh_funding_account(); - let pkey = funding_account.key; + let system_program = system_program::id(); + let mut funding_balance = LAMPORTS_PER_SOL.clone(); + let funding_account = AccountInfo::new( + &funding_key, + true, + true, + &mut funding_balance, + &mut [], + &system_program, + false, + Epoch::default(), + ); let mut mapping_balance = Rent::minimum_balance(&Rent::default(), size_of::()); @@ -246,36 +244,41 @@ mod test { mapping_data.magic_ = PC_MAGIC; mapping_data.ver_ = PC_VERSION; mapping_data.type_ = PC_ACCTYPE_MAPPING; + let mut mapping_bytes = bytemuck::bytes_of_mut(&mut mapping_data); let mapping_account = AccountInfo::new( &mkey, true, true, &mut mapping_balance, - &mut bytemuck::bytes_of_mut(&mut mapping_data), + &mut mapping_bytes, &program_id, false, Epoch::default(), ); + let mut product_balance = + Rent::minimum_balance(&Rent::default(), PC_PROD_ACC_SIZE as usize); let mut prod_raw_data = [0u8; PC_PROD_ACC_SIZE as usize]; let product_account = AccountInfo::new( &product_key_1, true, true, - &mut Rent::minimum_balance(&Rent::default(), PC_PROD_ACC_SIZE as usize), + &mut product_balance, &mut prod_raw_data, &program_id, false, Epoch::default(), ); + let mut product_balance_2 = + Rent::minimum_balance(&Rent::default(), PC_PROD_ACC_SIZE as usize); let mut prod_raw_data_2 = [0u8; PC_PROD_ACC_SIZE as usize]; let product_account_2 = AccountInfo::new( &product_key_2, true, true, - &mut Rent::minimum_balance(&Rent::default(), PC_PROD_ACC_SIZE as usize), + &mut product_balance_2, &mut prod_raw_data_2, &program_id, false, @@ -302,7 +305,10 @@ mod test { assert_eq!(product_data.type_, PC_ACCTYPE_PRODUCT); assert_eq!(product_data.size_, size_of::() as u32); assert_eq!(mapping_data.num_, 1); - assert_eq!(mapping_data.prod_[0].k1_, product_account.key.to_bytes()); + assert!(pubkey_equal( + &mapping_data.prod_[0], + &product_account.key.to_bytes() + )); } assert!(add_product( @@ -318,33 +324,78 @@ mod test { { let mapping_data = load_mapping_account_mut(&mapping_account, PC_VERSION).unwrap(); assert_eq!(mapping_data.num_, 2); - assert_eq!( - mapping_data.prod_[1].k1_, - product_account_2.key.to_bytes() - ); + assert!(pubkey_equal( + &mapping_data.prod_[1], + &product_account_2.key.to_bytes() + )); } - /* - // invalid acc size - acc[2].data_len = 1; - cr_assert( ERROR_INVALID_ARGUMENT== dispatch( &prm, acc ) ); - acc[2].data_len = PC_PROD_ACC_SIZE; + // invalid account size + let product_key_3 = Pubkey::new_unique(); + let mut product_balance_3 = + Rent::minimum_balance(&Rent::default(), PC_PROD_ACC_SIZE as usize); + let mut prod_raw_data_3 = [0u8; PC_PROD_ACC_SIZE as usize - 1]; + let product_account_3 = AccountInfo::new( + &product_key_3, + true, + true, + &mut product_balance_3, + &mut prod_raw_data_3, + &program_id, + false, + Epoch::default(), + ); + assert!(add_product( + &program_id, + &[ + funding_account.clone(), + mapping_account.clone(), + product_account_3.clone() + ], + instruction_data + ) + .is_err()); // test fill up of mapping table - sol_memset( mptr, 0, sizeof( pc_map_table_t ) ); - mptr->magic_ = PC_MAGIC; - mptr->ver_ = PC_VERSION; - mptr->type_ = PC_ACCTYPE_MAPPING; - for( unsigned i = 0;; ++i ) { - sol_memset( sptr, 0, PC_PROD_ACC_SIZE ); - uint64_t rc = dispatch( &prm, acc ); - if ( rc != SUCCESS ) { - cr_assert( i == ( unsigned )(PC_MAP_TABLE_SIZE) ); - break; - } - cr_assert( mptr->num_ == i + 1 ); - cr_assert( rc == SUCCESS ); + clear_account(&mapping_account).unwrap(); + initialize_mapping_account(&mapping_account, PC_VERSION).unwrap(); + + for i in 0..PC_MAP_TABLE_SIZE { + clear_account(&product_account).unwrap(); + + assert!(add_product( + &program_id, + &[ + funding_account.clone(), + mapping_account.clone(), + product_account.clone() + ], + instruction_data + ) + .is_ok()); + let mapping_data = load_mapping_account_mut(&mapping_account, PC_VERSION).unwrap(); + assert_eq!(mapping_data.num_, i + 1); } - */ + + clear_account(&product_account).unwrap(); + + assert!(add_product( + &program_id, + &[ + funding_account.clone(), + mapping_account.clone(), + product_account.clone() + ], + instruction_data + ) + .is_err()); + + let mapping_data = load_mapping_account_mut(&mapping_account, PC_VERSION).unwrap(); + assert_eq!(mapping_data.num_, PC_MAP_TABLE_SIZE); + } + + // Assign pubkey bytes from source to target, fails if source is not 32 bytes + fn pubkey_equal(target: &pc_pub_key_t, source: &[u8]) -> bool { + unsafe { target.k1_ == *source } } } From 49cfa47970ecd17a52ac9fd236c0b75f6b166714 Mon Sep 17 00:00:00 2001 From: Jayant Krishnamurthy Date: Sat, 6 Aug 2022 07:43:57 -0700 Subject: [PATCH 4/7] delete c impl --- program/c/src/oracle/oracle.c | 47 +++------------------------------ program/rust/src/test_oracle.rs | 17 ------------ 2 files changed, 3 insertions(+), 61 deletions(-) diff --git a/program/c/src/oracle/oracle.c b/program/c/src/oracle/oracle.c index 09615a5a7..bbb8f1c3e 100644 --- a/program/c/src/oracle/oracle.c +++ b/program/c/src/oracle/oracle.c @@ -99,48 +99,6 @@ static uint64_t add_mapping( SolParameters *prm, SolAccountInfo *ka ) return SUCCESS; } -static uint64_t add_product( SolParameters *prm, SolAccountInfo *ka ) -{ - // Account (1) is the mapping account that we're going to add to and - // must be the tail (or last) mapping account in chain - // Account (2) is the new product account - // Verify that these are signed, writable accounts with correct ownership - // and size - if ( prm->ka_num != 3 || - !valid_funding_account( &ka[0] ) || - !valid_signable_account( prm, &ka[1], sizeof( pc_map_table_t ) ) || - !valid_signable_account( prm, &ka[2], PC_PROD_ACC_SIZE ) ) { - return ERROR_INVALID_ARGUMENT; - } - - // Verify that mapping account is a valid tail account - // that the new product account is uninitialized and that there is space - // in the mapping account - cmd_hdr_t *hdr = (cmd_hdr_t*)prm->data; - pc_map_table_t *mptr = (pc_map_table_t*)ka[1].data; - pc_prod_t *pptr = (pc_prod_t*)ka[2].data; - if ( mptr->magic_ != PC_MAGIC || - mptr->ver_ != hdr->ver_ || - mptr->type_ != PC_ACCTYPE_MAPPING || - pptr->magic_ != 0 || - mptr->num_ >= PC_MAP_TABLE_SIZE ) { - return ERROR_INVALID_ARGUMENT; - } - - // Initialize product account - sol_memset( pptr, 0, PC_PROD_ACC_SIZE ); - pptr->magic_ = PC_MAGIC; - pptr->ver_ = hdr->ver_; - pptr->type_ = PC_ACCTYPE_PRODUCT; - pptr->size_ = sizeof( pc_prod_t ); - - // finally add mapping account link - pc_pub_key_assign( &mptr->prod_[mptr->num_++], (pc_pub_key_t*)ka[2].key ); - mptr->size_ = sizeof( pc_map_table_t ) - sizeof( mptr->prod_ ) + - mptr->num_ * sizeof( pc_pub_key_t ); - return SUCCESS; -} - #define PC_ADD_STR \ tag = (pc_str_t*)src;\ tag_len = 1 + tag->len_;\ @@ -511,10 +469,11 @@ static uint64_t dispatch( SolParameters *prm, SolAccountInfo *ka ) case e_cmd_upd_price: case e_cmd_agg_price: return upd_price( prm, ka ); case e_cmd_upd_price_no_fail_on_error: return upd_price_no_fail_on_error( prm, ka ); - // init_mapping is overridden in Rust, but still implemented here to make the C unit tests pass. + // init_mapping is implemented in Rust. case e_cmd_init_mapping: return ERROR_INVALID_ARGUMENT; case e_cmd_add_mapping: return add_mapping( prm, ka ); - case e_cmd_add_product: return add_product( prm, ka ); + // init_mapping is implemented in Rust. + case e_cmd_add_product: return ERROR_INVALID_ARGUMENT; case e_cmd_upd_product: return upd_product( prm, ka ); case e_cmd_add_price: return add_price( prm, ka ); case e_cmd_add_publisher: return add_publisher( prm, ka ); diff --git a/program/rust/src/test_oracle.rs b/program/rust/src/test_oracle.rs index b54fe617f..9e86edc2a 100644 --- a/program/rust/src/test_oracle.rs +++ b/program/rust/src/test_oracle.rs @@ -194,23 +194,6 @@ mod test { .is_ok()); } - /* - macro_rules! zero_account { - ($size:expr) => { - AccountInfo::new( - &Pubkey::new_unique(), - true, - true, - &mut Rent::minimum_balance(&Rent::default(), size as usize), - &mut [0u8; size], - &program_id, - false, - Epoch::default(), - ); - } - } - */ - #[test] fn test_add_product() { let hdr = cmd_hdr_t { From c2dbb0a78be49f7243b97b5b7ce166793ee15cda Mon Sep 17 00:00:00 2001 From: Jayant Krishnamurthy Date: Mon, 8 Aug 2022 08:04:09 -0700 Subject: [PATCH 5/7] pr comments --- program/c/src/oracle/oracle.c | 2 +- program/rust/src/rust_oracle.rs | 14 +++++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/program/c/src/oracle/oracle.c b/program/c/src/oracle/oracle.c index bbb8f1c3e..74b5efb8f 100644 --- a/program/c/src/oracle/oracle.c +++ b/program/c/src/oracle/oracle.c @@ -472,7 +472,7 @@ static uint64_t dispatch( SolParameters *prm, SolAccountInfo *ka ) // init_mapping is implemented in Rust. case e_cmd_init_mapping: return ERROR_INVALID_ARGUMENT; case e_cmd_add_mapping: return add_mapping( prm, ka ); - // init_mapping is implemented in Rust. + // add_product is implemented in Rust. case e_cmd_add_product: return ERROR_INVALID_ARGUMENT; case e_cmd_upd_product: return upd_product( prm, ka ); case e_cmd_add_price: return add_price( prm, ka ); diff --git a/program/rust/src/rust_oracle.rs b/program/rust/src/rust_oracle.rs index 0f377abbd..66bd1b6dd 100644 --- a/program/rust/src/rust_oracle.rs +++ b/program/rust/src/rust_oracle.rs @@ -38,6 +38,7 @@ use crate::c_oracle_header::{ PC_PTYPE_UNKNOWN, }; use crate::error::OracleResult; +use crate::OracleError; use crate::utils::pyth_assert; @@ -199,15 +200,16 @@ pub fn add_product( initialize_product_account(new_product_account, hdr.ver_)?; - let current_index = mapping_data.num_ as usize; + let current_index: usize = try_convert(mapping_data.num_)?; unsafe { mapping_data.prod_[current_index] .k1_ .copy_from_slice(&new_product_account.key.to_bytes()) } mapping_data.num_ += 1; - mapping_data.size_ = (size_of::() - size_of_val(&mapping_data.prod_)) as u32 - + mapping_data.num_ * (size_of::() as u32); + mapping_data.size_ = + try_convert::<_, u32>(size_of::() - size_of_val(&mapping_data.prod_))? + + mapping_data.num_ * try_convert::<_, u32>(size_of::())?; Ok(SUCCESS) } @@ -316,3 +318,9 @@ pub fn load_product_account_mut<'a>( fn pubkey_assign(target: &mut pc_pub_key_t, source: &[u8]) { unsafe { target.k1_.copy_from_slice(source) } } + +/// Convert `x: T` into a `U`, returning the appropriate `OracleError` if the conversion fails. +fn try_convert>(x: T) -> Result { + // Note: the error here assumes we're only applying this function to integers right now. + U::try_from(x).map_err(|_| OracleError::IntegerCastingError) +} From 3b1ed753f39b23630a91e94999bc66385a5590af Mon Sep 17 00:00:00 2001 From: Jayant Krishnamurthy Date: Mon, 8 Aug 2022 08:17:58 -0700 Subject: [PATCH 6/7] delete C test --- program/c/src/oracle/test_oracle.c | 95 ------------------------------ 1 file changed, 95 deletions(-) diff --git a/program/c/src/oracle/test_oracle.c b/program/c/src/oracle/test_oracle.c index 8076ae83e..1c18f0299 100644 --- a/program/c/src/oracle/test_oracle.c +++ b/program/c/src/oracle/test_oracle.c @@ -82,101 +82,6 @@ Test(oracle, add_mapping ) { cr_assert( SUCCESS == dispatch( &prm, acc ) ); } -Test(oracle, add_product) { - // start with perfect inputs - cmd_add_product_t idata = { - .ver_ = PC_VERSION, - .cmd_ = e_cmd_add_product, - }; - SolPubkey p_id = {.x = { 0xff, }}; - SolPubkey pkey = {.x = { 1, }}; - SolPubkey mkey = {.x = { 2, }}; - SolPubkey skey = {.x = { 3, }}; - SolPubkey skey2 = {.x = { 4, }}; - uint64_t pqty = 100; - pc_map_table_t mptr[1]; - sol_memset( mptr, 0, sizeof( pc_map_table_t ) ); - mptr->magic_ = PC_MAGIC; - mptr->ver_ = PC_VERSION; - mptr->type_ = PC_ACCTYPE_MAPPING; - char sdata[PC_PROD_ACC_SIZE]; - pc_prod_t *sptr = (pc_prod_t*)sdata; - sol_memset( sptr, 0, PC_PROD_ACC_SIZE ); - SolAccountInfo acc[] = {{ - .key = &pkey, - .lamports = &pqty, - .data_len = 0, - .data = NULL, - .owner = NULL, - .rent_epoch = 0, - .is_signer = true, - .is_writable = true, - .executable = false - },{ - .key = &mkey, - .lamports = &MAPPING_ACCOUNT_LAMPORTS, - .data_len = sizeof( pc_map_table_t ), - .data = (uint8_t*)mptr, - .owner = &p_id, - .rent_epoch = 0, - .is_signer = true, - .is_writable = true, - .executable = false - },{ - .key = &skey, - .lamports = &PRODUCT_ACCOUNT_LAMPORTS, - .data_len = PC_PROD_ACC_SIZE, - .data = (uint8_t*)sptr, - .owner = &p_id, - .rent_epoch = 0, - .is_signer = true, - .is_writable = true, - .executable = false - }}; - SolParameters prm = { - .ka = acc, - .ka_num = 3, - .data = (const uint8_t*)&idata, - .data_len = sizeof( idata ), - .program_id = &p_id - }; - cr_assert( SUCCESS == dispatch( &prm, acc ) ); - cr_assert( sptr->magic_ == PC_MAGIC ); - cr_assert( sptr->ver_ == PC_VERSION ); - cr_assert( sptr->type_ == PC_ACCTYPE_PRODUCT ); - cr_assert( sptr->size_ == sizeof( pc_prod_t ) ); - cr_assert( mptr->num_ == 1 ); - cr_assert( pc_pub_key_equal( (pc_pub_key_t*)&skey, &mptr->prod_[0] ) ); - - // 2nd product - acc[2].key = &skey2; - sol_memset( sptr, 0, PC_PROD_ACC_SIZE ); - cr_assert( SUCCESS == dispatch( &prm, acc ) ); - cr_assert( mptr->num_ == 2 ); - cr_assert( pc_pub_key_equal( &mptr->prod_[1], (pc_pub_key_t*)&skey2 ) ); - - // invalid acc size - acc[2].data_len = 1; - cr_assert( ERROR_INVALID_ARGUMENT== dispatch( &prm, acc ) ); - acc[2].data_len = PC_PROD_ACC_SIZE; - - // test fill up of mapping table - sol_memset( mptr, 0, sizeof( pc_map_table_t ) ); - mptr->magic_ = PC_MAGIC; - mptr->ver_ = PC_VERSION; - mptr->type_ = PC_ACCTYPE_MAPPING; - for( unsigned i = 0;; ++i ) { - sol_memset( sptr, 0, PC_PROD_ACC_SIZE ); - uint64_t rc = dispatch( &prm, acc ); - if ( rc != SUCCESS ) { - cr_assert( i == ( unsigned )(PC_MAP_TABLE_SIZE) ); - break; - } - cr_assert( mptr->num_ == i + 1 ); - cr_assert( rc == SUCCESS ); - } -} - Test( oracle, add_publisher ) { // start with perfect inputs cmd_add_publisher_t idata = { From ca4479ac609b0457cd51e35a802809a9bc54f639 Mon Sep 17 00:00:00 2001 From: Jayant Krishnamurthy Date: Mon, 8 Aug 2022 09:00:38 -0700 Subject: [PATCH 7/7] one more --- program/rust/src/rust_oracle.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/rust/src/rust_oracle.rs b/program/rust/src/rust_oracle.rs index d0726be83..2ff23916b 100644 --- a/program/rust/src/rust_oracle.rs +++ b/program/rust/src/rust_oracle.rs @@ -289,7 +289,7 @@ pub fn initialize_product_account(account: &AccountInfo, version: u32) -> Result prod_account.magic_ = PC_MAGIC; prod_account.ver_ = version; prod_account.type_ = PC_ACCTYPE_PRODUCT; - prod_account.size_ = size_of::() as u32; + prod_account.size_ = try_convert(size_of::())?; Ok(()) }