@@ -9,6 +9,7 @@ use std::mem::{
99} ;
1010
1111use bytemuck:: {
12+ bytes_of,
1213 try_from_bytes,
1314 try_from_bytes_mut,
1415 Pod ,
@@ -21,12 +22,21 @@ use solana_program::pubkey::Pubkey;
2122use solana_program:: rent:: Rent ;
2223
2324use crate :: c_oracle_header:: {
25+ cmd_add_price_t,
2426 cmd_hdr_t,
2527 pc_acc,
2628 pc_map_table_t,
29+ pc_price_t,
30+ pc_prod_t,
31+ pc_pub_key_t,
2732 PC_ACCTYPE_MAPPING ,
33+ PC_ACCTYPE_PRICE ,
34+ PC_ACCTYPE_PRODUCT ,
2835 PC_MAGIC ,
2936 PC_MAP_TABLE_SIZE ,
37+ PC_MAX_NUM_DECIMALS ,
38+ PC_PROD_ACC_SIZE ,
39+ PC_PTYPE_UNKNOWN ,
3040} ;
3141use crate :: error:: OracleResult ;
3242
@@ -110,13 +120,56 @@ pub fn add_mapping(
110120 ) ?;
111121
112122 initialize_mapping_account ( next_mapping, hdr. ver_ ) ?;
113- unsafe {
114- cur_mapping
115- . next_
116- . k1_
117- . copy_from_slice ( & next_mapping. key . to_bytes ( ) ) ;
123+ pubkey_assign ( & mut cur_mapping. next_ , & next_mapping. key . to_bytes ( ) ) ;
124+
125+ Ok ( SUCCESS )
126+ }
127+
128+ /// add a price account to a product account
129+ /// accounts[0] funding account [signer writable]
130+ /// accounts[1] product account to add the price account to [signer writable]
131+ /// accounts[2] newly created price account [signer writable]
132+ pub fn add_price (
133+ program_id : & Pubkey ,
134+ accounts : & [ AccountInfo ] ,
135+ instruction_data : & [ u8 ] ,
136+ ) -> OracleResult {
137+ let cmd_args = load :: < cmd_add_price_t > ( instruction_data) ?;
138+
139+ if cmd_args. expo_ > PC_MAX_NUM_DECIMALS as i32
140+ || cmd_args. expo_ < -( PC_MAX_NUM_DECIMALS as i32 )
141+ || cmd_args. ptype_ == PC_PTYPE_UNKNOWN
142+ {
143+ return Err ( ProgramError :: InvalidArgument ) ;
118144 }
119145
146+ let [ _funding_account, product_account, price_account] = match accounts {
147+ [ x, y, z]
148+ if valid_funding_account ( x)
149+ && valid_signable_account ( program_id, y, PC_PROD_ACC_SIZE as usize )
150+ && valid_signable_account ( program_id, z, size_of :: < pc_price_t > ( ) )
151+ && valid_fresh_account ( z) =>
152+ {
153+ Ok ( [ x, y, z] )
154+ }
155+ _ => Err ( ProgramError :: InvalidArgument ) ,
156+ } ?;
157+
158+ let mut product_data = load_product_account_mut ( product_account, cmd_args. ver_ ) ?;
159+
160+ clear_account ( price_account) ?;
161+
162+ let mut price_data = load_account_as_mut :: < pc_price_t > ( price_account) ?;
163+ price_data. magic_ = PC_MAGIC ;
164+ price_data. ver_ = cmd_args. ver_ ;
165+ price_data. type_ = PC_ACCTYPE_PRICE ;
166+ price_data. size_ = ( size_of :: < pc_price_t > ( ) - size_of_val ( & price_data. comp_ ) ) as u32 ;
167+ price_data. expo_ = cmd_args. expo_ ;
168+ price_data. ptype_ = cmd_args. ptype_ ;
169+ pubkey_assign ( & mut price_data. prod_ , & product_account. key . to_bytes ( ) ) ;
170+ pubkey_assign ( & mut price_data. next_ , bytes_of ( & product_data. px_acc_ ) ) ;
171+ pubkey_assign ( & mut product_data. px_acc_ , & price_account. key . to_bytes ( ) ) ;
172+
120173 Ok ( SUCCESS )
121174}
122175
@@ -154,7 +207,11 @@ pub fn clear_account(account: &AccountInfo) -> Result<(), ProgramError> {
154207
155208/// Interpret the bytes in `data` as a value of type `T`
156209fn load < T : Pod > ( data : & [ u8 ] ) -> Result < & T , ProgramError > {
157- try_from_bytes ( & data[ 0 ..size_of :: < T > ( ) ] ) . map_err ( |_| ProgramError :: InvalidArgument )
210+ try_from_bytes (
211+ data. get ( 0 ..size_of :: < T > ( ) )
212+ . ok_or ( ProgramError :: InvalidArgument ) ?,
213+ )
214+ . map_err ( |_| ProgramError :: InvalidArgument )
158215}
159216
160217/// Interpret the bytes in `data` as a mutable value of type `T`
@@ -191,17 +248,16 @@ fn load_mapping_account_mut<'a>(
191248 account : & ' a AccountInfo ,
192249 expected_version : u32 ,
193250) -> Result < RefMut < ' a , pc_map_table_t > , ProgramError > {
194- let mapping_account_ref = load_account_as_mut :: < pc_map_table_t > ( account) ?;
195- let mapping_account = * mapping_account_ref;
251+ let mapping_data = load_account_as_mut :: < pc_map_table_t > ( account) ?;
196252
197253 pyth_assert (
198- mapping_account . magic_ == PC_MAGIC
199- && mapping_account . ver_ == expected_version
200- && mapping_account . type_ == PC_ACCTYPE_MAPPING ,
254+ mapping_data . magic_ == PC_MAGIC
255+ && mapping_data . ver_ == expected_version
256+ && mapping_data . type_ == PC_ACCTYPE_MAPPING ,
201257 ProgramError :: InvalidArgument ,
202258 ) ?;
203259
204- Ok ( mapping_account_ref )
260+ Ok ( mapping_data )
205261}
206262
207263/// Initialize account as a new mapping account. This function will zero out any existing data in
@@ -218,3 +274,27 @@ fn initialize_mapping_account(account: &AccountInfo, version: u32) -> Result<(),
218274
219275 Ok ( ( ) )
220276}
277+
278+ /// Mutably borrow the data in `account` as a product account, validating that the account
279+ /// is properly formatted. Any mutations to the returned value will be reflected in the
280+ /// account data. Use this to read already-initialized accounts.
281+ fn load_product_account_mut < ' a > (
282+ account : & ' a AccountInfo ,
283+ expected_version : u32 ,
284+ ) -> Result < RefMut < ' a , pc_prod_t > , ProgramError > {
285+ let product_data = load_account_as_mut :: < pc_prod_t > ( account) ?;
286+
287+ pyth_assert (
288+ product_data. magic_ == PC_MAGIC
289+ && product_data. ver_ == expected_version
290+ && product_data. type_ == PC_ACCTYPE_PRODUCT ,
291+ ProgramError :: InvalidArgument ,
292+ ) ?;
293+
294+ Ok ( product_data)
295+ }
296+
297+ // Assign pubkey bytes from source to target, fails if source is not 32 bytes
298+ fn pubkey_assign ( target : & mut pc_pub_key_t , source : & [ u8 ] ) {
299+ unsafe { target. k1_ . copy_from_slice ( source) }
300+ }
0 commit comments