@@ -5,27 +5,25 @@ use std::mem::{
5
5
size_of_val,
6
6
} ;
7
7
8
- use crate :: deserialize:: {
9
- load,
10
- load_account_as,
11
- load_account_as_mut,
12
- } ;
13
8
use bytemuck:: {
14
9
bytes_of,
15
10
bytes_of_mut,
16
11
} ;
17
-
18
12
use solana_program:: account_info:: AccountInfo ;
19
13
use solana_program:: entrypoint:: SUCCESS ;
20
14
use solana_program:: program_error:: ProgramError ;
21
- use solana_program:: program_memory:: sol_memset;
15
+ use solana_program:: program_memory:: {
16
+ sol_memcpy,
17
+ sol_memset,
18
+ } ;
22
19
use solana_program:: pubkey:: Pubkey ;
23
20
use solana_program:: rent:: Rent ;
24
21
25
22
use crate :: c_oracle_header:: {
26
23
cmd_add_price_t,
27
24
cmd_add_publisher_t,
28
25
cmd_hdr_t,
26
+ cmd_upd_product_t,
29
27
pc_acc,
30
28
pc_map_table_t,
31
29
pc_price_comp,
@@ -40,10 +38,14 @@ use crate::c_oracle_header::{
40
38
PC_PROD_ACC_SIZE ,
41
39
PC_PTYPE_UNKNOWN ,
42
40
} ;
41
+ use crate :: deserialize:: {
42
+ load,
43
+ load_account_as,
44
+ load_account_as_mut,
45
+ } ;
43
46
use crate :: error:: OracleResult ;
44
- use crate :: OracleError ;
45
-
46
47
use crate :: utils:: pyth_assert;
48
+ use crate :: OracleError ;
47
49
48
50
use super :: c_entrypoint_wrapper;
49
51
@@ -260,6 +262,66 @@ pub fn add_product(
260
262
Ok ( SUCCESS )
261
263
}
262
264
265
+ /// Update the metadata associated with a product, overwriting any existing metadata.
266
+ /// The metadata is provided as a list of key-value pairs at the end of the `instruction_data`.
267
+ pub fn upd_product (
268
+ program_id : & Pubkey ,
269
+ accounts : & [ AccountInfo ] ,
270
+ instruction_data : & [ u8 ] ,
271
+ ) -> OracleResult {
272
+ let [ funding_account, product_account] = match accounts {
273
+ [ x, y] => Ok ( [ x, y] ) ,
274
+ _ => Err ( ProgramError :: InvalidArgument ) ,
275
+ } ?;
276
+
277
+ check_valid_funding_account ( funding_account) ?;
278
+ check_valid_signable_account ( program_id, product_account, try_convert ( PC_PROD_ACC_SIZE ) ?) ?;
279
+
280
+ let hdr = load :: < cmd_hdr_t > ( instruction_data) ?;
281
+ {
282
+ // Validate that product_account contains the appropriate account header
283
+ let mut _product_data = load_checked :: < pc_prod_t > ( product_account, hdr. ver_ ) ?;
284
+ }
285
+
286
+ pyth_assert (
287
+ instruction_data. len ( ) >= size_of :: < cmd_upd_product_t > ( ) ,
288
+ ProgramError :: InvalidInstructionData ,
289
+ ) ?;
290
+ let new_data_len = instruction_data. len ( ) - size_of :: < cmd_upd_product_t > ( ) ;
291
+ let max_data_len = try_convert :: < _ , usize > ( PC_PROD_ACC_SIZE ) ? - size_of :: < pc_prod_t > ( ) ;
292
+ pyth_assert ( new_data_len <= max_data_len, ProgramError :: InvalidArgument ) ?;
293
+
294
+ let new_data = & instruction_data[ size_of :: < cmd_upd_product_t > ( ) ..instruction_data. len ( ) ] ;
295
+ let mut idx = 0 ;
296
+ // new_data must be a list of key-value pairs, both of which are instances of pc_str_t.
297
+ // Try reading the key-value pairs to validate that new_data is properly formatted.
298
+ while idx < new_data. len ( ) {
299
+ let key = read_pc_str_t ( & new_data[ idx..] ) ?;
300
+ idx += key. len ( ) ;
301
+ let value = read_pc_str_t ( & new_data[ idx..] ) ?;
302
+ idx += value. len ( ) ;
303
+ }
304
+
305
+ // This assertion shouldn't ever fail, but be defensive.
306
+ pyth_assert ( idx == new_data. len ( ) , ProgramError :: InvalidArgument ) ?;
307
+
308
+ {
309
+ let mut data = product_account. try_borrow_mut_data ( ) ?;
310
+ // Note that this memcpy doesn't necessarily overwrite all existing data in the account.
311
+ // This case is handled by updating the .size_ field below.
312
+ sol_memcpy (
313
+ & mut data[ size_of :: < pc_prod_t > ( ) ..] ,
314
+ new_data,
315
+ new_data. len ( ) ,
316
+ ) ;
317
+ }
318
+
319
+ let mut product_data = load_checked :: < pc_prod_t > ( product_account, hdr. ver_ ) ?;
320
+ product_data. size_ = try_convert ( size_of :: < pc_prod_t > ( ) + new_data. len ( ) ) ?;
321
+
322
+ Ok ( SUCCESS )
323
+ }
324
+
263
325
fn valid_funding_account ( account : & AccountInfo ) -> bool {
264
326
account. is_signer && account. is_writable
265
327
}
@@ -362,7 +424,22 @@ pub fn pubkey_equal(target: &pc_pub_key_t, source: &[u8]) -> bool {
362
424
}
363
425
364
426
/// Convert `x: T` into a `U`, returning the appropriate `OracleError` if the conversion fails.
365
- fn try_convert < T , U : TryFrom < T > > ( x : T ) -> Result < U , OracleError > {
427
+ pub fn try_convert < T , U : TryFrom < T > > ( x : T ) -> Result < U , OracleError > {
366
428
// Note: the error here assumes we're only applying this function to integers right now.
367
429
U :: try_from ( x) . map_err ( |_| OracleError :: IntegerCastingError )
368
430
}
431
+
432
+ /// Read a `pc_str_t` from the beginning of `source`. Returns a slice of `source` containing
433
+ /// the bytes of the `pc_str_t`.
434
+ pub fn read_pc_str_t ( source : & [ u8 ] ) -> Result < & [ u8 ] , ProgramError > {
435
+ if source. is_empty ( ) {
436
+ Err ( ProgramError :: InvalidArgument )
437
+ } else {
438
+ let tag_len: usize = try_convert ( source[ 0 ] ) ?;
439
+ if tag_len + 1 > source. len ( ) {
440
+ Err ( ProgramError :: InvalidArgument )
441
+ } else {
442
+ Ok ( & source[ ..( 1 + tag_len) ] )
443
+ }
444
+ }
445
+ }
0 commit comments