tests: Check mango account backwards compatibility (#878)
This commit is contained in:
parent
e57dcdc2a9
commit
4f5ec41d7a
Binary file not shown.
|
@ -86,7 +86,7 @@ impl MangoAccountPdaSeeds {
|
|||
// When not reading via idl, MangoAccount binary data is backwards compatible: when ignoring trailing bytes,
|
||||
// a v2 account can be read as a v1 account and a v3 account can be read as v1 or v2 etc.
|
||||
#[account]
|
||||
#[derive(Derivative)]
|
||||
#[derive(Derivative, PartialEq)]
|
||||
#[derivative(Debug)]
|
||||
pub struct MangoAccount {
|
||||
// fixed
|
||||
|
@ -747,6 +747,12 @@ impl<
|
|||
self.dynamic.deref_or_borrow()
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn dynamic_reserved_bytes(&self) -> &[u8] {
|
||||
let reserved_offset = self.header().reserved_bytes_offset();
|
||||
&self.dynamic()[reserved_offset..reserved_offset + DYNAMIC_RESERVED_BYTES]
|
||||
}
|
||||
|
||||
/// Returns
|
||||
/// - the position
|
||||
/// - the raw index into the token positions list (for use with get_raw/deactivate)
|
||||
|
@ -1876,6 +1882,7 @@ impl<'a, 'info: 'a> MangoAccountLoader<'a> for &'a AccountLoader<'info, MangoAcc
|
|||
mod tests {
|
||||
use bytemuck::Zeroable;
|
||||
use itertools::Itertools;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::state::PostOrderType;
|
||||
|
||||
|
@ -2402,12 +2409,7 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
let reserved_offset = account.header.reserved_bytes_offset();
|
||||
assert!(
|
||||
account.dynamic[reserved_offset..reserved_offset + DYNAMIC_RESERVED_BYTES]
|
||||
.iter()
|
||||
.all(|&v| v == 0)
|
||||
);
|
||||
assert!(account.dynamic_reserved_bytes().iter().all(|&v| v == 0));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -2862,4 +2864,88 @@ mod tests {
|
|||
|
||||
assert_eq!(to_be_closed_account_opt.unwrap().market_index, 3)
|
||||
}
|
||||
|
||||
// Attempts reading old mango account data with borsh and with zerocopy
|
||||
#[test]
|
||||
fn test_mango_account_backwards_compatibility() -> Result<()> {
|
||||
use solana_program_test::{find_file, read_file};
|
||||
|
||||
let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
||||
d.push("resources/test");
|
||||
|
||||
// Grab live accounts with
|
||||
// solana account CZGf1qbYPaSoabuA1EmdN8W5UHvH5CeXcNZ7RTx65aVQ --output-file programs/mango-v4/resources/test/mangoaccount-v0.21.3.bin
|
||||
let fixtures = vec!["mangoaccount-v0.21.3"];
|
||||
|
||||
for fixture in fixtures {
|
||||
let filename = format!("resources/test/{}.bin", fixture);
|
||||
let account_bytes = read_file(find_file(&filename).unwrap());
|
||||
|
||||
// Read with borsh
|
||||
let mut account_bytes_slice: &[u8] = &account_bytes;
|
||||
let borsh_account = MangoAccount::try_deserialize(&mut account_bytes_slice)?;
|
||||
|
||||
// Read with zerocopy
|
||||
let zerocopy_reader = MangoAccountValue::from_bytes(&account_bytes[8..])?;
|
||||
let fixed = &zerocopy_reader.fixed;
|
||||
let zerocopy_account = MangoAccount {
|
||||
group: fixed.group,
|
||||
owner: fixed.owner,
|
||||
name: fixed.name,
|
||||
delegate: fixed.delegate,
|
||||
account_num: fixed.account_num,
|
||||
being_liquidated: fixed.being_liquidated,
|
||||
in_health_region: fixed.in_health_region,
|
||||
bump: fixed.bump,
|
||||
padding: Default::default(),
|
||||
net_deposits: fixed.net_deposits,
|
||||
perp_spot_transfers: fixed.perp_spot_transfers,
|
||||
health_region_begin_init_health: fixed.health_region_begin_init_health,
|
||||
frozen_until: fixed.frozen_until,
|
||||
buyback_fees_accrued_current: fixed.buyback_fees_accrued_current,
|
||||
buyback_fees_accrued_previous: fixed.buyback_fees_accrued_previous,
|
||||
buyback_fees_expiry_timestamp: fixed.buyback_fees_expiry_timestamp,
|
||||
next_token_conditional_swap_id: fixed.next_token_conditional_swap_id,
|
||||
temporary_delegate: fixed.temporary_delegate,
|
||||
temporary_delegate_expiry: fixed.temporary_delegate_expiry,
|
||||
last_collateral_fee_charge: fixed.last_collateral_fee_charge,
|
||||
reserved: [0u8; 152],
|
||||
|
||||
header_version: *zerocopy_reader.header_version(),
|
||||
padding3: Default::default(),
|
||||
|
||||
padding4: Default::default(),
|
||||
tokens: zerocopy_reader.all_token_positions().cloned().collect_vec(),
|
||||
|
||||
padding5: Default::default(),
|
||||
serum3: zerocopy_reader.all_serum3_orders().cloned().collect_vec(),
|
||||
|
||||
padding6: Default::default(),
|
||||
perps: zerocopy_reader.all_perp_positions().cloned().collect_vec(),
|
||||
|
||||
padding7: Default::default(),
|
||||
perp_open_orders: zerocopy_reader.all_perp_orders().cloned().collect_vec(),
|
||||
|
||||
padding8: Default::default(),
|
||||
token_conditional_swaps: zerocopy_reader
|
||||
.all_token_conditional_swaps()
|
||||
.cloned()
|
||||
.collect_vec(),
|
||||
|
||||
reserved_dynamic: zerocopy_reader.dynamic_reserved_bytes().try_into().unwrap(),
|
||||
};
|
||||
|
||||
// Both methods agree?
|
||||
assert_eq!(borsh_account, zerocopy_account);
|
||||
|
||||
// Serializing and deserializing produces the same data?
|
||||
let mut borsh_bytes = Vec::new();
|
||||
borsh_account.try_serialize(&mut borsh_bytes)?;
|
||||
let mut slice: &[u8] = &borsh_bytes;
|
||||
let roundtrip_account = MangoAccount::try_deserialize(&mut slice)?;
|
||||
assert_eq!(borsh_account, roundtrip_account);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ use crate::state::*;
|
|||
pub const FREE_ORDER_SLOT: PerpMarketIndex = PerpMarketIndex::MAX;
|
||||
|
||||
#[zero_copy]
|
||||
#[derive(AnchorDeserialize, AnchorSerialize, Derivative)]
|
||||
#[derive(AnchorDeserialize, AnchorSerialize, Derivative, PartialEq)]
|
||||
#[derivative(Debug)]
|
||||
pub struct TokenPosition {
|
||||
// TODO: Why did we have deposits and borrows as two different values
|
||||
|
@ -110,7 +110,7 @@ impl TokenPosition {
|
|||
}
|
||||
|
||||
#[zero_copy]
|
||||
#[derive(AnchorSerialize, AnchorDeserialize, Derivative)]
|
||||
#[derive(AnchorSerialize, AnchorDeserialize, Derivative, PartialEq)]
|
||||
#[derivative(Debug)]
|
||||
pub struct Serum3Orders {
|
||||
pub open_orders: Pubkey,
|
||||
|
@ -203,7 +203,7 @@ impl Default for Serum3Orders {
|
|||
}
|
||||
|
||||
#[zero_copy]
|
||||
#[derive(AnchorSerialize, AnchorDeserialize, Derivative)]
|
||||
#[derive(AnchorSerialize, AnchorDeserialize, Derivative, PartialEq)]
|
||||
#[derivative(Debug)]
|
||||
pub struct PerpPosition {
|
||||
pub market_index: PerpMarketIndex,
|
||||
|
@ -785,7 +785,7 @@ impl PerpPosition {
|
|||
}
|
||||
|
||||
#[zero_copy]
|
||||
#[derive(AnchorSerialize, AnchorDeserialize, Derivative)]
|
||||
#[derive(AnchorSerialize, AnchorDeserialize, Derivative, PartialEq)]
|
||||
#[derivative(Debug)]
|
||||
pub struct PerpOpenOrder {
|
||||
pub side_and_tree: u8, // SideAndOrderTree -- enums aren't POD
|
||||
|
|
|
@ -45,7 +45,7 @@ pub enum TokenConditionalSwapType {
|
|||
}
|
||||
|
||||
#[zero_copy]
|
||||
#[derive(AnchorDeserialize, AnchorSerialize, Derivative)]
|
||||
#[derive(AnchorDeserialize, AnchorSerialize, Derivative, PartialEq)]
|
||||
#[derivative(Debug)]
|
||||
pub struct TokenConditionalSwap {
|
||||
pub id: u64,
|
||||
|
|
Loading…
Reference in New Issue