Skip to content

Commit b515715

Browse files
authored
Rust/add publisher (#229)
* Base test, clippy stopped working * Add publisher * Remove comment * Restore test * Copy syntax from add product * Add checker functions * Rust oracle
1 parent 29f9976 commit b515715

File tree

3 files changed

+111
-7
lines changed

3 files changed

+111
-7
lines changed

program/rust/src/c_oracle_header.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,10 +84,28 @@ unsafe impl Zeroable for cmd_add_price_t {
8484
unsafe impl Pod for cmd_add_price_t {
8585
}
8686

87+
#[cfg(target_endian = "little")]
88+
unsafe impl Zeroable for cmd_add_publisher_t {
89+
}
90+
91+
#[cfg(target_endian = "little")]
92+
unsafe impl Pod for cmd_add_publisher_t {
93+
}
94+
95+
8796
#[cfg(target_endian = "little")]
8897
unsafe impl Zeroable for pc_pub_key_t {
8998
}
9099

91100
#[cfg(target_endian = "little")]
92101
unsafe impl Pod for pc_pub_key_t {
93102
}
103+
104+
105+
#[cfg(target_endian = "little")]
106+
unsafe impl Zeroable for pc_price_comp_t {
107+
}
108+
109+
#[cfg(target_endian = "little")]
110+
unsafe impl Pod for pc_price_comp_t {
111+
}

program/rust/src/processor.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use crate::c_oracle_header::{
1010
command_t_e_cmd_add_mapping,
1111
command_t_e_cmd_add_price,
1212
command_t_e_cmd_add_product,
13+
command_t_e_cmd_add_publisher,
1314
command_t_e_cmd_agg_price,
1415
command_t_e_cmd_init_mapping,
1516
command_t_e_cmd_upd_account_version,
@@ -25,6 +26,7 @@ use crate::rust_oracle::{
2526
add_mapping,
2627
add_price,
2728
add_product,
29+
add_publisher,
2830
init_mapping,
2931
update_price,
3032
update_version,
@@ -65,6 +67,7 @@ pub fn process_instruction(
6567
command_t_e_cmd_add_price => add_price(program_id, accounts, instruction_data),
6668
command_t_e_cmd_init_mapping => init_mapping(program_id, accounts, instruction_data),
6769
command_t_e_cmd_add_mapping => add_mapping(program_id, accounts, instruction_data),
70+
command_t_e_cmd_add_publisher => add_publisher(program_id, accounts, instruction_data),
6871
command_t_e_cmd_add_product => add_product(program_id, accounts, instruction_data),
6972
_ => c_entrypoint_wrapper(input),
7073
}

program/rust/src/rust_oracle.rs

Lines changed: 90 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -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

1618
use solana_program::account_info::AccountInfo;
1719
use solana_program::entrypoint::SUCCESS;
@@ -22,15 +24,18 @@ use solana_program::rent::Rent;
2224

2325
use 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+
}
173230
pub 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
339415
pub 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.
344427
fn 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

Comments
 (0)