|
6 | 6 | PriceAccountFlags,
|
7 | 7 | PythAccount,
|
8 | 8 | },
|
9 |
| - c_oracle_header::PC_VERSION, |
10 |
| - deserialize::load_checked, |
| 9 | + c_oracle_header::{PC_STATUS_TRADING, PC_VERSION}, |
| 10 | + deserialize::{load_checked, load_mut}, |
11 | 11 | instruction::{
|
12 | 12 | AddPublisherArgs,
|
13 |
| - OracleCommand, |
| 13 | + OracleCommand, UpdPriceArgs, |
14 | 14 | },
|
15 | 15 | processor::{
|
16 |
| - process_instruction, |
17 |
| - ENABLE_ACCUMULATOR_V2, |
| 16 | + process_instruction, DISABLE_ACCUMULATOR_V2, ENABLE_ACCUMULATOR_V2 |
18 | 17 | },
|
19 |
| - tests::test_utils::AccountSetup, |
20 |
| - }, |
21 |
| - bytemuck::bytes_of, |
22 |
| - solana_program::{ |
| 18 | + tests::test_utils::{update_clock_slot, AccountSetup}, |
| 19 | + }, bytemuck::bytes_of, solana_program::{ |
23 | 20 | pubkey::Pubkey,
|
24 | 21 | rent::Rent,
|
25 |
| - }, |
| 22 | + }, solana_sdk::account_info::AccountInfo, std::mem::size_of |
26 | 23 | };
|
27 | 24 |
|
28 |
| -#[test] |
29 |
| -fn test_aggregate_v2() { |
30 |
| - let program_id = Pubkey::new_unique(); |
31 |
| - let publisher = Pubkey::new_unique(); |
32 |
| - |
33 |
| - let mut cmd = AddPublisherArgs { |
34 |
| - header: OracleCommand::AddPublisher.into(), |
35 |
| - publisher, |
36 |
| - }; |
37 |
| - let mut instruction_data = bytes_of::<AddPublisherArgs>(&cmd); |
38 |
| - |
39 |
| - let mut funding_setup = AccountSetup::new_funding(); |
40 |
| - let funding_account = funding_setup.as_account_info(); |
| 25 | +struct Accounts { |
| 26 | + program_id: Pubkey, |
| 27 | + publisher_account: AccountSetup, |
| 28 | + funding_account: AccountSetup, |
| 29 | + price_account: AccountSetup, |
| 30 | + permissions_account: AccountSetup, |
| 31 | + clock_account: AccountSetup, |
| 32 | +} |
41 | 33 |
|
42 |
| - let mut price_setup = AccountSetup::new::<PriceAccount>(&program_id); |
43 |
| - let price_account = price_setup.as_account_info(); |
44 |
| - PriceAccount::initialize(&price_account, PC_VERSION).unwrap(); |
| 34 | +impl Accounts { |
| 35 | + fn new() -> Self { |
| 36 | + let program_id = Pubkey::new_unique(); |
| 37 | + let publisher_account = AccountSetup::new_funding(); |
| 38 | + let clock_account = AccountSetup::new_clock(); |
| 39 | + let mut funding_account = AccountSetup::new_funding(); |
| 40 | + let mut permissions_account = AccountSetup::new_permission(&program_id); |
| 41 | + let mut price_account = AccountSetup::new::<PriceAccount>(&program_id); |
45 | 42 |
|
46 |
| - **price_account.try_borrow_mut_lamports().unwrap() = 100; |
| 43 | + PriceAccount::initialize(&price_account.as_account_info(), PC_VERSION).unwrap(); |
47 | 44 |
|
48 |
| - let mut permissions_setup = AccountSetup::new_permission(&program_id); |
49 |
| - let permissions_account = permissions_setup.as_account_info(); |
| 45 | + { |
| 46 | + let permissions_account_info = permissions_account.as_account_info(); |
| 47 | + let mut permissions_account_data = |
| 48 | + PermissionAccount::initialize(&permissions_account_info, PC_VERSION).unwrap(); |
| 49 | + permissions_account_data.master_authority = *funding_account.as_account_info().key; |
| 50 | + permissions_account_data.data_curation_authority = *funding_account.as_account_info().key; |
| 51 | + permissions_account_data.security_authority = *funding_account.as_account_info().key; |
| 52 | + } |
50 | 53 |
|
51 |
| - { |
52 |
| - let mut permissions_account_data = |
53 |
| - PermissionAccount::initialize(&permissions_account, PC_VERSION).unwrap(); |
54 |
| - permissions_account_data.master_authority = *funding_account.key; |
55 |
| - permissions_account_data.data_curation_authority = *funding_account.key; |
56 |
| - permissions_account_data.security_authority = *funding_account.key; |
| 54 | + Self { |
| 55 | + program_id, |
| 56 | + publisher_account, |
| 57 | + funding_account, |
| 58 | + price_account, |
| 59 | + permissions_account, |
| 60 | + clock_account, |
| 61 | + } |
57 | 62 | }
|
| 63 | +} |
58 | 64 |
|
59 |
| - // Give the price account enough lamports to be rent exempt |
60 |
| - **price_account.try_borrow_mut_lamports().unwrap() = |
61 |
| - Rent::minimum_balance(&Rent::default(), PriceAccount::MINIMUM_SIZE); |
| 65 | +fn add_publisher(accounts: &mut Accounts, publisher: Option<Pubkey>) { |
| 66 | + let args = AddPublisherArgs { |
| 67 | + header: OracleCommand::AddPublisher.into(), |
| 68 | + publisher: publisher.unwrap_or(*accounts.publisher_account.as_account_info().key), |
| 69 | + }; |
62 | 70 |
|
63 | 71 | assert!(process_instruction(
|
64 |
| - &program_id, |
| 72 | + &accounts.program_id, |
65 | 73 | &[
|
66 |
| - funding_account.clone(), |
67 |
| - price_account.clone(), |
68 |
| - permissions_account.clone(), |
| 74 | + accounts.funding_account.as_account_info(), |
| 75 | + accounts.price_account.as_account_info(), |
| 76 | + accounts.permissions_account.as_account_info(), |
69 | 77 | ],
|
70 |
| - instruction_data |
| 78 | + bytes_of::<AddPublisherArgs>(&args) |
71 | 79 | )
|
72 | 80 | .is_ok());
|
| 81 | +} |
73 | 82 |
|
74 |
| - { |
75 |
| - let price_data = load_checked::<PriceAccount>(&price_account, PC_VERSION).unwrap(); |
76 |
| - assert_eq!(price_data.num_, 1); |
77 |
| - assert_eq!(price_data.header.size, PriceAccount::INITIAL_SIZE); |
78 |
| - assert!(price_data.comp_[0].pub_ == publisher); |
79 |
| - // Make sure that v2 aggregation is disabled |
80 |
| - assert!(!price_data.flags.contains(PriceAccountFlags::ACCUMULATOR_V2)); |
81 |
| - } |
| 83 | +fn update_price(accounts: &mut Accounts, price: i64, conf: u64, slot: u64) { |
| 84 | + let instruction_data = &mut [0u8; size_of::<UpdPriceArgs>()]; |
| 85 | + let mut cmd = load_mut::<UpdPriceArgs>(instruction_data).unwrap(); |
| 86 | + cmd.header = OracleCommand::UpdPrice.into(); |
| 87 | + cmd.status = PC_STATUS_TRADING; |
| 88 | + cmd.price = price; |
| 89 | + cmd.confidence = conf; |
| 90 | + cmd.publishing_slot = slot; |
| 91 | + cmd.unused_ = 0; |
82 | 92 |
|
83 |
| - cmd.publisher = ENABLE_ACCUMULATOR_V2.into(); |
84 |
| - instruction_data = bytes_of::<AddPublisherArgs>(&cmd); |
85 |
| - assert!(process_instruction( |
86 |
| - &program_id, |
| 93 | + let mut clock = accounts.clock_account.as_account_info(); |
| 94 | + clock.is_signer = false; |
| 95 | + clock.is_writable = false; |
| 96 | + |
| 97 | + process_instruction( |
| 98 | + &accounts.program_id, |
87 | 99 | &[
|
88 |
| - funding_account.clone(), |
89 |
| - price_account.clone(), |
90 |
| - permissions_account.clone(), |
| 100 | + accounts.publisher_account.as_account_info(), |
| 101 | + accounts.price_account.as_account_info(), |
| 102 | + clock, |
91 | 103 | ],
|
92 | 104 | instruction_data
|
93 | 105 | )
|
94 |
| - .is_ok()); |
| 106 | + .unwrap(); |
| 107 | +} |
| 108 | + |
| 109 | +#[test] |
| 110 | +fn test_aggregate_v2_toggle() { |
| 111 | + let accounts = &mut Accounts::new(); |
| 112 | + |
| 113 | + // Add an initial Publisher to test with. |
| 114 | + add_publisher(accounts, None); |
| 115 | + |
| 116 | + // Update the price, no aggregation will happen on the first slot. |
| 117 | + { |
| 118 | + update_clock_slot(&mut accounts.clock_account.as_account_info(), 1); |
| 119 | + update_price(accounts, 42, 2, 1); |
| 120 | + let info = accounts.price_account.as_account_info(); |
| 121 | + let price_data = load_checked::<PriceAccount>(&info, PC_VERSION).unwrap(); |
| 122 | + assert_eq!(price_data.last_slot_, 0); |
| 123 | + assert!(!price_data.flags.contains(PriceAccountFlags::ACCUMULATOR_V2)); |
| 124 | + } |
| 125 | + |
| 126 | + // Update again, component is now TRADING so aggregation should trigger. |
| 127 | + { |
| 128 | + update_clock_slot(&mut accounts.clock_account.as_account_info(), 2); |
| 129 | + update_price(accounts, 42, 2, 2); |
| 130 | + let info = accounts.price_account.as_account_info(); |
| 131 | + let price_data = load_checked::<PriceAccount>(&info, PC_VERSION).unwrap(); |
| 132 | + assert_eq!(price_data.last_slot_, 2); |
| 133 | + assert!(!price_data.flags.contains(PriceAccountFlags::ACCUMULATOR_V2)); |
| 134 | + } |
95 | 135 |
|
96 |
| - // Make sure that v2 aggregation is enabled |
| 136 | + // Enable v2 Aggregation |
| 137 | + add_publisher(accounts, Some(ENABLE_ACCUMULATOR_V2.into())); |
| 138 | + |
| 139 | + // Update again, with accumulator bit set, aggregation should not have |
| 140 | + // happened, as its now the validators job. |
97 | 141 | {
|
98 |
| - let price_data = load_checked::<PriceAccount>(&price_account, PC_VERSION).unwrap(); |
| 142 | + update_clock_slot(&mut accounts.clock_account.as_account_info(), 3); |
| 143 | + update_price(accounts, 42, 2, 3); |
| 144 | + let info = accounts.price_account.as_account_info(); |
| 145 | + let price_data = load_checked::<PriceAccount>(&info, PC_VERSION).unwrap(); |
| 146 | + assert_eq!(price_data.last_slot_, 2); |
99 | 147 | assert!(price_data.flags.contains(PriceAccountFlags::ACCUMULATOR_V2));
|
100 | 148 | }
|
| 149 | + |
| 150 | + add_publisher(accounts, Some(DISABLE_ACCUMULATOR_V2.into())); |
| 151 | + |
| 152 | + // Confirm disabling v2 Aggregation re-enables the aggregation flow. |
| 153 | + { |
| 154 | + update_clock_slot(&mut accounts.clock_account.as_account_info(), 4); |
| 155 | + update_price(accounts, 42, 2, 4); |
| 156 | + let info = accounts.price_account.as_account_info(); |
| 157 | + let price_data = load_checked::<PriceAccount>(&info, PC_VERSION).unwrap(); |
| 158 | + assert_eq!(price_data.last_slot_, 4); |
| 159 | + assert!(!price_data.flags.contains(PriceAccountFlags::ACCUMULATOR_V2)); |
| 160 | + } |
101 | 161 | }
|
0 commit comments