cartesi_solana/
account_manager.rs1use borsh::BorshSerialize;
2use once_cell::sync::Lazy;
3use serde::{Deserialize, Serialize};
4use solana_program::account_info::AccountInfo;
5use solana_program::pubkey::Pubkey;
6use std::cell::RefCell;
7use std::collections::HashMap;
8use std::io::ErrorKind::NotFound;
9use std::rc::Rc;
10use std::sync::Mutex;
11use std::{fs, str::FromStr};
12
13static mut ACCOUNT_INFO_DATA: Vec<Vec<u8>> = Vec::new();
14static mut MEM_DATA: Vec<AccountMemData> = Vec::new();
15static mut HASH_INFO_DATA: Lazy<HashMap<Pubkey, usize>> = Lazy::new(|| HashMap::new());
16
17lazy_static::lazy_static! {
18 static ref INFO_DATA: Mutex<Vec<AccountMemData>> = Mutex::new(Vec::new());
19}
20
21struct AccountMemData {
22 key: Pubkey,
23 owner: Pubkey,
24 data: Box<[u8]>,
25 lamports: u64,
26}
27
28pub fn clear() {
29 INFO_DATA.lock().unwrap().clear();
30 unsafe {
31 MEM_DATA.clear();
32 ACCOUNT_INFO_DATA.clear();
33 HASH_INFO_DATA.clear();
34 }
35}
36
37pub fn serialize_with_padding<B: BorshSerialize>(account_info: &AccountInfo, borsh_structure: &B) {
38 let mut serialized_data = vec![0u8; 0];
41 borsh_structure.serialize(&mut serialized_data).unwrap();
42 let diff = account_info.data_len() - serialized_data.len();
43 for _ in 0..diff {
44 serialized_data.push(0);
45 }
46 set_data(account_info, serialized_data);
47}
48
49pub fn set_data(account_info: &AccountInfo, data: Vec<u8>) {
50 println!(
51 "set_data: key = {:?}; data.len = {}",
52 account_info.key,
53 data.len()
54 );
55 if account_info.data_len() == data.len() {
56 println!("set_data: account_info's data keep the same memory space");
57 let mut data_info = account_info.try_borrow_mut_data().unwrap();
58 for (i, byte) in data.iter().enumerate() {
59 data_info[i] = *byte;
60 }
61 } else {
62 unsafe {
63 println!("set_data: RefCell replacing the account_info data");
64 let tot = ACCOUNT_INFO_DATA.len();
65 ACCOUNT_INFO_DATA.push(data);
66 account_info.data.replace(&mut ACCOUNT_INFO_DATA[tot]);
67 HASH_INFO_DATA.insert(account_info.key.to_owned(), tot);
68 }
69 }
70}
71
72pub fn get_resized(key: &Pubkey) -> Option<Vec<u8>> {
73 unsafe {
74 let res = HASH_INFO_DATA.get(key);
75 if let Some(index) = res {
76 Some(ACCOUNT_INFO_DATA[*index].to_owned())
77 } else {
78 None
79 }
80 }
81}
82
83pub fn set_data_size(account_info: &AccountInfo, size: usize) {
84 unsafe {
85 println!(
86 "set_data_size: key = {:?}; size = {}",
87 account_info.key, size
88 );
89 if account_info.data_len() != size {
90 let tot = ACCOUNT_INFO_DATA.len();
91 let data = vec![0; size];
92 ACCOUNT_INFO_DATA.push(data);
93 account_info.data.replace(&mut ACCOUNT_INFO_DATA[tot]);
94 HASH_INFO_DATA.insert(account_info.key.to_owned(), tot);
95 } else {
96 println!("set_data_size: skipped");
97 }
98 }
99}
100
101pub fn create_account_info<'a>(
102 key: &Pubkey,
103 is_signer: bool,
104 is_writable: bool,
105 lamports: u64,
106 data: Vec<u8>,
107 owner: Pubkey,
108 executable: bool,
109) -> AccountInfo<'a> {
110 unsafe {
111 let tot_mem_data = MEM_DATA.len();
112 MEM_DATA.push(AccountMemData {
113 key: key.to_owned(),
114 owner,
115 lamports,
116 data: data.as_slice().into(),
117 });
118 let mem_data = &mut MEM_DATA[tot_mem_data];
119 AccountInfo {
120 key: &mem_data.key,
121 is_signer,
122 is_writable,
123 lamports: Rc::new(RefCell::new(&mut mem_data.lamports)),
124 data: Rc::new(RefCell::new(&mut mem_data.data)),
125 owner: &mem_data.owner,
126 executable,
127 rent_epoch: 1,
128 }
129 }
130}
131
132pub fn create_account_manager() -> AccountManager {
133 let mut account_manager = AccountManager::new().unwrap();
134 let result = std::env::var("SOLANA_DATA_PATH");
135 match result {
136 Ok(path) => {
137 account_manager.set_base_path(path);
139 return account_manager;
140 }
141 Err(_) => {
142 println!("default base path");
143 account_manager.set_base_path("./".to_owned());
144 return account_manager;
145 }
146 };
147}
148
149#[derive(Debug)]
150pub struct AccountManager {
151 base_path: String,
152}
153
154impl AccountManager {
155 pub fn new() -> std::result::Result<AccountManager, Box<dyn std::error::Error>> {
156 Ok(Self {
157 base_path: "tests/fixtures".to_string(),
158 })
159 }
160
161 pub fn find_program_accounts(
162 &self,
163 pubkey: &Pubkey,
164 ) -> std::result::Result<Vec<(Pubkey, AccountFileData)>, Box<dyn std::error::Error>> {
165 let paths = fs::read_dir(&self.base_path)?;
166 let mut result: Vec<(Pubkey, AccountFileData)> = vec![];
167 for path in paths {
168 let file_path = path?.path();
169 let account_info = self.read_account_file(file_path.to_str().unwrap().to_string())?;
170 if account_info.owner == *pubkey {
171 let key = file_path.file_name().unwrap().to_str().unwrap();
172 let pk = Pubkey::from_str(&key[..(key.len() - 5)]).unwrap();
173 println!("program {:?} owns {:?}", &pubkey, &pk);
174 result.push((pk, account_info));
175 }
176 }
177 Ok(result)
178 }
179
180 pub fn set_base_path(&mut self, base_path: String) {
181 self.base_path = base_path;
182 }
183
184 pub fn write_account(
185 &self,
186 pubkey: &Pubkey,
187 account_file_data: &AccountFileData,
188 ) -> std::result::Result<(), Box<dyn std::error::Error>> {
189 let file_path = format!("{}/{}.json", &self.base_path, pubkey.to_string());
190 let contents = serde_json::to_string(account_file_data)?;
191 fs::write(file_path, contents)?;
192 println!(
193 "saved {:?}; data.len() = {}",
194 pubkey,
195 account_file_data.data.len()
196 );
197 Ok(())
198 }
199
200 pub fn read_account(
201 &self,
202 pubkey: &Pubkey,
203 ) -> std::result::Result<AccountFileData, Box<dyn std::error::Error>> {
204 let file_path = format!("{}/{}.json", &self.base_path, pubkey.to_string());
205 self.read_account_file(file_path)
206 }
207
208 fn read_account_file(
209 &self,
210 file_path: String,
211 ) -> std::result::Result<AccountFileData, Box<dyn std::error::Error>> {
212 let read = fs::read_to_string(&file_path);
213 match read {
214 Ok(contents) => {
215 let account = serde_json::from_str::<AccountFileData>(&contents)?;
216 Ok(account)
217 }
218 Err(error) => {
219 println!("Account not found: {:?}", file_path);
220 Err(Box::new(error))
221 },
222 }
223 }
224
225 pub fn delete_account(
226 &self,
227 pubkey: &Pubkey,
228 ) -> std::result::Result<(), Box<dyn std::error::Error>> {
229 let file_path = format!("{}/{}.json", &self.base_path, pubkey.to_string());
230 let delete_result = fs::remove_file(file_path);
231 match delete_result {
232 Ok(_) => {
233 return Ok(());
234 }
235 Err(error) => {
236 if error.kind() == NotFound {
237 return Ok(());
238 } else {
239 return Err(Box::new(error));
240 }
241 }
242 }
243 }
244}
245
246#[derive(Serialize, Deserialize)]
247pub struct AccountFileData {
248 pub owner: Pubkey,
252 pub data: Vec<u8>,
253 pub lamports: u64,
254}