Skip to content

Commit 3686a5a

Browse files
jayantkJayant Krishnamurthy
andauthored
Implement add_mapping instruction (#221)
* add_mapping instruction * cleanup Co-authored-by: Jayant Krishnamurthy <jkrishnamurthy@jumptrading.com>
1 parent 199c1b3 commit 3686a5a

File tree

4 files changed

+87
-6
lines changed

4 files changed

+87
-6
lines changed

program/rust/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ mod log;
1111
mod processor;
1212
mod rust_oracle;
1313
mod time_machine_types;
14+
mod utils;
1415

1516
use crate::c_oracle_header::SUCCESSFULLY_UPDATED_AGGREGATE;
1617
use crate::error::{

program/rust/src/processor.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use solana_program::sysvar::slot_history::AccountInfo;
88
use crate::c_entrypoint_wrapper;
99
use crate::c_oracle_header::{
1010
cmd_hdr,
11+
command_t_e_cmd_add_mapping,
1112
command_t_e_cmd_agg_price,
1213
command_t_e_cmd_init_mapping,
1314
command_t_e_cmd_upd_account_version,
@@ -20,6 +21,7 @@ use crate::error::{
2021
OracleResult,
2122
};
2223
use crate::rust_oracle::{
24+
add_mapping,
2325
init_mapping,
2426
update_price,
2527
update_version,
@@ -57,6 +59,7 @@ pub fn process_instruction(
5759
update_version(program_id, accounts, instruction_data)
5860
}
5961
command_t_e_cmd_init_mapping => init_mapping(program_id, accounts, instruction_data),
62+
command_t_e_cmd_add_mapping => add_mapping(program_id, accounts, instruction_data),
6063
_ => c_entrypoint_wrapper(input),
6164
}
6265
}

program/rust/src/rust_oracle.rs

Lines changed: 74 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,12 @@ use crate::c_oracle_header::{
2626
pc_map_table_t,
2727
PC_ACCTYPE_MAPPING,
2828
PC_MAGIC,
29+
PC_MAP_TABLE_SIZE,
2930
};
3031
use crate::error::OracleResult;
3132

33+
use crate::utils::pyth_assert;
34+
3235
use super::c_entrypoint_wrapper;
3336

3437
///Calls the c oracle update_price, and updates the Time Machine if needed
@@ -75,14 +78,44 @@ pub fn init_mapping(
7578
}?;
7679

7780
// Initialize by setting to zero again (just in case) and populating the account header
78-
clear_account(fresh_mapping_account)?;
81+
let hdr = load::<cmd_hdr_t>(instruction_data)?;
82+
initialize_mapping_account(fresh_mapping_account, hdr.ver_)?;
83+
84+
Ok(SUCCESS)
85+
}
86+
87+
pub fn add_mapping(
88+
program_id: &Pubkey,
89+
accounts: &[AccountInfo],
90+
instruction_data: &[u8],
91+
) -> OracleResult {
92+
let [_funding_account, cur_mapping, next_mapping] = match accounts {
93+
[x, y, z]
94+
if valid_funding_account(x)
95+
&& valid_signable_account(program_id, y, size_of::<pc_map_table_t>())
96+
&& valid_signable_account(program_id, z, size_of::<pc_map_table_t>())
97+
&& valid_fresh_account(z) =>
98+
{
99+
Ok([x, y, z])
100+
}
101+
_ => Err(ProgramError::InvalidArgument),
102+
}?;
79103

80104
let hdr = load::<cmd_hdr_t>(instruction_data)?;
81-
let mut mapping_data = load_account_as_mut::<pc_map_table_t>(fresh_mapping_account)?;
82-
mapping_data.magic_ = PC_MAGIC;
83-
mapping_data.ver_ = hdr.ver_;
84-
mapping_data.type_ = PC_ACCTYPE_MAPPING;
85-
mapping_data.size_ = (size_of::<pc_map_table_t>() - size_of_val(&mapping_data.prod_)) as u32;
105+
let mut cur_mapping = load_mapping_account_mut(cur_mapping, hdr.ver_)?;
106+
pyth_assert(
107+
cur_mapping.num_ == PC_MAP_TABLE_SIZE
108+
&& unsafe { cur_mapping.next_.k8_.iter().all(|x| *x == 0) },
109+
ProgramError::InvalidArgument,
110+
)?;
111+
112+
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());
118+
}
86119

87120
Ok(SUCCESS)
88121
}
@@ -150,3 +183,38 @@ fn load_account_as_mut<'a, T: Pod>(
150183
bytemuck::from_bytes_mut(&mut data[0..size_of::<T>()])
151184
}))
152185
}
186+
187+
/// Mutably borrow the data in `account` as a mapping account, validating that the account
188+
/// is properly formatted. Any mutations to the returned value will be reflected in the
189+
/// account data. Use this to read already-initialized accounts.
190+
fn load_mapping_account_mut<'a>(
191+
account: &'a AccountInfo,
192+
expected_version: u32,
193+
) -> 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;
196+
197+
pyth_assert(
198+
mapping_account.magic_ == PC_MAGIC
199+
&& mapping_account.ver_ == expected_version
200+
&& mapping_account.type_ == PC_ACCTYPE_MAPPING,
201+
ProgramError::InvalidArgument,
202+
)?;
203+
204+
Ok(mapping_account_ref)
205+
}
206+
207+
/// Initialize account as a new mapping account. This function will zero out any existing data in
208+
/// the account.
209+
fn initialize_mapping_account(account: &AccountInfo, version: u32) -> Result<(), ProgramError> {
210+
clear_account(account)?;
211+
212+
let mut mapping_account = load_account_as_mut::<pc_map_table_t>(account)?;
213+
mapping_account.magic_ = PC_MAGIC;
214+
mapping_account.ver_ = version;
215+
mapping_account.type_ = PC_ACCTYPE_MAPPING;
216+
mapping_account.size_ =
217+
(size_of::<pc_map_table_t>() - size_of_val(&mapping_account.prod_)) as u32;
218+
219+
Ok(())
220+
}

program/rust/src/utils.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
use solana_program::program_error::ProgramError;
2+
3+
pub fn pyth_assert(condition: bool, error_code: ProgramError) -> Result<(), ProgramError> {
4+
if !condition {
5+
Result::Err(error_code)
6+
} else {
7+
Result::Ok(())
8+
}
9+
}

0 commit comments

Comments
 (0)