@@ -10,8 +10,10 @@ use crate::deserialize::{
10
10
load_account_as,
11
11
load_account_as_mut,
12
12
} ;
13
-
14
- use bytemuck:: bytes_of;
13
+ use bytemuck:: {
14
+ bytes_of,
15
+ bytes_of_mut,
16
+ } ;
15
17
16
18
use solana_program:: account_info:: AccountInfo ;
17
19
use solana_program:: entrypoint:: SUCCESS ;
@@ -22,15 +24,18 @@ use solana_program::rent::Rent;
22
24
23
25
use crate :: c_oracle_header:: {
24
26
cmd_add_price_t,
27
+ cmd_add_publisher_t,
25
28
cmd_hdr_t,
26
29
pc_acc,
27
30
pc_map_table_t,
31
+ pc_price_comp,
28
32
pc_price_t,
29
33
pc_prod_t,
30
34
pc_pub_key_t,
31
35
PC_ACCTYPE_MAPPING ,
32
36
PC_ACCTYPE_PRICE ,
33
37
PC_ACCTYPE_PRODUCT ,
38
+ PC_COMP_SIZE ,
34
39
PC_MAGIC ,
35
40
PC_MAP_TABLE_SIZE ,
36
41
PC_MAX_NUM_DECIMALS ,
@@ -170,6 +175,58 @@ pub fn add_price(
170
175
Ok ( SUCCESS )
171
176
}
172
177
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
+ }
173
230
pub fn add_product (
174
231
program_id : & Pubkey ,
175
232
accounts : & [ AccountInfo ] ,
@@ -200,11 +257,10 @@ pub fn add_product(
200
257
initialize_product_account ( new_product_account, hdr. ver_ ) ?;
201
258
202
259
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
+ ) ;
208
264
mapping_data. num_ += 1 ;
209
265
mapping_data. size_ =
210
266
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>(
335
391
Ok ( product_data)
336
392
}
337
393
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
+
338
414
// Assign pubkey bytes from source to target, fails if source is not 32 bytes
339
415
pub fn pubkey_assign ( target : & mut pc_pub_key_t , source : & [ u8 ] ) {
340
416
unsafe { target. k1_ . copy_from_slice ( source) }
341
417
}
342
418
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
+ }
343
426
/// Convert `x: T` into a `U`, returning the appropriate `OracleError` if the conversion fails.
344
427
fn try_convert < T , U : TryFrom < T > > ( x : T ) -> Result < U , OracleError > {
345
428
// Note: the error here assumes we're only applying this function to integers right now.
0 commit comments