Skip to content

Commit 94439bd

Browse files
committed
Switch to wasm global gas meter
1 parent 649cfd4 commit 94439bd

File tree

25 files changed

+811
-471
lines changed

25 files changed

+811
-471
lines changed

crates/gas/src/lib.rs

Lines changed: 90 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,11 @@ pub struct Gas {
248248
}
249249

250250
impl Gas {
251+
/// Initialize a new gas value from its sub units.
252+
pub const fn new(sub_units: u64) -> Self {
253+
Self { sub: sub_units }
254+
}
255+
251256
/// Checked add of `Gas`. Returns `None` on overflow
252257
pub fn checked_add(&self, rhs: Self) -> Option<Self> {
253258
self.sub.checked_add(rhs.sub).map(|sub| Self { sub })
@@ -353,6 +358,29 @@ pub trait GasMetering {
353358
/// will still be updated
354359
fn consume(&mut self, gas: Gas) -> Result<()>;
355360

361+
/// Get the gas initially available to the gas meter
362+
///
363+
/// This value will be equal to the gas limit minus some
364+
/// gas that may have been consumed before the current
365+
/// meter was initialized
366+
fn get_initially_available_gas(&self) -> Gas;
367+
368+
/// Get the gas consumed thus far
369+
fn get_consumed_gas(&self) -> Gas;
370+
371+
/// Get the gas limit
372+
fn get_gas_limit(&self) -> Gas;
373+
374+
/// Get the protocol gas scale
375+
fn get_gas_scale(&self) -> u64;
376+
377+
/// Get the amount of gas still available to the transaction
378+
fn get_available_gas(&self) -> Gas {
379+
self.get_gas_limit()
380+
.checked_sub(self.get_consumed_gas())
381+
.unwrap_or_default()
382+
}
383+
356384
/// Add the compiling cost proportionate to the code length
357385
fn add_compiling_gas(&mut self, bytes_len: u64) -> Result<()> {
358386
self.consume(
@@ -383,56 +411,64 @@ pub trait GasMetering {
383411
)
384412
}
385413

386-
/// Get the gas consumed by the tx
387-
fn get_tx_consumed_gas(&self) -> Gas;
388-
389-
/// Get the gas limit
390-
fn get_gas_limit(&self) -> Gas;
391-
392-
/// Get the protocol gas scale
393-
fn get_gas_scale(&self) -> u64;
414+
/// Check if the meter ran out of gas. Starts with the initial gas.
415+
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+
}
394426

395-
/// Check if the vps went out of gas. Starts with the gas consumed by the
396-
/// transaction.
397-
fn check_vps_limit(&self, vps_gas: Gas) -> Result<()> {
398-
let total = self
399-
.get_tx_consumed_gas()
400-
.checked_add(vps_gas)
401-
.ok_or(Error::GasOverflow)?;
402-
let gas_limit = self.get_gas_limit();
403-
if total > gas_limit {
404-
return Err(Error::TransactionGasExceededError(
405-
gas_limit.get_whole_gas_units(self.get_gas_scale()),
406-
));
407-
}
427+
/// Add the gas required by a wrapper transaction which is comprised of:
428+
/// - cost of validating the wrapper tx
429+
/// - space that the transaction requires in the block
430+
/// - cost of downloading (as part of the block) the transaction bytes over
431+
/// the network
432+
fn add_wrapper_gas(&mut self, tx_bytes: &[u8]) -> Result<()> {
433+
self.consume(WRAPPER_TX_VALIDATION_GAS.into())?;
408434

409-
Ok(())
435+
let bytes_len = tx_bytes.len() as u64;
436+
self.consume(
437+
bytes_len
438+
.checked_mul(
439+
STORAGE_OCCUPATION_GAS_PER_BYTE
440+
+ NETWORK_TRANSMISSION_GAS_PER_BYTE,
441+
)
442+
.ok_or(Error::GasOverflow)?
443+
.into(),
444+
)
410445
}
411446
}
412447

413448
/// Gas metering in a transaction
414-
#[derive(Debug)]
449+
#[derive(Debug, Default)]
415450
pub struct TxGasMeter {
416451
/// Track gas overflow
417452
gas_overflow: bool,
418-
// The protocol gas scale
453+
/// The protocol gas scale
419454
gas_scale: u64,
420455
/// The gas limit for a transaction
421-
pub tx_gas_limit: Gas,
456+
tx_gas_limit: Gas,
457+
/// Gas consumption of the tx
422458
transaction_gas: Gas,
423459
}
424460

425461
/// Gas metering in a validity predicate
426-
#[derive(Debug)]
462+
#[derive(Debug, Default)]
427463
pub struct VpGasMeter {
428464
/// Track gas overflow
429465
gas_overflow: bool,
430-
// The protocol gas scale
466+
/// The protocol gas scale
431467
gas_scale: u64,
432468
/// The transaction gas limit
433469
tx_gas_limit: Gas,
434470
/// The gas consumed by the transaction before the Vp
435-
initial_gas: Gas,
471+
prev_meter_consumed_gas: Gas,
436472
/// The current gas usage in the VP
437473
current_gas: Gas,
438474
}
@@ -460,8 +496,12 @@ impl GasMetering for TxGasMeter {
460496
Ok(())
461497
}
462498

463-
/// Get the entire gas used by the transaction up until this point
464-
fn get_tx_consumed_gas(&self) -> Gas {
499+
#[inline]
500+
fn get_initially_available_gas(&self) -> Gas {
501+
self.get_gas_limit()
502+
}
503+
504+
fn get_consumed_gas(&self) -> Gas {
465505
if !self.gas_overflow {
466506
self.transaction_gas.clone()
467507
} else {
@@ -490,53 +530,6 @@ impl TxGasMeter {
490530
transaction_gas: Gas::default(),
491531
}
492532
}
493-
494-
/// Add the gas required by a wrapper transaction which is comprised of:
495-
/// - cost of validating the wrapper tx
496-
/// - space that the transaction requires in the block
497-
/// - cost of downloading (as part of the block) the transaction bytes over
498-
/// the network
499-
pub fn add_wrapper_gas(&mut self, tx_bytes: &[u8]) -> Result<()> {
500-
self.consume(WRAPPER_TX_VALIDATION_GAS.into())?;
501-
502-
let bytes_len = tx_bytes.len() as u64;
503-
self.consume(
504-
bytes_len
505-
.checked_mul(
506-
STORAGE_OCCUPATION_GAS_PER_BYTE
507-
+ NETWORK_TRANSMISSION_GAS_PER_BYTE,
508-
)
509-
.ok_or(Error::GasOverflow)?
510-
.into(),
511-
)
512-
}
513-
514-
/// Get the amount of gas still available to the transaction
515-
pub fn get_available_gas(&self) -> Gas {
516-
self.tx_gas_limit
517-
.checked_sub(self.transaction_gas.clone())
518-
.unwrap_or_default()
519-
}
520-
521-
/// Set the amount of gas still available to the transaction.
522-
///
523-
/// WARNING: Utmost care must be taken when using this function! This must
524-
/// never increase the amount available.
525-
pub fn set_available_gas(&mut self, gas: impl Into<Gas>) {
526-
let gas: Gas = gas.into();
527-
let used_gas = self
528-
.tx_gas_limit
529-
.checked_sub(gas)
530-
.unwrap_or_else(|| self.tx_gas_limit.clone());
531-
debug_assert!(
532-
used_gas > self.transaction_gas,
533-
"Used gas must not decrease from {:?}, but trying to set it to \
534-
{:?}",
535-
self.transaction_gas,
536-
used_gas
537-
);
538-
self.transaction_gas = used_gas;
539-
}
540533
}
541534

542535
impl GasMetering for VpGasMeter {
@@ -554,7 +547,7 @@ impl GasMetering for VpGasMeter {
554547
})?;
555548

556549
let current_total = self
557-
.initial_gas
550+
.prev_meter_consumed_gas
558551
.checked_add(self.current_gas.clone())
559552
.ok_or(Error::GasOverflow)?;
560553

@@ -567,14 +560,16 @@ impl GasMetering for VpGasMeter {
567560
Ok(())
568561
}
569562

570-
/// Get the gas consumed by the tx alone before the vps were executed
571-
fn get_tx_consumed_gas(&self) -> Gas {
572-
if !self.gas_overflow {
573-
self.initial_gas.clone()
574-
} else {
575-
hints::cold();
576-
u64::MAX.into()
577-
}
563+
fn get_initially_available_gas(&self) -> Gas {
564+
self.tx_gas_limit
565+
.checked_sub(self.prev_meter_consumed_gas.clone())
566+
.unwrap_or_default()
567+
}
568+
569+
fn get_consumed_gas(&self) -> Gas {
570+
self.prev_meter_consumed_gas
571+
.checked_add(self.get_vp_consumed_gas())
572+
.unwrap_or_else(|| u64::MAX.into())
578573
}
579574

580575
fn get_gas_limit(&self) -> Gas {
@@ -587,13 +582,18 @@ impl GasMetering for VpGasMeter {
587582
}
588583

589584
impl VpGasMeter {
590-
/// Initialize a new VP gas meter from the `TxGasMeter`
585+
/// Initialize a new VP gas meter from the [`TxGasMeter`]
591586
pub fn new_from_tx_meter(tx_gas_meter: &TxGasMeter) -> Self {
587+
Self::new_from_meter(tx_gas_meter)
588+
}
589+
590+
/// Initialize a new VP gas meter from the given generic gas meter
591+
pub fn new_from_meter(gas_meter: &impl GasMetering) -> Self {
592592
Self {
593593
gas_overflow: false,
594-
gas_scale: tx_gas_meter.gas_scale,
595-
tx_gas_limit: tx_gas_meter.tx_gas_limit.clone(),
596-
initial_gas: tx_gas_meter.transaction_gas.clone(),
594+
gas_scale: gas_meter.get_gas_scale(),
595+
tx_gas_limit: gas_meter.get_gas_limit(),
596+
prev_meter_consumed_gas: gas_meter.get_consumed_gas(),
597597
current_gas: Gas::default(),
598598
}
599599
}
@@ -602,15 +602,6 @@ impl VpGasMeter {
602602
pub fn get_vp_consumed_gas(&self) -> Gas {
603603
self.current_gas.clone()
604604
}
605-
606-
/// Get the amount of gas still available to the VP
607-
pub fn get_available_gas(&self) -> Gas {
608-
self.tx_gas_limit
609-
.checked_sub(self.initial_gas.clone())
610-
.unwrap_or_default()
611-
.checked_sub(self.current_gas.clone())
612-
.unwrap_or_default()
613-
}
614605
}
615606

616607
#[cfg(test)]

crates/node/src/dry_run_tx.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ where
115115
tx_result_string,
116116
tx_gas_meter
117117
.borrow()
118-
.get_tx_consumed_gas()
118+
.get_consumed_gas()
119119
.get_whole_gas_units(gas_scale),
120120
);
121121

crates/node/src/protocol.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -888,7 +888,7 @@ where
888888

889889
tx_gas_meter
890890
.borrow_mut()
891-
.consume(masp_gas_meter.borrow().get_tx_consumed_gas())
891+
.consume(masp_gas_meter.borrow().get_consumed_gas())
892892
.map_err(|e| Error::GasError(e.to_string()))?;
893893

894894
Ok(valid_batched_tx_result)
@@ -1273,7 +1273,7 @@ where
12731273
|| (VpsResult::default(), Gas::from(0)),
12741274
|(mut result, mut vps_gas), addr| {
12751275
let gas_meter =
1276-
RefCell::new(VpGasMeter::new_from_tx_meter(tx_gas_meter));
1276+
RefCell::new(VpGasMeter::new_from_meter(tx_gas_meter));
12771277
let tx_accepted = match &addr {
12781278
Address::Implicit(_) | Address::Established(_) => {
12791279
let (vp_hash, gas) = state
@@ -1471,7 +1471,7 @@ where
14711471
))?;
14721472
gas_meter
14731473
.borrow()
1474-
.check_vps_limit(vps_gas.clone())
1474+
.check_limit(vps_gas.clone())
14751475
.map_err(|err| Error::GasError(err.to_string()))?;
14761476

14771477
Ok((result, vps_gas))
@@ -1503,7 +1503,7 @@ fn merge_vp_results(
15031503
.checked_add(b_gas)
15041504
.ok_or(Error::GasError(gas::Error::GasOverflow.to_string()))?;
15051505
tx_gas_meter
1506-
.check_vps_limit(vps_gas.clone())
1506+
.check_limit(vps_gas.clone())
15071507
.map_err(|err| Error::GasError(err.to_string()))?;
15081508

15091509
Ok((

crates/node/src/shell/finalize_block.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -413,7 +413,7 @@ where
413413
let gas_scale = tx_data.tx_gas_meter.get_gas_scale();
414414
let scaled_gas = tx_data
415415
.tx_gas_meter
416-
.get_tx_consumed_gas()
416+
.get_consumed_gas()
417417
.get_whole_gas_units(gas_scale);
418418
tx_logs
419419
.tx_event
@@ -446,7 +446,7 @@ where
446446
let gas_scale = tx_data.tx_gas_meter.get_gas_scale();
447447
let scaled_gas = tx_data
448448
.tx_gas_meter
449-
.get_tx_consumed_gas()
449+
.get_consumed_gas()
450450
.get_whole_gas_units(gas_scale);
451451

452452
tx_logs
@@ -523,7 +523,7 @@ where
523523
let gas_scale = tx_data.tx_gas_meter.get_gas_scale();
524524
let scaled_gas = tx_data
525525
.tx_gas_meter
526-
.get_tx_consumed_gas()
526+
.get_consumed_gas()
527527
.get_whole_gas_units(gas_scale);
528528

529529
tx_logs
@@ -806,7 +806,7 @@ where
806806
&mut self.state,
807807
);
808808
let tx_gas_meter = tx_gas_meter.into_inner();
809-
let consumed_gas = tx_gas_meter.get_tx_consumed_gas();
809+
let consumed_gas = tx_gas_meter.get_consumed_gas();
810810

811811
// save the gas cost
812812
self.update_tx_gas(tx_hash, consumed_gas);
@@ -880,7 +880,7 @@ where
880880
&mut self.state,
881881
);
882882
let tx_gas_meter = tx_gas_meter.into_inner();
883-
let consumed_gas = tx_gas_meter.get_tx_consumed_gas();
883+
let consumed_gas = tx_gas_meter.get_consumed_gas();
884884

885885
// update the gas cost of the corresponding wrapper
886886
self.update_tx_gas(tx_hash, consumed_gas);

crates/node/src/shell/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ use namada_sdk::eth_bridge::protocol::validation::validator_set_update::validate
4242
use namada_sdk::eth_bridge::{EthBridgeQueries, EthereumOracleConfig};
4343
use namada_sdk::ethereum_events::EthereumEvent;
4444
use namada_sdk::events::log::EventLog;
45-
use namada_sdk::gas::{Gas, TxGasMeter};
45+
use namada_sdk::gas::{Gas, GasMetering, TxGasMeter};
4646
use namada_sdk::hash::Hash;
4747
use namada_sdk::key::*;
4848
use namada_sdk::migrations::ScheduledMigration;
@@ -1306,7 +1306,7 @@ where
13061306
let block_gas_limit: Gas =
13071307
Gas::from_whole_units(max_block_gas.into(), gas_scale)
13081308
.expect("Gas limit from parameter must not overflow");
1309-
if gas_meter.tx_gas_limit > block_gas_limit {
1309+
if gas_meter.get_gas_limit() > block_gas_limit {
13101310
response.code = ResultCode::AllocationError.into();
13111311
response.log = format!(
13121312
"{INVALID_MSG}: Wrapper transaction exceeds the \

crates/node/src/shell/prepare_proposal.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
use std::cell::RefCell;
44

55
use namada_sdk::address::Address;
6-
use namada_sdk::gas::TxGasMeter;
6+
use namada_sdk::gas::{GasMetering, TxGasMeter};
77
use namada_sdk::key::tm_raw_hash_to_string;
88
use namada_sdk::parameters::get_gas_scale;
99
use namada_sdk::proof_of_stake::storage::find_validator_by_raw_hash;

0 commit comments

Comments
 (0)