cartesi_solana/
account_manager.rs

1use 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    // borsh_structure.serialize(&mut *account_info.data.borrow_mut()).unwrap();
39
40    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            //println!("base path from env {}", path);
138            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    /**
249     * program owner
250     */
251    pub owner: Pubkey,
252    pub data: Vec<u8>,
253    pub lamports: u64,
254}