@@ -10,8 +10,10 @@ use crate::deserialize::{
1010 load_account_as,
1111 load_account_as_mut,
1212} ;
13-
14- use bytemuck:: bytes_of;
13+ use bytemuck:: {
14+ bytes_of,
15+ bytes_of_mut,
16+ } ;
1517
1618use solana_program:: account_info:: AccountInfo ;
1719use solana_program:: entrypoint:: SUCCESS ;
@@ -22,15 +24,18 @@ use solana_program::rent::Rent;
2224
2325use crate :: c_oracle_header:: {
2426 cmd_add_price_t,
27+ cmd_add_publisher_t,
2528 cmd_hdr_t,
2629 pc_acc,
2730 pc_map_table_t,
31+ pc_price_comp,
2832 pc_price_t,
2933 pc_prod_t,
3034 pc_pub_key_t,
3135 PC_ACCTYPE_MAPPING ,
3236 PC_ACCTYPE_PRICE ,
3337 PC_ACCTYPE_PRODUCT ,
38+ PC_COMP_SIZE ,
3439 PC_MAGIC ,
3540 PC_MAP_TABLE_SIZE ,
3641 PC_MAX_NUM_DECIMALS ,
@@ -170,6 +175,58 @@ pub fn add_price(
170175 Ok ( SUCCESS )
171176}
172177
178+ /// add a publisher to a price account
179+ /// accounts[0] funding account [signer writable]
180+ /// accounts[1] price account to add the publisher to [signer writable]
181+ pub fn add_publisher (
182+ program_id : & Pubkey ,
183+ accounts : & [ AccountInfo ] ,
184+ instruction_data : & [ u8 ] ,
185+ ) -> OracleResult {
186+ let cmd_args = load :: < cmd_add_publisher_t > ( instruction_data) ?;
187+
188+ pyth_assert (
189+ instruction_data. len ( ) == size_of :: < cmd_add_publisher_t > ( )
190+ && !pubkey_is_zero ( & cmd_args. pub_ ) ,
191+ ProgramError :: InvalidArgument ,
192+ ) ?;
193+
194+ let [ funding_account, price_account] = match accounts {
195+ [ x, y] => Ok ( [ x, y] ) ,
196+ _ => Err ( ProgramError :: InvalidArgument ) ,
197+ } ?;
198+
199+ check_valid_funding_account ( funding_account) ?;
200+ check_valid_signable_account ( program_id, price_account, size_of :: < pc_price_t > ( ) ) ?;
201+
202+ let mut price_data = load_price_account_mut ( price_account, cmd_args. ver_ ) ?;
203+
204+ if price_data. num_ >= PC_COMP_SIZE {
205+ return Err ( ProgramError :: InvalidArgument ) ;
206+ }
207+
208+ for i in 0 ..( price_data. num_ as usize ) {
209+ if pubkey_equal ( & cmd_args. pub_ , & price_data. comp_ [ i] . pub_ ) {
210+ return Err ( ProgramError :: InvalidArgument ) ;
211+ }
212+ }
213+
214+ let current_index: usize = try_convert ( price_data. num_ ) ?;
215+ sol_memset (
216+ bytes_of_mut ( & mut price_data. comp_ [ current_index] ) ,
217+ 0 ,
218+ size_of :: < pc_price_comp > ( ) ,
219+ ) ;
220+ pubkey_assign (
221+ & mut price_data. comp_ [ current_index] . pub_ ,
222+ bytes_of ( & cmd_args. pub_ ) ,
223+ ) ;
224+ price_data. num_ += 1 ;
225+ price_data. size_ =
226+ try_convert :: < _ , u32 > ( size_of :: < pc_price_t > ( ) - size_of_val ( & price_data. comp_ ) ) ?
227+ + price_data. num_ * try_convert :: < _ , u32 > ( size_of :: < pc_price_comp > ( ) ) ?;
228+ Ok ( SUCCESS )
229+ }
173230pub fn add_product (
174231 program_id : & Pubkey ,
175232 accounts : & [ AccountInfo ] ,
@@ -200,11 +257,10 @@ pub fn add_product(
200257 initialize_product_account ( new_product_account, hdr. ver_ ) ?;
201258
202259 let current_index: usize = try_convert ( mapping_data. num_ ) ?;
203- unsafe {
204- mapping_data. prod_ [ current_index]
205- . k1_
206- . copy_from_slice ( & new_product_account. key . to_bytes ( ) )
207- }
260+ pubkey_assign (
261+ & mut mapping_data. prod_ [ current_index] ,
262+ bytes_of ( & new_product_account. key . to_bytes ( ) ) ,
263+ ) ;
208264 mapping_data. num_ += 1 ;
209265 mapping_data. size_ =
210266 try_convert :: < _ , u32 > ( size_of :: < pc_map_table_t > ( ) - size_of_val ( & mapping_data. prod_ ) ) ?
@@ -335,11 +391,38 @@ pub fn load_product_account_mut<'a>(
335391 Ok ( product_data)
336392}
337393
394+ /// Mutably borrow the data in `account` as a price account, validating that the account
395+ /// is properly formatted. Any mutations to the returned value will be reflected in the
396+ /// account data. Use this to read already-initialized accounts.
397+ fn load_price_account_mut < ' a > (
398+ account : & ' a AccountInfo ,
399+ expected_version : u32 ,
400+ ) -> Result < RefMut < ' a , pc_price_t > , ProgramError > {
401+ let price_data = load_account_as_mut :: < pc_price_t > ( account) ?;
402+
403+ pyth_assert (
404+ price_data. magic_ == PC_MAGIC
405+ && price_data. ver_ == expected_version
406+ && price_data. type_ == PC_ACCTYPE_PRICE ,
407+ ProgramError :: InvalidArgument ,
408+ ) ?;
409+
410+ Ok ( price_data)
411+ }
412+
413+
338414// Assign pubkey bytes from source to target, fails if source is not 32 bytes
339415pub fn pubkey_assign ( target : & mut pc_pub_key_t , source : & [ u8 ] ) {
340416 unsafe { target. k1_ . copy_from_slice ( source) }
341417}
342418
419+ fn pubkey_is_zero ( key : & pc_pub_key_t ) -> bool {
420+ return unsafe { key. k8_ . iter ( ) . all ( |x| * x == 0 ) } ;
421+ }
422+
423+ fn pubkey_equal ( key1 : & pc_pub_key_t , key2 : & pc_pub_key_t ) -> bool {
424+ return unsafe { key1. k1_ . iter ( ) . zip ( & key2. k1_ ) . all ( |( x, y) | * x == * y) } ;
425+ }
343426/// Convert `x: T` into a `U`, returning the appropriate `OracleError` if the conversion fails.
344427fn try_convert < T , U : TryFrom < T > > ( x : T ) -> Result < U , OracleError > {
345428 // Note: the error here assumes we're only applying this function to integers right now.
0 commit comments