tests: Check mango account backwards compatibility (#878)

This commit is contained in:
Christian Kamm 2024-02-14 10:00:09 +01:00 committed by GitHub
parent e57dcdc2a9
commit 4f5ec41d7a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 98 additions and 12 deletions

View File

@ -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(())
}
}

View File

@ -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

View File

@ -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,