Skip to content

Commit b439053

Browse files
authored
Intercepting price updates (#204)
* changed c return value of update price to indicate aggregation (#190) * changed c return value of update price to indicate aggregation * forgot to add bindings * removed the need for casting * fixed update_no_fail * removed unnecessary docker packages * timeless coments * a convinient way to derive traits * changed importing style * added register_traits method * fixed formatting * add a new insctruction and interecepted update price calls * fixed formatting * more helpful error message for incorrect c account size * fixed merging errors * addressed some warning * updated comment * merging formatting issues * memory borrowing issues after rebasing * updated import * more import style changes * removed panics * bug introduced by merge * moved dispatching to processor * better formatting * cleaner error handling * error handling wrapper for c_entrypoint * removed unwrap * more comments * removed unused imports + changed import style
1 parent 9934c60 commit b439053

File tree

9 files changed

+126
-20
lines changed

9 files changed

+126
-20
lines changed

program/c/src/oracle/oracle.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,12 @@ typedef enum {
269269
// key[1] price account [writable]
270270
// key[2] sysvar_clock account [readable]
271271
e_cmd_upd_price_no_fail_on_error,
272+
273+
// performs migation logic on the upgraded account. (resizes price accounts)
274+
// key[0] funding account [signer writable]
275+
// key[1] upgraded account [writable]
276+
// key[2] system program [readable]
277+
e_cmd_upd_account_version,
272278
} command_t;
273279

274280
typedef struct cmd_hdr

program/rust/build.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@ fn main() {
1313
parser.register_traits("pc_price_info", borsh_derives.to_vec());
1414
parser.register_traits("cmd_upd_price", borsh_derives.to_vec());
1515

16-
17-
1816
//generate and write bindings
1917
let bindings = Builder::default()
2018
.header("./src/bindings.h")

program/rust/build_utils.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use bindgen::callbacks::ParseCallbacks;
22
use std::collections::HashMap;
33
use std::panic::UnwindSafe;
4+
45
///This type stores a hashmap from structnames
56
///to vectors of trait names, and ensures
67
///that the traits of each struct are added to its

program/rust/src/error.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,22 @@
11
//! Error types
2-
32
use solana_program::program_error::ProgramError;
3+
use std::result::Result;
44
use thiserror::Error;
5+
// similar to ProgramResult but allows for multiple success values
6+
pub type OracleResult = Result<u64, ProgramError>;
57

68
/// Errors that may be returned by the oracle program
79
#[derive(Clone, Debug, Eq, Error, PartialEq)]
810
pub enum OracleError {
911
/// Generic catch all error
1012
#[error("Generic")]
1113
Generic = 600,
14+
/// integer casting error
15+
#[error("IntegerCastingError")]
16+
IntegerCastingError = 601,
17+
/// c_entrypoint returned an unexpected value
18+
#[error("UnknownCError")]
19+
UnknownCError = 602,
1220
}
1321

1422
impl From<OracleError> for ProgramError {

program/rust/src/lib.rs

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
mod c_oracle_header;
2-
mod time_machine_types;
32
mod error;
43
mod log;
4+
mod processor;
5+
mod rust_oracle;
6+
mod time_machine_types;
57

8+
use crate::c_oracle_header::SUCCESSFULLY_UPDATED_AGGREGATE;
9+
use crate::error::{OracleError, OracleResult};
610
use crate::log::{post_log, pre_log};
7-
use solana_program::entrypoint::deserialize;
11+
use processor::process_instruction;
12+
use solana_program::program_error::ProgramError;
13+
use solana_program::{custom_heap_default, custom_panic_default, entrypoint::deserialize};
814

915
//Below is a high lever description of the rust/c setup.
1016

@@ -36,29 +42,43 @@ pub extern "C" fn c_entrypoint(input: *mut u8) -> u64 {
3642
0 //SUCCESS value
3743
}
3844

45+
pub fn c_entrypoint_wrapper(input: *mut u8) -> OracleResult {
46+
//Throwing an exception from C into Rust is undefined behavior
47+
//This seems to be the best we can do
48+
match unsafe { c_entrypoint(input) } {
49+
0 => Ok(0), // Success
50+
SUCCESSFULLY_UPDATED_AGGREGATE => Ok(SUCCESSFULLY_UPDATED_AGGREGATE),
51+
2 => Err(ProgramError::InvalidArgument), //2 is ERROR_INVALID_ARGUMENT in solana_sdk.h
52+
_ => Err(OracleError::UnknownCError.into()),
53+
}
54+
}
55+
3956
#[no_mangle]
4057
pub extern "C" fn entrypoint(input: *mut u8) -> u64 {
41-
let (_program_id, accounts, instruction_data) = unsafe { deserialize(input) };
58+
let (program_id, accounts, instruction_data) = unsafe { deserialize(input) };
4259

43-
match pre_log(&accounts,instruction_data) {
60+
match pre_log(&accounts, instruction_data) {
4461
Err(error) => return error.into(),
4562
_ => {}
4663
}
4764

48-
let c_ret_val = unsafe { c_entrypoint(input) };
65+
let c_ret_val = match process_instruction(program_id, &accounts, instruction_data, input) {
66+
Err(error) => error.into(),
67+
Ok(success_status) => success_status,
68+
};
4969

5070
match post_log(c_ret_val, &accounts) {
5171
Err(error) => return error.into(),
5272
_ => {}
5373
}
5474

55-
if c_ret_val == c_oracle_header::SUCCESSFULLY_UPDATED_AGGREGATE {
75+
if c_ret_val == SUCCESSFULLY_UPDATED_AGGREGATE {
5676
//0 is the SUCCESS value for solana
5777
return 0;
5878
} else {
5979
return c_ret_val;
6080
}
6181
}
6282

63-
solana_program::custom_heap_default!();
64-
solana_program::custom_panic_default!();
83+
custom_heap_default!();
84+
custom_panic_default!();

program/rust/src/log.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use solana_program::entrypoint::ProgramResult;
66
use solana_program::msg;
77
use std::mem::size_of;
88

9-
pub fn pre_log(accounts : &[AccountInfo], instruction_data: &[u8]) -> ProgramResult {
9+
pub fn pre_log(accounts: &[AccountInfo], instruction_data: &[u8]) -> ProgramResult {
1010
msg!("Pyth oracle contract");
1111

1212
let instruction_header: cmd_hdr = cmd_hdr::try_from_slice(&instruction_data[..8])?;
@@ -15,7 +15,7 @@ pub fn pre_log(accounts : &[AccountInfo], instruction_data: &[u8]) -> ProgramRes
1515
.try_into()
1616
.map_err(|_| OracleError::Generic)?;
1717
match instruction_id {
18-
command_t_e_cmd_upd_price | command_t_e_cmd_agg_price=> {
18+
command_t_e_cmd_upd_price | command_t_e_cmd_agg_price => {
1919
let instruction: cmd_upd_price = cmd_upd_price::try_from_slice(instruction_data)?;
2020
msg!(
2121
"UpdatePrice: publisher={:}, price_account={:}, price={:}, conf={:}, status={:}, slot={:}",
@@ -29,7 +29,7 @@ pub fn pre_log(accounts : &[AccountInfo], instruction_data: &[u8]) -> ProgramRes
2929
instruction.pub_slot_
3030
);
3131
}
32-
command_t_e_cmd_upd_price_no_fail_on_error=> {
32+
command_t_e_cmd_upd_price_no_fail_on_error => {
3333
let instruction: cmd_upd_price = cmd_upd_price::try_from_slice(instruction_data)?;
3434
msg!(
3535
"UpdatePriceNoFailOnError: publisher={:}, price_account={:}, price={:}, conf={:}, status={:}, slot={:}",
@@ -93,8 +93,7 @@ pub fn post_log(c_ret_val: u64, accounts: &[AccountInfo]) -> ProgramResult {
9393
)?;
9494
msg!(
9595
"UpdateAggregate : price_account={:}, price={:}, conf={:}, status={:}, slot={:}",
96-
accounts.get(1)
97-
.ok_or(OracleError::Generic)?.key,
96+
accounts.get(1).ok_or(OracleError::Generic)?.key,
9897
aggregate_price_info.price_,
9998
aggregate_price_info.conf_,
10099
aggregate_price_info.status_,

program/rust/src/processor.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
use crate::c_entrypoint_wrapper;
2+
use crate::c_oracle_header::{
3+
cmd_hdr, command_t_e_cmd_agg_price, command_t_e_cmd_upd_account_version,
4+
command_t_e_cmd_upd_price, command_t_e_cmd_upd_price_no_fail_on_error, PC_VERSION,
5+
};
6+
use crate::error::{OracleError, OracleResult};
7+
use crate::rust_oracle::{update_price, update_version};
8+
use ::std::mem::size_of;
9+
use borsh::BorshDeserialize;
10+
use solana_program::pubkey::Pubkey;
11+
use solana_program::sysvar::slot_history::AccountInfo;
12+
///dispatch to the right instruction in the oracle
13+
pub fn process_instruction(
14+
program_id: &Pubkey,
15+
accounts: &Vec<AccountInfo>,
16+
instruction_data: &[u8],
17+
input: *mut u8,
18+
) -> OracleResult {
19+
let cmd_hdr_size = size_of::<cmd_hdr>();
20+
let cmd_data = cmd_hdr::try_from_slice(&instruction_data[..cmd_hdr_size])?;
21+
22+
if cmd_data.ver_ != PC_VERSION {
23+
//FIXME: I am not sure what's best to do here (this is copied from C)
24+
// it seems to me like we should not break when version numbers change
25+
//instead we should log a message that asks users to call update_version
26+
panic!("incorrect version numbers");
27+
}
28+
29+
match cmd_data
30+
.cmd_
31+
.try_into()
32+
.map_err(|_| OracleError::IntegerCastingError)?
33+
{
34+
command_t_e_cmd_upd_price
35+
| command_t_e_cmd_upd_price_no_fail_on_error
36+
| command_t_e_cmd_agg_price => {
37+
update_price(program_id, &accounts, &instruction_data, input)
38+
}
39+
command_t_e_cmd_upd_account_version => {
40+
update_version(program_id, &accounts, &instruction_data)
41+
}
42+
_ => c_entrypoint_wrapper(input),
43+
}
44+
}

program/rust/src/rust_oracle.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
use super::c_entrypoint_wrapper;
2+
use crate::error::OracleResult;
3+
use solana_program::pubkey::Pubkey;
4+
use solana_program::sysvar::slot_history::AccountInfo;
5+
6+
///Calls the c oracle update_price, and updates the Time Machine if needed
7+
pub fn update_price(
8+
program_id: &Pubkey,
9+
accounts: &Vec<AccountInfo>,
10+
instruction_data: &[u8],
11+
input: *mut u8,
12+
) -> OracleResult {
13+
//For now, we did not change the behavior of this. this is just to show the proposed structure of the
14+
//program
15+
c_entrypoint_wrapper(input)
16+
}
17+
/// has version number/ account type dependant logic to make sure the given account is compatible
18+
/// with the current version
19+
/// updates the version number for all accounts, and resizes price accounts
20+
pub fn update_version(
21+
program_id: &Pubkey,
22+
accounts: &Vec<AccountInfo>,
23+
instruction_data: &[u8],
24+
) -> OracleResult {
25+
panic!("Need to merge fix to pythd in order to implement this");
26+
Ok(0) //SUCCESS
27+
}

program/rust/src/time_machine_types.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
use super::c_oracle_header;
1+
use super::c_oracle_header::TIME_MACHINE_STRUCT_SIZE;
2+
use ::std::mem::size_of;
23
#[derive(Debug, Clone)]
34
#[repr(C)]
45
/// this wraps multiple SMA and tick trackers, and includes all the state
@@ -13,8 +14,10 @@ pub struct TimeMachineWrapper {
1314
///defined in Rust
1415
fn c_time_machine_size_is_correct() {
1516
assert_eq!(
16-
::std::mem::size_of::<TimeMachineWrapper>(),
17-
c_oracle_header::TIME_MACHINE_STRUCT_SIZE.try_into().unwrap(),
18-
"expected TIME_MACHINE_STRUCT_SIZE in oracle.h to the same as the size of TimeMachineWrapper"
17+
size_of::<TimeMachineWrapper>(),
18+
TIME_MACHINE_STRUCT_SIZE.try_into().unwrap(),
19+
"expected TIME_MACHINE_STRUCT_SIZE ({}) in oracle.h to the same as the size of TimeMachineWrapper ({})",
20+
TIME_MACHINE_STRUCT_SIZE,
21+
size_of::<TimeMachineWrapper>()
1922
);
2023
}

0 commit comments

Comments
 (0)