Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce a gas-based Storage limit per tx #1142

Open
wants to merge 45 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
5b8860f
A new trait for recording foreign metrics
ahmadkaouk Jul 31, 2023
d692104
Remove Metric trait
ahmadkaouk Aug 2, 2023
523b18c
rename BasicMetric to Metric
ahmadkaouk Aug 2, 2023
b168105
add unit tests and a new MetricError enum
ahmadkaouk Aug 2, 2023
ce75d15
Add ProofSizeMeter
ahmadkaouk Aug 3, 2023
26a85ad
Add ProofSizeMeter to WeightInfo
ahmadkaouk Aug 3, 2023
78273fb
formatting
ahmadkaouk Aug 3, 2023
df4f5cf
Add RefTimeMeter to WeightInfo
ahmadkaouk Aug 3, 2023
1f86da7
add storage meter to WeightInfo
ahmadkaouk Aug 4, 2023
230879a
rename metric to resource
ahmadkaouk Aug 4, 2023
54b7c01
code refactor
ahmadkaouk Aug 7, 2023
d9f04e2
remove unused functions
ahmadkaouk Aug 8, 2023
af358ad
make clippy happy
ahmadkaouk Aug 8, 2023
8fa4dd2
minor improvements
ahmadkaouk Aug 9, 2023
65d8b97
rename meter to resource
ahmadkaouk Aug 9, 2023
6a344db
compute new storage created by SSTORE
ahmadkaouk Aug 10, 2023
dea63f6
revert changes
ahmadkaouk Aug 14, 2023
4c45d4f
compute storage growth for opcodes
ahmadkaouk Aug 17, 2023
cea9817
compute the storage quota per tx
ahmadkaouk Aug 18, 2023
e412643
pin evm temporarily
ahmadkaouk Aug 20, 2023
dbf0370
record storage growth for opcodes
ahmadkaouk Aug 20, 2023
84b487f
update GAS_LIMIT_STORAGE_GROWTH_RATIO for tests
ahmadkaouk Aug 21, 2023
cce7cf5
add support to record storage growth for precompiles
ahmadkaouk Aug 21, 2023
4670129
fix tests
ahmadkaouk Aug 21, 2023
dee68d8
fix formatting
ahmadkaouk Aug 21, 2023
f110333
fix storage growth parameters for tests
ahmadkaouk Aug 22, 2023
332bde7
add rust integration tests
ahmadkaouk Aug 23, 2023
1623f9c
fix typo
ahmadkaouk Aug 23, 2023
19ba629
fix recording storage growth of a special use case of SSTORE
ahmadkaouk Aug 28, 2023
7578175
use saturating add
ahmadkaouk Aug 28, 2023
d0304fe
minor improvements
ahmadkaouk Aug 28, 2023
fb31dac
add license to meter.rs
ahmadkaouk Aug 28, 2023
a6da7ef
fix clippy warnings
ahmadkaouk Aug 28, 2023
4a6ea48
pin evm to master
ahmadkaouk Aug 28, 2023
ba9552d
update evm to the latest commit
ahmadkaouk Aug 29, 2023
f14d3e2
check limit exceedance before updating the usage
ahmadkaouk Sep 12, 2023
ca9e4d3
Use BtreeSet
ahmadkaouk Sep 15, 2023
21bf9d0
Merge remote-tracking branch 'upstream/master' into ahmad-add-storage…
ahmadkaouk Sep 15, 2023
4a343e6
add support for storage growth in precompiles
ahmadkaouk Sep 15, 2023
e72809d
clippy warnings
ahmadkaouk Sep 15, 2023
de7876e
Merge remote-tracking branch 'upstream/master' into ahmad-add-storage…
ahmadkaouk Nov 2, 2023
e46e222
Merge remote-tracking branch 'upstream/master' into ahmad-add-storage…
ahmadkaouk Nov 21, 2023
553984f
update to evm 0.41.0
ahmadkaouk Nov 21, 2023
063ea9d
Update Cargo.toml
ahmadkaouk Nov 21, 2023
74f321c
update Cargo.lock
ahmadkaouk Nov 21, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 57 additions & 18 deletions frame/evm/src/runner/meter.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,33 @@
use evm::{gasometer::GasCost, Opcode};
use evm::{
gasometer::{GasCost, StorageTarget},
Opcode,
};
use fp_evm::ACCOUNT_STORAGE_PROOF_SIZE;
use sp_core::H256;

/// A meter for tracking the storage growth.
#[derive(Clone, Copy)]
pub struct StorageMeter {
usage: u64,
limit: u64,
}
use sp_core::{H160, H256};
use sp_std::collections::btree_map::BTreeMap;

/// An error that is returned when the storage limit has been exceeded.
#[derive(Debug, PartialEq)]
pub enum MeterError {
LimitExceeded,
}

/// A meter for tracking the storage growth.
#[derive(Clone)]
pub struct StorageMeter {
usage: u64,
limit: u64,
recorded_new_entries: BTreeMap<(H160, H256), ()>,
ahmadkaouk marked this conversation as resolved.
Show resolved Hide resolved
}

impl StorageMeter {
/// Creates a new storage meter with the given limit.
pub fn new(limit: u64) -> Self {
Self { usage: 0, limit }
Self {
usage: 0,
limit,
recorded_new_entries: BTreeMap::new(),
}
}

/// Records the given amount of storage usage. The amount is added to the current usage.
Expand All @@ -40,15 +49,29 @@ impl StorageMeter {
&mut self,
_opcode: Opcode,
gas_cost: GasCost,
target: StorageTarget,
) -> Result<(), MeterError> {
match gas_cost {
GasCost::SStore { original, new, .. }
if original == H256::default() && !new.is_zero() =>
{
self.record(ACCOUNT_STORAGE_PROOF_SIZE)
GasCost::SStore { original, new, .. } => {
// Validate if storage growth for the current slot has been accounted for within this transaction.
// Comparing Original and new to determine if a new entry is being created is not sufficient, because
// 'original' updates only at the end of the transaction. So, if a new entry
// is created and updated multiple times within the same transaction, the storage growth is
// accounted for multiple times, because 'original' is always zero for the subsequent updates.
// To avoid this, we keep track of the new entries that are created within the transaction.
let (address, index) = match target {
StorageTarget::Slot(address, index) => (address, index),
_ => return Ok(()),
};
let recorded = self.recorded_new_entries.contains_key(&(address, index));
if !recorded && original == H256::default() && !new.is_zero() {
self.record(ACCOUNT_STORAGE_PROOF_SIZE)?;
self.recorded_new_entries.insert((address, index), ());
}
}
_ => Ok(()),
_ => {}
}
Ok(())
}

/// Returns the current usage of storage.
Expand Down Expand Up @@ -123,8 +146,10 @@ mod test {
new: H256::from_low_u64_be(2),
target_is_cold: false,
};
let target = StorageTarget::Slot(H160::default(), H256::from_low_u64_be(1));

meter
.record_dynamic_opcode_cost(Opcode::SSTORE, gas_cost)
.record_dynamic_opcode_cost(Opcode::SSTORE, gas_cost, target)
.unwrap();
assert_eq!(meter.usage(), 0);

Expand All @@ -136,7 +161,19 @@ mod test {
target_is_cold: false,
};
meter
.record_dynamic_opcode_cost(Opcode::SSTORE, gas_cost)
.record_dynamic_opcode_cost(Opcode::SSTORE, gas_cost, target)
.unwrap();
assert_eq!(meter.usage(), ACCOUNT_STORAGE_PROOF_SIZE);

// Try to record the same storage growth again. No change in storage growth.
let gas_cost = GasCost::SStore {
original: H256::default(),
current: Default::default(),
new: H256::from_low_u64_be(1),
target_is_cold: false,
};
meter
.record_dynamic_opcode_cost(Opcode::SSTORE, gas_cost, target)
.unwrap();
assert_eq!(meter.usage(), ACCOUNT_STORAGE_PROOF_SIZE);

Expand All @@ -147,7 +184,9 @@ mod test {
new: H256::from_low_u64_be(2),
target_is_cold: false,
};
let res = meter.record_dynamic_opcode_cost(Opcode::SSTORE, gas_cost);
let target = StorageTarget::Slot(H160::default(), H256::from_low_u64_be(2));

let res = meter.record_dynamic_opcode_cost(Opcode::SSTORE, gas_cost, target);
assert!(res.is_err());
assert_eq!(res, Err(MeterError::LimitExceeded));
assert_eq!(meter.usage(), 232);
Expand Down
4 changes: 2 additions & 2 deletions frame/evm/src/runner/stack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ where
let (reason, retv) = f(&mut executor);

// Compute the storage gas cost based on the storage growth.
let storage_gas = match executor.state().storage_meter {
let storage_gas = match &executor.state().storage_meter {
Some(storage_meter) => storage_meter.storage_to_gas(storage_growth_ratio),
None => 0,
};
Expand Down Expand Up @@ -1049,7 +1049,7 @@ where
) -> Result<(), ExitError> {
if let Some(storage_meter) = self.storage_meter.as_mut() {
storage_meter
.record_dynamic_opcode_cost(opcode, gas_cost)
.record_dynamic_opcode_cost(opcode, gas_cost, target)
.map_err(|_| ExitError::OutOfGas)?;
}

Expand Down