From ac2718d9dd06323c00eeaf9cf35c367d6fa7081c Mon Sep 17 00:00:00 2001 From: Guillermo Bescos Date: Thu, 28 Jul 2022 00:07:30 +0000 Subject: [PATCH 1/6] Log time and ema --- program/rust/build.rs | 1 + program/rust/src/log.rs | 34 ++++++++++++++++++++++++------ program/rust/src/price_t_offsets.h | 1 + 3 files changed, 29 insertions(+), 7 deletions(-) diff --git a/program/rust/build.rs b/program/rust/build.rs index 4b8c39def..b8aee2acb 100644 --- a/program/rust/build.rs +++ b/program/rust/build.rs @@ -12,6 +12,7 @@ fn main() { parser.register_traits("pc_acc", borsh_derives.to_vec()); parser.register_traits("pc_price_info", borsh_derives.to_vec()); parser.register_traits("cmd_upd_price", borsh_derives.to_vec()); + parser.register_traits("pc_ema", borsh_derives.to_vec()); //generate and write bindings let bindings = Builder::default() diff --git a/program/rust/src/log.rs b/program/rust/src/log.rs index 7e0274352..43b1876f8 100644 --- a/program/rust/src/log.rs +++ b/program/rust/src/log.rs @@ -2,8 +2,10 @@ use crate::c_oracle_header::*; use crate::error::OracleError; use borsh::BorshDeserialize; use solana_program::account_info::AccountInfo; +use solana_program::clock::Clock; use solana_program::entrypoint::ProgramResult; use solana_program::msg; +use solana_program::sysvar::Sysvar; use std::mem::size_of; pub fn pre_log(accounts: &[AccountInfo], instruction_data: &[u8]) -> ProgramResult { @@ -14,11 +16,14 @@ pub fn pre_log(accounts: &[AccountInfo], instruction_data: &[u8]) -> ProgramResu .cmd_ .try_into() .map_err(|_| OracleError::Generic)?; + + let clock = Clock::get()?; + match instruction_id { command_t_e_cmd_upd_price | command_t_e_cmd_agg_price => { let instruction: cmd_upd_price = cmd_upd_price::try_from_slice(instruction_data)?; msg!( - "UpdatePrice: publisher={:}, price_account={:}, price={:}, conf={:}, status={:}, slot={:}", + "UpdatePrice: publisher={:}, price_account={:}, price={:}, conf={:}, status={:}, slot={:}, solana_time={:}", accounts.get(0) .ok_or(OracleError::Generic)?.key, accounts.get(1) @@ -26,13 +31,14 @@ pub fn pre_log(accounts: &[AccountInfo], instruction_data: &[u8]) -> ProgramResu instruction.price_, instruction.conf_, instruction.status_, - instruction.pub_slot_ + instruction.pub_slot_, + clock.unix_timestamp ); } command_t_e_cmd_upd_price_no_fail_on_error => { let instruction: cmd_upd_price = cmd_upd_price::try_from_slice(instruction_data)?; msg!( - "UpdatePriceNoFailOnError: publisher={:}, price_account={:}, price={:}, conf={:}, status={:}, slot={:}", + "UpdatePriceNoFailOnError: publisher={:}, price_account={:}, price={:}, conf={:}, status={:}, slot={:}, solana_time={:}", accounts.get(0) .ok_or(OracleError::Generic)?.key, accounts.get(1) @@ -40,7 +46,8 @@ pub fn pre_log(accounts: &[AccountInfo], instruction_data: &[u8]) -> ProgramResu instruction.price_, instruction.conf_, instruction.status_, - instruction.pub_slot_ + instruction.pub_slot_, + clock.unix_timestamp ); } command_t_e_cmd_add_mapping => { @@ -91,13 +98,26 @@ pub fn post_log(c_ret_val: u64, accounts: &[AccountInfo]) -> ProgramResult { .ok_or(OracleError::Generic)? .try_borrow_data()?[start..(start + size_of::())], )?; + + let ema_info: pc_ema = pc_ema::try_from_slice( + &accounts + .get(1) + .ok_or(OracleError::Generic)? + .try_borrow_data()?[start..(start + size_of::())], + )?; + + let clock = Clock::get()?; + msg!( - "UpdateAggregate : price_account={:}, price={:}, conf={:}, status={:}, slot={:}", - accounts.get(1).ok_or(OracleError::Generic)?.key, + "UpdateAggregate : price_account={:}, price={:}, conf={:}, status={:}, slot={:}, solana_time={:}, ema={:}", + accounts.get(1) + .ok_or(OracleError::Generic)?.key, aggregate_price_info.price_, aggregate_price_info.conf_, aggregate_price_info.status_, - aggregate_price_info.pub_slot_ + aggregate_price_info.pub_slot_, + clock.unix_timestamp, + ema_info.val_ ); } Ok(()) diff --git a/program/rust/src/price_t_offsets.h b/program/rust/src/price_t_offsets.h index 072be70cf..d515398b3 100644 --- a/program/rust/src/price_t_offsets.h +++ b/program/rust/src/price_t_offsets.h @@ -9,6 +9,7 @@ const size_t PRICE_T_AGGREGATE_OFFSET = offsetof(struct pc_price, agg_); const size_t PRICE_T_AGGREGATE_CONF_OFFSET = offsetof(struct pc_price, agg_) + offsetof(struct pc_price_info, conf_); const size_t PRICE_T_AGGREGATE_PRICE_OFFSET = offsetof(struct pc_price, agg_) + offsetof(struct pc_price_info, price_); const size_t PRICE_T_AGGREGATE_STATUS_OFFSET = offsetof(struct pc_price, agg_) + offsetof(struct pc_price_info, status_); +const size_t PRICE_T_EMA_OFFSET = offsetof(struct pc_price, twap_); const size_t PRICE_T_PREV_TIMESTAMP_OFFSET = offsetof(struct pc_price, prev_timestamp_); const size_t PRICE_T_PREV_CONF_OFFSET = offsetof(struct pc_price, prev_conf_); const size_t PRICE_T_PREV_AGGREGATE_OFFSET = offsetof(struct pc_price, prev_price_); From 715a55b02f47d42aba02d1bfb5900c6653daa12f Mon Sep 17 00:00:00 2001 From: Guillermo Bescos Date: Thu, 28 Jul 2022 00:14:12 +0000 Subject: [PATCH 2/6] Fix ema deserialize --- program/rust/src/log.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/program/rust/src/log.rs b/program/rust/src/log.rs index 43b1876f8..ced0b7e33 100644 --- a/program/rust/src/log.rs +++ b/program/rust/src/log.rs @@ -88,7 +88,7 @@ pub fn pre_log(accounts: &[AccountInfo], instruction_data: &[u8]) -> ProgramResu pub fn post_log(c_ret_val: u64, accounts: &[AccountInfo]) -> ProgramResult { if c_ret_val == SUCCESSFULLY_UPDATED_AGGREGATE { - let start: usize = PRICE_T_AGGREGATE_OFFSET + let aggregate_price_start: usize = PRICE_T_AGGREGATE_OFFSET .try_into() .map_err(|_| OracleError::Generic)?; // We trust that the C oracle has properly checked this account @@ -96,14 +96,18 @@ pub fn post_log(c_ret_val: u64, accounts: &[AccountInfo]) -> ProgramResult { &accounts .get(1) .ok_or(OracleError::Generic)? - .try_borrow_data()?[start..(start + size_of::())], + .try_borrow_data()?[aggregate_price_start..(aggregate_price_start + size_of::())], )?; + let ema_start: usize = PRICE_T_EMA_OFFSET + .try_into() + .map_err(|_| OracleError::Generic)?; + let ema_info: pc_ema = pc_ema::try_from_slice( &accounts .get(1) .ok_or(OracleError::Generic)? - .try_borrow_data()?[start..(start + size_of::())], + .try_borrow_data()?[ema_start..(ema_start + size_of::())], )?; let clock = Clock::get()?; From 8410e80f8347c6d85addabd6d92117f6b9eab3cf Mon Sep 17 00:00:00 2001 From: Guillermo Bescos Date: Thu, 28 Jul 2022 00:15:51 +0000 Subject: [PATCH 3/6] Revert format --- program/rust/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/program/rust/src/lib.rs b/program/rust/src/lib.rs index ad9aca120..2ed760075 100644 --- a/program/rust/src/lib.rs +++ b/program/rust/src/lib.rs @@ -1,4 +1,5 @@ mod c_oracle_header; +mod time_machine_types; mod error; mod log; mod processor; @@ -67,7 +68,7 @@ pub fn c_entrypoint_wrapper(input: *mut u8) -> OracleResult { pub extern "C" fn entrypoint(input: *mut u8) -> u64 { let (program_id, accounts, instruction_data) = unsafe { deserialize(input) }; - match pre_log(&accounts, instruction_data) { + match pre_log(&accounts,instruction_data) { Err(error) => return error.into(), _ => {} } From 905172a5ac6bd11bb89a9182f9192dfe00adc34c Mon Sep 17 00:00:00 2001 From: Guillermo Bescos Date: Thu, 28 Jul 2022 02:42:35 +0000 Subject: [PATCH 4/6] Add expo --- program/rust/src/log.rs | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/program/rust/src/log.rs b/program/rust/src/log.rs index ced0b7e33..b08c980f6 100644 --- a/program/rust/src/log.rs +++ b/program/rust/src/log.rs @@ -19,17 +19,28 @@ pub fn pre_log(accounts: &[AccountInfo], instruction_data: &[u8]) -> ProgramResu let clock = Clock::get()?; + let expo_start : usize = PRICE_T_EXPO_OFFSET.try_into() + .map_err(|_| OracleError::Generic)?; + + let expo: i32 = i32::try_from_slice( + &accounts + .get(1) + .ok_or(OracleError::Generic)? + .try_borrow_data()?[expo_start..(expo_start + size_of::())], + )?; + match instruction_id { command_t_e_cmd_upd_price | command_t_e_cmd_agg_price => { let instruction: cmd_upd_price = cmd_upd_price::try_from_slice(instruction_data)?; msg!( - "UpdatePrice: publisher={:}, price_account={:}, price={:}, conf={:}, status={:}, slot={:}, solana_time={:}", + "UpdatePrice: publisher={:}, price_account={:}, price={:}, conf={:}, expo={:}, status={:}, slot={:}, solana_time={:}", accounts.get(0) .ok_or(OracleError::Generic)?.key, accounts.get(1) .ok_or(OracleError::Generic)?.key, instruction.price_, instruction.conf_, + expo, instruction.status_, instruction.pub_slot_, clock.unix_timestamp @@ -38,13 +49,14 @@ pub fn pre_log(accounts: &[AccountInfo], instruction_data: &[u8]) -> ProgramResu command_t_e_cmd_upd_price_no_fail_on_error => { let instruction: cmd_upd_price = cmd_upd_price::try_from_slice(instruction_data)?; msg!( - "UpdatePriceNoFailOnError: publisher={:}, price_account={:}, price={:}, conf={:}, status={:}, slot={:}, solana_time={:}", + "UpdatePriceNoFailOnError: publisher={:}, price_account={:}, price={:}, conf={:}, expo={:}, status={:}, slot={:}, solana_time={:}", accounts.get(0) .ok_or(OracleError::Generic)?.key, accounts.get(1) .ok_or(OracleError::Generic)?.key, instruction.price_, instruction.conf_, + expo, instruction.status_, instruction.pub_slot_, clock.unix_timestamp @@ -110,14 +122,25 @@ pub fn post_log(c_ret_val: u64, accounts: &[AccountInfo]) -> ProgramResult { .try_borrow_data()?[ema_start..(ema_start + size_of::())], )?; + let expo_start : usize = PRICE_T_EXPO_OFFSET.try_into() + .map_err(|_| OracleError::Generic)?; + + let expo: i32 = i32::try_from_slice( + &accounts + .get(1) + .ok_or(OracleError::Generic)? + .try_borrow_data()?[expo_start..(expo_start + size_of::())], + )?; + let clock = Clock::get()?; msg!( - "UpdateAggregate : price_account={:}, price={:}, conf={:}, status={:}, slot={:}, solana_time={:}, ema={:}", + "UpdateAggregate : price_account={:}, price={:}, conf={:}, expo={:}, status={:}, slot={:}, solana_time={:}, ema={:}", accounts.get(1) .ok_or(OracleError::Generic)?.key, aggregate_price_info.price_, aggregate_price_info.conf_, + expo, aggregate_price_info.status_, aggregate_price_info.pub_slot_, clock.unix_timestamp, From 8a690b8132dcac8d0abaf6b52060e94bc177f2bb Mon Sep 17 00:00:00 2001 From: Guillermo Bescos Date: Thu, 28 Jul 2022 21:46:00 +0000 Subject: [PATCH 5/6] Fix comments --- program/rust/src/deserialize.rs | 36 +++++++++++++ program/rust/src/error.rs | 8 +-- program/rust/src/lib.rs | 5 +- program/rust/src/log.rs | 94 ++++++++++++++------------------- 4 files changed, 83 insertions(+), 60 deletions(-) create mode 100644 program/rust/src/deserialize.rs diff --git a/program/rust/src/deserialize.rs b/program/rust/src/deserialize.rs new file mode 100644 index 000000000..12565e6f3 --- /dev/null +++ b/program/rust/src/deserialize.rs @@ -0,0 +1,36 @@ +use crate::c_oracle_header::size_t; +use crate::error::OracleError; +use borsh::BorshDeserialize; +use solana_program::account_info::AccountInfo; +use solana_program::program_error::ProgramError; +use std::mem::size_of; +use std::result::Result; + +/// Deserialize field in `source` with offset `offset` +pub fn deserialize_single_field_from_buffer( + source: &[u8], + offset: Option, +) -> Result { + let start: usize = offset + .unwrap_or(0) + .try_into() + .map_err(|_| OracleError::IntegerCastingError)?; + + let res: T = T::try_from_slice(&source[start..(start + size_of::())])?; + Ok(res) +} + +/// Deserialize field in `i` rank of `accounts` with offset `offset` +pub fn deserialize_single_field_from_account( + accounts: &[AccountInfo], + i: usize, + offset: Option, +) -> Result { + Ok(deserialize_single_field_from_buffer::( + &accounts + .get(i) + .ok_or(ProgramError::NotEnoughAccountKeys)? + .try_borrow_data()?, + offset, + )?) +} diff --git a/program/rust/src/error.rs b/program/rust/src/error.rs index 514fd8676..0bdc2b1ac 100644 --- a/program/rust/src/error.rs +++ b/program/rust/src/error.rs @@ -10,13 +10,15 @@ pub type OracleResult = Result; pub enum OracleError { /// Generic catch all error #[error("Generic")] - Generic = 600, + Generic = 600, /// integer casting error #[error("IntegerCastingError")] - IntegerCastingError = 601, + IntegerCastingError = 601, /// c_entrypoint returned an unexpected value #[error("UnknownCError")] - UnknownCError = 602, + UnknownCError = 602, + #[error("UnrecognizedInstruction")] + UnrecognizedInstruction = 603, } impl From for ProgramError { diff --git a/program/rust/src/lib.rs b/program/rust/src/lib.rs index 2ed760075..bd0373ca9 100644 --- a/program/rust/src/lib.rs +++ b/program/rust/src/lib.rs @@ -1,5 +1,5 @@ mod c_oracle_header; -mod time_machine_types; +mod deserialize; mod error; mod log; mod processor; @@ -7,6 +7,7 @@ mod rust_oracle; mod time_machine_types; use crate::c_oracle_header::SUCCESSFULLY_UPDATED_AGGREGATE; +use crate::deserialize::deserialize_single_field_from_buffer; use crate::error::{ OracleError, OracleResult, @@ -68,7 +69,7 @@ pub fn c_entrypoint_wrapper(input: *mut u8) -> OracleResult { pub extern "C" fn entrypoint(input: *mut u8) -> u64 { let (program_id, accounts, instruction_data) = unsafe { deserialize(input) }; - match pre_log(&accounts,instruction_data) { + match pre_log(&accounts, instruction_data) { Err(error) => return error.into(), _ => {} } diff --git a/program/rust/src/log.rs b/program/rust/src/log.rs index b08c980f6..a9485b9b1 100644 --- a/program/rust/src/log.rs +++ b/program/rust/src/log.rs @@ -1,65 +1,71 @@ use crate::c_oracle_header::*; +use crate::deserialize::{ + deserialize_single_field_from_account, + deserialize_single_field_from_buffer, +}; use crate::error::OracleError; use borsh::BorshDeserialize; use solana_program::account_info::AccountInfo; use solana_program::clock::Clock; use solana_program::entrypoint::ProgramResult; use solana_program::msg; +use solana_program::program_error::ProgramError; use solana_program::sysvar::Sysvar; -use std::mem::size_of; pub fn pre_log(accounts: &[AccountInfo], instruction_data: &[u8]) -> ProgramResult { msg!("Pyth oracle contract"); - let instruction_header: cmd_hdr = cmd_hdr::try_from_slice(&instruction_data[..8])?; + let instruction_header: cmd_hdr = + deserialize_single_field_from_buffer::(&instruction_data, None)?; let instruction_id: u32 = instruction_header .cmd_ .try_into() - .map_err(|_| OracleError::Generic)?; + .map_err(|_| OracleError::IntegerCastingError)?; - let clock = Clock::get()?; - - let expo_start : usize = PRICE_T_EXPO_OFFSET.try_into() - .map_err(|_| OracleError::Generic)?; - - let expo: i32 = i32::try_from_slice( - &accounts - .get(1) - .ok_or(OracleError::Generic)? - .try_borrow_data()?[expo_start..(expo_start + size_of::())], - )?; match instruction_id { command_t_e_cmd_upd_price | command_t_e_cmd_agg_price => { let instruction: cmd_upd_price = cmd_upd_price::try_from_slice(instruction_data)?; + // Account 1 is price_info in this instruction + let expo: i32 = deserialize_single_field_from_account::( + accounts, + 1, + Some(PRICE_T_EXPO_OFFSET), + )?; msg!( "UpdatePrice: publisher={:}, price_account={:}, price={:}, conf={:}, expo={:}, status={:}, slot={:}, solana_time={:}", accounts.get(0) - .ok_or(OracleError::Generic)?.key, + .ok_or(ProgramError::NotEnoughAccountKeys)?.key, accounts.get(1) - .ok_or(OracleError::Generic)?.key, + .ok_or(ProgramError::NotEnoughAccountKeys)?.key, instruction.price_, instruction.conf_, expo, instruction.status_, instruction.pub_slot_, - clock.unix_timestamp + Clock::get()?.unix_timestamp ); } command_t_e_cmd_upd_price_no_fail_on_error => { let instruction: cmd_upd_price = cmd_upd_price::try_from_slice(instruction_data)?; + // Account 1 is price_info in this instruction + let expo: i32 = deserialize_single_field_from_account::( + accounts, + 1, + Some(PRICE_T_EXPO_OFFSET), + )?; msg!( "UpdatePriceNoFailOnError: publisher={:}, price_account={:}, price={:}, conf={:}, expo={:}, status={:}, slot={:}, solana_time={:}", accounts.get(0) - .ok_or(OracleError::Generic)?.key, + .ok_or(ProgramError::NotEnoughAccountKeys)?.key, accounts.get(1) - .ok_or(OracleError::Generic)?.key, + .ok_or(ProgramError::NotEnoughAccountKeys)?.key, instruction.price_, instruction.conf_, expo, instruction.status_, instruction.pub_slot_, - clock.unix_timestamp + Clock::get()?.unix_timestamp ); } command_t_e_cmd_add_mapping => { @@ -92,7 +98,7 @@ pub fn pre_log(accounts: &[AccountInfo], instruction_data: &[u8]) -> ProgramResu } _ => { msg!("UnrecognizedInstruction"); - return Err(OracleError::Generic.into()); + return Err(OracleError::UnrecognizedInstruction.into()); } } Ok(()) @@ -100,50 +106,28 @@ pub fn pre_log(accounts: &[AccountInfo], instruction_data: &[u8]) -> ProgramResu pub fn post_log(c_ret_val: u64, accounts: &[AccountInfo]) -> ProgramResult { if c_ret_val == SUCCESSFULLY_UPDATED_AGGREGATE { - let aggregate_price_start: usize = PRICE_T_AGGREGATE_OFFSET - .try_into() - .map_err(|_| OracleError::Generic)?; - // We trust that the C oracle has properly checked this account - let aggregate_price_info: pc_price_info = pc_price_info::try_from_slice( - &accounts - .get(1) - .ok_or(OracleError::Generic)? - .try_borrow_data()?[aggregate_price_start..(aggregate_price_start + size_of::())], - )?; - - let ema_start: usize = PRICE_T_EMA_OFFSET - .try_into() - .map_err(|_| OracleError::Generic)?; - - let ema_info: pc_ema = pc_ema::try_from_slice( - &accounts - .get(1) - .ok_or(OracleError::Generic)? - .try_borrow_data()?[ema_start..(ema_start + size_of::())], + // We trust that the C oracle has properly checked account 1, we can only get here through + // the update price instructions + let aggregate_price_info: pc_price_info = deserialize_single_field_from_account::< + pc_price_info, + >( + accounts, 1, Some(PRICE_T_AGGREGATE_OFFSET) )?; - - let expo_start : usize = PRICE_T_EXPO_OFFSET.try_into() - .map_err(|_| OracleError::Generic)?; - - let expo: i32 = i32::try_from_slice( - &accounts - .get(1) - .ok_or(OracleError::Generic)? - .try_borrow_data()?[expo_start..(expo_start + size_of::())], - )?; - - let clock = Clock::get()?; + let ema_info: pc_ema = + deserialize_single_field_from_account::(accounts, 1, Some(PRICE_T_EMA_OFFSET))?; + let expo: i32 = + deserialize_single_field_from_account::(accounts, 1, Some(PRICE_T_EXPO_OFFSET))?; msg!( "UpdateAggregate : price_account={:}, price={:}, conf={:}, expo={:}, status={:}, slot={:}, solana_time={:}, ema={:}", accounts.get(1) - .ok_or(OracleError::Generic)?.key, + .ok_or(ProgramError::NotEnoughAccountKeys)?.key, aggregate_price_info.price_, aggregate_price_info.conf_, expo, aggregate_price_info.status_, aggregate_price_info.pub_slot_, - clock.unix_timestamp, + Clock::get()?.unix_timestamp, ema_info.val_ ); } From a5509f04279d00be8283ce9fa29220ddde4532b1 Mon Sep 17 00:00:00 2001 From: Guillermo Bescos Date: Thu, 28 Jul 2022 21:52:27 +0000 Subject: [PATCH 6/6] Unused import --- program/rust/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/program/rust/src/lib.rs b/program/rust/src/lib.rs index bd0373ca9..05938cc95 100644 --- a/program/rust/src/lib.rs +++ b/program/rust/src/lib.rs @@ -7,7 +7,6 @@ mod rust_oracle; mod time_machine_types; use crate::c_oracle_header::SUCCESSFULLY_UPDATED_AGGREGATE; -use crate::deserialize::deserialize_single_field_from_buffer; use crate::error::{ OracleError, OracleResult,