1use std::{cell::RefCell, collections::HashMap};
23
24use pchain_types::cryptography::PublicAddress;
25use pchain_world_state::{
26 keys::AppKey,
27 states::{AccountStorageState, WorldState},
28 storage::WorldStateStorage,
29};
30use wasmer::Store;
31
32use crate::{
33 contract::{self, Module, SmartContractContext},
34 cost::CostChange,
35 gas,
36};
37
38#[derive(Clone)]
40pub(crate) struct ReadWriteSet<S>
41where
42 S: WorldStateStorage + Send + Sync + Clone,
43{
44 pub ws: WorldState<S>,
46 pub writes: HashMap<CacheKey, CacheValue>,
48 pub reads: RefCell<HashMap<CacheKey, Option<CacheValue>>>,
50 pub write_gas: CostChange,
52 pub read_gas: RefCell<CostChange>,
54}
55
56impl<S> ReadWriteSet<S>
57where
58 S: WorldStateStorage + Send + Sync + Clone,
59{
60 pub fn new(ws: WorldState<S>) -> Self {
61 Self {
62 ws,
63 writes: HashMap::new(),
64 reads: RefCell::new(HashMap::new()),
65 write_gas: CostChange::default(),
66 read_gas: RefCell::new(CostChange::default()),
67 }
68 }
69
70 pub fn balance(&self, address: PublicAddress) -> (u64, CostChange) {
72 match self.get(CacheKey::Balance(address)) {
73 (Some(CacheValue::Balance(value)), cost) => (value, cost),
74 _ => panic!(),
75 }
76 }
77
78 pub fn set_balance(&mut self, address: PublicAddress, balance: u64) -> CostChange {
80 self.set(CacheKey::Balance(address), CacheValue::Balance(balance))
81 }
82
83 pub fn purge_balance(&mut self, address: PublicAddress) -> u64 {
85 let (balance, _) = self.balance(address);
86 let key = CacheKey::Balance(address);
87 self.writes.remove(&key);
88 balance
89 }
90
91 pub fn code(&self, address: PublicAddress) -> (Option<Vec<u8>>, CostChange) {
93 match self.get(CacheKey::ContractCode(address)) {
94 (Some(CacheValue::ContractCode(value)), cost) => (Some(value), cost),
95 (None, cost) => (None, cost),
96 _ => panic!(),
97 }
98 }
99
100 pub fn code_from_sc_cache(
102 &self,
103 address: PublicAddress,
104 sc_context: &SmartContractContext,
105 ) -> (Option<(Module, Store)>, CostChange) {
106 let wasmer_store = sc_context.instantiate_store();
107 let cached_module = match &sc_context.cache {
108 Some(sc_cache) => contract::Module::from_cache(address, sc_cache, &wasmer_store),
109 None => None,
110 };
111
112 if let Some(module) = cached_module {
114 let cost_change = CostChange::deduct(gas::get_code_cost(module.bytes_length()));
115 *self.read_gas.borrow_mut() += cost_change;
116 return (Some((module, wasmer_store)), cost_change);
117 }
118
119 let (bytes, cost_change) = self.code(address);
121 let contract_code = match bytes {
122 Some(bs) => bs,
123 None => return (None, cost_change),
124 };
125
126 let module = match contract::Module::from_wasm_bytecode_unchecked(
128 contract::CBI_VERSION,
129 &contract_code,
130 &wasmer_store,
131 ) {
132 Ok(module) => {
133 if let Some(sc_cache) = &sc_context.cache {
135 module.cache_to(address, &mut sc_cache.clone());
136 }
137 module
138 }
139 Err(_) => return (None, cost_change),
140 };
141
142 (Some((module, wasmer_store)), cost_change)
143 }
144
145 pub fn set_code(&mut self, address: PublicAddress, code: Vec<u8>) -> CostChange {
147 self.set(
148 CacheKey::ContractCode(address),
149 CacheValue::ContractCode(code),
150 )
151 }
152
153 pub fn cbi_version(&self, address: PublicAddress) -> (Option<u32>, CostChange) {
155 match self.get(CacheKey::CBIVersion(address)) {
156 (Some(CacheValue::CBIVersion(value)), cost) => (Some(value), cost),
157 (None, cost) => (None, cost),
158 _ => panic!(),
159 }
160 }
161
162 pub fn set_cbi_version(&mut self, address: PublicAddress, cbi_version: u32) -> CostChange {
164 self.set(
165 CacheKey::CBIVersion(address),
166 CacheValue::CBIVersion(cbi_version),
167 )
168 }
169
170 pub fn app_data(
172 &self,
173 address: PublicAddress,
174 app_key: AppKey,
175 ) -> (Option<Vec<u8>>, CostChange) {
176 match self.get(CacheKey::App(address, app_key)) {
177 (Some(CacheValue::App(value)), cost) => {
178 if value.is_empty() {
179 (None, cost)
180 } else {
181 (Some(value), cost)
182 }
183 }
184 (None, cost) => (None, cost),
185 _ => panic!(),
186 }
187 }
188
189 pub fn set_app_data(
191 &mut self,
192 address: PublicAddress,
193 app_key: AppKey,
194 value: Vec<u8>,
195 ) -> CostChange {
196 self.set(CacheKey::App(address, app_key), CacheValue::App(value))
197 }
198
199 pub fn set_app_data_uncharged(
202 &mut self,
203 address: PublicAddress,
204 app_key: AppKey,
205 value: Vec<u8>,
206 ) {
207 self.writes
208 .insert(CacheKey::App(address, app_key), CacheValue::App(value));
209 }
210
211 pub fn contains_app_data(&self, address: PublicAddress, app_key: AppKey) -> bool {
213 let cache_key = CacheKey::App(address, app_key.clone());
214
215 *self.read_gas.borrow_mut() += CostChange::deduct(gas::contains_cost(cache_key.len()));
217
218 self.writes
220 .get(&cache_key)
221 .filter(|v| v.len() != 0)
222 .is_some()
223 || self
224 .reads
225 .borrow()
226 .get(&cache_key)
227 .filter(|v| v.is_some())
228 .is_some()
229 || self.ws.contains().storage_value(&address, &app_key)
230 }
231
232 pub fn contains_app_data_from_account_storage_state(
234 &self,
235 account_storage_state: &AccountStorageState<S>,
236 app_key: AppKey,
237 ) -> bool {
238 let address = account_storage_state.address();
239 let cache_key = CacheKey::App(address, app_key.clone());
240
241 self.writes
243 .get(&cache_key)
244 .filter(|v| v.len() != 0)
245 .is_some()
246 || self
247 .reads
248 .borrow()
249 .get(&cache_key)
250 .filter(|v| v.is_some())
251 .is_some()
252 || self
253 .ws
254 .contains()
255 .storage_value_from_account_storage_state(account_storage_state, &app_key)
256 }
257
258 pub fn app_data_from_account_storage_state(
260 &self,
261 account_storage_state: &AccountStorageState<S>,
262 app_key: AppKey,
263 ) -> Option<Vec<u8>> {
264 let address = account_storage_state.address();
265 let cache_key = CacheKey::App(address, app_key.clone());
266
267 match self.writes.get(&cache_key) {
268 Some(CacheValue::App(value)) => return Some(value.clone()),
269 Some(_) => panic!(),
270 None => {}
271 }
272
273 match self.reads.borrow().get(&cache_key) {
274 Some(Some(CacheValue::App(value))) => return Some(value.clone()),
275 Some(None) => return None,
276 Some(_) => panic!(),
277 None => {}
278 }
279
280 self.ws
281 .cached_get()
282 .storage_value(account_storage_state.address(), &app_key)
283 .or_else(|| account_storage_state.get(&app_key))
284 }
285
286 fn get(&self, key: CacheKey) -> (Option<CacheValue>, CostChange) {
288 if let Some(value) = self.writes.get(&key) {
290 let cost_change = self.charge_read_cost(&key, Some(value));
291 return (Some(value.clone()), cost_change);
292 }
293
294 if let Some(value) = self.reads.borrow().get(&key) {
296 let cost_change = self.charge_read_cost(&key, value.as_ref());
297 return (value.clone(), cost_change);
298 }
299
300 let value = key.get_from_world_state(&self.ws);
302 let cost_change = self.charge_read_cost(&key, value.as_ref());
303
304 self.reads.borrow_mut().insert(key, value.clone());
306
307 (value, cost_change)
308 }
309
310 fn set(&mut self, key: CacheKey, value: CacheValue) -> CostChange {
312 let key_len = key.len();
313 let new_val_len = value.len();
314
315 let old_val_len = self.get(key.clone()).0.map_or(0, |v| v.len());
317
318 self.writes.insert(key, value);
320
321 self.charge_write_cost(key_len, old_val_len, new_val_len)
323 }
324
325 fn charge_read_cost(&self, key: &CacheKey, value: Option<&CacheValue>) -> CostChange {
326 let cost_change = match key {
327 CacheKey::ContractCode(_) => {
328 CostChange::deduct(gas::get_code_cost(value.as_ref().map_or(0, |v| v.len())))
329 }
330 _ => CostChange::deduct(gas::get_cost(
331 key.len(),
332 value.as_ref().map_or(0, |v| v.len()),
333 )),
334 };
335 *self.read_gas.borrow_mut() += cost_change;
336 cost_change
337 }
338
339 fn charge_write_cost(
340 &mut self,
341 key_len: usize,
342 old_val_len: usize,
343 new_val_len: usize,
344 ) -> CostChange {
345 let new_cost_change =
346 CostChange::reward(gas::set_cost_delete_old_value(key_len, old_val_len, new_val_len)) +
348 CostChange::deduct(gas::set_cost_write_new_value(new_val_len)) +
349 CostChange::deduct(gas::set_cost_rehash(key_len));
350 self.write_gas += new_cost_change;
351 new_cost_change
352 }
353
354 pub fn commit_to_world_state(self) -> WorldState<S> {
355 let mut ws = self.ws;
356 self.writes.into_iter().for_each(|(cache_key, new_value)| {
358 new_value.set_to_world_state(cache_key, &mut ws);
359 });
360 ws.commit();
361 ws
362 }
363}
364
365#[derive(Clone, Debug, Hash, PartialEq, Eq)]
370pub(crate) enum CacheKey {
371 App(PublicAddress, AppKey),
372 Balance(PublicAddress),
373 ContractCode(PublicAddress),
374 CBIVersion(PublicAddress),
375}
376
377impl CacheKey {
378 pub fn len(&self) -> usize {
380 match self {
381 CacheKey::App(address, key) => {
382 gas::ACCOUNT_STATE_KEY_LENGTH + address.len() + key.len()
383 }
384 CacheKey::Balance(_) | CacheKey::ContractCode(_) | CacheKey::CBIVersion(_) => {
385 gas::ACCOUNT_STATE_KEY_LENGTH
386 }
387 }
388 }
389
390 fn get_from_world_state<S>(&self, ws: &WorldState<S>) -> Option<CacheValue>
392 where
393 S: WorldStateStorage + Send + Sync + Clone,
394 {
395 match &self {
396 CacheKey::App(address, app_key) => {
397 ws.storage_value(address, app_key).map(CacheValue::App)
398 }
399 CacheKey::Balance(address) => Some(CacheValue::Balance(ws.balance(address.to_owned()))),
400 CacheKey::ContractCode(address) => {
401 ws.code(address.to_owned()).map(CacheValue::ContractCode)
402 }
403 CacheKey::CBIVersion(address) => ws
404 .cbi_version(address.to_owned())
405 .map(CacheValue::CBIVersion),
406 }
407 }
408}
409
410#[derive(Clone, Debug)]
415pub(crate) enum CacheValue {
416 App(Vec<u8>),
417 Balance(u64),
418 ContractCode(Vec<u8>),
419 CBIVersion(u32),
420}
421
422impl CacheValue {
423 pub fn len(&self) -> usize {
425 match self {
426 CacheValue::App(value) => value.len(),
427 CacheValue::Balance(balance) => std::mem::size_of_val(balance),
428 CacheValue::ContractCode(code) => code.len(),
429 CacheValue::CBIVersion(cbi_version) => std::mem::size_of_val(cbi_version),
430 }
431 }
432
433 fn set_to_world_state<S>(self, key: CacheKey, ws: &mut WorldState<S>)
435 where
436 S: WorldStateStorage + Send + Sync + Clone,
437 {
438 match self {
439 CacheValue::App(value) => {
440 if let CacheKey::App(address, app_key) = key {
441 ws.cached().set_storage_value(address, app_key, value);
442 }
443 }
444 CacheValue::Balance(value) => {
445 if let CacheKey::Balance(address) = key {
446 ws.cached().set_balance(address, value);
447 }
448 }
449 CacheValue::ContractCode(value) => {
450 if let CacheKey::ContractCode(address) = key {
451 ws.cached().set_code(address, value);
452 }
453 }
454 CacheValue::CBIVersion(value) => {
455 if let CacheKey::CBIVersion(address) = key {
456 ws.cached().set_cbi_version(address, value);
457 }
458 }
459 }
460 }
461}