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

EVM + Weight v2 support #1039

Merged
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
4bab80e
wip `record_extarnal_cost`
tgmichel Apr 11, 2023
416d618
Add support for external cost recording from the EVM
tgmichel Apr 13, 2023
24c91b5
Add refund external cost support
tgmichel Apr 14, 2023
3dff83e
Separate dispatchables with weight limit
tgmichel Apr 17, 2023
3ace0af
introduce tbd EthereumWeigher
tgmichel Apr 17, 2023
6deaacb
Validate gas to weight conversion
tgmichel Apr 19, 2023
74708f5
Add static opcode recording with temp costs
tgmichel Apr 20, 2023
8ea977b
Add optional `transaction_len` parameter to `GasWeightMapping`
tgmichel Apr 20, 2023
dc95f98
Account for pallet and call indexes
tgmichel Apr 20, 2023
ac5ed2d
Rollback `transaction_len` param, use MAX_POV_SIZE ratio instead
tgmichel Apr 21, 2023
45c9e25
wip tests
tgmichel Apr 25, 2023
dc33686
Add `uncached_account_code_proof_size_accounting_works` test
tgmichel Apr 26, 2023
312146f
wip tests
tgmichel Apr 26, 2023
88c7c7c
Temp remove static external cost accounting
tgmichel Apr 27, 2023
163b0c5
fix build
tgmichel Apr 27, 2023
a17e331
Temp remove static external cost accounting 2
tgmichel Apr 27, 2023
8cc4568
warning cleanup
tgmichel Apr 27, 2023
3646639
fmt
tgmichel Apr 27, 2023
87e5f6f
clippy
tgmichel Apr 27, 2023
cc4e173
taplo
tgmichel Apr 27, 2023
eb2afed
Add `evm-with-weight-limit` to ci
tgmichel Apr 27, 2023
7814419
Temp set evm fork + update Cargo.lock
tgmichel Apr 27, 2023
255f663
Merge branch 'master' into tgm-record-external-costs
tgmichel Apr 27, 2023
00ffebc
handle `code_hash`
tgmichel Apr 27, 2023
5ab6432
taplo
tgmichel Apr 27, 2023
4bb5e70
fmt
tgmichel Apr 27, 2023
538c072
Handle `transact_with_weight_limit` as self contained
tgmichel Apr 28, 2023
7686543
fix ts tests
tgmichel Apr 28, 2023
e12afd7
suggestion
tgmichel Apr 28, 2023
cd77d2d
remove precompile test
tgmichel Apr 28, 2023
91ce2ed
some suggestions
tgmichel Apr 28, 2023
d6d7a00
remove `transact_with_weight_limit`
tgmichel Apr 28, 2023
83e228a
configurable `MaxPovSize`
tgmichel May 3, 2023
c369476
test pov size constants
tgmichel May 3, 2023
296cb2a
accessed storage overlayed cache
tgmichel May 4, 2023
c8138af
`new_from_weight_limit` suggestion
tgmichel May 4, 2023
34be73b
remove unnecessary check
tgmichel May 4, 2023
44c93c0
warnings cleanup
tgmichel May 5, 2023
17abbd3
set constant gas limit max pov size ratio
tgmichel May 5, 2023
3572aad
check state version for suicide
tgmichel May 5, 2023
4bbbe3d
just completely remove suicide accounting
tgmichel May 5, 2023
bd734b1
- `code` must be able to oog
tgmichel May 11, 2023
0a22130
Merge branch 'master' into tgm-record-external-costs
tgmichel May 12, 2023
1a1a47f
`is_empty` accounting
tgmichel May 12, 2023
a63f1d2
fix build
tgmichel May 12, 2023
e59eb9c
`SSTORE` must record account storage proof size
tgmichel May 12, 2023
91a5259
suggestion: move weight_limit checks
tgmichel May 16, 2023
3915652
editorconfig
tgmichel May 16, 2023
d09fc5b
fmt
tgmichel May 16, 2023
e9df020
rename `transaction_len` to `proof_size_base_cost` in runner
tgmichel May 16, 2023
a8fd45e
move `proof_size_base_cost` to validation primitive
tgmichel May 16, 2023
caefbec
gas limit saturated conversion
tgmichel May 24, 2023
df032b5
remove transaction proof size check outside validation
tgmichel May 24, 2023
e53da69
Merge branch 'master' into tgm-record-external-costs
tgmichel May 25, 2023
76c2f6c
pin evm
tgmichel May 25, 2023
6f6827a
pin evm+
tgmichel May 25, 2023
0154cc9
fix todos
tgmichel May 25, 2023
881f690
fix build
tgmichel May 25, 2023
eddef23
scope of already recorded codes and storages must be per transaction
tgmichel Jun 9, 2023
d6d4765
pin evm + implement new `record_external_operation`
tgmichel Jun 14, 2023
12fbeee
fix runtime api versioning + legacy `ExecutionInfo` handling
tgmichel Jun 19, 2023
8a968e9
Merge branch 'master' into tgm-record-external-costs
tgmichel Jun 19, 2023
da32bba
editorconfig
tgmichel Jun 19, 2023
5ba5fcc
cargo fmt
tgmichel Jun 19, 2023
6d3bfb3
clippy
tgmichel Jun 19, 2023
ebd1486
suggestion remove `evm-with-weight-limit` feature
tgmichel Jun 21, 2023
7198ca4
fmt
tgmichel Jun 21, 2023
d95b7ed
update comment
tgmichel Jun 21, 2023
d9f12de
update tests for additional `AccountBasicRead` in the evm
tgmichel Jun 21, 2023
074711b
update evm pin
tgmichel Jun 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
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ bn = { package = "substrate-bn", version = "0.6", default-features = false }
environmental = { version = "1.1.3", default-features = false }
ethereum = { version = "0.14.0", default-features = false }
ethereum-types = { version = "0.14.1", default-features = false }
evm = { version = "0.37.0", default-features = false }
evm = { version = "0.37.0", default-features = false, features = [ "with-substrate" ] }
impl-serde = { version = "0.4.0", default-features = false }
jsonrpsee = "0.16.2"
kvdb-rocksdb = "0.17.0"
Expand Down
18 changes: 13 additions & 5 deletions frame/ethereum/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -545,7 +545,7 @@ impl<T: Config> Pallet<T> {
let transaction_hash = transaction.hash();
let transaction_index = pending.len() as u32;

let (reason, status, used_gas, dest) = match info {
let (reason, status, weight_info, used_gas, dest) = match info {
CallOrCreateInfo::Call(info) => (
info.exit_reason,
TransactionStatus {
Expand All @@ -561,6 +561,7 @@ impl<T: Config> Pallet<T> {
bloom
},
},
info.weight_info,
info.used_gas,
to,
),
Expand All @@ -579,6 +580,7 @@ impl<T: Config> Pallet<T> {
bloom
},
},
info.weight_info,
info.used_gas,
Some(info.value),
),
Expand Down Expand Up @@ -632,10 +634,16 @@ impl<T: Config> Pallet<T> {
});

Ok(PostDispatchInfo {
actual_weight: Some(T::GasWeightMapping::gas_to_weight(
used_gas.unique_saturated_into(),
true,
)),
actual_weight: {
// Until opcodes are properly benchmarked, we still use gas to weight conversion
// and replace proof_size.
let mut gas_to_weight = T::GasWeightMapping::gas_to_weight(
used_gas.unique_saturated_into(),
true,
);
*gas_to_weight.proof_size_mut() = weight_info.proof_size_usage;
Some(gas_to_weight)
},
pays_fee: Pays::No,
})
}
Expand Down
5 changes: 4 additions & 1 deletion frame/evm/precompile/dispatch/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,11 @@ where
let cost = T::GasWeightMapping::weight_to_gas(
post_info.actual_weight.unwrap_or(info.weight),
);

handle.record_cost(cost)?;
handle.record_external_cost(
tgmichel marked this conversation as resolved.
Show resolved Hide resolved
info.weight.ref_time(),
info.weight.proof_size(),
)?;

Ok(PrecompileOutput {
exit_status: ExitSucceed::Stopped,
Expand Down
7 changes: 7 additions & 0 deletions frame/evm/precompile/dispatch/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,9 @@ pub(crate) struct MockHandle {
}

impl PrecompileHandle for MockHandle {

type ExternalCost = ();

fn call(
&mut self,
_: H160,
Expand All @@ -183,6 +186,10 @@ impl PrecompileHandle for MockHandle {
Ok(())
}

fn record_external_cost(&mut self, _: Self::ExternalCost) -> Result<(), ExitError> {
Ok(())
}

fn remaining_gas(&self) -> u64 {
unimplemented!()
}
Expand Down
36 changes: 24 additions & 12 deletions frame/evm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,10 +244,14 @@ pub mod pallet {
};

Ok(PostDispatchInfo {
actual_weight: Some(T::GasWeightMapping::gas_to_weight(
info.used_gas.unique_saturated_into(),
true,
)),
actual_weight: {
let mut gas_to_weight = T::GasWeightMapping::gas_to_weight(
info.used_gas.unique_saturated_into(),
true,
);
*gas_to_weight.proof_size_mut() = info.weight_info.proof_size_usage;
Some(gas_to_weight)
},
pays_fee: Pays::No,
})
}
Expand Down Expand Up @@ -321,10 +325,14 @@ pub mod pallet {
}

Ok(PostDispatchInfo {
actual_weight: Some(T::GasWeightMapping::gas_to_weight(
info.used_gas.unique_saturated_into(),
true,
)),
actual_weight: {
let mut gas_to_weight = T::GasWeightMapping::gas_to_weight(
info.used_gas.unique_saturated_into(),
true,
);
*gas_to_weight.proof_size_mut() = info.weight_info.proof_size_usage;
Some(gas_to_weight)
},
pays_fee: Pays::No,
})
}
Expand Down Expand Up @@ -399,10 +407,14 @@ pub mod pallet {
}

Ok(PostDispatchInfo {
actual_weight: Some(T::GasWeightMapping::gas_to_weight(
info.used_gas.unique_saturated_into(),
true,
)),
actual_weight: {
let mut gas_to_weight = T::GasWeightMapping::gas_to_weight(
info.used_gas.unique_saturated_into(),
true,
);
*gas_to_weight.proof_size_mut() = info.weight_info.proof_size_usage;
Some(gas_to_weight)
},
pays_fee: Pays::No,
})
}
Expand Down
127 changes: 121 additions & 6 deletions frame/evm/src/runner/stack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@
use crate::{
runner::Runner as RunnerT, AccountCodes, AccountStorages, AddressMapping, BalanceOf,
BlockHashMapping, Config, Error, Event, FeeCalculator, OnChargeEVMTransaction, OnCreate,
Pallet, RunnerError,
Pallet, RunnerError, GasWeightMapping,
};
use evm::{
backend::Backend as BackendT,
executor::stack::{Accessed, StackExecutor, StackState as StackStateT, StackSubstateMetadata},
ExitError, ExitReason, Transfer,
ExitError, ExitReason, Transfer, Opcode, gasometer::GasCost,
};
use fp_evm::{CallInfo, CreateInfo, ExecutionInfo, Log, PrecompileSet, Vicinity};
use fp_evm::{CallInfo, CreateInfo, ExecutionInfo, Log, PrecompileSet, Vicinity, WeightInfo};
use frame_support::traits::{Currency, ExistenceRequirement, Get};
use sp_core::{H160, H256, U256};
use sp_runtime::traits::UniqueSaturatedInto;
Expand Down Expand Up @@ -200,7 +200,15 @@ where
};

let metadata = StackSubstateMetadata::new(gas_limit, config);
let state = SubstrateStackState::new(&vicinity, metadata);
let weight_info = T::GasWeightMapping::gas_to_weight(gas_limit, true);
// Used to record the external costs in the evm through the StackState implementation
let weight_info = WeightInfo {
ref_time_limit: weight_info.ref_time(),
proof_size_limit: weight_info.proof_size(),
tgmichel marked this conversation as resolved.
Show resolved Hide resolved
ref_time_usage: 0u64,
proof_size_usage: 0u64
};
let state = SubstrateStackState::new(&vicinity, metadata, weight_info);
let mut executor = StackExecutor::new_with_precompiles(state, config, precompiles);

let (reason, retv) = f(&mut executor);
Expand Down Expand Up @@ -252,7 +260,7 @@ where

let state = executor.into_state();

for address in state.substate.deletes {
for address in &state.substate.deletes {
log::debug!(
target: "evm",
"Deleting account at {:?}",
Expand Down Expand Up @@ -284,6 +292,7 @@ where
value: retv,
exit_reason: reason,
used_gas,
weight_info: state.weight_info(),
logs: state.substate.logs,
})
}
Expand Down Expand Up @@ -588,12 +597,13 @@ pub struct SubstrateStackState<'vicinity, 'config, T> {
vicinity: &'vicinity Vicinity,
substate: SubstrateStackSubstate<'config>,
original_storage: BTreeMap<(H160, H256), H256>,
weight_info: WeightInfo,
_marker: PhantomData<T>,
}

impl<'vicinity, 'config, T: Config> SubstrateStackState<'vicinity, 'config, T> {
/// Create a new backend with given vicinity.
pub fn new(vicinity: &'vicinity Vicinity, metadata: StackSubstateMetadata<'config>) -> Self {
pub fn new(vicinity: &'vicinity Vicinity, metadata: StackSubstateMetadata<'config>, weight_info: WeightInfo) -> Self {
Self {
vicinity,
substate: SubstrateStackSubstate {
Expand All @@ -604,8 +614,13 @@ impl<'vicinity, 'config, T: Config> SubstrateStackState<'vicinity, 'config, T> {
},
_marker: PhantomData,
original_storage: BTreeMap::new(),
weight_info,
}
}

pub fn weight_info(&self) -> WeightInfo {
self.weight_info
}
}

impl<'vicinity, 'config, T: Config> BackendT for SubstrateStackState<'vicinity, 'config, T> {
Expand Down Expand Up @@ -827,6 +842,106 @@ where
self.substate
.recursive_is_cold(&|a: &Accessed| a.accessed_storage.contains(&(address, key)))
}

// TODO this impl behind a feature flag, otherwise no-op
fn record_external_opcode_cost(&mut self, opcode: Opcode, gas_cost: GasCost, target: evm::gasometer::StorageTarget) -> Result<(), ExitError> {
tgmichel marked this conversation as resolved.
Show resolved Hide resolved
// Record ref_time first
// TODO benchmark opcodes, until this is done we do used_gas to weight conversion for ref_time

// Record proof_size
let maybe_record = match gas_cost {
GasCost::ExtCodeSize { target_is_cold } |
GasCost::Balance { target_is_cold } |
GasCost::ExtCodeHash { target_is_cold } |
GasCost::CallCode { target_is_cold, .. } |
GasCost::StaticCall { target_is_cold, .. } |
GasCost::ExtCodeCopy { target_is_cold, .. } |
GasCost::SLoad { target_is_cold, .. } |
GasCost::DelegateCall { target_is_cold, .. } |
GasCost::SStore { target_is_cold, .. } |
GasCost::Call { target_is_cold, .. } => target_is_cold,

GasCost::Create => true,

GasCost::Suicide { target_is_cold, already_removed, .. } => target_is_cold && !already_removed,

_ => false,
};

if !maybe_record {
return Ok(());
}
// Proof size is fixed length for writes (a 32-byte hash in a merkle trie), and
// the full key/value for reads. For read and writes over the same storage, the full value
// is included.
// For cold reads involving code (call, callcode, staticcall and delegatecall):
// - We depend on https://github.com/paritytech/frontier/pull/893
// - Try to get the cached size or compute it on the fly
// - We record the actual size after caching, refunding the difference between it and the initially deducted
// contract size limit.
let opcode_proof_size = match opcode {
// Basic account fixed length
Opcode::BALANCE => U256::from(64),
// TODO requires https://github.com/paritytech/frontier/pull/893
Opcode::EXTCODESIZE |
Opcode::EXTCODECOPY |
Opcode::EXTCODEHASH |
Opcode::CALLCODE |
Opcode::CALL |
Opcode::DELEGATECALL |
Opcode::STATICCALL => {
let address = match target {
StorageTarget::Address(address) | StorageTarget::Slot(address, _) => address,
// This must be unreachable, a valid Target must be set for this opcode(s)
// TODO decide how do we want to gracefully handle.
_ => return Err(ExitError::OutOfGas),
}
// First try to record fixed sized `AccountCodesMetadata` read
// Temptatively 20 + 8 + 32
self.weight_info.try_record_proof_size_or_fail(60)?;
let size = if let Some(meta) = <AccountCodesMetadata<T>>::get(address) {
meta.size
} else {
// If it does not exist, try to record `create_contract_limit` first.
let size_limit: u64 = self.metadata()
.gasometer()
.config()
.create_contract_limit
.unwrap_or_default() as u64;
self.weight_info.try_record_proof_size_or_fail(size_limit)?;
let meta = Pallet::<T>::account_code_metadata(address);
let actual_size = meta.size;
// Refund if applies
self.weight_info.refund_proof_size(size_limit.saturating_sub(actual_size));
actual_size
};
U256::from(size)
},
// (H160, H256) double map blake2 128 concat key size (68) + value 32
Opcode::SLOAD => U256::from(100),
// Fixed trie 32 byte hash
Opcode::SSTORE |
Opcode::CREATE |
Opcode::CREATE2 |
Opcode::SUICIDE => U256::from(32),
// This must be unreachable, free of (proof) cost opcodes cannot be recorded.
// TODO decide how do we want to gracefully handle.
_ => return Err(ExitError::OutOfGas),
};

if opcode_proof_size > U256::from(u64::MAX) {
self.weight_info.proof_size_usage = self.weight_info.proof_size_limit;
return Err(ExitError::OutOfGas);
}

self.record_external_cost(0u64, opcode_proof_size.low_u64())
}

fn record_external_cost(&mut self, _ref_time: u64, proof_size: u64) -> Result<(), ExitError> {
// Record ref_time first
// TODO benchmark opcodes, until this is done we do used_gas to weight conversion for ref_time
self.weight_info.try_record_proof_size_or_fail(proof_size)
}
}

#[cfg(feature = "forbid-evm-reentrancy")]
Expand Down
4 changes: 4 additions & 0 deletions frame/evm/test-vector-support/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ impl PrecompileHandle for MockHandle {
Ok(())
}

fn record_external_cost(&mut self, _: u64, _: u64) -> Result<(), ExitError> {
Ok(())
}

fn log(&mut self, _: H160, _: Vec<H256>, _: Vec<u8>) -> Result<(), ExitError> {
unimplemented!()
}
Expand Down
39 changes: 39 additions & 0 deletions primitives/evm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,51 @@ pub struct Vicinity {
pub origin: H160,
}

#[derive(Clone, Copy, Eq, PartialEq, Encode, Decode)]
#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))]
pub struct WeightInfo {
pub ref_time_limit: u64,
pub proof_size_limit: u64,
tgmichel marked this conversation as resolved.
Show resolved Hide resolved
pub ref_time_usage: u64,
pub proof_size_usage: u64,
}

impl WeightInfo {
fn try_consume(&self, cost: u64, limit: u64, usage: u64) -> Result<u64, ExitError> {
let usage = usage
.checked_add(cost)
.ok_or(ExitError::OutOfGas)?;
if usage > limit {
return Err(ExitError::OutOfGas);
}
Ok(usage)
}
pub fn try_record_ref_time_or_fail(&mut self, cost: u64) -> Result<(), ExitError> {
self.ref_time_usage = self.try_consume(cost, self.ref_time_limit, self.ref_time_usage)?;
if self.ref_time_usage > self.ref_time_limit {
return Err(ExitError::OutOfGas);
}
Ok(())
}
pub fn try_record_proof_size_or_fail(&mut self, cost: u64) -> Result<(), ExitError> {
self.proof_size_usage = self.try_consume(cost, self.proof_size_limit, self.proof_size_usage)?;
if self.proof_size_usage > self.proof_size_limit {
return Err(ExitError::OutOfGas);
}
Ok(())
}
pub fn refund_proof_size(&mut self, amount: u64) {
tgmichel marked this conversation as resolved.
Show resolved Hide resolved
self.proof_size_usage = self.proof_size_usage.saturating_sub(amount);
}
}

#[derive(Clone, Eq, PartialEq, Encode, Decode)]
#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))]
pub struct ExecutionInfo<T> {
pub exit_reason: ExitReason,
pub value: T,
pub used_gas: U256,
pub weight_info: WeightInfo,
pub logs: Vec<Log>,
}

Expand Down