Skip to content

Commit d9aa1fd

Browse files
committed
Merge branch 'main' into add-price-rust
2 parents c8f6b23 + 840b68f commit d9aa1fd

File tree

7 files changed

+275
-113
lines changed

7 files changed

+275
-113
lines changed

program/c/src/oracle/oracle.c

Lines changed: 1 addition & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -59,33 +59,6 @@ static bool valid_writable_account( SolParameters *prm,
5959
is_rent_exempt( *ka->lamports, ka->data_len );
6060
}
6161

62-
static uint64_t init_mapping( SolParameters *prm, SolAccountInfo *ka )
63-
{
64-
// Verify that the new account is signed and writable, with correct
65-
// ownership and size
66-
if ( prm->ka_num != 2 ||
67-
!valid_funding_account( &ka[0] ) ||
68-
!valid_signable_account( prm, &ka[1], sizeof( pc_map_table_t ) ) ) {
69-
return ERROR_INVALID_ARGUMENT;
70-
}
71-
72-
// Check that the account has not already been initialized
73-
pc_map_table_t *tptr = (pc_map_table_t*)ka[1].data;
74-
if ( tptr->magic_ != 0 || tptr->ver_ != 0 ) {
75-
return ERROR_INVALID_ARGUMENT;
76-
}
77-
78-
// Initialize by setting to zero again (just in case) and setting
79-
// the version number
80-
cmd_hdr_t *hdr = (cmd_hdr_t*)prm->data;
81-
sol_memset( tptr, 0, sizeof( pc_map_table_t ) );
82-
tptr->magic_ = PC_MAGIC;
83-
tptr->ver_ = hdr->ver_;
84-
tptr->type_ = PC_ACCTYPE_MAPPING;
85-
tptr->size_ = sizeof( pc_map_table_t ) - sizeof( tptr->prod_ );
86-
return SUCCESS;
87-
}
88-
8962
static uint64_t add_mapping( SolParameters *prm, SolAccountInfo *ka )
9063
{
9164
// Account (1) is the tail or last mapping account in the chain
@@ -539,7 +512,7 @@ static uint64_t dispatch( SolParameters *prm, SolAccountInfo *ka )
539512
case e_cmd_agg_price: return upd_price( prm, ka );
540513
case e_cmd_upd_price_no_fail_on_error: return upd_price_no_fail_on_error( prm, ka );
541514
// init_mapping is overridden in Rust, but still implemented here to make the C unit tests pass.
542-
case e_cmd_init_mapping: return init_mapping( prm, ka );
515+
case e_cmd_init_mapping: return ERROR_INVALID_ARGUMENT;
543516
case e_cmd_add_mapping: return add_mapping( prm, ka );
544517
case e_cmd_add_product: return add_product( prm, ka );
545518
case e_cmd_upd_product: return upd_product( prm, ka );

program/c/src/oracle/test_oracle.c

Lines changed: 0 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -7,81 +7,6 @@ uint64_t MAPPING_ACCOUNT_LAMPORTS = 143821440;
77
uint64_t PRODUCT_ACCOUNT_LAMPORTS = 4454400;
88
uint64_t PRICE_ACCOUNT_LAMPORTS = 23942400;
99

10-
Test(oracle, init_mapping) {
11-
12-
// start with perfect inputs
13-
cmd_hdr_t idata = {
14-
.ver_ = PC_VERSION,
15-
.cmd_ = e_cmd_init_mapping
16-
};
17-
SolPubkey p_id = {.x = { 0xff, }};
18-
SolPubkey p_id2 = {.x = { 0xfe, }};
19-
SolPubkey pkey = {.x = { 1, }};
20-
SolPubkey mkey = {.x = { 2, }};
21-
uint64_t pqty = 100;
22-
pc_map_table_t mptr[1];
23-
sol_memset( mptr, 0, sizeof( pc_map_table_t ) );
24-
SolAccountInfo acc[] = {{
25-
.key = &pkey,
26-
.lamports = &pqty,
27-
.data_len = 0,
28-
.data = NULL,
29-
.owner = NULL,
30-
.rent_epoch = 0,
31-
.is_signer = true,
32-
.is_writable = true,
33-
.executable = false
34-
},{
35-
.key = &mkey,
36-
.lamports = &MAPPING_ACCOUNT_LAMPORTS,
37-
.data_len = sizeof( pc_map_table_t ),
38-
.data = (uint8_t*)mptr,
39-
.owner = &p_id,
40-
.rent_epoch = 0,
41-
.is_signer = true,
42-
.is_writable = true,
43-
.executable = false
44-
}};
45-
SolParameters prm = {
46-
.ka = acc,
47-
.ka_num = 2,
48-
.data = (const uint8_t*)&idata,
49-
.data_len = sizeof( idata ),
50-
.program_id = &p_id
51-
};
52-
cr_assert( SUCCESS == dispatch( &prm, acc ) );
53-
cr_assert( mptr->ver_ == PC_VERSION );
54-
cr_assert( mptr->type_ == PC_ACCTYPE_MAPPING );
55-
56-
// cant reinitialize because version has been set
57-
cr_assert( ERROR_INVALID_ARGUMENT == dispatch( &prm, acc ) );
58-
sol_memset( mptr, 0, sizeof( pc_map_table_t ) );
59-
60-
// check other params
61-
prm.ka_num = 1;
62-
cr_assert( ERROR_INVALID_ARGUMENT == dispatch( &prm, acc ) );
63-
prm.ka_num = 2;
64-
acc[0].is_signer = false;
65-
cr_assert( ERROR_INVALID_ARGUMENT == dispatch( &prm, acc ) );
66-
acc[0].is_signer = true;
67-
acc[1].is_signer = false;
68-
cr_assert( ERROR_INVALID_ARGUMENT == dispatch( &prm, acc ) );
69-
acc[1].is_signer = true;
70-
acc[0].is_writable = false;
71-
cr_assert( ERROR_INVALID_ARGUMENT == dispatch( &prm, acc ) );
72-
acc[0].is_writable = true;
73-
acc[1].is_writable = false;
74-
cr_assert( ERROR_INVALID_ARGUMENT == dispatch( &prm, acc ) );
75-
acc[1].is_writable = true;
76-
acc[1].owner = &p_id2;
77-
cr_assert( ERROR_INVALID_ARGUMENT == dispatch( &prm, acc ) );
78-
acc[1].owner = &p_id;
79-
acc[1].data_len = 0;
80-
cr_assert( ERROR_INVALID_ARGUMENT == dispatch( &prm, acc ) );
81-
acc[1].data_len = sizeof( pc_map_table_t );
82-
cr_assert( SUCCESS == dispatch( &prm, acc ) );
83-
}
84-
8510
Test(oracle, add_mapping ) {
8611
// start with perfect inputs
8712
cmd_hdr_t idata = {

program/rust/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ mod error;
1010
mod log;
1111
mod processor;
1212
mod rust_oracle;
13+
mod test_oracle;
1314
mod time_machine_types;
15+
mod utils;
1416

1517
use crate::c_oracle_header::SUCCESSFULLY_UPDATED_AGGREGATE;
1618
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_add_price,
1213
command_t_e_cmd_agg_price,
1314
command_t_e_cmd_init_mapping,
@@ -21,6 +22,7 @@ use crate::error::{
2122
OracleResult,
2223
};
2324
use crate::rust_oracle::{
25+
add_mapping,
2426
add_price,
2527
init_mapping,
2628
update_price,
@@ -60,6 +62,7 @@ pub fn process_instruction(
6062
}
6163
command_t_e_cmd_add_price => add_price(program_id, accounts, instruction_data),
6264
command_t_e_cmd_init_mapping => init_mapping(program_id, accounts, instruction_data),
65+
command_t_e_cmd_add_mapping => add_mapping(program_id, accounts, instruction_data),
6366
_ => c_entrypoint_wrapper(input),
6467
}
6568
}

program/rust/src/rust_oracle.rs

Lines changed: 78 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,12 @@ use bytemuck::{
1313
try_from_bytes_mut,
1414
Pod,
1515
};
16+
use solana_program::account_info::AccountInfo;
1617
use solana_program::entrypoint::SUCCESS;
1718
use solana_program::program_error::ProgramError;
1819
use solana_program::program_memory::sol_memset;
1920
use solana_program::pubkey::Pubkey;
2021
use solana_program::rent::Rent;
21-
use solana_program::sysvar::slot_history::AccountInfo;
2222

2323
use crate::c_oracle_header::{
2424
cmd_add_price_t,
@@ -31,12 +31,15 @@ use crate::c_oracle_header::{
3131
PC_ACCTYPE_PRICE,
3232
PC_ACCTYPE_PRODUCT,
3333
PC_MAGIC,
34+
PC_MAP_TABLE_SIZE,
3435
PC_MAX_NUM_DECIMALS,
3536
PC_PROD_ACC_SIZE,
3637
PC_PTYPE_UNKNOWN,
3738
};
3839
use crate::error::OracleResult;
3940

41+
use crate::utils::pyth_assert;
42+
4043
use super::c_entrypoint_wrapper;
4144

4245
///Calls the c oracle update_price, and updates the Time Machine if needed
@@ -83,14 +86,44 @@ pub fn init_mapping(
8386
}?;
8487

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

88112
let hdr = load::<cmd_hdr_t>(instruction_data)?;
89-
let mut mapping_data = load_account_as_mut::<pc_map_table_t>(fresh_mapping_account)?;
90-
mapping_data.magic_ = PC_MAGIC;
91-
mapping_data.ver_ = hdr.ver_;
92-
mapping_data.type_ = PC_ACCTYPE_MAPPING;
93-
mapping_data.size_ = (size_of::<pc_map_table_t>() - size_of_val(&mapping_data.prod_)) as u32;
113+
let mut cur_mapping = load_mapping_account_mut(cur_mapping, hdr.ver_)?;
114+
pyth_assert(
115+
cur_mapping.num_ == PC_MAP_TABLE_SIZE
116+
&& unsafe { cur_mapping.next_.k8_.iter().all(|x| *x == 0) },
117+
ProgramError::InvalidArgument,
118+
)?;
119+
120+
initialize_mapping_account(next_mapping, hdr.ver_)?;
121+
unsafe {
122+
cur_mapping
123+
.next_
124+
.k1_
125+
.copy_from_slice(&next_mapping.key.to_bytes());
126+
}
94127

95128
Ok(SUCCESS)
96129
}
@@ -180,7 +213,7 @@ fn valid_fresh_account(account: &AccountInfo) -> bool {
180213
}
181214

182215
/// Sets the data of account to all-zero
183-
fn clear_account(account: &AccountInfo) -> Result<(), ProgramError> {
216+
pub fn clear_account(account: &AccountInfo) -> Result<(), ProgramError> {
184217
let mut data = account
185218
.try_borrow_mut_data()
186219
.map_err(|_| ProgramError::InvalidArgument)?;
@@ -201,7 +234,7 @@ fn load_mut<T: Pod>(data: &mut [u8]) -> Result<&mut T, ProgramError> {
201234
}
202235

203236
/// Get the data stored in `account` as a value of type `T`
204-
fn load_account_as<'a, T: Pod>(account: &'a AccountInfo) -> Result<Ref<'a, T>, ProgramError> {
237+
pub fn load_account_as<'a, T: Pod>(account: &'a AccountInfo) -> Result<Ref<'a, T>, ProgramError> {
205238
let data = account.try_borrow_data()?;
206239

207240
Ok(Ref::map(data, |data| {
@@ -211,7 +244,7 @@ fn load_account_as<'a, T: Pod>(account: &'a AccountInfo) -> Result<Ref<'a, T>, P
211244

212245
/// Mutably borrow the data in `account` as a value of type `T`.
213246
/// Any mutations to the returned value will be reflected in the account data.
214-
fn load_account_as_mut<'a, T: Pod>(
247+
pub fn load_account_as_mut<'a, T: Pod>(
215248
account: &'a AccountInfo,
216249
) -> Result<RefMut<'a, T>, ProgramError> {
217250
let data = account.try_borrow_mut_data()?;
@@ -220,3 +253,38 @@ fn load_account_as_mut<'a, T: Pod>(
220253
bytemuck::from_bytes_mut(&mut data[0..size_of::<T>()])
221254
}))
222255
}
256+
257+
/// Mutably borrow the data in `account` as a mapping account, validating that the account
258+
/// is properly formatted. Any mutations to the returned value will be reflected in the
259+
/// account data. Use this to read already-initialized accounts.
260+
fn load_mapping_account_mut<'a>(
261+
account: &'a AccountInfo,
262+
expected_version: u32,
263+
) -> Result<RefMut<'a, pc_map_table_t>, ProgramError> {
264+
let mapping_account_ref = load_account_as_mut::<pc_map_table_t>(account)?;
265+
let mapping_account = *mapping_account_ref;
266+
267+
pyth_assert(
268+
mapping_account.magic_ == PC_MAGIC
269+
&& mapping_account.ver_ == expected_version
270+
&& mapping_account.type_ == PC_ACCTYPE_MAPPING,
271+
ProgramError::InvalidArgument,
272+
)?;
273+
274+
Ok(mapping_account_ref)
275+
}
276+
277+
/// Initialize account as a new mapping account. This function will zero out any existing data in
278+
/// the account.
279+
fn initialize_mapping_account(account: &AccountInfo, version: u32) -> Result<(), ProgramError> {
280+
clear_account(account)?;
281+
282+
let mut mapping_account = load_account_as_mut::<pc_map_table_t>(account)?;
283+
mapping_account.magic_ = PC_MAGIC;
284+
mapping_account.ver_ = version;
285+
mapping_account.type_ = PC_ACCTYPE_MAPPING;
286+
mapping_account.size_ =
287+
(size_of::<pc_map_table_t>() - size_of_val(&mapping_account.prod_)) as u32;
288+
289+
Ok(())
290+
}

0 commit comments

Comments
 (0)