diff --git a/program/c/src/oracle/oracle.c b/program/c/src/oracle/oracle.c index 09615a5a7..e91f68ed2 100644 --- a/program/c/src/oracle/oracle.c +++ b/program/c/src/oracle/oracle.c @@ -59,46 +59,6 @@ static bool valid_writable_account( SolParameters *prm, is_rent_exempt( *ka->lamports, ka->data_len ); } -static uint64_t add_mapping( SolParameters *prm, SolAccountInfo *ka ) -{ - // Account (1) is the tail or last mapping account in the chain - // Account (2) is the new mapping account and will become the new tail - // 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], sizeof( pc_map_table_t ) ) ) { - return ERROR_INVALID_ARGUMENT; - } - // Verify that last mapping account in chain is initialized, full - // and not pointing to a another account in the chain - // Also verify that the new account is uninitialized - cmd_hdr_t *hdr = (cmd_hdr_t*)prm->data; - pc_map_table_t *pptr = (pc_map_table_t*)ka[1].data; - pc_map_table_t *nptr = (pc_map_table_t*)ka[2].data; - if ( pptr->magic_ != PC_MAGIC || - pptr->ver_ != hdr->ver_ || - pptr->type_ != PC_ACCTYPE_MAPPING || - nptr->magic_ != 0 || - pptr->num_ < PC_MAP_TABLE_SIZE || - nptr->num_ != 0 || - !pc_pub_key_is_zero( &pptr->next_ ) ) { - return ERROR_INVALID_ARGUMENT; - } - // Initialize new account and set version number - sol_memset( nptr, 0, sizeof( pc_map_table_t ) ); - nptr->magic_ = PC_MAGIC; - nptr->ver_ = hdr->ver_; - nptr->type_ = PC_ACCTYPE_MAPPING; - nptr->size_ = sizeof( pc_map_table_t ) - sizeof( nptr->prod_ ); - - // Set last mapping account to point to this mapping account - pc_pub_key_t *nkey = (pc_pub_key_t*)ka[2].key; - pc_pub_key_assign( &pptr->next_, nkey ); - 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 @@ -513,7 +473,7 @@ static uint64_t dispatch( SolParameters *prm, SolAccountInfo *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 ERROR_INVALID_ARGUMENT; - case e_cmd_add_mapping: return add_mapping( prm, ka ); + case e_cmd_add_mapping: return ERROR_INVALID_ARGUMENT; case e_cmd_add_product: return add_product( prm, ka ); case e_cmd_upd_product: return upd_product( prm, ka ); case e_cmd_add_price: return add_price( prm, ka ); diff --git a/program/c/src/oracle/test_oracle.c b/program/c/src/oracle/test_oracle.c index 8076ae83e..c879b7e23 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, add_mapping ) { - // start with perfect inputs - cmd_hdr_t idata = { - .ver_ = PC_VERSION, - .cmd_ = e_cmd_add_mapping - }; - pc_map_table_t tptr[1]; - sol_memset( tptr, 0, sizeof( pc_map_table_t ) ); - tptr->magic_ = PC_MAGIC; - tptr->ver_ = PC_VERSION; - tptr->num_ = PC_MAP_TABLE_SIZE; - tptr->type_ = PC_ACCTYPE_MAPPING; - - SolPubkey p_id = {.x = { 0xff, }}; - SolPubkey pkey = {.x = { 1, }}; - SolPubkey tkey = {.x = { 2, }}; - SolPubkey mkey = {.x = { 3, }}; - 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 = &tkey, - .lamports = &MAPPING_ACCOUNT_LAMPORTS, - .data_len = sizeof( pc_map_table_t ), - .data = (uint8_t*)tptr, - .owner = &p_id, - .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 = 3, - .data = (const uint8_t*)&idata, - .data_len = sizeof( idata ), - .program_id = &p_id - }; - cr_assert( SUCCESS == dispatch( &prm, acc ) ); - cr_assert( mptr->magic_ == PC_MAGIC ); - cr_assert( mptr->ver_ == PC_VERSION ); - cr_assert( pc_pub_key_equal( &tptr->next_, (pc_pub_key_t*)&mkey ) ); - cr_assert( pc_pub_key_is_zero( &mptr->next_ ) ); - pc_pub_key_set_zero( &tptr->next_ ); - sol_memset( mptr, 0, sizeof( pc_map_table_t ) ); - tptr->num_ = 0; - cr_assert( ERROR_INVALID_ARGUMENT == dispatch( &prm, acc ) ); - cr_assert( pc_pub_key_is_zero( &tptr->next_ ) ); - tptr->num_ = PC_MAP_TABLE_SIZE; - tptr->magic_ = 0; - cr_assert( ERROR_INVALID_ARGUMENT == dispatch( &prm, acc ) ); - tptr->magic_ = PC_MAGIC; - cr_assert( SUCCESS == dispatch( &prm, acc ) ); -} - Test(oracle, add_product) { // start with perfect inputs cmd_add_product_t idata = { diff --git a/program/rust/src/lib.rs b/program/rust/src/lib.rs index 1d852e5cd..bcf864e2d 100644 --- a/program/rust/src/lib.rs +++ b/program/rust/src/lib.rs @@ -10,10 +10,12 @@ mod error; mod log; mod processor; mod rust_oracle; -mod test_oracle; mod time_machine_types; mod utils; +#[cfg(test)] +mod tests; + use crate::c_oracle_header::SUCCESSFULLY_UPDATED_AGGREGATE; use crate::error::{ OracleError, diff --git a/program/rust/src/log.rs b/program/rust/src/log.rs index e91dad706..00fbf4b88 100644 --- a/program/rust/src/log.rs +++ b/program/rust/src/log.rs @@ -20,7 +20,6 @@ pub fn pre_log(accounts: &[AccountInfo], instruction_data: &[u8]) -> ProgramResu .try_into() .map_err(|_| OracleError::IntegerCastingError)?; - match instruction_id { command_t_e_cmd_upd_price | command_t_e_cmd_agg_price => { let instruction: &cmd_upd_price = load::(instruction_data)?; diff --git a/program/rust/src/rust_oracle.rs b/program/rust/src/rust_oracle.rs index ed4427ecb..a41454021 100644 --- a/program/rust/src/rust_oracle.rs +++ b/program/rust/src/rust_oracle.rs @@ -66,7 +66,6 @@ pub fn update_version( // Ok(SUCCESS) } - /// initialize the first mapping account in a new linked-list of mapping accounts /// accounts[0] funding account [signer writable] /// accounts[1] new mapping account [signer writable] @@ -208,7 +207,7 @@ pub fn clear_account(account: &AccountInfo) -> Result<(), ProgramError> { /// 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> { @@ -226,7 +225,7 @@ 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)?; @@ -259,6 +258,6 @@ fn load_product_account_mut<'a>( } // Assign pubkey bytes from source to target, fails if source is not 32 bytes -fn pubkey_assign(target: &mut pc_pub_key_t, source: &[u8]) { +pub fn pubkey_assign(target: &mut pc_pub_key_t, source: &[u8]) { unsafe { target.k1_.copy_from_slice(source) } } diff --git a/program/rust/src/test_oracle.rs b/program/rust/src/test_oracle.rs deleted file mode 100644 index 1bba9e914..000000000 --- a/program/rust/src/test_oracle.rs +++ /dev/null @@ -1,182 +0,0 @@ -#[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::deserialize::load_account_as; - use crate::rust_oracle::{ - clear_account, - init_mapping, - }; - 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()); - } -} diff --git a/program/rust/src/tests/mod.rs b/program/rust/src/tests/mod.rs new file mode 100644 index 000000000..bee79ef71 --- /dev/null +++ b/program/rust/src/tests/mod.rs @@ -0,0 +1,2 @@ +mod test_add_mapping; +mod test_init_mapping; diff --git a/program/rust/src/tests/test_add_mapping.rs b/program/rust/src/tests/test_add_mapping.rs new file mode 100644 index 000000000..ddcba4d00 --- /dev/null +++ b/program/rust/src/tests/test_add_mapping.rs @@ -0,0 +1,168 @@ +use crate::c_oracle_header::{ + cmd_hdr_t, + command_t_e_cmd_add_mapping, + pc_map_table_t, + PC_MAGIC, + PC_MAP_TABLE_SIZE, + PC_VERSION, +}; +use crate::rust_oracle::{ + add_mapping, + clear_account, + initialize_mapping_account, + load_account_as_mut, + load_mapping_account_mut, + pubkey_assign, +}; +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::mem::size_of; + +#[test] +fn test_add_mapping() { + let hdr: cmd_hdr_t = cmd_hdr_t { + ver_: PC_VERSION, + cmd_: command_t_e_cmd_add_mapping as i32, + }; + let instruction_data = bytes_of::(&hdr); + + let program_id = Pubkey::new_unique(); + let funding_key = Pubkey::new_unique(); + let cur_mapping_key = Pubkey::new_unique(); + let next_mapping_key = Pubkey::new_unique(); + 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 cur_mapping_balance = + Rent::minimum_balance(&Rent::default(), size_of::()); + let mut cur_mapping_raw_data = [0u8; size_of::()]; + + let cur_mapping = AccountInfo::new( + &cur_mapping_key, + true, + true, + &mut cur_mapping_balance, + &mut cur_mapping_raw_data, + &program_id, + false, + Epoch::default(), + ); + + initialize_mapping_account(&cur_mapping, PC_VERSION).unwrap(); + + { + let mut cur_mapping_data = load_mapping_account_mut(&cur_mapping, PC_VERSION).unwrap(); + cur_mapping_data.num_ = PC_MAP_TABLE_SIZE; + } + + let mut next_mapping_balance = + Rent::minimum_balance(&Rent::default(), size_of::()); + let mut next_mapping_raw_data = [0u8; size_of::()]; + + let next_mapping = AccountInfo::new( + &next_mapping_key, + true, + true, + &mut next_mapping_balance, + &mut next_mapping_raw_data, + &program_id, + false, + Epoch::default(), + ); + + assert!(add_mapping( + &program_id, + &[ + funding_account.clone(), + cur_mapping.clone(), + next_mapping.clone() + ], + instruction_data + ) + .is_ok()); + + { + let next_mapping_data = load_mapping_account_mut(&next_mapping, PC_VERSION).unwrap(); + let mut cur_mapping_data = load_mapping_account_mut(&cur_mapping, PC_VERSION).unwrap(); + + assert!(unsafe { + cur_mapping_data + .next_ + .k1_ + .iter() + .zip(&next_mapping_key.to_bytes()) + .all(|(x, y)| *x == *y) + }); + assert!(unsafe { next_mapping_data.next_.k8_.iter().all(|x| *x == 0) }); + pubkey_assign(&mut cur_mapping_data.next_, &Pubkey::default().to_bytes()); + cur_mapping_data.num_ = 0; + } + + clear_account(&next_mapping).unwrap(); + + assert_eq!( + add_mapping( + &program_id, + &[ + funding_account.clone(), + cur_mapping.clone(), + next_mapping.clone() + ], + instruction_data + ), + Err(ProgramError::InvalidArgument) + ); + + { + let mut cur_mapping_data = load_mapping_account_mut(&cur_mapping, PC_VERSION).unwrap(); + assert!(unsafe { cur_mapping_data.next_.k8_.iter().all(|x| *x == 0) }); + cur_mapping_data.num_ = PC_MAP_TABLE_SIZE; + cur_mapping_data.magic_ = 0; + } + + assert_eq!( + add_mapping( + &program_id, + &[ + funding_account.clone(), + cur_mapping.clone(), + next_mapping.clone() + ], + instruction_data + ), + Err(ProgramError::InvalidArgument) + ); + + { + let mut cur_mapping_data = load_account_as_mut::(&cur_mapping).unwrap(); + cur_mapping_data.magic_ = PC_MAGIC; + } + + assert!(add_mapping( + &program_id, + &[ + funding_account.clone(), + cur_mapping.clone(), + next_mapping.clone() + ], + instruction_data + ) + .is_ok()); +} diff --git a/program/rust/src/tests/test_init_mapping.rs b/program/rust/src/tests/test_init_mapping.rs new file mode 100644 index 000000000..079615f21 --- /dev/null +++ b/program/rust/src/tests/test_init_mapping.rs @@ -0,0 +1,178 @@ +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()); +}