diff --git a/program/c/src/oracle/oracle.c b/program/c/src/oracle/oracle.c index fe0f837ae..09615a5a7 100644 --- a/program/c/src/oracle/oracle.c +++ b/program/c/src/oracle/oracle.c @@ -59,33 +59,6 @@ static bool valid_writable_account( SolParameters *prm, is_rent_exempt( *ka->lamports, ka->data_len ); } -static uint64_t init_mapping( SolParameters *prm, SolAccountInfo *ka ) -{ - // Verify that the new account is signed and writable, with correct - // ownership and size - if ( prm->ka_num != 2 || - !valid_funding_account( &ka[0] ) || - !valid_signable_account( prm, &ka[1], sizeof( pc_map_table_t ) ) ) { - return ERROR_INVALID_ARGUMENT; - } - - // Check that the account has not already been initialized - pc_map_table_t *tptr = (pc_map_table_t*)ka[1].data; - if ( tptr->magic_ != 0 || tptr->ver_ != 0 ) { - return ERROR_INVALID_ARGUMENT; - } - - // Initialize by setting to zero again (just in case) and setting - // the version number - cmd_hdr_t *hdr = (cmd_hdr_t*)prm->data; - sol_memset( tptr, 0, sizeof( pc_map_table_t ) ); - tptr->magic_ = PC_MAGIC; - tptr->ver_ = hdr->ver_; - tptr->type_ = PC_ACCTYPE_MAPPING; - tptr->size_ = sizeof( pc_map_table_t ) - sizeof( tptr->prod_ ); - return SUCCESS; -} - static uint64_t add_mapping( SolParameters *prm, SolAccountInfo *ka ) { // Account (1) is the tail or last mapping account in the chain @@ -539,7 +512,7 @@ static uint64_t dispatch( SolParameters *prm, SolAccountInfo *ka ) 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. - case e_cmd_init_mapping: return init_mapping( prm, ka ); + 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 ); case e_cmd_upd_product: return upd_product( prm, ka ); diff --git a/program/c/src/oracle/test_oracle.c b/program/c/src/oracle/test_oracle.c index 84d62d118..8076ae83e 100644 --- a/program/c/src/oracle/test_oracle.c +++ b/program/c/src/oracle/test_oracle.c @@ -7,81 +7,6 @@ uint64_t MAPPING_ACCOUNT_LAMPORTS = 143821440; uint64_t PRODUCT_ACCOUNT_LAMPORTS = 4454400; uint64_t PRICE_ACCOUNT_LAMPORTS = 23942400; -Test(oracle, init_mapping) { - - // start with perfect inputs - cmd_hdr_t idata = { - .ver_ = PC_VERSION, - .cmd_ = e_cmd_init_mapping - }; - SolPubkey p_id = {.x = { 0xff, }}; - SolPubkey p_id2 = {.x = { 0xfe, }}; - SolPubkey pkey = {.x = { 1, }}; - SolPubkey mkey = {.x = { 2, }}; - uint64_t pqty = 100; - pc_map_table_t mptr[1]; - sol_memset( mptr, 0, sizeof( pc_map_table_t ) ); - 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 - }}; - SolParameters prm = { - .ka = acc, - .ka_num = 2, - .data = (const uint8_t*)&idata, - .data_len = sizeof( idata ), - .program_id = &p_id - }; - cr_assert( SUCCESS == dispatch( &prm, acc ) ); - cr_assert( mptr->ver_ == PC_VERSION ); - cr_assert( mptr->type_ == PC_ACCTYPE_MAPPING ); - - // cant reinitialize because version has been set - cr_assert( ERROR_INVALID_ARGUMENT == dispatch( &prm, acc ) ); - sol_memset( mptr, 0, sizeof( pc_map_table_t ) ); - - // check other params - prm.ka_num = 1; - cr_assert( ERROR_INVALID_ARGUMENT == dispatch( &prm, acc ) ); - prm.ka_num = 2; - acc[0].is_signer = false; - cr_assert( ERROR_INVALID_ARGUMENT == dispatch( &prm, acc ) ); - acc[0].is_signer = true; - acc[1].is_signer = false; - cr_assert( ERROR_INVALID_ARGUMENT == dispatch( &prm, acc ) ); - acc[1].is_signer = true; - acc[0].is_writable = false; - cr_assert( ERROR_INVALID_ARGUMENT == dispatch( &prm, acc ) ); - acc[0].is_writable = true; - acc[1].is_writable = false; - cr_assert( ERROR_INVALID_ARGUMENT == dispatch( &prm, acc ) ); - acc[1].is_writable = true; - acc[1].owner = &p_id2; - cr_assert( ERROR_INVALID_ARGUMENT == dispatch( &prm, acc ) ); - acc[1].owner = &p_id; - acc[1].data_len = 0; - cr_assert( ERROR_INVALID_ARGUMENT == dispatch( &prm, acc ) ); - acc[1].data_len = sizeof( pc_map_table_t ); - cr_assert( SUCCESS == dispatch( &prm, acc ) ); -} - Test(oracle, add_mapping ) { // start with perfect inputs cmd_hdr_t idata = { diff --git a/program/rust/src/lib.rs b/program/rust/src/lib.rs index e397a0506..ad1839fbc 100644 --- a/program/rust/src/lib.rs +++ b/program/rust/src/lib.rs @@ -10,6 +10,7 @@ mod error; mod log; mod processor; mod rust_oracle; +mod test_oracle; mod time_machine_types; use crate::c_oracle_header::SUCCESSFULLY_UPDATED_AGGREGATE; diff --git a/program/rust/src/rust_oracle.rs b/program/rust/src/rust_oracle.rs index e19a09680..c8c63583a 100644 --- a/program/rust/src/rust_oracle.rs +++ b/program/rust/src/rust_oracle.rs @@ -13,12 +13,12 @@ use bytemuck::{ try_from_bytes_mut, Pod, }; +use solana_program::account_info::AccountInfo; use solana_program::entrypoint::SUCCESS; use solana_program::program_error::ProgramError; use solana_program::program_memory::sol_memset; use solana_program::pubkey::Pubkey; use solana_program::rent::Rent; -use solana_program::sysvar::slot_history::AccountInfo; use crate::c_oracle_header::{ cmd_hdr_t, @@ -110,7 +110,7 @@ fn valid_fresh_account(account: &AccountInfo) -> bool { } /// Sets the data of account to all-zero -fn clear_account(account: &AccountInfo) -> Result<(), ProgramError> { +pub fn clear_account(account: &AccountInfo) -> Result<(), ProgramError> { let mut data = account .try_borrow_mut_data() .map_err(|_| ProgramError::InvalidArgument)?; @@ -131,7 +131,7 @@ fn load_mut(data: &mut [u8]) -> Result<&mut T, ProgramError> { } /// Get the data stored in `account` as a value of type `T` -fn load_account_as<'a, T: Pod>(account: &'a AccountInfo) -> Result, ProgramError> { +pub fn load_account_as<'a, T: Pod>(account: &'a AccountInfo) -> Result, ProgramError> { let data = account.try_borrow_data()?; Ok(Ref::map(data, |data| { @@ -141,7 +141,7 @@ fn load_account_as<'a, T: Pod>(account: &'a AccountInfo) -> Result, P /// Mutably borrow the data in `account` as a value of type `T`. /// Any mutations to the returned value will be reflected in the account data. -fn load_account_as_mut<'a, T: Pod>( +pub fn load_account_as_mut<'a, T: Pod>( account: &'a AccountInfo, ) -> Result, ProgramError> { let data = account.try_borrow_mut_data()?; diff --git a/program/rust/src/test_oracle.rs b/program/rust/src/test_oracle.rs new file mode 100644 index 000000000..0e313cc49 --- /dev/null +++ b/program/rust/src/test_oracle.rs @@ -0,0 +1,182 @@ +#[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 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() { + let hdr: cmd_hdr_t = cmd_hdr_t { + ver_: PC_VERSION, + cmd_: command_t_e_cmd_init_mapping as i32, + }; + let instruction_data = bytes_of::(&hdr); + + let program_id = Pubkey::new_unique(); + let program_id_2 = Pubkey::new_unique(); + let funding_key = Pubkey::new_unique(); + let mapping_key = Pubkey::new_unique(); + let system_program = system_program::id(); + + let mut funding_balance = LAMPORTS_PER_SOL.clone(); + let mut 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::()); + let mut mapping_raw_data = [0u8; size_of::()]; + + let mut mapping_account = AccountInfo::new( + &mapping_key, + true, + true, + &mut mapping_balance, + &mut mapping_raw_data, + &program_id, + false, + Epoch::default(), + ); + + assert!(init_mapping( + &program_id, + &[funding_account.clone(), mapping_account.clone()], + instruction_data + ) + .is_ok()); + + { + let mapping_data = load_account_as::(&mapping_account).unwrap(); + + assert_eq!(mapping_data.ver_, PC_VERSION); + assert_eq!(mapping_data.magic_, PC_MAGIC); + assert_eq!(mapping_data.type_, PC_ACCTYPE_MAPPING); + assert_eq!(mapping_data.size_, 56); + } + + assert_eq!( + init_mapping( + &program_id, + &[funding_account.clone(), mapping_account.clone()], + instruction_data + ), + Err(ProgramError::InvalidArgument) + ); + + clear_account(&mapping_account).unwrap(); + + assert_eq!( + init_mapping(&program_id, &[funding_account.clone()], instruction_data), + Err(ProgramError::InvalidArgument) + ); + + funding_account.is_signer = false; + + assert_eq!( + init_mapping( + &program_id, + &[funding_account.clone(), mapping_account.clone()], + instruction_data + ), + Err(ProgramError::InvalidArgument) + ); + + funding_account.is_signer = true; + mapping_account.is_signer = false; + + assert_eq!( + init_mapping( + &program_id, + &[funding_account.clone(), mapping_account.clone()], + instruction_data + ), + Err(ProgramError::InvalidArgument) + ); + + mapping_account.is_signer = true; + funding_account.is_writable = false; + + assert_eq!( + init_mapping( + &program_id, + &[funding_account.clone(), mapping_account.clone()], + instruction_data + ), + Err(ProgramError::InvalidArgument) + ); + + funding_account.is_writable = true; + mapping_account.is_writable = false; + + assert_eq!( + init_mapping( + &program_id, + &[funding_account.clone(), mapping_account.clone()], + instruction_data + ), + Err(ProgramError::InvalidArgument) + ); + + mapping_account.is_writable = true; + mapping_account.owner = &program_id_2; + + assert_eq!( + init_mapping( + &program_id, + &[funding_account.clone(), mapping_account.clone()], + instruction_data + ), + Err(ProgramError::InvalidArgument) + ); + + mapping_account.owner = &program_id; + let prev_data = mapping_account.data; + mapping_account.data = Rc::new(RefCell::new(&mut [])); + + assert_eq!( + init_mapping( + &program_id, + &[funding_account.clone(), mapping_account.clone()], + instruction_data + ), + Err(ProgramError::InvalidArgument) + ); + + mapping_account.data = prev_data; + + assert!(init_mapping( + &program_id, + &[funding_account.clone(), mapping_account.clone()], + instruction_data + ) + .is_ok()); + } +}