Skip to content

Commit 608d2ed

Browse files
guibescosJayant Krishnamurthy
andauthored
Add price rust (#220)
* v1 * stuff * fix * stuff * maybe? * whoops * ok * blah * more constants * ok * blah * imlement * maybe * ok * ok * ok * ok * ok * ok * ok * ok * ok * ok * ok * ok * ok * ok * ok * ok * ok * ok * ok * ok * ok * ok * ok * ok * ok * ok * ok * delete instruction from c * format * add lifetime * ok * does this work * reformat * test clippy * hm * cleanup * fix defines * Fix version of bytemuck and clean import * Rust oracle * Fix double borrow * Finalize merge * Refactor derivs * Load checks length * Refactor load product * Pubkey assign * Comments * Fix pubkey assign Co-authored-by: Jayant Krishnamurthy <jkrishnamurthy@jumptrading.com>
1 parent 840b68f commit 608d2ed

File tree

3 files changed

+111
-12
lines changed

3 files changed

+111
-12
lines changed

program/rust/src/c_oracle_header.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,19 @@ unsafe impl Zeroable for cmd_hdr {
5555
#[cfg(target_endian = "little")]
5656
unsafe impl Pod for cmd_hdr {
5757
}
58+
59+
#[cfg(target_endian = "little")]
60+
unsafe impl Zeroable for cmd_add_price_t {
61+
}
62+
63+
#[cfg(target_endian = "little")]
64+
unsafe impl Pod for cmd_add_price_t {
65+
}
66+
67+
#[cfg(target_endian = "little")]
68+
unsafe impl Zeroable for pc_pub_key_t {
69+
}
70+
71+
#[cfg(target_endian = "little")]
72+
unsafe impl Pod for pc_pub_key_t {
73+
}

program/rust/src/processor.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use crate::c_entrypoint_wrapper;
99
use crate::c_oracle_header::{
1010
cmd_hdr,
1111
command_t_e_cmd_add_mapping,
12+
command_t_e_cmd_add_price,
1213
command_t_e_cmd_agg_price,
1314
command_t_e_cmd_init_mapping,
1415
command_t_e_cmd_upd_account_version,
@@ -22,6 +23,7 @@ use crate::error::{
2223
};
2324
use crate::rust_oracle::{
2425
add_mapping,
26+
add_price,
2527
init_mapping,
2628
update_price,
2729
update_version,
@@ -58,6 +60,7 @@ pub fn process_instruction(
5860
command_t_e_cmd_upd_account_version => {
5961
update_version(program_id, accounts, instruction_data)
6062
}
63+
command_t_e_cmd_add_price => add_price(program_id, accounts, instruction_data),
6164
command_t_e_cmd_init_mapping => init_mapping(program_id, accounts, instruction_data),
6265
command_t_e_cmd_add_mapping => add_mapping(program_id, accounts, instruction_data),
6366
_ => c_entrypoint_wrapper(input),

program/rust/src/rust_oracle.rs

Lines changed: 92 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use std::mem::{
99
};
1010

1111
use bytemuck::{
12+
bytes_of,
1213
try_from_bytes,
1314
try_from_bytes_mut,
1415
Pod,
@@ -21,12 +22,21 @@ use solana_program::pubkey::Pubkey;
2122
use solana_program::rent::Rent;
2223

2324
use crate::c_oracle_header::{
25+
cmd_add_price_t,
2426
cmd_hdr_t,
2527
pc_acc,
2628
pc_map_table_t,
29+
pc_price_t,
30+
pc_prod_t,
31+
pc_pub_key_t,
2732
PC_ACCTYPE_MAPPING,
33+
PC_ACCTYPE_PRICE,
34+
PC_ACCTYPE_PRODUCT,
2835
PC_MAGIC,
2936
PC_MAP_TABLE_SIZE,
37+
PC_MAX_NUM_DECIMALS,
38+
PC_PROD_ACC_SIZE,
39+
PC_PTYPE_UNKNOWN,
3040
};
3141
use crate::error::OracleResult;
3242

@@ -110,13 +120,56 @@ pub fn add_mapping(
110120
)?;
111121

112122
initialize_mapping_account(next_mapping, hdr.ver_)?;
113-
unsafe {
114-
cur_mapping
115-
.next_
116-
.k1_
117-
.copy_from_slice(&next_mapping.key.to_bytes());
123+
pubkey_assign(&mut cur_mapping.next_, &next_mapping.key.to_bytes());
124+
125+
Ok(SUCCESS)
126+
}
127+
128+
/// add a price account to a product account
129+
/// accounts[0] funding account [signer writable]
130+
/// accounts[1] product account to add the price account to [signer writable]
131+
/// accounts[2] newly created price account [signer writable]
132+
pub fn add_price(
133+
program_id: &Pubkey,
134+
accounts: &[AccountInfo],
135+
instruction_data: &[u8],
136+
) -> OracleResult {
137+
let cmd_args = load::<cmd_add_price_t>(instruction_data)?;
138+
139+
if cmd_args.expo_ > PC_MAX_NUM_DECIMALS as i32
140+
|| cmd_args.expo_ < -(PC_MAX_NUM_DECIMALS as i32)
141+
|| cmd_args.ptype_ == PC_PTYPE_UNKNOWN
142+
{
143+
return Err(ProgramError::InvalidArgument);
118144
}
119145

146+
let [_funding_account, product_account, price_account] = match accounts {
147+
[x, y, z]
148+
if valid_funding_account(x)
149+
&& valid_signable_account(program_id, y, PC_PROD_ACC_SIZE as usize)
150+
&& valid_signable_account(program_id, z, size_of::<pc_price_t>())
151+
&& valid_fresh_account(z) =>
152+
{
153+
Ok([x, y, z])
154+
}
155+
_ => Err(ProgramError::InvalidArgument),
156+
}?;
157+
158+
let mut product_data = load_product_account_mut(product_account, cmd_args.ver_)?;
159+
160+
clear_account(price_account)?;
161+
162+
let mut price_data = load_account_as_mut::<pc_price_t>(price_account)?;
163+
price_data.magic_ = PC_MAGIC;
164+
price_data.ver_ = cmd_args.ver_;
165+
price_data.type_ = PC_ACCTYPE_PRICE;
166+
price_data.size_ = (size_of::<pc_price_t>() - size_of_val(&price_data.comp_)) as u32;
167+
price_data.expo_ = cmd_args.expo_;
168+
price_data.ptype_ = cmd_args.ptype_;
169+
pubkey_assign(&mut price_data.prod_, &product_account.key.to_bytes());
170+
pubkey_assign(&mut price_data.next_, bytes_of(&product_data.px_acc_));
171+
pubkey_assign(&mut product_data.px_acc_, &price_account.key.to_bytes());
172+
120173
Ok(SUCCESS)
121174
}
122175

@@ -154,7 +207,11 @@ pub fn clear_account(account: &AccountInfo) -> Result<(), ProgramError> {
154207

155208
/// Interpret the bytes in `data` as a value of type `T`
156209
fn load<T: Pod>(data: &[u8]) -> Result<&T, ProgramError> {
157-
try_from_bytes(&data[0..size_of::<T>()]).map_err(|_| ProgramError::InvalidArgument)
210+
try_from_bytes(
211+
data.get(0..size_of::<T>())
212+
.ok_or(ProgramError::InvalidArgument)?,
213+
)
214+
.map_err(|_| ProgramError::InvalidArgument)
158215
}
159216

160217
/// Interpret the bytes in `data` as a mutable value of type `T`
@@ -191,17 +248,16 @@ fn load_mapping_account_mut<'a>(
191248
account: &'a AccountInfo,
192249
expected_version: u32,
193250
) -> Result<RefMut<'a, pc_map_table_t>, ProgramError> {
194-
let mapping_account_ref = load_account_as_mut::<pc_map_table_t>(account)?;
195-
let mapping_account = *mapping_account_ref;
251+
let mapping_data = load_account_as_mut::<pc_map_table_t>(account)?;
196252

197253
pyth_assert(
198-
mapping_account.magic_ == PC_MAGIC
199-
&& mapping_account.ver_ == expected_version
200-
&& mapping_account.type_ == PC_ACCTYPE_MAPPING,
254+
mapping_data.magic_ == PC_MAGIC
255+
&& mapping_data.ver_ == expected_version
256+
&& mapping_data.type_ == PC_ACCTYPE_MAPPING,
201257
ProgramError::InvalidArgument,
202258
)?;
203259

204-
Ok(mapping_account_ref)
260+
Ok(mapping_data)
205261
}
206262

207263
/// Initialize account as a new mapping account. This function will zero out any existing data in
@@ -218,3 +274,27 @@ fn initialize_mapping_account(account: &AccountInfo, version: u32) -> Result<(),
218274

219275
Ok(())
220276
}
277+
278+
/// Mutably borrow the data in `account` as a product account, validating that the account
279+
/// is properly formatted. Any mutations to the returned value will be reflected in the
280+
/// account data. Use this to read already-initialized accounts.
281+
fn load_product_account_mut<'a>(
282+
account: &'a AccountInfo,
283+
expected_version: u32,
284+
) -> Result<RefMut<'a, pc_prod_t>, ProgramError> {
285+
let product_data = load_account_as_mut::<pc_prod_t>(account)?;
286+
287+
pyth_assert(
288+
product_data.magic_ == PC_MAGIC
289+
&& product_data.ver_ == expected_version
290+
&& product_data.type_ == PC_ACCTYPE_PRODUCT,
291+
ProgramError::InvalidArgument,
292+
)?;
293+
294+
Ok(product_data)
295+
}
296+
297+
// Assign pubkey bytes from source to target, fails if source is not 32 bytes
298+
fn pubkey_assign(target: &mut pc_pub_key_t, source: &[u8]) {
299+
unsafe { target.k1_.copy_from_slice(source) }
300+
}

0 commit comments

Comments
 (0)