Skip to content

Commit 651e3bb

Browse files
authored
Move to utils (#247)
* moved helpers out of rust oacle * renamed method that has same name as a solana method
1 parent 205a254 commit 651e3bb

12 files changed

+249
-203
lines changed

program/rust/src/deserialize.rs

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,22 @@ use bytemuck::{
66
Pod,
77
};
88

9+
use crate::c_oracle_header::{
10+
pc_acc,
11+
PythAccount,
12+
PC_MAGIC,
13+
};
14+
use crate::utils::{
15+
clear_account,
16+
pyth_assert,
17+
};
18+
use solana_program::account_info::AccountInfo;
19+
use solana_program::program_error::ProgramError;
920
use std::cell::{
1021
Ref,
1122
RefMut,
1223
};
1324

14-
use solana_program::account_info::AccountInfo;
15-
use solana_program::program_error::ProgramError;
16-
1725
/// Interpret the bytes in `data` as a value of type `T`
1826
pub fn load<T: Pod>(data: &[u8]) -> Result<&T, ProgramError> {
1927
try_from_bytes(
@@ -53,3 +61,37 @@ pub fn load_account_as_mut<'a, T: Pod>(
5361
bytemuck::from_bytes_mut(&mut data[0..size_of::<T>()])
5462
}))
5563
}
64+
65+
pub fn load_checked<'a, T: PythAccount>(
66+
account: &'a AccountInfo,
67+
version: u32,
68+
) -> Result<RefMut<'a, T>, ProgramError> {
69+
{
70+
let account_header = load_account_as::<pc_acc>(account)?;
71+
pyth_assert(
72+
account_header.magic_ == PC_MAGIC
73+
&& account_header.ver_ == version
74+
&& account_header.type_ == T::ACCOUNT_TYPE,
75+
ProgramError::InvalidArgument,
76+
)?;
77+
}
78+
79+
load_account_as_mut::<T>(account)
80+
}
81+
82+
pub fn initialize_pyth_account_checked<'a, T: PythAccount>(
83+
account: &'a AccountInfo,
84+
version: u32,
85+
) -> Result<RefMut<'a, T>, ProgramError> {
86+
clear_account(account)?;
87+
88+
{
89+
let mut account_header = load_account_as_mut::<pc_acc>(account)?;
90+
account_header.magic_ = PC_MAGIC;
91+
account_header.ver_ = version;
92+
account_header.type_ = T::ACCOUNT_TYPE;
93+
account_header.size_ = T::INITIAL_SIZE;
94+
}
95+
96+
load_account_as_mut::<T>(account)
97+
}

program/rust/src/rust_oracle.rs

Lines changed: 20 additions & 145 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
use std::borrow::BorrowMut;
2-
use std::cell::RefMut;
31
use std::mem::{
42
size_of,
53
size_of_val,
@@ -34,33 +32,42 @@ use crate::c_oracle_header::{
3432
cmd_init_price_t,
3533
cmd_set_min_pub_t,
3634
cmd_upd_product_t,
37-
pc_acc,
3835
pc_ema_t,
3936
pc_map_table_t,
4037
pc_price_comp,
4138
pc_price_info_t,
4239
pc_price_t,
4340
pc_prod_t,
4441
pc_pub_key_t,
45-
PythAccount,
4642
PC_COMP_SIZE,
47-
PC_MAGIC,
4843
PC_MAP_TABLE_SIZE,
49-
PC_MAX_NUM_DECIMALS,
5044
PC_PROD_ACC_SIZE,
5145
PC_PTYPE_UNKNOWN,
5246
PC_VERSION,
5347
SUCCESSFULLY_UPDATED_AGGREGATE,
5448
};
5549
use crate::deserialize::{
50+
initialize_pyth_account_checked, /* TODO: This has a confusingly similar name to a Solana
51+
* sdk function */
5652
load,
57-
load_account_as,
5853
load_account_as_mut,
54+
load_checked,
5955
};
6056
use crate::error::OracleResult;
6157
use crate::OracleError;
6258

63-
use crate::utils::pyth_assert;
59+
use crate::utils::{
60+
check_exponent_range,
61+
check_valid_fresh_account,
62+
check_valid_funding_account,
63+
check_valid_signable_account,
64+
pubkey_assign,
65+
pubkey_equal,
66+
pubkey_is_zero,
67+
pyth_assert,
68+
read_pc_str_t,
69+
try_convert,
70+
};
6471

6572
use super::c_entrypoint_wrapper;
6673
const PRICE_T_SIZE: usize = size_of::<pc_price_t>();
@@ -187,7 +194,7 @@ pub fn init_mapping(
187194

188195
// Initialize by setting to zero again (just in case) and populating the account header
189196
let hdr = load::<cmd_hdr_t>(instruction_data)?;
190-
initialize_checked::<pc_map_table_t>(fresh_mapping_account, hdr.ver_)?;
197+
initialize_pyth_account_checked::<pc_map_table_t>(fresh_mapping_account, hdr.ver_)?;
191198

192199
Ok(SUCCESS)
193200
}
@@ -214,7 +221,7 @@ pub fn add_mapping(
214221
ProgramError::InvalidArgument,
215222
)?;
216223

217-
initialize_checked::<pc_map_table_t>(next_mapping, hdr.ver_)?;
224+
initialize_pyth_account_checked::<pc_map_table_t>(next_mapping, hdr.ver_)?;
218225
pubkey_assign(&mut cur_mapping.next_, &next_mapping.key.to_bytes());
219226

220227
Ok(SUCCESS)
@@ -250,7 +257,8 @@ pub fn add_price(
250257

251258
let mut product_data = load_checked::<pc_prod_t>(product_account, cmd_args.ver_)?;
252259

253-
let mut price_data = initialize_checked::<pc_price_t>(price_account, cmd_args.ver_)?;
260+
let mut price_data =
261+
initialize_pyth_account_checked::<pc_price_t>(price_account, cmd_args.ver_)?;
254262
price_data.expo_ = cmd_args.expo_;
255263
price_data.ptype_ = cmd_args.ptype_;
256264
pubkey_assign(&mut price_data.prod_, &product_account.key.to_bytes());
@@ -450,7 +458,7 @@ pub fn add_product(
450458
ProgramError::InvalidArgument,
451459
)?;
452460

453-
initialize_checked::<pc_prod_t>(new_product_account, hdr.ver_)?;
461+
initialize_pyth_account_checked::<pc_prod_t>(new_product_account, hdr.ver_)?;
454462

455463
let current_index: usize = try_convert(mapping_data.num_)?;
456464
pubkey_assign(
@@ -550,136 +558,3 @@ pub fn set_min_pub(
550558

551559
Ok(SUCCESS)
552560
}
553-
554-
fn valid_funding_account(account: &AccountInfo) -> bool {
555-
account.is_signer && account.is_writable
556-
}
557-
558-
fn check_valid_funding_account(account: &AccountInfo) -> Result<(), ProgramError> {
559-
pyth_assert(
560-
valid_funding_account(account),
561-
OracleError::InvalidFundingAccount.into(),
562-
)
563-
}
564-
565-
fn valid_signable_account(program_id: &Pubkey, account: &AccountInfo, minimum_size: usize) -> bool {
566-
account.is_signer
567-
&& account.is_writable
568-
&& account.owner == program_id
569-
&& account.data_len() >= minimum_size
570-
&& Rent::default().is_exempt(account.lamports(), account.data_len())
571-
}
572-
573-
fn check_valid_signable_account(
574-
program_id: &Pubkey,
575-
account: &AccountInfo,
576-
minimum_size: usize,
577-
) -> Result<(), ProgramError> {
578-
pyth_assert(
579-
valid_signable_account(program_id, account, minimum_size),
580-
OracleError::InvalidSignableAccount.into(),
581-
)
582-
}
583-
584-
/// Returns `true` if the `account` is fresh, i.e., its data can be overwritten.
585-
/// Use this check to prevent accidentally overwriting accounts whose data is already populated.
586-
fn valid_fresh_account(account: &AccountInfo) -> bool {
587-
let pyth_acc = load_account_as::<pc_acc>(account);
588-
match pyth_acc {
589-
Ok(pyth_acc) => pyth_acc.magic_ == 0 && pyth_acc.ver_ == 0,
590-
Err(_) => false,
591-
}
592-
}
593-
594-
fn check_valid_fresh_account(account: &AccountInfo) -> Result<(), ProgramError> {
595-
pyth_assert(
596-
valid_fresh_account(account),
597-
OracleError::InvalidFreshAccount.into(),
598-
)
599-
}
600-
601-
// Check that an exponent is within the range of permitted exponents for price accounts.
602-
fn check_exponent_range(expo: i32) -> Result<(), ProgramError> {
603-
pyth_assert(
604-
expo >= -(PC_MAX_NUM_DECIMALS as i32) && expo <= PC_MAX_NUM_DECIMALS as i32,
605-
ProgramError::InvalidArgument,
606-
)
607-
}
608-
609-
/// Sets the data of account to all-zero
610-
pub fn clear_account(account: &AccountInfo) -> Result<(), ProgramError> {
611-
let mut data = account
612-
.try_borrow_mut_data()
613-
.map_err(|_| ProgramError::InvalidArgument)?;
614-
let length = data.len();
615-
sol_memset(data.borrow_mut(), 0, length);
616-
Ok(())
617-
}
618-
619-
pub fn load_checked<'a, T: PythAccount>(
620-
account: &'a AccountInfo,
621-
version: u32,
622-
) -> Result<RefMut<'a, T>, ProgramError> {
623-
{
624-
let account_header = load_account_as::<pc_acc>(account)?;
625-
pyth_assert(
626-
account_header.magic_ == PC_MAGIC
627-
&& account_header.ver_ == version
628-
&& account_header.type_ == T::ACCOUNT_TYPE,
629-
ProgramError::InvalidArgument,
630-
)?;
631-
}
632-
633-
load_account_as_mut::<T>(account)
634-
}
635-
636-
pub fn initialize_checked<'a, T: PythAccount>(
637-
account: &'a AccountInfo,
638-
version: u32,
639-
) -> Result<RefMut<'a, T>, ProgramError> {
640-
clear_account(account)?;
641-
642-
{
643-
let mut account_header = load_account_as_mut::<pc_acc>(account)?;
644-
account_header.magic_ = PC_MAGIC;
645-
account_header.ver_ = version;
646-
account_header.type_ = T::ACCOUNT_TYPE;
647-
account_header.size_ = T::INITIAL_SIZE;
648-
}
649-
650-
load_account_as_mut::<T>(account)
651-
}
652-
653-
// Assign pubkey bytes from source to target, fails if source is not 32 bytes
654-
pub fn pubkey_assign(target: &mut pc_pub_key_t, source: &[u8]) {
655-
unsafe { target.k1_.copy_from_slice(source) }
656-
}
657-
658-
pub fn pubkey_is_zero(key: &pc_pub_key_t) -> bool {
659-
return unsafe { key.k8_.iter().all(|x| *x == 0) };
660-
}
661-
662-
pub fn pubkey_equal(target: &pc_pub_key_t, source: &[u8]) -> bool {
663-
unsafe { target.k1_ == *source }
664-
}
665-
666-
/// Convert `x: T` into a `U`, returning the appropriate `OracleError` if the conversion fails.
667-
pub fn try_convert<T, U: TryFrom<T>>(x: T) -> Result<U, OracleError> {
668-
// Note: the error here assumes we're only applying this function to integers right now.
669-
U::try_from(x).map_err(|_| OracleError::IntegerCastingError)
670-
}
671-
672-
/// Read a `pc_str_t` from the beginning of `source`. Returns a slice of `source` containing
673-
/// the bytes of the `pc_str_t`.
674-
pub fn read_pc_str_t(source: &[u8]) -> Result<&[u8], ProgramError> {
675-
if source.is_empty() {
676-
Err(ProgramError::InvalidArgument)
677-
} else {
678-
let tag_len: usize = try_convert(source[0])?;
679-
if tag_len + 1 > source.len() {
680-
Err(ProgramError::InvalidArgument)
681-
} else {
682-
Ok(&source[..(1 + tag_len)])
683-
}
684-
}
685-
}

program/rust/src/tests/test_add_mapping.rs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,20 @@ use crate::c_oracle_header::{
66
PC_MAP_TABLE_SIZE,
77
PC_VERSION,
88
};
9-
use crate::deserialize::load_account_as_mut;
10-
use crate::rust_oracle::{
11-
add_mapping,
12-
clear_account,
13-
initialize_checked,
9+
use crate::deserialize::{
10+
initialize_pyth_account_checked,
11+
load_account_as_mut,
1412
load_checked,
13+
};
14+
15+
use crate::rust_oracle::add_mapping;
16+
use crate::tests::test_utils::AccountSetup;
17+
use crate::utils::{
18+
clear_account,
1519
pubkey_assign,
1620
pubkey_equal,
1721
pubkey_is_zero,
1822
};
19-
use crate::tests::test_utils::AccountSetup;
2023
use bytemuck::bytes_of;
2124
use solana_program::program_error::ProgramError;
2225
use solana_program::pubkey::Pubkey;
@@ -36,7 +39,7 @@ fn test_add_mapping() {
3639

3740
let mut curr_mapping_setup = AccountSetup::new::<pc_map_table_t>(&program_id);
3841
let cur_mapping = curr_mapping_setup.to_account_info();
39-
initialize_checked::<pc_map_table_t>(&cur_mapping, PC_VERSION).unwrap();
42+
initialize_pyth_account_checked::<pc_map_table_t>(&cur_mapping, PC_VERSION).unwrap();
4043

4144
let mut next_mapping_setup = AccountSetup::new::<pc_map_table_t>(&program_id);
4245
let next_mapping = next_mapping_setup.to_account_info();

program/rust/src/tests/test_add_price.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,16 @@ use crate::c_oracle_header::{
1414
pc_prod_t,
1515
PC_VERSION,
1616
};
17+
use crate::deserialize::{
18+
initialize_pyth_account_checked,
19+
load_checked,
20+
};
1721
use crate::rust_oracle::{
1822
add_price,
1923
add_product,
24+
};
25+
use crate::utils::{
2026
clear_account,
21-
initialize_checked,
22-
load_checked,
2327
pubkey_equal,
2428
pubkey_is_zero,
2529
};
@@ -47,7 +51,7 @@ fn test_add_price() {
4751

4852
let mut mapping_setup = AccountSetup::new::<pc_map_table_t>(&program_id);
4953
let mapping_account = mapping_setup.to_account_info();
50-
initialize_checked::<pc_map_table_t>(&mapping_account, PC_VERSION).unwrap();
54+
initialize_pyth_account_checked::<pc_map_table_t>(&mapping_account, PC_VERSION).unwrap();
5155

5256
let mut product_setup = AccountSetup::new::<pc_prod_t>(&program_id);
5357
let product_account = product_setup.to_account_info();

program/rust/src/tests/test_add_product.rs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,18 @@ use crate::c_oracle_header::{
2121
PC_PROD_ACC_SIZE,
2222
PC_VERSION,
2323
};
24-
use crate::deserialize::load_account_as;
25-
use crate::rust_oracle::{
26-
add_product,
27-
clear_account,
28-
initialize_checked,
24+
use crate::deserialize::{
25+
initialize_pyth_account_checked,
26+
load_account_as,
2927
load_checked,
28+
};
29+
use crate::utils::{
30+
clear_account,
3031
pubkey_equal,
3132
};
3233

34+
use crate::rust_oracle::add_product;
35+
3336
#[test]
3437
fn test_add_product() {
3538
let hdr = cmd_hdr_t {
@@ -45,7 +48,7 @@ fn test_add_product() {
4548

4649
let mut mapping_setup = AccountSetup::new::<pc_map_table_t>(&program_id);
4750
let mapping_account = mapping_setup.to_account_info();
48-
initialize_checked::<pc_map_table_t>(&mapping_account, PC_VERSION).unwrap();
51+
initialize_pyth_account_checked::<pc_map_table_t>(&mapping_account, PC_VERSION).unwrap();
4952

5053
let mut product_setup = AccountSetup::new::<pc_prod_t>(&program_id);
5154
let product_account = product_setup.to_account_info();
@@ -129,7 +132,7 @@ fn test_add_product() {
129132

130133
// test fill up of mapping table
131134
clear_account(&mapping_account).unwrap();
132-
initialize_checked::<pc_map_table_t>(&mapping_account, PC_VERSION).unwrap();
135+
initialize_pyth_account_checked::<pc_map_table_t>(&mapping_account, PC_VERSION).unwrap();
133136

134137
for i in 0..PC_MAP_TABLE_SIZE {
135138
clear_account(&product_account).unwrap();

0 commit comments

Comments
 (0)