Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions program/rust/src/c_oracle_header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,28 @@ unsafe impl Zeroable for cmd_add_price_t {
unsafe impl Pod for cmd_add_price_t {
}

#[cfg(target_endian = "little")]
unsafe impl Zeroable for cmd_add_publisher_t {
}

#[cfg(target_endian = "little")]
unsafe impl Pod for cmd_add_publisher_t {
}


#[cfg(target_endian = "little")]
unsafe impl Zeroable for pc_pub_key_t {
}

#[cfg(target_endian = "little")]
unsafe impl Pod for pc_pub_key_t {
}


#[cfg(target_endian = "little")]
unsafe impl Zeroable for pc_price_comp_t {
}

#[cfg(target_endian = "little")]
unsafe impl Pod for pc_price_comp_t {
}
3 changes: 3 additions & 0 deletions program/rust/src/processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use crate::c_oracle_header::{
command_t_e_cmd_add_mapping,
command_t_e_cmd_add_price,
command_t_e_cmd_add_product,
command_t_e_cmd_add_publisher,
command_t_e_cmd_agg_price,
command_t_e_cmd_init_mapping,
command_t_e_cmd_upd_account_version,
Expand All @@ -25,6 +26,7 @@ use crate::rust_oracle::{
add_mapping,
add_price,
add_product,
add_publisher,
init_mapping,
update_price,
update_version,
Expand Down Expand Up @@ -65,6 +67,7 @@ pub fn process_instruction(
command_t_e_cmd_add_price => add_price(program_id, accounts, instruction_data),
command_t_e_cmd_init_mapping => init_mapping(program_id, accounts, instruction_data),
command_t_e_cmd_add_mapping => add_mapping(program_id, accounts, instruction_data),
command_t_e_cmd_add_publisher => add_publisher(program_id, accounts, instruction_data),
command_t_e_cmd_add_product => add_product(program_id, accounts, instruction_data),
_ => c_entrypoint_wrapper(input),
}
Expand Down
97 changes: 90 additions & 7 deletions program/rust/src/rust_oracle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ use crate::deserialize::{
load_account_as,
load_account_as_mut,
};

use bytemuck::bytes_of;
use bytemuck::{
bytes_of,
bytes_of_mut,
};

use solana_program::account_info::AccountInfo;
use solana_program::entrypoint::SUCCESS;
Expand All @@ -22,15 +24,18 @@ use solana_program::rent::Rent;

use crate::c_oracle_header::{
cmd_add_price_t,
cmd_add_publisher_t,
cmd_hdr_t,
pc_acc,
pc_map_table_t,
pc_price_comp,
pc_price_t,
pc_prod_t,
pc_pub_key_t,
PC_ACCTYPE_MAPPING,
PC_ACCTYPE_PRICE,
PC_ACCTYPE_PRODUCT,
PC_COMP_SIZE,
PC_MAGIC,
PC_MAP_TABLE_SIZE,
PC_MAX_NUM_DECIMALS,
Expand Down Expand Up @@ -170,6 +175,58 @@ pub fn add_price(
Ok(SUCCESS)
}

/// add a publisher to a price account
/// accounts[0] funding account [signer writable]
/// accounts[1] price account to add the publisher to [signer writable]
pub fn add_publisher(
program_id: &Pubkey,
accounts: &[AccountInfo],
instruction_data: &[u8],
) -> OracleResult {
let cmd_args = load::<cmd_add_publisher_t>(instruction_data)?;

pyth_assert(
instruction_data.len() == size_of::<cmd_add_publisher_t>()
&& !pubkey_is_zero(&cmd_args.pub_),
ProgramError::InvalidArgument,
)?;

let [funding_account, price_account] = match accounts {
[x, y] => Ok([x, y]),
_ => Err(ProgramError::InvalidArgument),
}?;

check_valid_funding_account(funding_account)?;
check_valid_signable_account(program_id, price_account, size_of::<pc_price_t>())?;

let mut price_data = load_price_account_mut(price_account, cmd_args.ver_)?;

if price_data.num_ >= PC_COMP_SIZE {
return Err(ProgramError::InvalidArgument);
}

for i in 0..(price_data.num_ as usize) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(i'm going to look at whether we can override some of the field definitions in the struct so they're usize instead of u32 so we don't have to do these casts all the time)

if pubkey_equal(&cmd_args.pub_, &price_data.comp_[i].pub_) {
return Err(ProgramError::InvalidArgument);
}
}

let current_index: usize = try_convert(price_data.num_)?;
sol_memset(
bytes_of_mut(&mut price_data.comp_[current_index]),
0,
size_of::<pc_price_comp>(),
);
pubkey_assign(
&mut price_data.comp_[current_index].pub_,
bytes_of(&cmd_args.pub_),
);
price_data.num_ += 1;
price_data.size_ =
try_convert::<_, u32>(size_of::<pc_price_t>() - size_of_val(&price_data.comp_))?
+ price_data.num_ * try_convert::<_, u32>(size_of::<pc_price_comp>())?;
Ok(SUCCESS)
}
pub fn add_product(
program_id: &Pubkey,
accounts: &[AccountInfo],
Expand Down Expand Up @@ -200,11 +257,10 @@ pub fn add_product(
initialize_product_account(new_product_account, hdr.ver_)?;

let current_index: usize = try_convert(mapping_data.num_)?;
unsafe {
mapping_data.prod_[current_index]
.k1_
.copy_from_slice(&new_product_account.key.to_bytes())
}
pubkey_assign(
&mut mapping_data.prod_[current_index],
bytes_of(&new_product_account.key.to_bytes()),
);
mapping_data.num_ += 1;
mapping_data.size_ =
try_convert::<_, u32>(size_of::<pc_map_table_t>() - size_of_val(&mapping_data.prod_))?
Expand Down Expand Up @@ -335,11 +391,38 @@ pub fn load_product_account_mut<'a>(
Ok(product_data)
}

/// Mutably borrow the data in `account` as a price account, validating that the account
/// is properly formatted. Any mutations to the returned value will be reflected in the
/// account data. Use this to read already-initialized accounts.
fn load_price_account_mut<'a>(
account: &'a AccountInfo,
expected_version: u32,
) -> Result<RefMut<'a, pc_price_t>, ProgramError> {
let price_data = load_account_as_mut::<pc_price_t>(account)?;

pyth_assert(
price_data.magic_ == PC_MAGIC
&& price_data.ver_ == expected_version
&& price_data.type_ == PC_ACCTYPE_PRICE,
ProgramError::InvalidArgument,
)?;

Ok(price_data)
}


// Assign pubkey bytes from source to target, fails if source is not 32 bytes
pub fn pubkey_assign(target: &mut pc_pub_key_t, source: &[u8]) {
unsafe { target.k1_.copy_from_slice(source) }
}

fn pubkey_is_zero(key: &pc_pub_key_t) -> bool {
return unsafe { key.k8_.iter().all(|x| *x == 0) };
}

fn pubkey_equal(key1: &pc_pub_key_t, key2: &pc_pub_key_t) -> bool {
return unsafe { key1.k1_.iter().zip(&key2.k1_).all(|(x, y)| *x == *y) };
}
/// Convert `x: T` into a `U`, returning the appropriate `OracleError` if the conversion fails.
fn try_convert<T, U: TryFrom<T>>(x: T) -> Result<U, OracleError> {
// Note: the error here assumes we're only applying this function to integers right now.
Expand Down