1use std::cell::RefCell;
4use std::fmt::Debug;
5
6use namada_core::address::{Address, ESTABLISHED_ADDRESS_BYTES_LEN};
7use namada_core::arith::checked;
8use namada_core::chain::{BlockHeader, BlockHeight, ChainId, Epoch, Epochs};
9use namada_core::hash::{HASH_LENGTH, Hash};
10use namada_core::storage::{Key, TX_INDEX_LENGTH, TxIndex};
11use namada_events::{Event, EventTypeBuilder};
12use namada_gas::{self as gas, Gas, GasMetering, MEMORY_ACCESS_GAS_PER_BYTE};
13use namada_tx::{BatchedTxRef, Section};
14use thiserror::Error;
15
16use crate::state::write_log::WriteLog;
17use crate::state::{DB, DBIter, PrefixIter, ResultExt, StateRead, write_log};
18pub use crate::state::{Error, Result};
19
20#[allow(missing_docs)]
22#[derive(Error, Debug)]
23pub enum RuntimeError {
24 #[error("Out of gas: {0}")]
25 OutOfGas(gas::Error),
26 #[error("Invalid transaction code hash")]
27 InvalidCodeHash,
28 #[error("No value found in result buffer")]
29 NoValueInResultBuffer,
30 #[error("The section signature is invalid: {0}")]
31 InvalidSectionSignature(String),
32}
33
34impl From<RuntimeError> for Error {
35 fn from(value: RuntimeError) -> Self {
36 Error::new(value)
37 }
38}
39
40pub type EnvResult<T> = std::result::Result<T, RuntimeError>;
42
43pub fn add_gas(
45 gas_meter: &RefCell<impl GasMetering>,
46 used_gas: Gas,
47) -> Result<()> {
48 gas_meter.borrow_mut().consume(used_gas).map_err(|err| {
49 tracing::info!("Stopping VP execution because of gas error: {}", err);
50 Error::new(RuntimeError::OutOfGas(err))
51 })
52}
53
54pub fn read_pre<S>(
57 gas_meter: &RefCell<impl GasMetering>,
58 state: &S,
59 key: &Key,
60) -> Result<Option<Vec<u8>>>
61where
62 S: StateRead + Debug,
63{
64 let (log_val, gas) =
65 state.write_log().read_pre(key).into_storage_result()?;
66 add_gas(gas_meter, gas)?;
67 match &log_val {
68 Some(write_log::StorageModification::Write { value }) => {
69 Ok(Some(value.clone()))
70 }
71 Some(write_log::StorageModification::Delete) => {
72 Ok(None)
74 }
75 Some(write_log::StorageModification::InitAccount { vp_code_hash }) => {
76 Ok(Some(vp_code_hash.to_vec()))
78 }
79 None => {
80 let (value, gas) = state.db_read(key)?;
82 add_gas(gas_meter, gas)?;
83 Ok(value)
84 }
85 }
86}
87
88pub fn read_post<S>(
91 gas_meter: &RefCell<impl GasMetering>,
92 state: &S,
93 key: &Key,
94) -> Result<Option<Vec<u8>>>
95where
96 S: StateRead + Debug,
97{
98 let (log_val, gas) = state.write_log().read(key).into_storage_result()?;
100 add_gas(gas_meter, gas)?;
101 match log_val {
102 Some(write_log::StorageModification::Write { value }) => {
103 Ok(Some(value.clone()))
104 }
105 Some(write_log::StorageModification::Delete) => {
106 Ok(None)
108 }
109 Some(write_log::StorageModification::InitAccount { vp_code_hash }) => {
110 Ok(Some(vp_code_hash.to_vec()))
112 }
113 None => {
114 let (value, gas) = state.db_read(key)?;
117 add_gas(gas_meter, gas)?;
118 Ok(value)
119 }
120 }
121}
122
123pub fn read_temp<S>(
126 gas_meter: &RefCell<impl GasMetering>,
127 state: &S,
128 key: &Key,
129) -> Result<Option<Vec<u8>>>
130where
131 S: StateRead + Debug,
132{
133 let (log_val, gas) =
134 state.write_log().read_temp(key).into_storage_result()?;
135 add_gas(gas_meter, gas)?;
136 Ok(log_val.cloned())
137}
138
139pub fn has_key_pre<S>(
142 gas_meter: &RefCell<impl GasMetering>,
143 state: &S,
144 key: &Key,
145) -> Result<bool>
146where
147 S: StateRead + Debug,
148{
149 let (log_val, gas) =
151 state.write_log().read_pre(key).into_storage_result()?;
152 add_gas(gas_meter, gas)?;
153 match log_val {
154 Some(&write_log::StorageModification::Write { .. }) => Ok(true),
155 Some(&write_log::StorageModification::Delete) => {
156 Ok(false)
158 }
159 Some(&write_log::StorageModification::InitAccount { .. }) => Ok(true),
160 None => {
161 let (present, gas) = state.db_has_key(key)?;
163 add_gas(gas_meter, gas)?;
164 Ok(present)
165 }
166 }
167}
168
169pub fn has_key_post<S>(
172 gas_meter: &RefCell<impl GasMetering>,
173 state: &S,
174 key: &Key,
175) -> Result<bool>
176where
177 S: StateRead + Debug,
178{
179 let (log_val, gas) = state.write_log().read(key).into_storage_result()?;
181 add_gas(gas_meter, gas)?;
182 match log_val {
183 Some(write_log::StorageModification::Write { .. }) => Ok(true),
184 Some(write_log::StorageModification::Delete) => {
185 Ok(false)
187 }
188 Some(write_log::StorageModification::InitAccount { .. }) => Ok(true),
189 None => {
190 let (present, gas) = state.db_has_key(key)?;
193 add_gas(gas_meter, gas)?;
194 Ok(present)
195 }
196 }
197}
198
199pub fn get_chain_id<S>(
201 gas_meter: &RefCell<impl GasMetering>,
202 state: &S,
203) -> Result<ChainId>
204where
205 S: StateRead + Debug,
206{
207 let (chain_id, gas) = state.in_mem().get_chain_id();
208 add_gas(gas_meter, gas)?;
209 Ok(chain_id)
210}
211
212pub fn get_block_height<S>(
218 gas_meter: &RefCell<impl GasMetering>,
219 state: &S,
220) -> Result<BlockHeight>
221where
222 S: StateRead + Debug,
223{
224 let (height, gas) = state.in_mem().get_block_height();
225 add_gas(gas_meter, gas)?;
226 Ok(height)
227}
228
229pub fn get_block_header<S>(
231 gas_meter: &RefCell<impl GasMetering>,
232 state: &S,
233 height: BlockHeight,
234) -> Result<Option<BlockHeader>>
235where
236 S: StateRead + Debug,
237{
238 let (header, gas) = StateRead::get_block_header(state, Some(height))?;
239 add_gas(gas_meter, gas)?;
240 Ok(header)
241}
242
243pub fn get_tx_code_hash(
246 gas_meter: &RefCell<impl GasMetering>,
247 batched_tx: &BatchedTxRef<'_>,
248) -> Result<Option<Hash>> {
249 add_gas(
250 gas_meter,
251 (HASH_LENGTH as u64)
252 .checked_mul(MEMORY_ACCESS_GAS_PER_BYTE)
253 .expect("Consts mul that cannot overflow")
254 .into(),
255 )?;
256 let hash = batched_tx
257 .tx
258 .get_section(batched_tx.cmt.code_sechash())
259 .and_then(|x| Section::code_sec(x.as_ref()))
260 .map(|x| x.code.hash());
261 Ok(hash)
262}
263
264pub fn get_block_epoch<S>(
267 gas_meter: &RefCell<impl GasMetering>,
268 state: &S,
269) -> Result<Epoch>
270where
271 S: StateRead + Debug,
272{
273 let (epoch, gas) = state.in_mem().get_current_epoch();
274 add_gas(gas_meter, gas)?;
275 Ok(epoch)
276}
277
278pub fn get_tx_index(
281 gas_meter: &RefCell<impl GasMetering>,
282 tx_index: &TxIndex,
283) -> Result<TxIndex> {
284 add_gas(
285 gas_meter,
286 (TX_INDEX_LENGTH as u64)
287 .checked_mul(MEMORY_ACCESS_GAS_PER_BYTE)
288 .expect("Consts mul that cannot overflow")
289 .into(),
290 )?;
291 Ok(*tx_index)
292}
293
294pub fn get_native_token<S>(
296 gas_meter: &RefCell<impl GasMetering>,
297 state: &S,
298) -> Result<Address>
299where
300 S: StateRead + Debug,
301{
302 add_gas(
303 gas_meter,
304 (ESTABLISHED_ADDRESS_BYTES_LEN as u64)
305 .checked_mul(MEMORY_ACCESS_GAS_PER_BYTE)
306 .expect("Consts mul that cannot overflow")
307 .into(),
308 )?;
309 Ok(state.in_mem().native_token.clone())
310}
311
312pub fn get_pred_epochs<S>(
314 gas_meter: &RefCell<impl GasMetering>,
315 state: &S,
316) -> Result<Epochs>
317where
318 S: StateRead + Debug,
319{
320 let len = state.in_mem().block.pred_epochs.first_block_heights.len() as u64;
321 add_gas(
322 gas_meter,
323 checked!(len * 8 * MEMORY_ACCESS_GAS_PER_BYTE)?.into(),
324 )?;
325 Ok(state.in_mem().block.pred_epochs.clone())
326}
327
328pub fn get_events<S>(
330 _gas_meter: &RefCell<impl GasMetering>,
331 state: &S,
332 event_type: String,
333) -> Result<Vec<Event>>
334where
335 S: StateRead + Debug,
336{
337 let event_type = EventTypeBuilder::new_with_type(event_type).build();
338
339 Ok(state
340 .write_log()
341 .lookup_events_with_prefix(&event_type)
342 .cloned()
343 .collect())
344}
345
346pub fn iter_prefix_pre<'a, D>(
349 gas_meter: &RefCell<impl GasMetering>,
350 write_log: &'a WriteLog,
354 db: &'a D,
355 prefix: &Key,
356) -> Result<PrefixIter<'a, D>>
357where
358 D: DB + for<'iter> DBIter<'iter>,
359{
360 let (iter, gas) = namada_state::iter_prefix_pre(write_log, db, prefix)?;
361 add_gas(gas_meter, gas)?;
362 Ok(iter)
363}
364
365pub fn iter_prefix_post<'a, D>(
368 gas_meter: &RefCell<impl GasMetering>,
369 write_log: &'a WriteLog,
373 db: &'a D,
374 prefix: &Key,
375) -> Result<PrefixIter<'a, D>>
376where
377 D: DB + for<'iter> DBIter<'iter>,
378{
379 let (iter, gas) = namada_state::iter_prefix_post(write_log, db, prefix)?;
380 add_gas(gas_meter, gas)?;
381 Ok(iter)
382}
383
384pub fn iter_next<DB>(
386 gas_meter: &RefCell<impl GasMetering>,
387 iter: &mut PrefixIter<'_, DB>,
388) -> Result<Option<(String, Vec<u8>)>>
389where
390 DB: namada_state::DB + for<'iter> namada_state::DBIter<'iter>,
391{
392 if let Some((key, val, gas)) = iter.next() {
393 add_gas(gas_meter, gas)?;
394 return Ok(Some((key, val)));
395 }
396 Ok(None)
397}