Skip to content

Commit

Permalink
Use associated type
Browse files Browse the repository at this point in the history
  • Loading branch information
pgherveou committed Dec 19, 2024
1 parent a59194a commit 24f62b2
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 86 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
spec_name: alloc::borrow::Cow::Borrowed("westmint"),
impl_name: alloc::borrow::Cow::Borrowed("westmint"),
authoring_version: 1,
spec_version: 1_017_002,
spec_version: 1_017_003,
impl_version: 0,
apis: RUNTIME_API_VERSIONS,
transaction_version: 16,
Expand Down Expand Up @@ -977,6 +977,7 @@ impl pallet_revive::Config for Runtime {
type Xcm = pallet_xcm::Pallet<Self>;
type ChainId = ConstU64<420_420_421>;
type NativeToEthRatio = ConstU32<1_000_000>; // 10^(18 - 12) Eth is 10^18, Native is 10^12.
type EthGasEncoder = ();
}

impl TryFrom<RuntimeCall> for pallet_revive::Call<Runtime> {
Expand Down
1 change: 1 addition & 0 deletions substrate/bin/node/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1468,6 +1468,7 @@ impl pallet_revive::Config for Runtime {
type Xcm = ();
type ChainId = ConstU64<420_420_420>;
type NativeToEthRatio = ConstU32<1_000_000>; // 10^(18 - 12) Eth is 10^18, Native is 10^12.
type EthGasEncoder = ();
}

impl pallet_sudo::Config for Runtime {
Expand Down
3 changes: 2 additions & 1 deletion substrate/frame/revive/src/evm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,6 @@

mod api;
pub use api::*;
pub mod gas_encoder;
mod gas_encoder;
pub use gas_encoder::*;
pub mod runtime;
163 changes: 92 additions & 71 deletions substrate/frame/revive/src/evm/gas_encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
// limitations under the License.
//! Encodes/Decodes EVM gas values.
use crate::{BalanceOf, Config, Weight};
use crate::Weight;
use core::ops::{Div, Rem};
use frame_support::pallet_prelude::CheckedShl;
use sp_arithmetic::traits::{One, Zero};
Expand Down Expand Up @@ -49,101 +49,122 @@ where
.unwrap_or(0) as u128
}

/// Encodes all components (deposit limit, weight reference time, and proof size) into a single
/// gas value.
///
/// The encoding follows the pattern `g...grrppdd`, where:
/// - `dd`: log2 Deposit value, encoded in the lowest 2 digits.
/// - `pp`: log2 Proof size, encoded in the next 2 digits.
/// - `rr`: log2 Reference time, encoded in the next 2 digits.
/// - `g...g`: Gas limit, encoded in the highest digits.
///
/// # Note
/// - Encoding fails if the deposit is not convertible to `u128`
/// - The deposit value is maxed by 2^99
pub fn encode<T: Config>(gas_limit: U256, weight: Weight, deposit: BalanceOf<T>) -> Option<U256> {
let deposit: u128 = deposit.try_into().ok()?;
let deposit_component = log2_round_up(deposit);
/// Encodes/Decodes EVM gas values.
pub trait GasEncoder<Balance> {
/// Encodes all components (deposit limit, weight reference time, and proof size) into a single
/// gas value.
fn encode(gas_limit: U256, weight: Weight, deposit: Balance) -> U256;

let proof_size = weight.proof_size();
let proof_size_component = SCALE * log2_round_up(proof_size);
/// Decodes the weight and deposit from the encoded gas value.
/// Returns `None` if the gas value is invalid
fn decode(gas: U256) -> Option<(Weight, Balance)>;
}

let ref_time = weight.ref_time();
let ref_time_component = SCALE.pow(2) * log2_round_up(ref_time);
impl<Balance> GasEncoder<Balance> for ()
where
Balance: Zero + One + CheckedShl + Into<u128>,
{
/// The encoding follows the pattern `g...grrppdd`, where:
/// - `dd`: log2 Deposit value, encoded in the lowest 2 digits.
/// - `pp`: log2 Proof size, encoded in the next 2 digits.
/// - `rr`: log2 Reference time, encoded in the next 2 digits.
/// - `g...g`: Gas limit, encoded in the highest digits.
///
/// # Note
/// - The deposit value is maxed by 2^99
fn encode(gas_limit: U256, weight: Weight, deposit: Balance) -> U256 {
let deposit: u128 = deposit.into();
let deposit_component = log2_round_up(deposit);

let proof_size = weight.proof_size();
let proof_size_component = SCALE * log2_round_up(proof_size);

let ref_time = weight.ref_time();
let ref_time_component = SCALE.pow(2) * log2_round_up(ref_time);

let components = U256::from(deposit_component + proof_size_component + ref_time_component);

let raw_gas_mask = U256::from(SCALE).pow(3.into());
let raw_gas_component = if gas_limit < raw_gas_mask.saturating_add(components) {
raw_gas_mask
} else {
round_up(gas_limit, raw_gas_mask).saturating_mul(raw_gas_mask)
};

components.saturating_add(raw_gas_component)
}

let components = U256::from(deposit_component + proof_size_component + ref_time_component);
fn decode(gas: U256) -> Option<(Weight, Balance)> {
let deposit = gas % SCALE;

let raw_gas_mask = U256::from(SCALE).pow(3.into());
let raw_gas_component = if gas_limit < raw_gas_mask.saturating_add(components) {
raw_gas_mask
} else {
round_up(gas_limit, raw_gas_mask).saturating_mul(raw_gas_mask)
};
// Casting with as_u32 is safe since all values are maxed by `SCALE`.
let deposit = deposit.as_u32();
let proof_time = ((gas / SCALE) % SCALE).as_u32();
let ref_time = ((gas / SCALE.pow(2)) % SCALE).as_u32();

Some(components.saturating_add(raw_gas_component))
}
let weight = Weight::from_parts(
if ref_time == 0 { 0 } else { 1u64.checked_shl(ref_time)? },
if proof_time == 0 { 0 } else { 1u64.checked_shl(proof_time)? },
);
let deposit =
if deposit == 0 { Balance::zero() } else { Balance::one().checked_shl(deposit)? };

/// Decodes the weight and deposit from the encoded gas value.
/// Returns `None` if the gas value is invalid
pub fn decode<T: Config>(gas: U256) -> Option<(Weight, BalanceOf<T>)> {
let deposit = gas % SCALE;

// Casting with as_u32 is safe since all values are maxed by `SCALE`.
let deposit = deposit.as_u32();
let proof_time = ((gas / SCALE) % SCALE).as_u32();
let ref_time = ((gas / SCALE.pow(2)) % SCALE).as_u32();

let weight = Weight::from_parts(
if ref_time == 0 { 0 } else { 1u64.checked_shl(ref_time)? },
if proof_time == 0 { 0 } else { 1u64.checked_shl(proof_time)? },
);
let deposit = if deposit == 0 {
BalanceOf::<T>::zero()
} else {
BalanceOf::<T>::one().checked_shl(deposit)?
};

Some((weight, deposit))
Some((weight, deposit))
}
}

#[cfg(test)]
mod test {
use super::*;
use crate::tests::Test;
use crate::{tests::Test, Config};

#[test]
fn test_gas_encoding_decoding_works() {
let raw_gas_limit = 111_111_999_999_999u128;
let weight = Weight::from_parts(222_999_999, 333_999_999);
let deposit = 444_999_999;
let deposit = 444_999_999u64;

let encoded_gas = encode::<Test>(raw_gas_limit.into(), weight, deposit).unwrap();
let encoded_gas =
<Test as Config>::EthGasEncoder::encode(raw_gas_limit.into(), weight, deposit);
assert_eq!(encoded_gas, U256::from(111_112_000_282_929u128));
assert!(encoded_gas > raw_gas_limit.into());

let (decoded_weight, decoded_deposit) = decode::<Test>(encoded_gas).unwrap();
let (decoded_weight, decoded_deposit) =
<Test as Config>::EthGasEncoder::decode(encoded_gas).unwrap();
assert!(decoded_weight.all_gte(weight));
assert!(weight.mul(2).all_gte(weight));

assert!(decoded_deposit >= deposit);
assert!(deposit * 2 >= decoded_deposit);
}

#[test]
fn test_encoding_zero_values_work() {
let encoded_gas =
encode::<Test>(Default::default(), Default::default(), Default::default()).unwrap();

assert_eq!(encoded_gas, U256::from(1_00_00_00));

let (decoded_weight, decoded_deposit) = decode::<Test>(encoded_gas).unwrap();
assert_eq!(Weight::default(), decoded_weight);
assert_eq!(BalanceOf::<Test>::zero(), decoded_deposit);
}

#[test]
fn test_overflow() {
assert_eq!(None, decode::<Test>(65_00u128.into()), "Invalid proof size");
assert_eq!(None, decode::<Test>(65_00_00u128.into()), "Invalid ref_time");
}
//#[test]
//fn test_encoding_zero_values_work() {
// let encoded_gas = <Test as Config>::EthGasEncoder::encode(
// Default::default(),
// Default::default(),
// Default::default(),
// );
//
// assert_eq!(encoded_gas, U256::from(1_00_00_00));
//
// let (decoded_weight, decoded_deposit) =
// <Test as Config>::EthGasEncoder::decode(encoded_gas).unwrap();
// assert_eq!(Weight::default(), decoded_weight);
// assert_eq!(0u64, decoded_deposit);
//}
//
//#[test]
//fn test_overflow() {
// assert_eq!(
// None,
// <Test as Config>::EthGasEncoder::decode(65_00u128.into()),
// "Invalid proof size"
// );
// assert_eq!(
// None,
// <Test as Config>::EthGasEncoder::decode(65_00_00u128.into()),
// "Invalid ref_time"
// );
//}
}
16 changes: 10 additions & 6 deletions substrate/frame/revive/src/evm/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
use crate::{
evm::{
api::{GenericTransaction, TransactionSigned},
gas_encoder,
GasEncoder,
},
AccountIdOf, AddressMapper, BalanceOf, MomentOf, LOG_TARGET,
};
Expand Down Expand Up @@ -339,10 +339,11 @@ pub trait EthExtra {
let data = input.unwrap_or_default().0;

let (gas_limit, storage_deposit_limit) =
gas_encoder::decode::<Self::Config>(gas.unwrap_or_default()).ok_or_else(|| {
log::debug!(target: LOG_TARGET, "Failed to decode gas: {gas:?}");
InvalidTransaction::Call
})?;
<Self::Config as crate::Config>::EthGasEncoder::decode(gas.unwrap_or_default())
.ok_or_else(|| {
log::debug!(target: LOG_TARGET, "Failed to decode gas: {gas:?}");
InvalidTransaction::Call
})?;

let call = if let Some(dest) = to {
crate::Call::call::<Self::Config> {
Expand Down Expand Up @@ -506,7 +507,10 @@ mod test {
log::debug!(target: LOG_TARGET, "Estimated gas: {:?}", dry_run.eth_gas);
self.tx.gas = Some(dry_run.eth_gas);
let (gas_limit, deposit) =
gas_encoder::decode::<Test>(dry_run.eth_gas).unwrap();
<<Test as crate::Config>::EthGasEncoder as GasEncoder<_>>::decode(
dry_run.eth_gas,
)
.unwrap();
self.gas_limit = gas_limit;
self.storage_deposit_limit = deposit;
},
Expand Down
14 changes: 7 additions & 7 deletions substrate/frame/revive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,8 @@ pub mod weights;

use crate::{
evm::{
gas_encoder,
runtime::{gas_from_fee, GAS_PRICE},
GenericTransaction,
GasEncoder, GenericTransaction,
},
exec::{AccountIdOf, ExecError, Executable, Ext, Key, Origin, Stack as ExecStack},
gas::GasMeter,
Expand Down Expand Up @@ -299,6 +298,10 @@ pub mod pallet {
/// The ratio between the decimal representation of the native token and the ETH token.
#[pallet::constant]
type NativeToEthRatio: Get<u32>;

/// Encode and decode Ethereum gas values. See [`GasEncoder`].
#[pallet::no_default_bounds]
type EthGasEncoder: GasEncoder<BalanceOf<Self>>;
}

/// Container for different types that implement [`DefaultConfig`]` of this pallet.
Expand Down Expand Up @@ -372,6 +375,7 @@ pub mod pallet {
type PVFMemory = ConstU32<{ 512 * 1024 * 1024 }>;
type ChainId = ConstU64<0>;
type NativeToEthRatio = ConstU32<1>;
type EthGasEncoder = ();
}
}

Expand Down Expand Up @@ -1418,11 +1422,7 @@ where
.into();
let eth_gas = gas_from_fee(fee);
let eth_gas =
gas_encoder::encode::<T>(eth_gas, result.gas_required, result.storage_deposit)
.ok_or_else(|| {
log::debug!(target: LOG_TARGET, "Failed to encode gas {eth_gas:?} {result:?}");
EthTransactError::Message("Failed to encode gas".into())
})?;
T::EthGasEncoder::encode(eth_gas, result.gas_required, result.storage_deposit);

if eth_gas == result.eth_gas {
log::trace!(target: LOG_TARGET, "bare_eth_call: encoded_len: {encoded_len:?} eth_gas: {eth_gas:?}");
Expand Down

0 comments on commit 24f62b2

Please sign in to comment.