devol_accounts_kit/accounts/
devol_account.rs1use std::cell::Ref;
2use std::error::Error;
3use solana_program::account_info::{Account, IntoAccountInfo};
4use solana_program::account_info::AccountInfo;
5use solana_program::pubkey::Pubkey;
6use crate::accounts::account_header::AccountHeader;
7use crate::dvl_error::DvlError;
8use crate::errors::*;
9
10pub trait DevolAccount {
11 fn expected_size() -> usize;
12
13 fn expected_tag() -> u8;
14
15 fn expected_version() -> u32;
16
17 #[inline(always)]
18 fn account_header<'a>(data: Ref<&mut [u8]>) -> &'a AccountHeader {
19 unsafe { &*(data.as_ptr() as *const AccountHeader) }
20 }
21
22 #[inline(always)]
23 fn check_basic(account_info: &AccountInfo, root_addr: &Pubkey, program_id: &Pubkey) -> Result<(), DvlError> {
24 let tag = AccountTag::from_u8(Self::expected_tag());
25 Self::check_size(tag, account_info.data.borrow())?;
26 let header = Self::account_header(account_info.data.borrow());
27 Self::check_tag_and_version(tag, header)?;
28 Self::check_root(tag, header, root_addr)?;
29 Self::check_program_id(tag, account_info, program_id)?;
30 Ok(())
31 }
32
33 #[inline(always)]
34 fn check_size(tag: AccountTag, account_data: Ref<&mut [u8]>) -> Result<(), DvlError> {
35 let actual_size= account_data.len();
36 if actual_size < Self::expected_size() {
37 Err(DvlError::new_with_account(tag, ContractError::AccountSize))
38 } else {
39 Ok(())
40 }
41 }
42
43 #[inline(always)]
44 fn check_tag_and_version(tag: AccountTag, header: &AccountHeader) -> Result<(), DvlError> {
45 if header.tag != Self::expected_tag() as u32 {
46 Err(DvlError::new_with_account(tag, ContractError::WrongAccountTag))
47 } else if header.version > Self::expected_version() {
48 Err(DvlError::new_with_account(tag, ContractError::AccountVersionTooHigh))
49 } else if header.version < Self::expected_version() {
50 Err(DvlError::new_with_account(tag, ContractError::AccountVersionTooLow))
51 } else {
52 Ok(())
53 }
54 }
55
56 #[inline(always)]
57 fn check_root(tag: AccountTag, header: &AccountHeader, root_addr: &Pubkey) -> Result<(), DvlError> {
58 if header.root != *root_addr {
59 Err(DvlError::new_with_account(tag, ContractError::RootAddress))
60 } else {
61 Ok(())
62 }
63 }
64
65 #[inline(always)]
66 fn check_program_id(tag: AccountTag, account_info: &AccountInfo, program_id: &Pubkey) -> Result<(), DvlError> {
67 if account_info.owner != program_id {
68 Err(DvlError::new_with_account(tag, ContractError::AccountOwner))
69 } else {
70 Ok(())
71 }
72 }
73
74 #[inline(always)]
76 fn from_account_info_basic<'a>(
77 account_info: &'a AccountInfo,
78 root_addr: &Pubkey,
79 program_id: &Pubkey,
80 ) -> Result<&'a Self, DvlError>
81 where
82 Self: Sized,
83 {
84 Self::check_basic(account_info, root_addr, program_id)?;
85 let account = unsafe { &*(account_info.data.borrow().as_ptr() as *const Self) };
86 Ok(account)
87 }
88
89 #[inline(always)]
92 fn from_account_info_mut_basic<'a>(
93 account_info: &'a AccountInfo,
94 root_addr: &Pubkey,
95 program_id: &Pubkey,
96 ) -> Result<&'a mut Self, DvlError>
97 where
98 Self: Sized,
99 {
100 Self::check_basic(account_info, root_addr, program_id)?;
101 if !account_info.is_writable {
102 return Err(DvlError::new_with_account(AccountTag::from_u8(Self::expected_tag()), ContractError::AccountWritableAttribute));
103 }
104 let account = unsafe { &mut *(account_info.data.borrow_mut().as_ptr() as *mut Self) };
105 Ok(account)
106 }
107
108 #[inline(always)]
110 fn from_account_basic(
111 key: &Pubkey,
112 account: &mut impl Account,
113 root_addr: &Pubkey,
114 program_id: &Pubkey,
115 ) -> Result<Box<Self>, Box<dyn Error>>
116 where
117 Self: Sized + Copy
118 {
119 let account_info = (key, account).into_account_info();
120 let account_ref = Self::from_account_info_basic(&account_info, root_addr, program_id)?;
121 Ok(Box::new(*account_ref))
122 }
123}
124
125#[cfg(not(feature = "on-chain"))]
126#[cfg(test)]
127mod tests {
128 use std::str::FromStr;
129 use super::*;
130 use solana_sdk::pubkey::Pubkey;
131 use solana_client::rpc_client::RpcClient;
132 use crate::accounts::devol_regular_account::DevolRegularAccount;
133 use crate::accounts::root::root_account::{ROOT_ACCOUNT_TAG, RootAccount};
134 use crate::constants::test_constants::{PROGRAM_ID, ROOT_ADDRESS, RPC_URL};
135
136 #[test]
137 fn test_read_root_account() {
138 let root_addr = Pubkey::from_str(ROOT_ADDRESS).unwrap();
139 let client = RpcClient::new(String::from(RPC_URL));
140 let mut account_data = client.get_account(&root_addr).unwrap();
141 let program_id = Pubkey::from_str(PROGRAM_ID).unwrap();
142
143 assert_eq!(account_data.data.len(), RootAccount::expected_size());
144
145 match RootAccount::from_account(&root_addr, &mut account_data, &root_addr, &program_id) {
146 Ok(root_account) => {
147 assert!(true, "RootAccount success");
148 assert_eq!(root_account.header.tag, ROOT_ACCOUNT_TAG as u32);
149 }
150 Err(e) => panic!("Error building RootAccount: {:?}", e),
151 }
152 }
153}