1#![doc(html_favicon_url = "https://dev.namada.net/master/favicon.png")]
5#![doc(html_logo_url = "https://dev.namada.net/master/rustdoc-logo.png")]
6#![deny(rustdoc::broken_intra_doc_links)]
7#![deny(rustdoc::private_intra_doc_links)]
8#![warn(
9 missing_docs,
10 rust_2018_idioms,
11 clippy::cast_sign_loss,
12 clippy::cast_possible_truncation,
13 clippy::cast_possible_wrap,
14 clippy::cast_lossless,
15 clippy::arithmetic_side_effects
16)]
17
18pub mod event;
19pub mod storage;
20
21use std::fmt::Display;
22use std::num::ParseIntError;
23use std::str::FromStr;
24
25use namada_core::borsh::{BorshDeserialize, BorshSchema, BorshSerialize};
26use namada_core::hints;
27use namada_macros::BorshDeserializer;
28#[cfg(feature = "migrations")]
29use namada_migrations::*;
30use serde::{Deserialize, Serialize};
31use thiserror::Error;
32
33#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
35pub enum GasMeterKind {
36 HostFn,
38 MutGlobal,
41}
42
43#[allow(missing_docs)]
44#[derive(Error, Debug, Clone, PartialEq, Eq)]
45pub enum Error {
46 #[error("Transaction gas exceeded the limit of {0} gas units")]
47 TransactionGasExceededError(WholeGas),
48 #[error("Block gas limit exceeded")]
49 BlockGasExceeded,
50 #[error("Overflow during gas operations")]
51 GasOverflow,
52}
53
54#[allow(missing_docs)]
55#[derive(Error, Debug, Clone, PartialEq, Eq)]
56pub enum GasParseError {
57 #[error("Failed to parse gas: {0}")]
58 Parse(ParseIntError),
59 #[error("Gas overflowed")]
60 Overflow,
61}
62
63const COMPILE_GAS_PER_BYTE_RAW: u64 = 1_664;
68const WASM_CODE_VALIDATION_GAS_PER_BYTE_RAW: u64 = 59;
69const WRAPPER_TX_VALIDATION_GAS_RAW: u64 = 1_526_700;
70const STORAGE_OCCUPATION_GAS_PER_BYTE_RAW: u64 =
77 PHYSICAL_STORAGE_LATENCY_PER_BYTE_RAW * (1 + 1_000);
78const PHYSICAL_STORAGE_LATENCY_PER_BYTE_RAW: u64 = 20;
85const NETWORK_TRANSMISSION_GAS_PER_BYTE_RAW: u64 = 848;
87
88const MEMORY_ACCESS_GAS_PER_BYTE_RAW: u64 = 39;
90const STORAGE_ACCESS_GAS_PER_BYTE_RAW: u64 =
92 93 + PHYSICAL_STORAGE_LATENCY_PER_BYTE_RAW;
93const STORAGE_WRITE_GAS_PER_BYTE_RAW: u64 = MEMORY_ACCESS_GAS_PER_BYTE_RAW
95 + 17_583
96 + STORAGE_OCCUPATION_GAS_PER_BYTE_RAW;
97const STORAGE_DELETE_GAS_PER_BYTE_RAW: u64 = MEMORY_ACCESS_GAS_PER_BYTE_RAW
99 + 17_583
100 + PHYSICAL_STORAGE_LATENCY_PER_BYTE_RAW;
101const VERIFY_TX_SIG_GAS_RAW: u64 = 435_190;
103const WASM_MEMORY_PAGE_GAS_RAW: u64 =
105 MEMORY_ACCESS_GAS_PER_BYTE_RAW * 64 * 1_024;
106const IBC_ACTION_VALIDATE_GAS_RAW: u64 = 290_935;
108const IBC_ACTION_EXECUTE_GAS_RAW: u64 = 1_685_733;
110const MASP_VERIFY_SIG_GAS_RAW: u64 = 1_908_750;
112const MASP_FIXED_SPEND_GAS_RAW: u64 = 59_521_000;
114const MASP_VARIABLE_SPEND_GAS_RAW: u64 = 9_849_000;
116const MASP_FIXED_CONVERT_GAS_RAW: u64 = 46_197_000;
118const MASP_VARIABLE_CONVERT_GAS_RAW: u64 = 10_245_000;
120const MASP_FIXED_OUTPUT_GAS_RAW: u64 = 53_439_000;
122const MASP_VARIABLE_OUTPUT_GAS_RAW: u64 = 9_710_000;
124const MASP_SPEND_CHECK_GAS_RAW: u64 = 405_070;
126const MASP_CONVERT_CHECK_GAS_RAW: u64 = 188_590;
128const MASP_OUTPUT_CHECK_GAS_RAW: u64 = 204_430;
130const MASP_FINAL_CHECK_GAS_RAW: u64 = 43;
132const GAS_COST_CORRECTION: u64 = 5;
145
146const COMPILE_GAS_PER_BYTE: u64 =
154 COMPILE_GAS_PER_BYTE_RAW * GAS_COST_CORRECTION / 100;
155const WASM_CODE_VALIDATION_GAS_PER_BYTE: u64 =
156 WASM_CODE_VALIDATION_GAS_PER_BYTE_RAW * GAS_COST_CORRECTION;
157const WRAPPER_TX_VALIDATION_GAS: u64 =
158 WRAPPER_TX_VALIDATION_GAS_RAW * GAS_COST_CORRECTION;
159const STORAGE_OCCUPATION_GAS_PER_BYTE: u64 =
160 STORAGE_OCCUPATION_GAS_PER_BYTE_RAW * GAS_COST_CORRECTION;
161const NETWORK_TRANSMISSION_GAS_PER_BYTE: u64 =
162 NETWORK_TRANSMISSION_GAS_PER_BYTE_RAW * GAS_COST_CORRECTION;
163pub const MEMORY_ACCESS_GAS_PER_BYTE: u64 =
165 MEMORY_ACCESS_GAS_PER_BYTE_RAW * GAS_COST_CORRECTION;
166pub const STORAGE_ACCESS_GAS_PER_BYTE: u64 =
168 STORAGE_ACCESS_GAS_PER_BYTE_RAW * GAS_COST_CORRECTION;
169pub const STORAGE_WRITE_GAS_PER_BYTE: u64 =
171 STORAGE_WRITE_GAS_PER_BYTE_RAW * GAS_COST_CORRECTION;
172pub const STORAGE_DELETE_GAS_PER_BYTE: u64 =
174 STORAGE_DELETE_GAS_PER_BYTE_RAW * GAS_COST_CORRECTION;
175pub const VERIFY_TX_SIG_GAS: u64 = VERIFY_TX_SIG_GAS_RAW * GAS_COST_CORRECTION;
177#[allow(clippy::cast_possible_truncation)] pub const WASM_MEMORY_PAGE_GAS: u32 =
180 (WASM_MEMORY_PAGE_GAS_RAW * GAS_COST_CORRECTION) as u32;
181pub const IBC_ACTION_VALIDATE_GAS: u64 =
183 IBC_ACTION_VALIDATE_GAS_RAW * GAS_COST_CORRECTION;
184pub const IBC_ACTION_EXECUTE_GAS: u64 =
186 IBC_ACTION_EXECUTE_GAS_RAW * GAS_COST_CORRECTION;
187pub const MASP_VERIFY_SIG_GAS: u64 =
189 MASP_VERIFY_SIG_GAS_RAW * GAS_COST_CORRECTION;
190pub const MASP_FIXED_SPEND_GAS: u64 =
192 MASP_FIXED_SPEND_GAS_RAW * GAS_COST_CORRECTION;
193pub const MASP_VARIABLE_SPEND_GAS: u64 =
195 MASP_VARIABLE_SPEND_GAS_RAW * GAS_COST_CORRECTION;
196pub const MASP_FIXED_CONVERT_GAS: u64 =
198 MASP_FIXED_CONVERT_GAS_RAW * GAS_COST_CORRECTION;
199pub const MASP_VARIABLE_CONVERT_GAS: u64 =
201 MASP_VARIABLE_CONVERT_GAS_RAW * GAS_COST_CORRECTION;
202pub const MASP_FIXED_OUTPUT_GAS: u64 =
204 MASP_FIXED_OUTPUT_GAS_RAW * GAS_COST_CORRECTION;
205pub const MASP_VARIABLE_OUTPUT_GAS: u64 =
207 MASP_VARIABLE_OUTPUT_GAS_RAW * GAS_COST_CORRECTION;
208pub const MASP_SPEND_CHECK_GAS: u64 =
210 MASP_SPEND_CHECK_GAS_RAW * GAS_COST_CORRECTION;
211pub const MASP_CONVERT_CHECK_GAS: u64 =
213 MASP_CONVERT_CHECK_GAS_RAW * GAS_COST_CORRECTION;
214pub const MASP_OUTPUT_CHECK_GAS: u64 =
216 MASP_OUTPUT_CHECK_GAS_RAW * GAS_COST_CORRECTION;
217pub const MASP_FINAL_CHECK_GAS: u64 =
219 MASP_FINAL_CHECK_GAS_RAW * GAS_COST_CORRECTION;
220pub type Result<T> = std::result::Result<T, Error>;
224
225#[derive(
233 Clone,
234 Debug,
235 Default,
236 PartialEq,
237 PartialOrd,
238 BorshDeserialize,
239 BorshDeserializer,
240 BorshSerialize,
241 BorshSchema,
242 Serialize,
243 Deserialize,
244)]
245#[must_use = "Gas must be accounted for by the gas meter"]
246pub struct Gas {
247 sub: u64,
248}
249
250impl Gas {
251 pub const fn new(sub_units: u64) -> Self {
253 Self { sub: sub_units }
254 }
255
256 pub fn checked_add(&self, rhs: Self) -> Option<Self> {
258 self.sub.checked_add(rhs.sub).map(|sub| Self { sub })
259 }
260
261 pub fn checked_sub(&self, rhs: Self) -> Option<Self> {
263 self.sub.checked_sub(rhs.sub).map(|sub| Self { sub })
264 }
265
266 pub fn checked_div(&self, rhs: u64) -> Option<Self> {
268 self.sub.checked_div(rhs).map(|sub| Self { sub })
269 }
270
271 pub fn get_whole_gas_units(&self, scale: u64) -> WholeGas {
274 let quotient = self
275 .sub
276 .checked_div(scale)
277 .expect("Gas quotient should not overflow on checked division");
278 if self
279 .sub
280 .checked_rem(scale)
281 .expect("Gas quotient remainder should not overflow")
282 == 0
283 {
284 quotient.into()
285 } else {
286 quotient
287 .checked_add(1)
288 .expect("Cannot overflow as the quotient is scaled down u64")
289 .into()
290 }
291 }
292
293 pub fn from_whole_units(whole: WholeGas, scale: u64) -> Option<Self> {
295 scale.checked_mul(whole.into()).map(|sub| Self { sub })
296 }
297}
298
299impl From<u64> for Gas {
300 fn from(sub: u64) -> Self {
301 Self { sub }
302 }
303}
304
305impl From<Gas> for u64 {
306 fn from(gas: Gas) -> Self {
307 gas.sub
308 }
309}
310
311#[derive(
314 Debug,
315 Clone,
316 Copy,
317 PartialEq,
318 BorshSerialize,
319 BorshDeserialize,
320 BorshDeserializer,
321 BorshSchema,
322 Serialize,
323 Deserialize,
324 Eq,
325)]
326pub struct WholeGas(u64);
327
328impl From<u64> for WholeGas {
329 fn from(amount: u64) -> WholeGas {
330 Self(amount)
331 }
332}
333
334impl From<WholeGas> for u64 {
335 fn from(whole: WholeGas) -> u64 {
336 whole.0
337 }
338}
339
340impl FromStr for WholeGas {
341 type Err = GasParseError;
342
343 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
344 Ok(Self(s.parse().map_err(GasParseError::Parse)?))
345 }
346}
347
348impl Display for WholeGas {
349 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
350 write!(f, "{}", self.0)
351 }
352}
353
354pub trait GasMetering {
356 fn consume(&mut self, gas: Gas) -> Result<()>;
360
361 fn get_initially_available_gas(&self) -> Gas;
367
368 fn get_consumed_gas(&self) -> Gas;
370
371 fn get_gas_limit(&self) -> Gas;
373
374 fn get_gas_scale(&self) -> u64;
376
377 fn get_available_gas(&self) -> Gas {
379 self.get_gas_limit()
380 .checked_sub(self.get_consumed_gas())
381 .unwrap_or_default()
382 }
383
384 fn add_compiling_gas(&mut self, bytes_len: u64) -> Result<()> {
386 self.consume(
387 bytes_len
388 .checked_mul(COMPILE_GAS_PER_BYTE)
389 .ok_or(Error::GasOverflow)?
390 .into(),
391 )
392 }
393
394 fn add_wasm_load_from_storage_gas(&mut self, bytes_len: u64) -> Result<()> {
396 self.consume(
397 bytes_len
398 .checked_mul(STORAGE_ACCESS_GAS_PER_BYTE)
399 .ok_or(Error::GasOverflow)?
400 .into(),
401 )
402 }
403
404 fn add_wasm_validation_gas(&mut self, bytes_len: u64) -> Result<()> {
406 self.consume(
407 bytes_len
408 .checked_mul(WASM_CODE_VALIDATION_GAS_PER_BYTE)
409 .ok_or(Error::GasOverflow)?
410 .into(),
411 )
412 }
413
414 fn check_limit(&self, gas: Gas) -> Result<()> {
416 self.get_initially_available_gas()
417 .checked_sub(gas)
418 .ok_or_else(|| {
419 Error::TransactionGasExceededError(
420 self.get_gas_limit()
421 .get_whole_gas_units(self.get_gas_scale()),
422 )
423 })
424 .and(Ok(()))
425 }
426}
427
428#[derive(Debug)]
430pub struct TxGasMeter {
431 gas_overflow: bool,
433 gas_scale: u64,
435 tx_gas_limit: Gas,
437 transaction_gas: Gas,
439}
440
441#[derive(Debug)]
443pub struct VpGasMeter {
444 gas_overflow: bool,
446 gas_scale: u64,
448 tx_gas_limit: Gas,
450 prev_meter_consumed_gas: Gas,
452 current_gas: Gas,
454}
455
456impl GasMetering for TxGasMeter {
457 fn consume(&mut self, gas: Gas) -> Result<()> {
458 if self.gas_overflow {
459 hints::cold();
460 return Err(Error::GasOverflow);
461 }
462
463 self.transaction_gas =
464 self.transaction_gas.checked_add(gas).ok_or_else(|| {
465 hints::cold();
466 self.gas_overflow = true;
467 Error::GasOverflow
468 })?;
469
470 if self.transaction_gas > self.tx_gas_limit {
471 return Err(Error::TransactionGasExceededError(
472 self.tx_gas_limit.get_whole_gas_units(self.gas_scale),
473 ));
474 }
475
476 Ok(())
477 }
478
479 #[inline]
480 fn get_initially_available_gas(&self) -> Gas {
481 self.get_gas_limit()
482 }
483
484 fn get_consumed_gas(&self) -> Gas {
485 if !self.gas_overflow {
486 self.transaction_gas.clone()
487 } else {
488 hints::cold();
489 u64::MAX.into()
490 }
491 }
492
493 fn get_gas_limit(&self) -> Gas {
494 self.tx_gas_limit.clone()
495 }
496
497 fn get_gas_scale(&self) -> u64 {
498 self.gas_scale
499 }
500}
501
502impl TxGasMeter {
503 pub const unsafe fn placeholder() -> Self {
510 Self {
511 gas_overflow: false,
512 gas_scale: 0u64,
513 tx_gas_limit: Gas::new(0u64),
514 transaction_gas: Gas::new(0u64),
515 }
516 }
517
518 pub fn new(tx_gas_limit: impl Into<Gas>, gas_scale: u64) -> Self {
521 Self {
522 gas_overflow: false,
523 gas_scale,
524 tx_gas_limit: tx_gas_limit.into(),
525 transaction_gas: Gas::default(),
526 }
527 }
528
529 pub fn add_wrapper_gas(&mut self, tx_bytes: &[u8]) -> Result<()> {
535 self.consume(WRAPPER_TX_VALIDATION_GAS.into())?;
536
537 let bytes_len = tx_bytes.len() as u64;
538 self.consume(
539 bytes_len
540 .checked_mul(
541 STORAGE_OCCUPATION_GAS_PER_BYTE
542 + NETWORK_TRANSMISSION_GAS_PER_BYTE,
543 )
544 .ok_or(Error::GasOverflow)?
545 .into(),
546 )
547 }
548}
549
550impl GasMetering for VpGasMeter {
551 fn consume(&mut self, gas: Gas) -> Result<()> {
552 if self.gas_overflow {
553 hints::cold();
554 return Err(Error::GasOverflow);
555 }
556
557 self.current_gas =
558 self.current_gas.checked_add(gas).ok_or_else(|| {
559 hints::cold();
560 self.gas_overflow = true;
561 Error::GasOverflow
562 })?;
563
564 let current_total = self
565 .prev_meter_consumed_gas
566 .checked_add(self.current_gas.clone())
567 .ok_or(Error::GasOverflow)?;
568
569 if current_total > self.tx_gas_limit {
570 return Err(Error::TransactionGasExceededError(
571 self.tx_gas_limit.get_whole_gas_units(self.gas_scale),
572 ));
573 }
574
575 Ok(())
576 }
577
578 fn get_initially_available_gas(&self) -> Gas {
579 self.tx_gas_limit
580 .checked_sub(self.prev_meter_consumed_gas.clone())
581 .unwrap_or_default()
582 }
583
584 fn get_consumed_gas(&self) -> Gas {
585 self.prev_meter_consumed_gas
586 .checked_add(self.get_vp_consumed_gas())
587 .unwrap_or_else(|| u64::MAX.into())
588 }
589
590 fn get_gas_limit(&self) -> Gas {
591 self.tx_gas_limit.clone()
592 }
593
594 fn get_gas_scale(&self) -> u64 {
595 self.gas_scale
596 }
597}
598
599impl VpGasMeter {
600 pub const unsafe fn placeholder() -> Self {
607 Self {
608 gas_overflow: false,
609 gas_scale: 0u64,
610 tx_gas_limit: Gas::new(0u64),
611 prev_meter_consumed_gas: Gas::new(0u64),
612 current_gas: Gas::new(0u64),
613 }
614 }
615
616 pub fn new_from_tx_meter(tx_gas_meter: &TxGasMeter) -> Self {
618 Self::new_from_meter(tx_gas_meter)
619 }
620
621 pub fn new_from_meter(gas_meter: &impl GasMetering) -> Self {
623 Self {
624 gas_overflow: false,
625 gas_scale: gas_meter.get_gas_scale(),
626 tx_gas_limit: gas_meter.get_gas_limit(),
627 prev_meter_consumed_gas: gas_meter.get_consumed_gas(),
628 current_gas: Gas::default(),
629 }
630 }
631
632 pub fn get_vp_consumed_gas(&self) -> Gas {
634 self.current_gas.clone()
635 }
636}
637
638#[cfg(test)]
639mod tests {
640 use assert_matches::assert_matches;
641 use proptest::prelude::*;
642
643 use super::*;
644 const BLOCK_GAS_LIMIT: u64 = 10_000_000_000;
645 const TX_GAS_LIMIT: u64 = 1_000_000;
646 const GAS_SCALE: u64 = 1;
647
648 proptest! {
649 #[test]
650 fn test_vp_gas_meter_add(gas in 0..BLOCK_GAS_LIMIT) {
651 let tx_gas_meter = TxGasMeter {
652 gas_overflow: false,
653 gas_scale: GAS_SCALE,
654 tx_gas_limit: BLOCK_GAS_LIMIT.into(),
655 transaction_gas: Gas::default(),
656 };
657 let mut meter = VpGasMeter::new_from_tx_meter(&tx_gas_meter);
658 meter.consume(gas.into()).expect("cannot add the gas");
659 }
660
661 }
662
663 #[test]
664 fn test_vp_gas_overflow() {
665 let tx_gas_meter = TxGasMeter {
666 gas_overflow: false,
667 gas_scale: GAS_SCALE,
668 tx_gas_limit: BLOCK_GAS_LIMIT.into(),
669 transaction_gas: (TX_GAS_LIMIT - 1).into(),
670 };
671 let mut meter = VpGasMeter::new_from_tx_meter(&tx_gas_meter);
672 assert_matches!(
673 meter
674 .consume(u64::MAX.into())
675 .expect_err("unexpectedly succeeded"),
676 Error::GasOverflow
677 );
678 }
679
680 #[test]
681 fn test_vp_gas_limit() {
682 let tx_gas_meter = TxGasMeter {
683 gas_overflow: false,
684 gas_scale: GAS_SCALE,
685 tx_gas_limit: TX_GAS_LIMIT.into(),
686 transaction_gas: (TX_GAS_LIMIT - 1).into(),
687 };
688 let mut meter = VpGasMeter::new_from_tx_meter(&tx_gas_meter);
689 assert_matches!(
690 meter
691 .consume(TX_GAS_LIMIT.into())
692 .expect_err("unexpectedly succeeded"),
693 Error::TransactionGasExceededError(_)
694 );
695 }
696
697 #[test]
698 fn test_tx_gas_overflow() {
699 let mut meter = TxGasMeter::new(BLOCK_GAS_LIMIT, GAS_SCALE);
700 meter.consume(1.into()).expect("cannot add the gas");
701 assert_matches!(
702 meter
703 .consume(u64::MAX.into())
704 .expect_err("unexpectedly succeeded"),
705 Error::GasOverflow
706 );
707 }
708
709 #[test]
710 fn test_tx_gas_limit() {
711 let mut meter = TxGasMeter::new(TX_GAS_LIMIT, GAS_SCALE);
712 assert_matches!(
713 meter
714 .consume((TX_GAS_LIMIT + 1).into())
715 .expect_err("unexpectedly succeeded"),
716 Error::TransactionGasExceededError(_)
717 );
718 }
719}