Skip to content

Commit 1857314

Browse files
guibescosmajabbour
andauthored
Twap tracker production (#214)
* Make c robust to price account size (#201) * make pythd robust * made the oracle accept new size * left space for extra publishers * Make c robust to price account size (#207) * make pythd robust * made the oracle accept new size * left space for extra publishers * 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 Co-authored-by: majabbour <[email protected]>
1 parent f514be5 commit 1857314

File tree

11 files changed

+137
-23
lines changed

11 files changed

+137
-23
lines changed

pc/request.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,7 @@ void product::update( T *res )
354354
return;
355355
}
356356
pc_prod_t *prod;
357-
size_t plen = std::max( sizeof(pc_price_t), (size_t)PC_PROD_ACC_SIZE );
357+
size_t plen = std::max( PRICE_ACCOUNT_SIZE, (size_t)PC_PROD_ACC_SIZE );
358358
if ( sizeof( pc_prod_t ) > res->get_data_ref( prod, plen ) ||
359359
prod->magic_ != PC_MAGIC ||
360360
!init_from_account( prod ) ) {
@@ -464,7 +464,7 @@ price::price( const pub_key& acc, product *prod )
464464
preq_->set_account( &apub_ );
465465
areq_->set_sub( this );
466466
preq_->set_sub( this );
467-
size_t tlen = ZSTD_compressBound( sizeof(pc_price_t) );
467+
size_t tlen = ZSTD_compressBound( PRICE_ACCOUNT_SIZE );
468468
pptr_ = (pc_price_t*)new char[tlen];
469469
__builtin_memset( pptr_, 0, tlen );
470470
}
@@ -952,7 +952,7 @@ void price::update( T *res )
952952
}
953953

954954
// get account data
955-
size_t tlen = ZSTD_compressBound( sizeof(pc_price_t) );
955+
size_t tlen = ZSTD_compressBound( PRICE_ACCOUNT_SIZE );
956956
res->get_data_val( pptr_, tlen );
957957
if ( PC_UNLIKELY( pptr_->magic_ != PC_MAGIC ) ) {
958958
on_error_sub( "bad price account header", this );

program/c/src/oracle/oracle.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include "oracle.h"
66
#include "upd_aggregate.h"
77

8+
89
// Returns the minimum number of lamports required to make an account
910
// with dlen bytes of data rent exempt. These values were calculated
1011
// using the getMinimumBalanceForRentExemption RPC call, and are
@@ -19,6 +20,8 @@ static uint64_t rent_exempt_amount( uint64_t dlen )
1920
return 4454400;
2021
case sizeof( pc_price_t ):
2122
return 23942400;
23+
case PRICE_ACCOUNT_SIZE:
24+
return 36915840;
2225
default:
2326
return UINT64_MAX;
2427
}

program/c/src/oracle/oracle.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ const uint64_t SUCCESSFULLY_UPDATED_AGGREGATE = 1000ULL;
1717
// Rust portion of the codebase.
1818
const uint64_t TIME_MACHINE_STRUCT_SIZE = 1864ULL;
1919

20+
const uint64_t EXTRA_PUBLISHER_SPACE = 1000ULL;
21+
22+
2023
// magic number at head of account
2124
#define PC_MAGIC 0xa1b2c3d4
2225

@@ -190,6 +193,8 @@ typedef struct pc_price
190193

191194
static_assert( sizeof( pc_price_t ) == 3312, "" );
192195

196+
const uint64_t PRICE_ACCOUNT_SIZE = TIME_MACHINE_STRUCT_SIZE + EXTRA_PUBLISHER_SPACE + sizeof( pc_price_t );
197+
193198
// command enumeration
194199
typedef enum {
195200

@@ -264,6 +269,12 @@ typedef enum {
264269
// key[1] price account [writable]
265270
// key[2] sysvar_clock account [readable]
266271
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,
267278
} command_t;
268279

269280
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)