Skip to content

Commit

Permalink
feat: resize mapping account
Browse files Browse the repository at this point in the history
  • Loading branch information
Riateche committed Sep 9, 2024
1 parent e1457a8 commit 20731e9
Show file tree
Hide file tree
Showing 11 changed files with 190 additions and 31 deletions.
4 changes: 2 additions & 2 deletions program/c/src/oracle/oracle.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ extern "C" {
// various size constants
#define PC_PUBKEY_SIZE 32
#define PC_PUBKEY_SIZE_64 (PC_PUBKEY_SIZE/sizeof(uint64_t))
#define PC_MAP_TABLE_SIZE 640
#define PC_MAP_TABLE_SIZE 5000

// Total price component slots available
#define PC_NUM_COMP_PYTHNET 128
Expand Down Expand Up @@ -117,7 +117,7 @@ typedef struct pc_map_table
pc_pub_key_t prod_[PC_MAP_TABLE_SIZE]; // product accounts
} pc_map_table_t;

static_assert( sizeof( pc_map_table_t ) == 20536, "" );
static_assert( sizeof( pc_map_table_t ) == 160056, "" );

// variable length string
typedef struct pc_str
Expand Down
2 changes: 2 additions & 0 deletions program/rust/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ pub enum OracleError {
MaxLastFeedIndexReached = 621,
#[error("FeedIndexAlreadyInitialized")]
FeedIndexAlreadyInitialized = 622,
#[error("NoNeedToResize")]
NoNeedToResize = 623,
}

impl From<OracleError> for ProgramError {
Expand Down
3 changes: 3 additions & 0 deletions program/rust/src/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,9 @@ pub enum OracleCommand {
// account[1] price account [writable]
// account[2] permissions account [writable]
InitPriceFeedIndex = 19,
// account[0] funding account [signer writable]
// account[1] mapping account [writable]
ResizeMapping = 20,
}

#[repr(C)]
Expand Down
3 changes: 3 additions & 0 deletions program/rust/src/processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ mod del_publisher;
mod init_mapping;
mod init_price;
mod init_price_feed_index;
mod resize_mapping;
mod set_max_latency;
mod set_min_pub;
mod upd_permissions;
Expand All @@ -53,6 +54,7 @@ pub use {
del_publisher::del_publisher,
init_mapping::init_mapping,
init_price::init_price,
resize_mapping::resize_mapping,
set_max_latency::set_max_latency,
set_min_pub::set_min_pub,
upd_permissions::upd_permissions,
Expand Down Expand Up @@ -106,6 +108,7 @@ pub fn process_instruction(
UpdPermissions => upd_permissions(program_id, accounts, instruction_data),
SetMaxLatency => set_max_latency(program_id, accounts, instruction_data),
InitPriceFeedIndex => init_price_feed_index(program_id, accounts, instruction_data),
ResizeMapping => resize_mapping(program_id, accounts, instruction_data),
}
}

Expand Down
71 changes: 71 additions & 0 deletions program/rust/src/processor/resize_mapping.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
use {
crate::{
accounts::{
AccountHeader,
MappingAccount,
PythAccount,
},
c_oracle_header::PC_MAGIC,
deserialize::{
load,
load_account_as,
},
instruction::CommandHeader,
utils::{
check_valid_writable_account,
pyth_assert,
},
OracleError,
},
solana_program::{
account_info::AccountInfo,
entrypoint::{
ProgramResult,
MAX_PERMITTED_DATA_INCREASE,
},
pubkey::Pubkey,
},
std::{
cmp::min,
mem::size_of,
},
};

/// Resize mapping account.
// account[1] mapping account [writable]
pub fn resize_mapping(
program_id: &Pubkey,
accounts: &[AccountInfo],
instruction_data: &[u8],
) -> ProgramResult {
let mapping_account = match accounts {
[x] => Ok(x),
_ => Err(OracleError::InvalidNumberOfAccounts),
}?;

let hdr = load::<CommandHeader>(instruction_data)?;

check_valid_writable_account(program_id, mapping_account)?;

{
let account_header = load_account_as::<AccountHeader>(mapping_account)?;
pyth_assert(
account_header.magic_number == PC_MAGIC
&& account_header.version == hdr.version
&& account_header.account_type == MappingAccount::ACCOUNT_TYPE,
OracleError::InvalidAccountHeader.into(),
)?;
}

pyth_assert(
mapping_account.data_len() < size_of::<MappingAccount>(),
OracleError::NoNeedToResize.into(),
)?;
let new_size = min(
size_of::<MappingAccount>(),
mapping_account.data_len() + MAX_PERMITTED_DATA_INCREASE,
);
mapping_account.realloc(new_size, true)?;

Ok(())
}
1 change: 1 addition & 0 deletions program/rust/src/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ mod test_message;
mod test_permission_migration;
mod test_publish;
mod test_publish_batch;
mod test_resize_mapping;
mod test_set_max_latency;
mod test_set_min_pub;
mod test_sizes;
Expand Down
25 changes: 25 additions & 0 deletions program/rust/src/tests/pyth_simulator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,31 @@ impl PythSimulator {
.map(|_| permissions_pubkey)
}

pub async fn truncate_account(&mut self, key: Pubkey, size: usize) {
let mut account = self.get_account(key).await.unwrap();
account.data.truncate(size);
self.context.set_account(&key, &account.into());
}

pub async fn resize_mapping(
&mut self,
mapping_keypair: &Keypair,
) -> Result<(), BanksClientError> {
let cmd: CommandHeader = OracleCommand::ResizeMapping.into();
let instruction = Instruction::new_with_bytes(
self.program_id,
bytes_of(&cmd),
vec![AccountMeta::new(mapping_keypair.pubkey(), true)],
);

self.process_ixs(
&[instruction],
&vec![mapping_keypair],
&copy_keypair(&self.genesis_keypair),
)
.await
}

/// Get the account at `key`. Returns `None` if no such account exists.
pub async fn get_account(&mut self, key: Pubkey) -> Option<Account> {
self.context.banks_client.get_account(key).await.unwrap()
Expand Down
44 changes: 22 additions & 22 deletions program/rust/src/tests/test_del_product.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use {
crate::{
accounts::MappingAccount,
deserialize::load,
tests::pyth_simulator::PythSimulator,
},
solana_program::pubkey::Pubkey,
Expand Down Expand Up @@ -41,32 +42,31 @@ async fn test_del_product() {

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

let mapping_data = sim
.get_account_data_as::<MappingAccount>(mapping_keypair.pubkey())
.await
.unwrap();
assert!(mapping_product_list_equals(
&mapping_data,
vec![
product1.pubkey(),
product5.pubkey(),
product3.pubkey(),
product4.pubkey()
]
));
{
let mapping_account = sim.get_account(mapping_keypair.pubkey()).await.unwrap();
let mapping_data = load::<MappingAccount>(&mapping_account.data).unwrap();
assert!(mapping_product_list_equals(
&mapping_data,
vec![
product1.pubkey(),
product5.pubkey(),
product3.pubkey(),
product4.pubkey()
]
));
}
assert!(sim.get_account(product5.pubkey()).await.is_some());


assert!(sim.del_product(&mapping_keypair, &product4).await.is_ok());
let mapping_data = sim
.get_account_data_as::<MappingAccount>(mapping_keypair.pubkey())
.await
.unwrap();

assert!(mapping_product_list_equals(
&mapping_data,
vec![product1.pubkey(), product5.pubkey(), product3.pubkey()]
));
{
let mapping_account = sim.get_account(mapping_keypair.pubkey()).await.unwrap();
let mapping_data = load::<MappingAccount>(&mapping_account.data).unwrap();
assert!(mapping_product_list_equals(
&mapping_data,
vec![product1.pubkey(), product5.pubkey(), product3.pubkey()]
));
}
}

/// Returns true if the list of products in `mapping_data` contains the keys in `expected` (in the
Expand Down
54 changes: 54 additions & 0 deletions program/rust/src/tests/test_resize_mapping.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
use {
super::pyth_simulator::PythSimulator,
crate::{
accounts::MappingAccount,
error::OracleError,
},
solana_sdk::{
instruction::InstructionError,
signer::Signer,
transaction::TransactionError,
},
std::mem::size_of,
};


#[tokio::test]
async fn test_resize_mapping() {
let mut sim = PythSimulator::new().await;
let mapping_keypair = sim.init_mapping().await.unwrap();
assert_eq!(
sim.resize_mapping(&mapping_keypair)
.await
.unwrap_err()
.unwrap(),
TransactionError::InstructionError(
0,
InstructionError::Custom(OracleError::NoNeedToResize as u32)
)
);

sim.truncate_account(mapping_keypair.pubkey(), 20536).await; // Old size.
for i in 0..14 {
println!("resize #{i}");
sim.resize_mapping(&mapping_keypair).await.unwrap();
}
assert_eq!(
sim.resize_mapping(&mapping_keypair)
.await
.unwrap_err()
.unwrap(),
TransactionError::InstructionError(
0,
InstructionError::Custom(OracleError::NoNeedToResize as u32)
)
);
assert_eq!(
sim.get_account(mapping_keypair.pubkey())
.await
.unwrap()
.data
.len(),
size_of::<MappingAccount>()
);
}
2 changes: 1 addition & 1 deletion program/rust/src/tests/test_sizes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ fn test_sizes() {
assert_eq!(size_of::<UpdPriceArgs>(), 40);
assert_eq!(size_of::<Pubkey>(), 32);
assert_eq!(size_of::<AccountHeader>(), 16);
assert_eq!(size_of::<MappingAccount>(), 20536);
assert_eq!(size_of::<MappingAccount>(), 160056);
assert_eq!(size_of::<ProductAccount>(), 48);
assert_eq!(size_of::<PriceComponent>(), 96);
assert_eq!(size_of::<PriceEma>(), 24);
Expand Down
12 changes: 6 additions & 6 deletions program/rust/src/tests/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ use {
solana_sdk::transaction::TransactionError,
};

const UPPER_BOUND_OF_ALL_ACCOUNT_SIZES: usize = 20536;
const UPPER_BOUND_OF_ALL_ACCOUNT_SIZES: usize = 160056;

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

impl AccountSetup {
Expand All @@ -59,7 +59,7 @@ impl AccountSetup {
let owner = *owner;
let balance = Rent::minimum_balance(&Rent::default(), T::MINIMUM_SIZE);
let size = T::MINIMUM_SIZE;
let data = [0; UPPER_BOUND_OF_ALL_ACCOUNT_SIZES];
let data = vec![0; UPPER_BOUND_OF_ALL_ACCOUNT_SIZES];
AccountSetup {
key,
owner,
Expand All @@ -74,7 +74,7 @@ impl AccountSetup {
let owner = system_program::id();
let balance = LAMPORTS_PER_SOL;
let size = 0;
let data = [0; UPPER_BOUND_OF_ALL_ACCOUNT_SIZES];
let data = vec![0; UPPER_BOUND_OF_ALL_ACCOUNT_SIZES];
AccountSetup {
key,
owner,
Expand All @@ -89,7 +89,7 @@ impl AccountSetup {
let owner = *owner;
let balance = Rent::minimum_balance(&Rent::default(), PermissionAccount::NEW_ACCOUNT_SPACE);
let size = PermissionAccount::NEW_ACCOUNT_SPACE;
let data = [0; UPPER_BOUND_OF_ALL_ACCOUNT_SIZES];
let data = vec![0; UPPER_BOUND_OF_ALL_ACCOUNT_SIZES];
AccountSetup {
key,
owner,
Expand All @@ -104,7 +104,7 @@ impl AccountSetup {
let owner = sysvar::id();
let balance = Rent::minimum_balance(&Rent::default(), clock::Clock::size_of());
let size = clock::Clock::size_of();
let data = [0u8; UPPER_BOUND_OF_ALL_ACCOUNT_SIZES];
let data = vec![0u8; UPPER_BOUND_OF_ALL_ACCOUNT_SIZES];
AccountSetup {
key,
owner,
Expand Down

0 comments on commit 20731e9

Please sign in to comment.