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