Skip to content

Commit 1b68e4f

Browse files
authored
feat: add SetMaxLatency instruction (#395)
* add SetMaxLatency instruction * Add SetMaxLatencyArgs struct to test_sizes.rs
1 parent 854c72e commit 1b68e4f

8 files changed

+209
-6
lines changed

.gitignore

+4-1
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,7 @@ cmake-build-*
77

88
# IntelliJ / CLion configuration
99
.idea
10-
*.iml
10+
*.iml
11+
12+
# CMake files
13+
features.h

program/rust/src/instruction.rs

+12
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,10 @@ pub enum OracleCommand {
9999
// key[2] permissions account [writable]
100100
// key[3] system program []
101101
UpdPermissions = 17,
102+
/// Set max latency
103+
// account[0] funding account [signer writable]
104+
// account[1] price account [signer writable]
105+
SetMaxLatency = 18,
102106
}
103107

104108
#[repr(C)]
@@ -162,3 +166,11 @@ pub struct UpdPermissionsArgs {
162166
pub data_curation_authority: Pubkey,
163167
pub security_authority: Pubkey,
164168
}
169+
170+
#[repr(C)]
171+
#[derive(Zeroable, Clone, Copy, Pod)]
172+
pub struct SetMaxLatencyArgs {
173+
pub header: CommandHeader,
174+
pub max_latency: u8,
175+
pub unused_: [u8; 3],
176+
}

program/rust/src/processor.rs

+3
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ mod del_product;
2121
mod del_publisher;
2222
mod init_mapping;
2323
mod init_price;
24+
mod set_max_latency;
2425
mod set_min_pub;
2526
mod upd_permissions;
2627
mod upd_price;
@@ -35,6 +36,7 @@ pub use {
3536
del_publisher::del_publisher,
3637
init_mapping::init_mapping,
3738
init_price::init_price,
39+
set_max_latency::set_max_latency,
3840
set_min_pub::set_min_pub,
3941
upd_permissions::upd_permissions,
4042
upd_price::{
@@ -76,5 +78,6 @@ pub fn process_instruction(
7678
DelPrice => del_price(program_id, accounts, instruction_data),
7779
DelProduct => del_product(program_id, accounts, instruction_data),
7880
UpdPermissions => upd_permissions(program_id, accounts, instruction_data),
81+
SetMaxLatency => set_max_latency(program_id, accounts, instruction_data),
7982
}
8083
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
use {
2+
crate::{
3+
accounts::PriceAccount,
4+
deserialize::{
5+
load,
6+
load_checked,
7+
},
8+
instruction::SetMaxLatencyArgs,
9+
utils::{
10+
check_valid_funding_account,
11+
check_valid_signable_account_or_permissioned_funding_account,
12+
pyth_assert,
13+
},
14+
OracleError,
15+
},
16+
solana_program::{
17+
account_info::AccountInfo,
18+
entrypoint::ProgramResult,
19+
program_error::ProgramError,
20+
pubkey::Pubkey,
21+
},
22+
std::mem::size_of,
23+
};
24+
25+
/// Set max latency
26+
// account[0] funding account [signer writable]
27+
// account[1] price account [signer writable]
28+
pub fn set_max_latency(
29+
program_id: &Pubkey,
30+
accounts: &[AccountInfo],
31+
instruction_data: &[u8],
32+
) -> ProgramResult {
33+
let cmd = load::<SetMaxLatencyArgs>(instruction_data)?; // Loading SetMaxLatencyArgs
34+
35+
pyth_assert(
36+
instruction_data.len() == size_of::<SetMaxLatencyArgs>(), // Checking size of SetMaxLatencyArgs
37+
ProgramError::InvalidArgument,
38+
)?;
39+
40+
let (funding_account, price_account, permissions_account_option) = match accounts {
41+
[x, y] => Ok((x, y, None)),
42+
[x, y, p] => Ok((x, y, Some(p))),
43+
_ => Err(OracleError::InvalidNumberOfAccounts),
44+
}?;
45+
46+
check_valid_funding_account(funding_account)?;
47+
check_valid_signable_account_or_permissioned_funding_account(
48+
program_id,
49+
price_account,
50+
funding_account,
51+
permissions_account_option,
52+
&cmd.header,
53+
)?;
54+
55+
let mut price_account_data = load_checked::<PriceAccount>(price_account, cmd.header.version)?;
56+
price_account_data.max_latency_ = cmd.max_latency;
57+
58+
Ok(())
59+
}

program/rust/src/tests/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ mod test_message;
1616
mod test_permission_migration;
1717
mod test_publish;
1818
mod test_publish_batch;
19+
mod test_set_max_latency;
1920
mod test_set_min_pub;
2021
mod test_sizes;
2122
mod test_upd_aggregate;
@@ -25,7 +26,6 @@ mod test_upd_price_no_fail_on_error;
2526
mod test_upd_product;
2627
mod test_utils;
2728

28-
2929
#[cfg(feature = "pythnet")]
3030
mod test_twap;
3131
#[cfg(feature = "pythnet")]

program/rust/src/tests/test_permission_migration.rs

+37-4
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,11 @@ use {
2424
DelPublisher,
2525
InitMapping,
2626
InitPrice,
27+
SetMaxLatency,
2728
SetMinPub,
2829
UpdProduct,
2930
},
31+
SetMaxLatencyArgs,
3032
SetMinPubArgs,
3133
},
3234
processor::process_instruction,
@@ -65,13 +67,11 @@ fn test_permission_migration() {
6567
let mut price_account = price_setup.as_account_info();
6668
PriceAccount::initialize(&price_account, PC_VERSION).unwrap();
6769

68-
6970
product_account.is_signer = false;
7071
mapping_account.is_signer = false;
7172
price_account.is_signer = false;
7273
next_mapping_account.is_signer = false;
7374

74-
7575
{
7676
let mut permissions_account_data =
7777
PermissionAccount::initialize(&permissions_account, PC_VERSION).unwrap();
@@ -92,7 +92,6 @@ fn test_permission_migration() {
9292
Err(OracleError::PermissionViolation.into())
9393
);
9494

95-
9695
assert_eq!(
9796
process_instruction(
9897
&program_id,
@@ -211,6 +210,23 @@ fn test_permission_migration() {
211210
Err(OracleError::PermissionViolation.into())
212211
);
213212

213+
assert_eq!(
214+
process_instruction(
215+
&program_id,
216+
&[
217+
attacker_account.clone(),
218+
price_account.clone(),
219+
permissions_account.clone()
220+
],
221+
bytes_of::<SetMaxLatencyArgs>(&SetMaxLatencyArgs {
222+
header: SetMaxLatency.into(),
223+
max_latency: 5,
224+
unused_: [0; 3],
225+
})
226+
),
227+
Err(OracleError::PermissionViolation.into())
228+
);
229+
214230
assert_eq!(
215231
process_instruction(
216232
&program_id,
@@ -256,7 +272,6 @@ fn test_permission_migration() {
256272
Err(OracleError::PermissionViolation.into())
257273
);
258274

259-
260275
// Security authority can't change minimum number of publishers
261276
assert_eq!(
262277
process_instruction(
@@ -275,6 +290,24 @@ fn test_permission_migration() {
275290
Err(OracleError::PermissionViolation.into())
276291
);
277292

293+
// Security authority can't change maximum latency
294+
assert_eq!(
295+
process_instruction(
296+
&program_id,
297+
&[
298+
security_auth_account.clone(),
299+
price_account.clone(),
300+
permissions_account.clone(),
301+
],
302+
bytes_of::<SetMaxLatencyArgs>(&SetMaxLatencyArgs {
303+
header: SetMaxLatency.into(),
304+
max_latency: 5,
305+
unused_: [0; 3],
306+
}),
307+
),
308+
Err(OracleError::PermissionViolation.into())
309+
);
310+
278311
// Security authority can't add publishers
279312
assert_eq!(
280313
process_instruction(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
use {
2+
crate::{
3+
accounts::{
4+
PermissionAccount,
5+
PriceAccount,
6+
PythAccount,
7+
},
8+
c_oracle_header::PC_VERSION,
9+
deserialize::{
10+
load_checked,
11+
load_mut,
12+
},
13+
instruction::{
14+
OracleCommand,
15+
SetMaxLatencyArgs,
16+
},
17+
processor::set_max_latency,
18+
tests::test_utils::AccountSetup,
19+
},
20+
solana_program::{
21+
account_info::AccountInfo,
22+
program_error::ProgramError,
23+
pubkey::Pubkey,
24+
},
25+
std::mem::size_of,
26+
};
27+
28+
#[test]
29+
fn test_set_max_latency() {
30+
let mut instruction_data = [0u8; size_of::<SetMaxLatencyArgs>()];
31+
32+
let program_id = Pubkey::new_unique();
33+
34+
let mut funding_setup = AccountSetup::new_funding();
35+
let funding_account = funding_setup.as_account_info();
36+
37+
let mut price_setup = AccountSetup::new::<PriceAccount>(&program_id);
38+
let price_account = price_setup.as_account_info();
39+
PriceAccount::initialize(&price_account, PC_VERSION).unwrap();
40+
41+
let mut permissions_setup = AccountSetup::new_permission(&program_id);
42+
let permissions_account = permissions_setup.as_account_info();
43+
44+
{
45+
let mut permissions_account_data =
46+
PermissionAccount::initialize(&permissions_account, PC_VERSION).unwrap();
47+
permissions_account_data.master_authority = *funding_account.key;
48+
permissions_account_data.data_curation_authority = *funding_account.key;
49+
permissions_account_data.security_authority = *funding_account.key;
50+
}
51+
52+
assert_eq!(get_max_latency(&price_account), Ok(0));
53+
54+
populate_instruction(&mut instruction_data, 10);
55+
assert!(set_max_latency(
56+
&program_id,
57+
&[
58+
funding_account.clone(),
59+
price_account.clone(),
60+
permissions_account.clone()
61+
],
62+
&instruction_data
63+
)
64+
.is_ok());
65+
assert_eq!(get_max_latency(&price_account), Ok(10));
66+
67+
populate_instruction(&mut instruction_data, 5);
68+
assert!(set_max_latency(
69+
&program_id,
70+
&[
71+
funding_account.clone(),
72+
price_account.clone(),
73+
permissions_account.clone()
74+
],
75+
&instruction_data
76+
)
77+
.is_ok());
78+
assert_eq!(get_max_latency(&price_account), Ok(5));
79+
}
80+
81+
// Populate the instruction data with SetMaxLatencyArgs
82+
fn populate_instruction(instruction_data: &mut [u8], max_latency: u8) {
83+
let mut hdr = load_mut::<SetMaxLatencyArgs>(instruction_data).unwrap();
84+
hdr.header = OracleCommand::SetMaxLatency.into();
85+
hdr.max_latency = max_latency;
86+
}
87+
88+
// Helper function to get the max latency from a PriceAccount
89+
fn get_max_latency(account: &AccountInfo) -> Result<u8, ProgramError> {
90+
Ok(load_checked::<PriceAccount>(account, PC_VERSION)?.max_latency_)
91+
}

program/rust/src/tests/test_sizes.rs

+2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ use {
2727
CommandHeader,
2828
DelPublisherArgs,
2929
InitPriceArgs,
30+
SetMaxLatencyArgs,
3031
SetMinPubArgs,
3132
UpdPriceArgs,
3233
},
@@ -98,6 +99,7 @@ fn test_sizes() {
9899
assert_eq!(size_of::<AddPriceArgs>(), 16);
99100
assert_eq!(size_of::<InitPriceArgs>(), 16);
100101
assert_eq!(size_of::<SetMinPubArgs>(), 12);
102+
assert_eq!(size_of::<SetMaxLatencyArgs>(), 12);
101103
assert_eq!(size_of::<AddPublisherArgs>(), 40);
102104
assert_eq!(size_of::<DelPublisherArgs>(), 40);
103105
assert_eq!(size_of::<UpdPriceArgs>(), 40);

0 commit comments

Comments
 (0)