Skip to content

Commit

Permalink
feat: add SetMaxLatency instruction (#395)
Browse files Browse the repository at this point in the history
* add SetMaxLatency instruction

* Add SetMaxLatencyArgs struct to test_sizes.rs
  • Loading branch information
cctdaniel authored Feb 27, 2024
1 parent 854c72e commit 1b68e4f
Show file tree
Hide file tree
Showing 8 changed files with 209 additions and 6 deletions.
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,7 @@ cmake-build-*

# IntelliJ / CLion configuration
.idea
*.iml
*.iml

# CMake files
features.h
12 changes: 12 additions & 0 deletions program/rust/src/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,10 @@ pub enum OracleCommand {
// key[2] permissions account [writable]
// key[3] system program []
UpdPermissions = 17,
/// Set max latency
// account[0] funding account [signer writable]
// account[1] price account [signer writable]
SetMaxLatency = 18,
}

#[repr(C)]
Expand Down Expand Up @@ -162,3 +166,11 @@ pub struct UpdPermissionsArgs {
pub data_curation_authority: Pubkey,
pub security_authority: Pubkey,
}

#[repr(C)]
#[derive(Zeroable, Clone, Copy, Pod)]
pub struct SetMaxLatencyArgs {
pub header: CommandHeader,
pub max_latency: u8,
pub unused_: [u8; 3],
}
3 changes: 3 additions & 0 deletions program/rust/src/processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ mod del_product;
mod del_publisher;
mod init_mapping;
mod init_price;
mod set_max_latency;
mod set_min_pub;
mod upd_permissions;
mod upd_price;
Expand All @@ -35,6 +36,7 @@ pub use {
del_publisher::del_publisher,
init_mapping::init_mapping,
init_price::init_price,
set_max_latency::set_max_latency,
set_min_pub::set_min_pub,
upd_permissions::upd_permissions,
upd_price::{
Expand Down Expand Up @@ -76,5 +78,6 @@ pub fn process_instruction(
DelPrice => del_price(program_id, accounts, instruction_data),
DelProduct => del_product(program_id, accounts, instruction_data),
UpdPermissions => upd_permissions(program_id, accounts, instruction_data),
SetMaxLatency => set_max_latency(program_id, accounts, instruction_data),
}
}
59 changes: 59 additions & 0 deletions program/rust/src/processor/set_max_latency.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
use {
crate::{
accounts::PriceAccount,
deserialize::{
load,
load_checked,
},
instruction::SetMaxLatencyArgs,
utils::{
check_valid_funding_account,
check_valid_signable_account_or_permissioned_funding_account,
pyth_assert,
},
OracleError,
},
solana_program::{
account_info::AccountInfo,
entrypoint::ProgramResult,
program_error::ProgramError,
pubkey::Pubkey,
},
std::mem::size_of,
};

/// Set max latency
// account[0] funding account [signer writable]
// account[1] price account [signer writable]
pub fn set_max_latency(
program_id: &Pubkey,
accounts: &[AccountInfo],
instruction_data: &[u8],
) -> ProgramResult {
let cmd = load::<SetMaxLatencyArgs>(instruction_data)?; // Loading SetMaxLatencyArgs

pyth_assert(
instruction_data.len() == size_of::<SetMaxLatencyArgs>(), // Checking size of SetMaxLatencyArgs
ProgramError::InvalidArgument,
)?;

let (funding_account, price_account, permissions_account_option) = match accounts {
[x, y] => Ok((x, y, None)),
[x, y, p] => Ok((x, y, Some(p))),
_ => Err(OracleError::InvalidNumberOfAccounts),
}?;

check_valid_funding_account(funding_account)?;
check_valid_signable_account_or_permissioned_funding_account(
program_id,
price_account,
funding_account,
permissions_account_option,
&cmd.header,
)?;

let mut price_account_data = load_checked::<PriceAccount>(price_account, cmd.header.version)?;
price_account_data.max_latency_ = cmd.max_latency;

Ok(())
}
2 changes: 1 addition & 1 deletion program/rust/src/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ mod test_message;
mod test_permission_migration;
mod test_publish;
mod test_publish_batch;
mod test_set_max_latency;
mod test_set_min_pub;
mod test_sizes;
mod test_upd_aggregate;
Expand All @@ -25,7 +26,6 @@ mod test_upd_price_no_fail_on_error;
mod test_upd_product;
mod test_utils;


#[cfg(feature = "pythnet")]
mod test_twap;
#[cfg(feature = "pythnet")]
Expand Down
41 changes: 37 additions & 4 deletions program/rust/src/tests/test_permission_migration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,11 @@ use {
DelPublisher,
InitMapping,
InitPrice,
SetMaxLatency,
SetMinPub,
UpdProduct,
},
SetMaxLatencyArgs,
SetMinPubArgs,
},
processor::process_instruction,
Expand Down Expand Up @@ -65,13 +67,11 @@ fn test_permission_migration() {
let mut price_account = price_setup.as_account_info();
PriceAccount::initialize(&price_account, PC_VERSION).unwrap();


product_account.is_signer = false;
mapping_account.is_signer = false;
price_account.is_signer = false;
next_mapping_account.is_signer = false;


{
let mut permissions_account_data =
PermissionAccount::initialize(&permissions_account, PC_VERSION).unwrap();
Expand All @@ -92,7 +92,6 @@ fn test_permission_migration() {
Err(OracleError::PermissionViolation.into())
);


assert_eq!(
process_instruction(
&program_id,
Expand Down Expand Up @@ -211,6 +210,23 @@ fn test_permission_migration() {
Err(OracleError::PermissionViolation.into())
);

assert_eq!(
process_instruction(
&program_id,
&[
attacker_account.clone(),
price_account.clone(),
permissions_account.clone()
],
bytes_of::<SetMaxLatencyArgs>(&SetMaxLatencyArgs {
header: SetMaxLatency.into(),
max_latency: 5,
unused_: [0; 3],
})
),
Err(OracleError::PermissionViolation.into())
);

assert_eq!(
process_instruction(
&program_id,
Expand Down Expand Up @@ -256,7 +272,6 @@ fn test_permission_migration() {
Err(OracleError::PermissionViolation.into())
);


// Security authority can't change minimum number of publishers
assert_eq!(
process_instruction(
Expand All @@ -275,6 +290,24 @@ fn test_permission_migration() {
Err(OracleError::PermissionViolation.into())
);

// Security authority can't change maximum latency
assert_eq!(
process_instruction(
&program_id,
&[
security_auth_account.clone(),
price_account.clone(),
permissions_account.clone(),
],
bytes_of::<SetMaxLatencyArgs>(&SetMaxLatencyArgs {
header: SetMaxLatency.into(),
max_latency: 5,
unused_: [0; 3],
}),
),
Err(OracleError::PermissionViolation.into())
);

// Security authority can't add publishers
assert_eq!(
process_instruction(
Expand Down
91 changes: 91 additions & 0 deletions program/rust/src/tests/test_set_max_latency.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
use {
crate::{
accounts::{
PermissionAccount,
PriceAccount,
PythAccount,
},
c_oracle_header::PC_VERSION,
deserialize::{
load_checked,
load_mut,
},
instruction::{
OracleCommand,
SetMaxLatencyArgs,
},
processor::set_max_latency,
tests::test_utils::AccountSetup,
},
solana_program::{
account_info::AccountInfo,
program_error::ProgramError,
pubkey::Pubkey,
},
std::mem::size_of,
};

#[test]
fn test_set_max_latency() {
let mut instruction_data = [0u8; size_of::<SetMaxLatencyArgs>()];

let program_id = Pubkey::new_unique();

let mut funding_setup = AccountSetup::new_funding();
let funding_account = funding_setup.as_account_info();

let mut price_setup = AccountSetup::new::<PriceAccount>(&program_id);
let price_account = price_setup.as_account_info();
PriceAccount::initialize(&price_account, PC_VERSION).unwrap();

let mut permissions_setup = AccountSetup::new_permission(&program_id);
let permissions_account = permissions_setup.as_account_info();

{
let mut permissions_account_data =
PermissionAccount::initialize(&permissions_account, PC_VERSION).unwrap();
permissions_account_data.master_authority = *funding_account.key;
permissions_account_data.data_curation_authority = *funding_account.key;
permissions_account_data.security_authority = *funding_account.key;
}

assert_eq!(get_max_latency(&price_account), Ok(0));

populate_instruction(&mut instruction_data, 10);
assert!(set_max_latency(
&program_id,
&[
funding_account.clone(),
price_account.clone(),
permissions_account.clone()
],
&instruction_data
)
.is_ok());
assert_eq!(get_max_latency(&price_account), Ok(10));

populate_instruction(&mut instruction_data, 5);
assert!(set_max_latency(
&program_id,
&[
funding_account.clone(),
price_account.clone(),
permissions_account.clone()
],
&instruction_data
)
.is_ok());
assert_eq!(get_max_latency(&price_account), Ok(5));
}

// Populate the instruction data with SetMaxLatencyArgs
fn populate_instruction(instruction_data: &mut [u8], max_latency: u8) {
let mut hdr = load_mut::<SetMaxLatencyArgs>(instruction_data).unwrap();
hdr.header = OracleCommand::SetMaxLatency.into();
hdr.max_latency = max_latency;
}

// Helper function to get the max latency from a PriceAccount
fn get_max_latency(account: &AccountInfo) -> Result<u8, ProgramError> {
Ok(load_checked::<PriceAccount>(account, PC_VERSION)?.max_latency_)
}
2 changes: 2 additions & 0 deletions program/rust/src/tests/test_sizes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ use {
CommandHeader,
DelPublisherArgs,
InitPriceArgs,
SetMaxLatencyArgs,
SetMinPubArgs,
UpdPriceArgs,
},
Expand Down Expand Up @@ -98,6 +99,7 @@ fn test_sizes() {
assert_eq!(size_of::<AddPriceArgs>(), 16);
assert_eq!(size_of::<InitPriceArgs>(), 16);
assert_eq!(size_of::<SetMinPubArgs>(), 12);
assert_eq!(size_of::<SetMaxLatencyArgs>(), 12);
assert_eq!(size_of::<AddPublisherArgs>(), 40);
assert_eq!(size_of::<DelPublisherArgs>(), 40);
assert_eq!(size_of::<UpdPriceArgs>(), 40);
Expand Down

0 comments on commit 1b68e4f

Please sign in to comment.