Skip to content

Commit 13ba56f

Browse files
authored
Simplistic trait (#232)
* Simplistic trait * Remove irrelevant file * Rename * Delete
1 parent 09f39a5 commit 13ba56f

File tree

5 files changed

+80
-111
lines changed

5 files changed

+80
-111
lines changed

program/rust/src/bindings.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,8 @@ typedef unsigned int uint32_t;
1111
typedef signed long int int64_t;
1212
typedef unsigned long int uint64_t;
1313

14+
#include <stddef.h>
1415
#include "../../c/src/oracle/oracle.h"
16+
17+
const size_t PC_PRICE_T_COMP_OFFSET = offsetof(struct pc_price, comp_);
18+
const size_t PC_MAP_TABLE_T_PROD_OFFSET = offsetof(struct pc_map_table, prod_);

program/rust/src/c_oracle_header.rs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,35 @@ use bytemuck::{
77
Pod,
88
Zeroable,
99
};
10-
10+
use std::mem::size_of;
1111
//bindings.rs is generated by build.rs to include
1212
//things defined in bindings.h
1313
include!("../bindings.rs");
1414

15+
/// The PythAccount trait's purpose is to attach constants to the 3 types of accounts that Pyth has
16+
/// (mapping, price, product). This allows less duplicated code, because now we can create generic
17+
/// functions to perform common checks on the accounts and to load and initialize the accounts.
18+
pub trait PythAccount: Pod {
19+
const ACCOUNT_TYPE: u32;
20+
const INITIAL_SIZE: u32;
21+
}
22+
23+
impl PythAccount for pc_map_table_t {
24+
const ACCOUNT_TYPE: u32 = PC_ACCTYPE_MAPPING;
25+
const INITIAL_SIZE: u32 = PC_MAP_TABLE_T_PROD_OFFSET as u32;
26+
}
27+
28+
impl PythAccount for pc_prod_t {
29+
const ACCOUNT_TYPE: u32 = PC_ACCTYPE_PRODUCT;
30+
const INITIAL_SIZE: u32 = size_of::<pc_prod_t>() as u32;
31+
}
32+
33+
impl PythAccount for pc_price_t {
34+
const ACCOUNT_TYPE: u32 = PC_ACCTYPE_PRICE;
35+
const INITIAL_SIZE: u32 = PC_PRICE_T_COMP_OFFSET as u32;
36+
}
37+
38+
1539
#[cfg(target_endian = "little")]
1640
unsafe impl Zeroable for pc_acc {
1741
}

program/rust/src/rust_oracle.rs

Lines changed: 34 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,7 @@ use crate::c_oracle_header::{
3232
pc_price_t,
3333
pc_prod_t,
3434
pc_pub_key_t,
35-
PC_ACCTYPE_MAPPING,
36-
PC_ACCTYPE_PRICE,
37-
PC_ACCTYPE_PRODUCT,
35+
PythAccount,
3836
PC_COMP_SIZE,
3937
PC_MAGIC,
4038
PC_MAP_TABLE_SIZE,
@@ -95,7 +93,7 @@ pub fn init_mapping(
9593

9694
// Initialize by setting to zero again (just in case) and populating the account header
9795
let hdr = load::<cmd_hdr_t>(instruction_data)?;
98-
initialize_mapping_account(fresh_mapping_account, hdr.ver_)?;
96+
initialize_checked::<pc_map_table_t>(fresh_mapping_account, hdr.ver_)?;
9997

10098
Ok(SUCCESS)
10199
}
@@ -116,13 +114,13 @@ pub fn add_mapping(
116114
check_valid_fresh_account(next_mapping)?;
117115

118116
let hdr = load::<cmd_hdr_t>(instruction_data)?;
119-
let mut cur_mapping = load_mapping_account_mut(cur_mapping, hdr.ver_)?;
117+
let mut cur_mapping = load_checked::<pc_map_table_t>(cur_mapping, hdr.ver_)?;
120118
pyth_assert(
121119
cur_mapping.num_ == PC_MAP_TABLE_SIZE && pubkey_is_zero(&cur_mapping.next_),
122120
ProgramError::InvalidArgument,
123121
)?;
124122

125-
initialize_mapping_account(next_mapping, hdr.ver_)?;
123+
initialize_checked::<pc_map_table_t>(next_mapping, hdr.ver_)?;
126124
pubkey_assign(&mut cur_mapping.next_, &next_mapping.key.to_bytes());
127125

128126
Ok(SUCCESS)
@@ -156,15 +154,9 @@ pub fn add_price(
156154
check_valid_signable_account(program_id, price_account, size_of::<pc_price_t>())?;
157155
check_valid_fresh_account(price_account)?;
158156

159-
let mut product_data = load_product_account_mut(product_account, cmd_args.ver_)?;
157+
let mut product_data = load_checked::<pc_prod_t>(product_account, cmd_args.ver_)?;
160158

161-
clear_account(price_account)?;
162-
163-
let mut price_data = load_account_as_mut::<pc_price_t>(price_account)?;
164-
price_data.magic_ = PC_MAGIC;
165-
price_data.ver_ = cmd_args.ver_;
166-
price_data.type_ = PC_ACCTYPE_PRICE;
167-
price_data.size_ = (size_of::<pc_price_t>() - size_of_val(&price_data.comp_)) as u32;
159+
let mut price_data = initialize_checked::<pc_price_t>(price_account, cmd_args.ver_)?;
168160
price_data.expo_ = cmd_args.expo_;
169161
price_data.ptype_ = cmd_args.ptype_;
170162
pubkey_assign(&mut price_data.prod_, &product_account.key.to_bytes());
@@ -198,7 +190,7 @@ pub fn add_publisher(
198190
check_valid_funding_account(funding_account)?;
199191
check_valid_signable_account(program_id, price_account, size_of::<pc_price_t>())?;
200192

201-
let mut price_data = load_price_account_mut(price_account, cmd_args.ver_)?;
193+
let mut price_data = load_checked::<pc_price_t>(price_account, cmd_args.ver_)?;
202194

203195
if price_data.num_ >= PC_COMP_SIZE {
204196
return Err(ProgramError::InvalidArgument);
@@ -246,14 +238,14 @@ pub fn add_product(
246238
check_valid_fresh_account(new_product_account)?;
247239

248240
let hdr = load::<cmd_hdr_t>(instruction_data)?;
249-
let mut mapping_data = load_mapping_account_mut(tail_mapping_account, hdr.ver_)?;
241+
let mut mapping_data = load_checked::<pc_map_table_t>(tail_mapping_account, hdr.ver_)?;
250242
// The mapping account must have free space to add the product account
251243
pyth_assert(
252244
mapping_data.num_ < PC_MAP_TABLE_SIZE,
253245
ProgramError::InvalidArgument,
254246
)?;
255247

256-
initialize_product_account(new_product_account, hdr.ver_)?;
248+
initialize_checked::<pc_prod_t>(new_product_account, hdr.ver_)?;
257249

258250
let current_index: usize = try_convert(mapping_data.num_)?;
259251
pubkey_assign(
@@ -322,94 +314,40 @@ pub fn clear_account(account: &AccountInfo) -> Result<(), ProgramError> {
322314
Ok(())
323315
}
324316

325-
326-
/// Mutably borrow the data in `account` as a mapping account, validating that the account
327-
/// is properly formatted. Any mutations to the returned value will be reflected in the
328-
/// account data. Use this to read already-initialized accounts.
329-
pub fn load_mapping_account_mut<'a>(
317+
pub fn load_checked<'a, T: PythAccount>(
330318
account: &'a AccountInfo,
331-
expected_version: u32,
332-
) -> Result<RefMut<'a, pc_map_table_t>, ProgramError> {
333-
let mapping_data = load_account_as_mut::<pc_map_table_t>(account)?;
334-
335-
pyth_assert(
336-
mapping_data.magic_ == PC_MAGIC
337-
&& mapping_data.ver_ == expected_version
338-
&& mapping_data.type_ == PC_ACCTYPE_MAPPING,
339-
ProgramError::InvalidArgument,
340-
)?;
341-
342-
Ok(mapping_data)
343-
}
344-
345-
/// Initialize account as a new mapping account. This function will zero out any existing data in
346-
/// the account.
347-
pub fn initialize_mapping_account(account: &AccountInfo, version: u32) -> Result<(), ProgramError> {
348-
clear_account(account)?;
349-
350-
let mut mapping_account = load_account_as_mut::<pc_map_table_t>(account)?;
351-
mapping_account.magic_ = PC_MAGIC;
352-
mapping_account.ver_ = version;
353-
mapping_account.type_ = PC_ACCTYPE_MAPPING;
354-
mapping_account.size_ =
355-
try_convert(size_of::<pc_map_table_t>() - size_of_val(&mapping_account.prod_))?;
356-
357-
Ok(())
358-
}
359-
360-
/// Initialize account as a new product account. This function will zero out any existing data in
361-
/// the account.
362-
pub fn initialize_product_account(account: &AccountInfo, version: u32) -> Result<(), ProgramError> {
363-
clear_account(account)?;
364-
365-
let mut prod_account = load_account_as_mut::<pc_prod_t>(account)?;
366-
prod_account.magic_ = PC_MAGIC;
367-
prod_account.ver_ = version;
368-
prod_account.type_ = PC_ACCTYPE_PRODUCT;
369-
prod_account.size_ = try_convert(size_of::<pc_prod_t>())?;
370-
371-
Ok(())
372-
}
373-
374-
/// Mutably borrow the data in `account` as a product account, validating that the account
375-
/// is properly formatted. Any mutations to the returned value will be reflected in the
376-
/// account data. Use this to read already-initialized accounts.
377-
pub fn load_product_account_mut<'a>(
378-
account: &'a AccountInfo,
379-
expected_version: u32,
380-
) -> Result<RefMut<'a, pc_prod_t>, ProgramError> {
381-
let product_data = load_account_as_mut::<pc_prod_t>(account)?;
382-
383-
pyth_assert(
384-
product_data.magic_ == PC_MAGIC
385-
&& product_data.ver_ == expected_version
386-
&& product_data.type_ == PC_ACCTYPE_PRODUCT,
387-
ProgramError::InvalidArgument,
388-
)?;
319+
version: u32,
320+
) -> Result<RefMut<'a, T>, ProgramError> {
321+
{
322+
let account_header = load_account_as::<pc_acc>(account)?;
323+
pyth_assert(
324+
account_header.magic_ == PC_MAGIC
325+
&& account_header.ver_ == version
326+
&& account_header.type_ == T::ACCOUNT_TYPE,
327+
ProgramError::InvalidArgument,
328+
)?;
329+
}
389330

390-
Ok(product_data)
331+
load_account_as_mut::<T>(account)
391332
}
392333

393-
/// Mutably borrow the data in `account` as a price account, validating that the account
394-
/// is properly formatted. Any mutations to the returned value will be reflected in the
395-
/// account data. Use this to read already-initialized accounts.
396-
fn load_price_account_mut<'a>(
334+
pub fn initialize_checked<'a, T: PythAccount>(
397335
account: &'a AccountInfo,
398-
expected_version: u32,
399-
) -> Result<RefMut<'a, pc_price_t>, ProgramError> {
400-
let price_data = load_account_as_mut::<pc_price_t>(account)?;
336+
version: u32,
337+
) -> Result<RefMut<'a, T>, ProgramError> {
338+
clear_account(account)?;
401339

402-
pyth_assert(
403-
price_data.magic_ == PC_MAGIC
404-
&& price_data.ver_ == expected_version
405-
&& price_data.type_ == PC_ACCTYPE_PRICE,
406-
ProgramError::InvalidArgument,
407-
)?;
340+
{
341+
let mut account_header = load_account_as_mut::<pc_acc>(account)?;
342+
account_header.magic_ = PC_MAGIC;
343+
account_header.ver_ = version;
344+
account_header.type_ = T::ACCOUNT_TYPE;
345+
account_header.size_ = T::INITIAL_SIZE;
346+
}
408347

409-
Ok(price_data)
348+
load_account_as_mut::<T>(account)
410349
}
411350

412-
413351
// Assign pubkey bytes from source to target, fails if source is not 32 bytes
414352
pub fn pubkey_assign(target: &mut pc_pub_key_t, source: &[u8]) {
415353
unsafe { target.k1_.copy_from_slice(source) }

program/rust/src/tests/test_add_mapping.rs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ use crate::deserialize::load_account_as_mut;
1010
use crate::rust_oracle::{
1111
add_mapping,
1212
clear_account,
13-
initialize_mapping_account,
14-
load_mapping_account_mut,
13+
initialize_checked,
14+
load_checked,
1515
pubkey_assign,
1616
pubkey_equal,
1717
pubkey_is_zero,
@@ -67,10 +67,11 @@ fn test_add_mapping() {
6767
Epoch::default(),
6868
);
6969

70-
initialize_mapping_account(&cur_mapping, PC_VERSION).unwrap();
70+
initialize_checked::<pc_map_table_t>(&cur_mapping, PC_VERSION).unwrap();
7171

7272
{
73-
let mut cur_mapping_data = load_mapping_account_mut(&cur_mapping, PC_VERSION).unwrap();
73+
let mut cur_mapping_data =
74+
load_checked::<pc_map_table_t>(&cur_mapping, PC_VERSION).unwrap();
7475
cur_mapping_data.num_ = PC_MAP_TABLE_SIZE;
7576
}
7677

@@ -101,8 +102,9 @@ fn test_add_mapping() {
101102
.is_ok());
102103

103104
{
104-
let next_mapping_data = load_mapping_account_mut(&next_mapping, PC_VERSION).unwrap();
105-
let mut cur_mapping_data = load_mapping_account_mut(&cur_mapping, PC_VERSION).unwrap();
105+
let next_mapping_data = load_checked::<pc_map_table_t>(&next_mapping, PC_VERSION).unwrap();
106+
let mut cur_mapping_data =
107+
load_checked::<pc_map_table_t>(&cur_mapping, PC_VERSION).unwrap();
106108

107109
assert!(pubkey_equal(
108110
&cur_mapping_data.next_,
@@ -129,7 +131,8 @@ fn test_add_mapping() {
129131
);
130132

131133
{
132-
let mut cur_mapping_data = load_mapping_account_mut(&cur_mapping, PC_VERSION).unwrap();
134+
let mut cur_mapping_data =
135+
load_checked::<pc_map_table_t>(&cur_mapping, PC_VERSION).unwrap();
133136
assert!(pubkey_is_zero(&cur_mapping_data.next_));
134137
cur_mapping_data.num_ = PC_MAP_TABLE_SIZE;
135138
cur_mapping_data.magic_ = 0;

program/rust/src/tests/test_add_product.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ use crate::deserialize::load_account_as;
2727
use crate::rust_oracle::{
2828
add_product,
2929
clear_account,
30-
initialize_mapping_account,
31-
load_mapping_account_mut,
30+
initialize_checked,
31+
load_checked,
3232
pubkey_equal,
3333
};
3434

@@ -116,7 +116,7 @@ fn test_add_product() {
116116

117117
{
118118
let product_data = load_account_as::<pc_prod_t>(&product_account).unwrap();
119-
let mapping_data = load_mapping_account_mut(&mapping_account, PC_VERSION).unwrap();
119+
let mapping_data = load_checked::<pc_map_table_t>(&mapping_account, PC_VERSION).unwrap();
120120

121121
assert_eq!(product_data.magic_, PC_MAGIC);
122122
assert_eq!(product_data.ver_, PC_VERSION);
@@ -140,7 +140,7 @@ fn test_add_product() {
140140
)
141141
.is_ok());
142142
{
143-
let mapping_data = load_mapping_account_mut(&mapping_account, PC_VERSION).unwrap();
143+
let mapping_data = load_checked::<pc_map_table_t>(&mapping_account, PC_VERSION).unwrap();
144144
assert_eq!(mapping_data.num_, 2);
145145
assert!(pubkey_equal(
146146
&mapping_data.prod_[1],
@@ -175,7 +175,7 @@ fn test_add_product() {
175175

176176
// test fill up of mapping table
177177
clear_account(&mapping_account).unwrap();
178-
initialize_mapping_account(&mapping_account, PC_VERSION).unwrap();
178+
initialize_checked::<pc_map_table_t>(&mapping_account, PC_VERSION).unwrap();
179179

180180
for i in 0..PC_MAP_TABLE_SIZE {
181181
clear_account(&product_account).unwrap();
@@ -190,7 +190,7 @@ fn test_add_product() {
190190
instruction_data
191191
)
192192
.is_ok());
193-
let mapping_data = load_mapping_account_mut(&mapping_account, PC_VERSION).unwrap();
193+
let mapping_data = load_checked::<pc_map_table_t>(&mapping_account, PC_VERSION).unwrap();
194194
assert_eq!(mapping_data.num_, i + 1);
195195
}
196196

@@ -207,6 +207,6 @@ fn test_add_product() {
207207
)
208208
.is_err());
209209

210-
let mapping_data = load_mapping_account_mut(&mapping_account, PC_VERSION).unwrap();
210+
let mapping_data = load_checked::<pc_map_table_t>(&mapping_account, PC_VERSION).unwrap();
211211
assert_eq!(mapping_data.num_, PC_MAP_TABLE_SIZE);
212212
}

0 commit comments

Comments
 (0)