Skip to content

Commit 2d6b817

Browse files
committed
feat: backport ResizeMapping ix from v2.35
1 parent 94babc4 commit 2d6b817

11 files changed

+190
-31
lines changed

program/c/src/oracle/oracle.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ extern "C" {
2020
// various size constants
2121
#define PC_PUBKEY_SIZE 32
2222
#define PC_PUBKEY_SIZE_64 (PC_PUBKEY_SIZE/sizeof(uint64_t))
23-
#define PC_MAP_TABLE_SIZE 640
23+
#define PC_MAP_TABLE_SIZE 5000
2424

2525
// Total price component slots available
2626
#define PC_NUM_COMP_PYTHNET 128
@@ -117,7 +117,7 @@ typedef struct pc_map_table
117117
pc_pub_key_t prod_[PC_MAP_TABLE_SIZE]; // product accounts
118118
} pc_map_table_t;
119119

120-
static_assert( sizeof( pc_map_table_t ) == 20536, "" );
120+
static_assert( sizeof( pc_map_table_t ) == 160056, "" );
121121

122122
// variable length string
123123
typedef struct pc_str

program/rust/src/error.rs

+2
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ pub enum OracleError {
5252
PermissionViolation = 619,
5353
#[error("NeedsSuccesfulAggregation")]
5454
NeedsSuccesfulAggregation = 620,
55+
#[error("NoNeedToResize")]
56+
NoNeedToResize = 623,
5557
}
5658

5759
impl From<OracleError> for ProgramError {

program/rust/src/instruction.rs

+3
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,9 @@ pub enum OracleCommand {
103103
// account[0] funding account [signer writable]
104104
// account[1] price account [signer writable]
105105
SetMaxLatency = 18,
106+
107+
// account[0] mapping account [writable]
108+
ResizeMapping = 20,
106109
}
107110

108111
#[repr(C)]

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 resize_mapping;
2425
mod set_max_latency;
2526
mod set_min_pub;
2627
mod upd_permissions;
@@ -41,6 +42,7 @@ pub use {
4142
del_publisher::del_publisher,
4243
init_mapping::init_mapping,
4344
init_price::init_price,
45+
resize_mapping::resize_mapping,
4446
set_max_latency::set_max_latency,
4547
set_min_pub::set_min_pub,
4648
upd_permissions::upd_permissions,
@@ -84,5 +86,6 @@ pub fn process_instruction(
8486
DelProduct => del_product(program_id, accounts, instruction_data),
8587
UpdPermissions => upd_permissions(program_id, accounts, instruction_data),
8688
SetMaxLatency => set_max_latency(program_id, accounts, instruction_data),
89+
ResizeMapping => resize_mapping(program_id, accounts, instruction_data),
8790
}
8891
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
use {
2+
crate::{
3+
accounts::{
4+
AccountHeader,
5+
MappingAccount,
6+
PythAccount,
7+
},
8+
c_oracle_header::PC_MAGIC,
9+
deserialize::{
10+
load,
11+
load_account_as,
12+
},
13+
instruction::CommandHeader,
14+
utils::{
15+
check_valid_writable_account,
16+
pyth_assert,
17+
},
18+
OracleError,
19+
},
20+
solana_program::{
21+
account_info::AccountInfo,
22+
entrypoint::{
23+
ProgramResult,
24+
MAX_PERMITTED_DATA_INCREASE,
25+
},
26+
pubkey::Pubkey,
27+
},
28+
std::{
29+
cmp::min,
30+
mem::size_of,
31+
},
32+
};
33+
34+
/// Resize mapping account.
35+
// account[0] mapping account [writable]
36+
pub fn resize_mapping(
37+
program_id: &Pubkey,
38+
accounts: &[AccountInfo],
39+
instruction_data: &[u8],
40+
) -> ProgramResult {
41+
let mapping_account = match accounts {
42+
[x] => Ok(x),
43+
_ => Err(OracleError::InvalidNumberOfAccounts),
44+
}?;
45+
46+
let hdr = load::<CommandHeader>(instruction_data)?;
47+
48+
check_valid_writable_account(program_id, mapping_account)?;
49+
50+
{
51+
let account_header = load_account_as::<AccountHeader>(mapping_account)?;
52+
pyth_assert(
53+
account_header.magic_number == PC_MAGIC
54+
&& account_header.version == hdr.version
55+
&& account_header.account_type == MappingAccount::ACCOUNT_TYPE,
56+
OracleError::InvalidAccountHeader.into(),
57+
)?;
58+
}
59+
60+
pyth_assert(
61+
mapping_account.data_len() < size_of::<MappingAccount>(),
62+
OracleError::NoNeedToResize.into(),
63+
)?;
64+
let new_size = min(
65+
size_of::<MappingAccount>(),
66+
mapping_account.data_len() + MAX_PERMITTED_DATA_INCREASE,
67+
);
68+
mapping_account.realloc(new_size, true)?;
69+
70+
Ok(())
71+
}

program/rust/src/tests/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ mod test_message;
1717
mod test_permission_migration;
1818
mod test_publish;
1919
mod test_publish_batch;
20+
mod test_resize_mapping;
2021
mod test_set_max_latency;
2122
mod test_set_min_pub;
2223
mod test_sizes;

program/rust/src/tests/pyth_simulator.rs

+25
Original file line numberDiff line numberDiff line change
@@ -478,6 +478,31 @@ impl PythSimulator {
478478
.map(|_| permissions_pubkey)
479479
}
480480

481+
pub async fn truncate_account(&mut self, key: Pubkey, size: usize) {
482+
let mut account = self.get_account(key).await.unwrap();
483+
account.data.truncate(size);
484+
self.context.set_account(&key, &account.into());
485+
}
486+
487+
pub async fn resize_mapping(
488+
&mut self,
489+
mapping_keypair: &Keypair,
490+
) -> Result<(), BanksClientError> {
491+
let cmd: CommandHeader = OracleCommand::ResizeMapping.into();
492+
let instruction = Instruction::new_with_bytes(
493+
self.program_id,
494+
bytes_of(&cmd),
495+
vec![AccountMeta::new(mapping_keypair.pubkey(), false)],
496+
);
497+
498+
self.process_ixs(
499+
&[instruction],
500+
&vec![],
501+
&copy_keypair(&self.genesis_keypair),
502+
)
503+
.await
504+
}
505+
481506
/// Get the account at `key`. Returns `None` if no such account exists.
482507
pub async fn get_account(&mut self, key: Pubkey) -> Option<Account> {
483508
self.context.banks_client.get_account(key).await.unwrap()

program/rust/src/tests/test_del_product.rs

+22-22
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use {
22
crate::{
33
accounts::MappingAccount,
4+
deserialize::load,
45
tests::pyth_simulator::PythSimulator,
56
},
67
solana_program::pubkey::Pubkey,
@@ -41,32 +42,31 @@ async fn test_del_product() {
4142

4243
assert!(sim.get_account(product2.pubkey()).await.is_none());
4344

44-
let mapping_data = sim
45-
.get_account_data_as::<MappingAccount>(mapping_keypair.pubkey())
46-
.await
47-
.unwrap();
48-
assert!(mapping_product_list_equals(
49-
&mapping_data,
50-
vec![
51-
product1.pubkey(),
52-
product5.pubkey(),
53-
product3.pubkey(),
54-
product4.pubkey()
55-
]
56-
));
45+
{
46+
let mapping_account = sim.get_account(mapping_keypair.pubkey()).await.unwrap();
47+
let mapping_data = load::<MappingAccount>(&mapping_account.data).unwrap();
48+
assert!(mapping_product_list_equals(
49+
mapping_data,
50+
vec![
51+
product1.pubkey(),
52+
product5.pubkey(),
53+
product3.pubkey(),
54+
product4.pubkey()
55+
]
56+
));
57+
}
5758
assert!(sim.get_account(product5.pubkey()).await.is_some());
5859

5960

6061
assert!(sim.del_product(&mapping_keypair, &product4).await.is_ok());
61-
let mapping_data = sim
62-
.get_account_data_as::<MappingAccount>(mapping_keypair.pubkey())
63-
.await
64-
.unwrap();
65-
66-
assert!(mapping_product_list_equals(
67-
&mapping_data,
68-
vec![product1.pubkey(), product5.pubkey(), product3.pubkey()]
69-
));
62+
{
63+
let mapping_account = sim.get_account(mapping_keypair.pubkey()).await.unwrap();
64+
let mapping_data = load::<MappingAccount>(&mapping_account.data).unwrap();
65+
assert!(mapping_product_list_equals(
66+
mapping_data,
67+
vec![product1.pubkey(), product5.pubkey(), product3.pubkey()]
68+
));
69+
}
7070
}
7171

7272
/// Returns true if the list of products in `mapping_data` contains the keys in `expected` (in the
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
use {
2+
super::pyth_simulator::PythSimulator,
3+
crate::{
4+
accounts::MappingAccount,
5+
error::OracleError,
6+
},
7+
solana_sdk::{
8+
instruction::InstructionError,
9+
signer::Signer,
10+
transaction::TransactionError,
11+
},
12+
std::mem::size_of,
13+
};
14+
15+
16+
#[tokio::test]
17+
async fn test_resize_mapping() {
18+
let mut sim = PythSimulator::new().await;
19+
let mapping_keypair = sim.init_mapping().await.unwrap();
20+
assert_eq!(
21+
sim.resize_mapping(&mapping_keypair)
22+
.await
23+
.unwrap_err()
24+
.unwrap(),
25+
TransactionError::InstructionError(
26+
0,
27+
InstructionError::Custom(OracleError::NoNeedToResize as u32)
28+
)
29+
);
30+
31+
sim.truncate_account(mapping_keypair.pubkey(), 20536).await; // Old size.
32+
for i in 0..14 {
33+
println!("resize #{i}");
34+
sim.resize_mapping(&mapping_keypair).await.unwrap();
35+
}
36+
assert_eq!(
37+
sim.resize_mapping(&mapping_keypair)
38+
.await
39+
.unwrap_err()
40+
.unwrap(),
41+
TransactionError::InstructionError(
42+
0,
43+
InstructionError::Custom(OracleError::NoNeedToResize as u32)
44+
)
45+
);
46+
assert_eq!(
47+
sim.get_account(mapping_keypair.pubkey())
48+
.await
49+
.unwrap()
50+
.data
51+
.len(),
52+
size_of::<MappingAccount>()
53+
);
54+
}

program/rust/src/tests/test_sizes.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ fn test_sizes() {
8686
assert_eq!(size_of::<UpdPriceArgs>(), 40);
8787
assert_eq!(size_of::<Pubkey>(), 32);
8888
assert_eq!(size_of::<AccountHeader>(), 16);
89-
assert_eq!(size_of::<MappingAccount>(), 20536);
89+
assert_eq!(size_of::<MappingAccount>(), 160056);
9090
assert_eq!(size_of::<ProductAccount>(), 48);
9191
assert_eq!(size_of::<PriceComponent>(), 96);
9292
assert_eq!(size_of::<PriceEma>(), 24);

program/rust/src/tests/test_utils.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ use {
3434
solana_sdk::transaction::TransactionError,
3535
};
3636

37-
const UPPER_BOUND_OF_ALL_ACCOUNT_SIZES: usize = 20536;
37+
const UPPER_BOUND_OF_ALL_ACCOUNT_SIZES: usize = 160056;
3838

3939
/// The goal of this struct is to easily instantiate fresh solana accounts
4040
/// for the Pyth program to use in tests.
@@ -50,7 +50,7 @@ pub struct AccountSetup {
5050
owner: Pubkey,
5151
balance: u64,
5252
size: usize,
53-
data: [u8; UPPER_BOUND_OF_ALL_ACCOUNT_SIZES],
53+
data: Vec<u8>,
5454
}
5555

5656
impl AccountSetup {
@@ -59,7 +59,7 @@ impl AccountSetup {
5959
let owner = *owner;
6060
let balance = Rent::minimum_balance(&Rent::default(), T::MINIMUM_SIZE);
6161
let size = T::MINIMUM_SIZE;
62-
let data = [0; UPPER_BOUND_OF_ALL_ACCOUNT_SIZES];
62+
let data = vec![0; UPPER_BOUND_OF_ALL_ACCOUNT_SIZES];
6363
AccountSetup {
6464
key,
6565
owner,
@@ -74,7 +74,7 @@ impl AccountSetup {
7474
let owner = system_program::id();
7575
let balance = LAMPORTS_PER_SOL;
7676
let size = 0;
77-
let data = [0; UPPER_BOUND_OF_ALL_ACCOUNT_SIZES];
77+
let data = vec![0; UPPER_BOUND_OF_ALL_ACCOUNT_SIZES];
7878
AccountSetup {
7979
key,
8080
owner,
@@ -89,7 +89,7 @@ impl AccountSetup {
8989
let owner = *owner;
9090
let balance = Rent::minimum_balance(&Rent::default(), PermissionAccount::MINIMUM_SIZE);
9191
let size = PermissionAccount::MINIMUM_SIZE;
92-
let data = [0; UPPER_BOUND_OF_ALL_ACCOUNT_SIZES];
92+
let data = vec![0; UPPER_BOUND_OF_ALL_ACCOUNT_SIZES];
9393
AccountSetup {
9494
key,
9595
owner,
@@ -104,7 +104,7 @@ impl AccountSetup {
104104
let owner = sysvar::id();
105105
let balance = Rent::minimum_balance(&Rent::default(), clock::Clock::size_of());
106106
let size = clock::Clock::size_of();
107-
let data = [0u8; UPPER_BOUND_OF_ALL_ACCOUNT_SIZES];
107+
let data = vec![0u8; UPPER_BOUND_OF_ALL_ACCOUNT_SIZES];
108108
AccountSetup {
109109
key,
110110
owner,

0 commit comments

Comments
 (0)