diff --git a/bin/node/cli/src/chain_spec.rs b/bin/node/cli/src/chain_spec.rs index 5cdfb5cab86f5..499a5e1d2c835 100644 --- a/bin/node/cli/src/chain_spec.rs +++ b/bin/node/cli/src/chain_spec.rs @@ -277,7 +277,6 @@ pub fn testnet_genesis( enable_println, // this should only be enabled on development chains ..Default::default() }, - gas_price: 1 * MILLICENTS, }), pallet_sudo: Some(SudoConfig { key: root_key, diff --git a/bin/node/executor/tests/basic.rs b/bin/node/executor/tests/basic.rs index 100bdf3fe60ee..c9ed6734f6163 100644 --- a/bin/node/executor/tests/basic.rs +++ b/bin/node/executor/tests/basic.rs @@ -587,6 +587,8 @@ const CODE_TRANSFER: &str = r#" #[test] fn deploying_wasm_contract_should_work() { + use pallet_contracts::ContractAddressFor; + let transfer_code = wabt::wat2wasm(CODE_TRANSFER).unwrap(); let transfer_ch = ::Hashing::hash(&transfer_code); @@ -608,7 +610,7 @@ fn deploying_wasm_contract_should_work() { CheckedExtrinsic { signed: Some((charlie(), signed_extra(0, 0))), function: Call::Contracts( - pallet_contracts::Call::put_code::(10_000, transfer_code) + pallet_contracts::Call::put_code::(transfer_code) ), }, CheckedExtrinsic { @@ -623,7 +625,7 @@ fn deploying_wasm_contract_should_work() { pallet_contracts::Call::call::( pallet_indices::address::Address::Id(addr.clone()), 10, - 10_000, + 100_000_000, vec![0x00, 0x01, 0x02, 0x03] ) ), diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index a98b1700fe1aa..e1a734b123200 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -82,7 +82,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // and set impl_version to 0. If only runtime // implementation changes and behavior does not, then leave spec_version as // is and increment impl_version. - spec_version: 221, + spec_version: 222, impl_version: 0, apis: RUNTIME_API_VERSIONS, }; @@ -404,13 +404,31 @@ impl pallet_treasury::Trait for Runtime { } parameter_types! { + // NOTE Those are significantly lower than the corresponding fees for transferring funds. + // Since if we charge the fee in gas there is basically not enough gas even to make one simple + // transfer. + // + // This basically means that in order to transfer funds it would make more sense to use the + // contracts module rather than balances module. + // + // This should be fixed in nearest future by using the synchronous transfer directly. It will + // cut fees from the senders account balance directly. + // + // TODO: https://github.com/paritytech/substrate/issues/4713 + pub const ContractTransferFee: Balance = 1000; + pub const ContractCreationFee: Balance = 1000; + pub const ContractFee: Balance = 1000; + pub const ContractTransactionBaseFee: Balance = 1 * CENTS; pub const ContractTransactionByteFee: Balance = 10 * MILLICENTS; - pub const ContractFee: Balance = 1 * CENTS; pub const TombstoneDeposit: Balance = 1 * DOLLARS; pub const RentByteFee: Balance = 1 * DOLLARS; pub const RentDepositOffset: Balance = 1000 * DOLLARS; pub const SurchargeReward: Balance = 150 * DOLLARS; + + /// These values mean that it is possible to store code up to approximatelly 250Kb. + pub const PutCodeBaseWeight: Weight = 10_000; + pub const PutCodePerByteWeight: Weight = 4; } impl pallet_contracts::Trait for Runtime { @@ -437,7 +455,9 @@ impl pallet_contracts::Trait for Runtime { type InstantiateBaseFee = pallet_contracts::DefaultInstantiateBaseFee; type MaxDepth = pallet_contracts::DefaultMaxDepth; type MaxValueSize = pallet_contracts::DefaultMaxValueSize; - type BlockGasLimit = pallet_contracts::DefaultBlockGasLimit; + type WeightToFee = LinearWeightToFee; + type PutCodeBaseWeight = PutCodeBaseWeight; + type PutCodePerByteWeight = PutCodePerByteWeight; } impl pallet_sudo::Trait for Runtime { @@ -618,7 +638,7 @@ construct_runtime!( FinalityTracker: pallet_finality_tracker::{Module, Call, Inherent}, Grandpa: pallet_grandpa::{Module, Call, Storage, Config, Event}, Treasury: pallet_treasury::{Module, Call, Storage, Config, Event}, - Contracts: pallet_contracts::{Module, Call, Config, Storage, Event}, + Contracts: pallet_contracts::{Module, Call, Config, Storage, Event}, Sudo: pallet_sudo::{Module, Call, Config, Storage, Event}, ImOnline: pallet_im_online::{Module, Call, Storage, Event, ValidateUnsigned, Config}, AuthorityDiscovery: pallet_authority_discovery::{Module, Call, Config}, @@ -649,7 +669,7 @@ pub type SignedExtra = ( frame_system::CheckNonce, frame_system::CheckWeight, pallet_transaction_payment::ChargeTransactionPayment, - pallet_contracts::CheckBlockGasLimit, + pallet_contracts::GasWeightConversion, ); /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; diff --git a/bin/node/testing/src/genesis.rs b/bin/node/testing/src/genesis.rs index 9c163ea6153b2..386df7ae92f0f 100644 --- a/bin/node/testing/src/genesis.rs +++ b/bin/node/testing/src/genesis.rs @@ -98,7 +98,6 @@ pub fn config_endowed( }), pallet_contracts: Some(ContractsConfig { current_schedule: Default::default(), - gas_price: 1 * MILLICENTS, }), pallet_babe: Some(Default::default()), pallet_grandpa: Some(GrandpaConfig { diff --git a/frame/contracts/src/gas.rs b/frame/contracts/src/gas.rs index c8572daaa43d4..0d279575d1519 100644 --- a/frame/contracts/src/gas.rs +++ b/frame/contracts/src/gas.rs @@ -14,15 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -use crate::{GasSpent, Module, Trait, BalanceOf, NegativeImbalanceOf}; -use sp_std::convert::TryFrom; -use sp_runtime::traits::{ - CheckedMul, Zero, SaturatedConversion, AtLeast32Bit, UniqueSaturatedInto, -}; -use frame_support::{ - traits::{Currency, ExistenceRequirement, Imbalance, OnUnbalanced, WithdrawReason}, StorageValue, - dispatch::DispatchError, -}; +use crate::{BalanceOf, Trait}; +use sp_runtime::traits::{AtLeast32Bit, SaturatedConversion, Zero}; #[cfg(test)] use std::{any::Any, fmt::Debug}; @@ -185,7 +178,7 @@ impl GasMeter { } /// Returns how much gas was spent. - fn spent(&self) -> Gas { + pub fn spent(&self) -> Gas { self.limit - self.gas_left } @@ -195,56 +188,6 @@ impl GasMeter { } } -/// Buy the given amount of gas. -/// -/// Cost is calculated by multiplying the gas cost (taken from the storage) by the `gas_limit`. -/// The funds are deducted from `transactor`. -pub fn buy_gas( - transactor: &T::AccountId, - gas_limit: Gas, -) -> Result<(GasMeter, NegativeImbalanceOf), DispatchError> { - // Buy the specified amount of gas. - let gas_price = >::gas_price(); - let cost = if gas_price.is_zero() { - >::zero() - } else { - as TryFrom>::try_from(gas_limit).ok() - .and_then(|gas_limit| gas_price.checked_mul(&gas_limit)) - .ok_or("overflow multiplying gas limit by price")? - }; - - let imbalance = T::Currency::withdraw( - transactor, - cost, - WithdrawReason::Fee.into(), - ExistenceRequirement::KeepAlive - )?; - - Ok((GasMeter::with_limit(gas_limit, gas_price), imbalance)) -} - -/// Refund the unused gas. -pub fn refund_unused_gas( - transactor: &T::AccountId, - gas_meter: GasMeter, - imbalance: NegativeImbalanceOf, -) { - let gas_spent = gas_meter.spent(); - let gas_left = gas_meter.gas_left(); - - // Increase total spent gas. - // This cannot overflow, since `gas_spent` is never greater than `block_gas_limit`, which - // also has Gas type. - GasSpent::mutate(|block_gas_spent| *block_gas_spent += gas_spent); - - // Refund gas left by the price it was bought at. - let refund = gas_meter.gas_price * gas_left.unique_saturated_into(); - let refund_imbalance = T::Currency::deposit_creating(transactor, refund); - if let Ok(imbalance) = imbalance.offset(refund_imbalance) { - T::GasPayment::on_unbalanced(imbalance); - } -} - /// A little handy utility for converting a value in balance units into approximate value in gas units /// at the given gas price. pub fn approx_gas_for_balance(gas_price: Balance, balance: Balance) -> Gas diff --git a/frame/contracts/src/gas_weight_conv.rs b/frame/contracts/src/gas_weight_conv.rs new file mode 100644 index 0000000000000..e7a0a4f730763 --- /dev/null +++ b/frame/contracts/src/gas_weight_conv.rs @@ -0,0 +1,195 @@ +// Copyright 2020 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +use crate::{Call, GasPrice, GasUsageReport, NegativeImbalanceOf, Trait}; +use frame_support::{ + storage::StorageValue, + traits::{Currency, ExistenceRequirement, Imbalance, OnUnbalanced, WithdrawReason}, + weights::{DispatchInfo, Weight}, + IsSubType, +}; +use sp_runtime::{ + traits::{CheckedDiv, Convert, SignedExtension, UniqueSaturatedInto}, + transaction_validity::{ + InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction, + }, +}; +use sp_std::{convert::TryFrom, fmt, marker::PhantomData, prelude::*}; + +/// Data which is collected during the pre-dispatch phase and needed at the post_dispatch phase. +#[cfg_attr(test, derive(Debug))] +pub struct GasCallEnvelope { + /// The account id who should receive the refund from the gas leftovers. + transactor: AccountId, + /// The negative imbalance obtained by withdrawing the value up to the requested gas limit. + imbalance: NegativeImbalance, +} + +/// A `SignedExtension` required to properly handle gas limits requested by contract executing +/// transactions. +#[derive(codec::Encode, codec::Decode, Clone, Eq, PartialEq)] +pub struct GasWeightConversion(PhantomData); + +impl GasWeightConversion { + pub fn new() -> Self { + Self(PhantomData) + } + + /// Perform pre-dispatch checks for the given call and origin. + fn perform_pre_dispatch_checks( + who: &T::AccountId, + call: &::Call, + ) -> Result< + Option>>, + TransactionValidityError, + > { + // This signed extension only deals with this module's `Call`. + let call = match call.is_sub_type() { + Some(call) => call, + None => return Ok(None), + }; + + match *call { + Call::claim_surcharge(_, _) | Call::update_schedule(_) | Call::put_code(_) => Ok(None), + Call::call(_, _, gas_limit, _) | Call::instantiate(_, gas_limit, _, _) => { + // Compute how much block weight this transaction can take at its limit, i.e. + // if this transaction depleted all provided gas to zero. + let gas_weight_limit = Weight::try_from(gas_limit) + .map_err(|_| InvalidTransaction::ExhaustsResources)?; + let weight_available = >::remaining_weight().into(); + if gas_weight_limit > weight_available { + // We discard the transaction if the requested limit exceeds the available + // amount of weight in the current block. + // + // Note that this transaction is left out only from the current block and txq + // might attempt to include this transaction again. + return Err(InvalidTransaction::ExhaustsResources.into()); + } + + // Compute the fee corresponding for the given gas_weight_limit and attempt + // withdrawing from the origin of this transaction. + let fee = T::WeightToFee::convert(gas_weight_limit); + + // Compute and store the effective price per unit of gas. + let gas_price = { + let divisor = gas_weight_limit.unique_saturated_into(); + fee.checked_div(&divisor).unwrap_or(1.into()) + }; + + // + // The place where we set GasPrice for the execution of this transaction. + // + >::put(gas_price); + + let imbalance = match T::Currency::withdraw( + who, + fee, + WithdrawReason::TransactionPayment.into(), + ExistenceRequirement::KeepAlive, + ) { + Ok(imbalance) => imbalance, + Err(_) => return Err(InvalidTransaction::Payment.into()), + }; + + Ok(Some(GasCallEnvelope { + transactor: who.clone(), + imbalance, + })) + } + Call::__PhantomItem(_, _) => unreachable!("Variant is never constructed"), + } + } +} + +impl Default for GasWeightConversion { + fn default() -> Self { + Self(PhantomData) + } +} + +impl fmt::Debug for GasWeightConversion { + #[cfg(feature = "std")] + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "GasWeightConversion") + } + + #[cfg(not(feature = "std"))] + fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { + Ok(()) + } +} + +impl SignedExtension for GasWeightConversion { + const IDENTIFIER: &'static str = "GasWeightConversion"; + type AccountId = T::AccountId; + type Call = ::Call; + type AdditionalSigned = (); + type DispatchInfo = DispatchInfo; + /// Optional pre-dispatch check data. + /// + /// It is present only for the contract calls that operate with gas. + type Pre = Option>>; + + fn additional_signed(&self) -> Result<(), TransactionValidityError> { + Ok(()) + } + + fn pre_dispatch( + self, + who: &Self::AccountId, + call: &Self::Call, + _: DispatchInfo, + _: usize, + ) -> Result { + Self::perform_pre_dispatch_checks(who, call) + } + + fn validate( + &self, + who: &Self::AccountId, + call: &Self::Call, + _: Self::DispatchInfo, + _: usize, + ) -> TransactionValidity { + Self::perform_pre_dispatch_checks(who, call).map(|_| ValidTransaction::default()) + } + + fn post_dispatch(pre: Self::Pre, _info: DispatchInfo, _len: usize) { + if let Some(GasCallEnvelope { + transactor, + imbalance, + }) = pre + { + // Take the report of consumed and left gas after the execution of the current + // transaction. + let (gas_left, gas_spent) = GasUsageReport::take(); + + // These should be OK since we don't buy more + let unused_weight = gas_left as Weight; + let spent_weight = gas_spent as Weight; + + let refund = T::WeightToFee::convert(unused_weight); + + // Refund the unused gas. + let refund_imbalance = T::Currency::deposit_creating(&transactor, refund); + if let Ok(imbalance) = imbalance.offset(refund_imbalance) { + T::GasPayment::on_unbalanced(imbalance); + } + + >::register_extra_weight_unchecked(spent_weight); + } + } +} diff --git a/frame/contracts/src/lib.rs b/frame/contracts/src/lib.rs index 42cbaa3a7c2af..acc1b545d2f7e 100644 --- a/frame/contracts/src/lib.rs +++ b/frame/contracts/src/lib.rs @@ -68,7 +68,7 @@ //! //! The contracts module defines the following extension: //! -//! - [`CheckBlockGasLimit`]: Ensures that the transaction does not exceeds the block gas limit. +//! - [`GasWeightConversion`]: Ensures that the transaction does not exceeds the block gas limit. //! //! The signed extension needs to be added as signed extra to the transaction type to be used in the //! runtime. @@ -93,42 +93,39 @@ mod gas; mod account_db; mod exec; -mod wasm; +mod gas_weight_conv; mod rent; +mod wasm; #[cfg(test)] mod tests; -use crate::exec::ExecutionContext; use crate::account_db::{AccountDb, DirectAccountDb}; +use crate::exec::ExecutionContext; use crate::wasm::{WasmLoader, WasmVm}; +pub use crate::exec::{ExecError, ExecResult, ExecReturnValue, StatusCode}; pub use crate::gas::{Gas, GasMeter}; -pub use crate::exec::{ExecResult, ExecReturnValue, ExecError, StatusCode}; +pub use gas_weight_conv::GasWeightConversion; -#[cfg(feature = "std")] -use serde::{Serialize, Deserialize}; -use sp_core::crypto::UncheckedFrom; -use sp_std::{prelude::*, marker::PhantomData, fmt::Debug}; -use codec::{Codec, Encode, Decode}; -use sp_io::hashing::blake2_256; -use sp_runtime::{ - traits::{Hash, StaticLookup, Zero, MaybeSerializeDeserialize, Member, SignedExtension}, - transaction_validity::{ - ValidTransaction, InvalidTransaction, TransactionValidity, TransactionValidityError, - }, - RuntimeDebug, -}; +use codec::{Codec, Decode, Encode}; use frame_support::dispatch::{DispatchResult, Dispatchable}; use frame_support::{ - Parameter, decl_module, decl_event, decl_storage, decl_error, storage::child, - parameter_types, IsSubType, - weights::DispatchInfo, + decl_error, decl_event, decl_module, decl_storage, parameter_types, + storage::child, + traits::{Currency, Get, OnReapAccount, OnUnbalanced, Randomness, Time}, + weights::{DispatchClass, FunctionOf, Weight}, + IsSubType, Parameter, }; -use frame_support::traits::{OnReapAccount, OnUnbalanced, Currency, Get, Time, Randomness}; -use frame_system::{self as system, ensure_signed, RawOrigin, ensure_root}; +use frame_system::{self as system, ensure_root, ensure_signed, RawOrigin}; +use pallet_contracts_primitives::{ContractAccessError, RentProjection}; +#[cfg(feature = "std")] +use serde::{Deserialize, Serialize}; +use sp_core::crypto::UncheckedFrom; use sp_core::storage::well_known_keys::CHILD_STORAGE_KEY_PREFIX; -use pallet_contracts_primitives::{RentProjection, ContractAccessError}; +use sp_io::hashing::blake2_256; +use sp_runtime::traits::{Convert, Hash, MaybeSerializeDeserialize, Member, StaticLookup, Zero}; +use sp_std::{fmt, marker::PhantomData, prelude::*}; pub type CodeHash = ::Hash; pub type TrieId = Vec; @@ -145,7 +142,7 @@ pub trait ComputeDispatchFee { /// Information for managing an account and its sub trie abstraction. /// This is the required info to cache for an account -#[derive(Encode, Decode, RuntimeDebug)] +#[derive(Encode, Decode, sp_runtime::RuntimeDebug)] pub enum ContractInfo { Alive(AliveContractInfo), Tombstone(TombstoneContractInfo), @@ -208,7 +205,7 @@ pub type AliveContractInfo = /// Information for managing an account and its sub trie abstraction. /// This is the required info to cache for an account. -#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)] +#[derive(Encode, Decode, Clone, PartialEq, Eq, sp_runtime::RuntimeDebug)] pub struct RawAliveContractInfo { /// Unique ID for the subtree encoded as a bytes vector. pub trie_id: TrieId, @@ -240,15 +237,21 @@ pub(crate) fn trie_unique_id(trie_id: &[u8]) -> child::ChildInfo { pub type TombstoneContractInfo = RawTombstoneContractInfo<::Hash, ::Hashing>; -#[derive(Encode, Decode, PartialEq, Eq, RuntimeDebug)] +#[derive(Encode, Decode, PartialEq, Eq, sp_runtime::RuntimeDebug)] pub struct RawTombstoneContractInfo(H, PhantomData); impl RawTombstoneContractInfo where - H: Member + MaybeSerializeDeserialize+ Debug - + AsRef<[u8]> + AsMut<[u8]> + Copy + Default - + sp_std::hash::Hash + Codec, - Hasher: Hash, + H: Member + + MaybeSerializeDeserialize + + fmt::Debug + + AsRef<[u8]> + + AsMut<[u8]> + + Copy + + Default + + sp_std::hash::Hash + + Codec, + Hasher: Hash, { fn new(storage_root: &[u8], code_hash: H) -> Self { let mut buf = Vec::new(); @@ -339,8 +342,6 @@ parameter_types! { pub const DefaultMaxDepth: u32 = 32; /// A reasonable default value for [`Trait::MaxValueSize`]. pub const DefaultMaxValueSize: u32 = 16_384; - /// A reasonable default value for [`Trait::BlockGasLimit`]. - pub const DefaultBlockGasLimit: u32 = 10_000_000; } pub trait Trait: frame_system::Trait { @@ -422,8 +423,16 @@ pub trait Trait: frame_system::Trait { /// The maximum size of a storage value in bytes. type MaxValueSize: Get; - /// The maximum amount of gas that could be expended per block. - type BlockGasLimit: Get; + /// Weight consumed by the `put_code` excluding per byte costs. + type PutCodeBaseWeight: Get; + + /// Weight consumed per one byte of code deployed in `put_code`. + type PutCodePerByteWeight: Get; + + /// Convert a weight value into a deductible fee based on the currency type. + /// + /// For now, only linear weight to fee is suppored. + type WeightToFee: Convert>; } /// Simple contract address determiner. @@ -539,10 +548,6 @@ decl_module! { /// The maximum size of a storage value in bytes. A reasonable default is 16 KiB. const MaxValueSize: u32 = T::MaxValueSize::get(); - /// The maximum amount of gas that could be expended per block. A reasonable - /// default value is 10_000_000. - const BlockGasLimit: Gas = T::BlockGasLimit::get(); - fn deposit_event() = default; /// Updates the schedule for metering contracts. @@ -562,23 +567,29 @@ decl_module! { /// Stores the given binary Wasm code into the chain's storage and returns its `codehash`. /// You can instantiate contracts only with stored code. + #[weight = + FunctionOf( + |args: (&Vec,)| { + let code_bytes = args.0.len() as u32; + code_bytes.saturating_mul(T::PutCodePerByteWeight::get()) + .saturating_add(T::PutCodeBaseWeight::get()) + }, + DispatchClass::Normal, + true, + ) + ] pub fn put_code( origin, - #[compact] gas_limit: Gas, code: Vec ) -> DispatchResult { - let origin = ensure_signed(origin)?; - - let (mut gas_meter, imbalance) = gas::buy_gas::(&origin, gas_limit)?; + let _origin = ensure_signed(origin)?; let schedule = >::current_schedule(); - let result = wasm::save_code::(code, &mut gas_meter, &schedule); + let result = wasm::save_code::(code, &schedule); if let Ok(code_hash) = result { Self::deposit_event(RawEvent::CodeStored(code_hash)); } - gas::refund_unused_gas::(&origin, gas_meter, imbalance); - result.map(|_| ()).map_err(Into::into) } @@ -662,10 +673,6 @@ decl_module! { T::Currency::deposit_into_existing(&rewarded, T::SurchargeReward::get())?; } } - - fn on_finalize() { - GasSpent::kill(); - } } } @@ -717,18 +724,18 @@ impl Module { fn execute_wasm( origin: T::AccountId, gas_limit: Gas, - func: impl FnOnce(&mut ExecutionContext, &mut GasMeter) -> ExecResult + func: impl FnOnce(&mut ExecutionContext, &mut GasMeter) -> ExecResult, ) -> ExecResult { - // Pay for the gas upfront. // - // NOTE: it is very important to avoid any state changes before - // paying for the gas. - let (mut gas_meter, imbalance) = - try_or_exec_error!( - gas::buy_gas::(&origin, gas_limit), - // We don't have a spare buffer here in the first place, so create a new empty one. - Vec::new() - ); + // Take the gas price prepared by the signed extension. + // + let gas_price = GasPrice::::take(); + debug_assert!( + gas_price != 0.into(), + "gas price should have been set by the signed extension", + ); + + let mut gas_meter = GasMeter::::with_limit(gas_limit, gas_price); let cfg = Config::preload(); let vm = WasmVm::new(&cfg.schedule); @@ -742,11 +749,10 @@ impl Module { DirectAccountDb.commit(ctx.overlay.into_change_set()); } - // Refund cost of the unused gas. - // - // NOTE: This should go after the commit to the storage, since the storage changes - // can alter the balance of the caller. - gas::refund_unused_gas::(&origin, gas_meter, imbalance); + // Save the gas usage report. It will be inspected later on at the post dispatch stage. + let gas_spent = gas_meter.spent(); + let gas_left = gas_meter.gas_left(); + GasUsageReport::put((gas_left, gas_spent)); // Execute deferred actions. ctx.deferred.into_iter().for_each(|deferred| { @@ -924,8 +930,10 @@ decl_event! { decl_storage! { trait Store for Module as Contract { - /// Gas spent so far in this block. - GasSpent get(fn gas_spent): Gas; + /// The amount of gas left from the execution of the latest contract. + /// + /// This value is transient and removed before block finalization. + GasUsageReport: (Gas, Gas); /// Current cost schedule for contracts. CurrentSchedule get(fn current_schedule) config(): Schedule = Schedule::default(); /// A mapping from an original code hash to the original code, untouched by instrumentation. @@ -937,7 +945,9 @@ decl_storage! { /// The code associated with a given account. pub ContractInfoOf: map hasher(blake2_256) T::AccountId => Option>; /// The price of one unit of gas. - GasPrice get(fn gas_price) config(): BalanceOf = 1.into(); + /// + /// This value is set in the signed extension and is removed after the execution. + GasPrice: BalanceOf = 1.into(); } } @@ -977,14 +987,11 @@ impl Config { /// Definition of the cost schedule and other parameterizations for wasm vm. #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] -#[derive(Clone, Encode, Decode, PartialEq, Eq, RuntimeDebug)] +#[derive(Clone, Encode, Decode, PartialEq, Eq, sp_runtime::RuntimeDebug)] pub struct Schedule { /// Version of the schedule. pub version: u32, - /// Cost of putting a byte of code into storage. - pub put_code_per_byte_cost: Gas, - /// Gas cost of a growing memory by single page. pub grow_mem_cost: Gas, @@ -1045,7 +1052,6 @@ impl Default for Schedule { fn default() -> Schedule { Schedule { version: 0, - put_code_per_byte_cost: 1, grow_mem_cost: 1, regular_op_cost: 1, return_data_per_byte_cost: 1, @@ -1066,69 +1072,3 @@ impl Default for Schedule { } } } - -/// `SignedExtension` that checks if a transaction would exhausts the block gas limit. -#[derive(Encode, Decode, Clone, Eq, PartialEq)] -pub struct CheckBlockGasLimit(PhantomData); - -impl Default for CheckBlockGasLimit { - fn default() -> Self { - Self(PhantomData) - } -} - -impl sp_std::fmt::Debug for CheckBlockGasLimit { - #[cfg(feature = "std")] - fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { - write!(f, "CheckBlockGasLimit") - } - - #[cfg(not(feature = "std"))] - fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { - Ok(()) - } -} - -impl SignedExtension for CheckBlockGasLimit { - const IDENTIFIER: &'static str = "CheckBlockGasLimit"; - type AccountId = T::AccountId; - type Call = ::Call; - type AdditionalSigned = (); - type DispatchInfo = DispatchInfo; - type Pre = (); - - fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> { Ok(()) } - - fn validate( - &self, - _: &Self::AccountId, - call: &Self::Call, - _: Self::DispatchInfo, - _: usize, - ) -> TransactionValidity { - let call = match call.is_sub_type() { - Some(call) => call, - None => return Ok(ValidTransaction::default()), - }; - - match call { - Call::claim_surcharge(_, _) | Call::update_schedule(_) => - Ok(ValidTransaction::default()), - Call::put_code(gas_limit, _) - | Call::call(_, _, gas_limit, _) - | Call::instantiate(_, gas_limit, _, _) - => { - // Check if the specified amount of gas is available in the current block. - // This cannot underflow since `gas_spent` is never greater than `T::BlockGasLimit`. - let gas_available = T::BlockGasLimit::get() - >::gas_spent(); - if *gas_limit > gas_available { - // gas limit reached, revert the transaction and retry again in the future - InvalidTransaction::ExhaustsResources.into() - } else { - Ok(ValidTransaction::default()) - } - }, - Call::__PhantomItem(_, _) => unreachable!("Variant is never constructed"), - } - } -} diff --git a/frame/contracts/src/tests.rs b/frame/contracts/src/tests.rs index ddd532334c158..99923a927e2b8 100644 --- a/frame/contracts/src/tests.rs +++ b/frame/contracts/src/tests.rs @@ -19,27 +19,36 @@ #![allow(unused)] +use crate::gas_weight_conv::GasWeightConversion; use crate::{ + account_db::{AccountDb, DirectAccountDb, OverlayAccountDb}, BalanceOf, ComputeDispatchFee, ContractAddressFor, ContractInfo, ContractInfoOf, GenesisConfig, - Module, RawAliveContractInfo, RawEvent, Trait, TrieId, TrieIdFromParentCounter, Schedule, - TrieIdGenerator, CheckBlockGasLimit, account_db::{AccountDb, DirectAccountDb, OverlayAccountDb}, + Module, RawAliveContractInfo, RawEvent, Schedule, Trait, TrieId, TrieIdFromParentCounter, + TrieIdGenerator, }; use assert_matches::assert_matches; -use hex_literal::*; use codec::{Decode, Encode, KeyedVec}; +use frame_support::{ + assert_err, assert_ok, impl_outer_dispatch, impl_outer_event, impl_outer_origin, + parameter_types, + storage::child, + traits::{Currency, Get}, + weights::{DispatchClass, DispatchInfo, Weight}, + StorageMap, StorageValue, +}; +use frame_system::{self as system, EventRecord, Phase}; +use hex_literal::*; +use sp_core::storage::well_known_keys; use sp_runtime::{ - Perbill, BuildStorage, transaction_validity::{InvalidTransaction, ValidTransaction}, - traits::{BlakeTwo256, Hash, IdentityLookup, SignedExtension}, testing::{Digest, DigestItem, Header, UintAuthorityId, H256}, + traits::{BlakeTwo256, Hash, IdentityLookup, SignedExtension}, + transaction_validity::{InvalidTransaction, ValidTransaction}, + BuildStorage, Perbill, }; -use frame_support::{ - assert_ok, assert_err, impl_outer_dispatch, impl_outer_event, impl_outer_origin, parameter_types, - storage::child, StorageMap, StorageValue, traits::{Currency, Get}, - weights::{DispatchInfo, DispatchClass, Weight}, +use std::{ + cell::RefCell, + sync::atomic::{AtomicUsize, Ordering}, }; -use std::{cell::RefCell, sync::atomic::{AtomicUsize, Ordering}}; -use sp_core::storage::well_known_keys; -use frame_system::{self as system, EventRecord, Phase}; mod contracts { // Re-export contents of the root. This basically @@ -72,7 +81,6 @@ thread_local! { static EXISTENTIAL_DEPOSIT: RefCell = RefCell::new(0); static TRANSFER_FEE: RefCell = RefCell::new(0); static INSTANTIATION_FEE: RefCell = RefCell::new(0); - static BLOCK_GAS_LIMIT: RefCell = RefCell::new(0); } pub struct ExistentialDeposit; @@ -85,9 +93,11 @@ impl Get for TransferFee { fn get() -> u64 { TRANSFER_FEE.with(|v| *v.borrow()) } } -pub struct BlockGasLimit; -impl Get for BlockGasLimit { - fn get() -> u64 { BLOCK_GAS_LIMIT.with(|v| *v.borrow()) } +pub struct CreationFee; +impl Get for CreationFee { + fn get() -> u64 { + INSTANTIATION_FEE.with(|v| *v.borrow()) + } } #[derive(Clone, Eq, PartialEq, Debug)] @@ -148,6 +158,8 @@ parameter_types! { pub const InstantiateBaseFee: u64 = 175; pub const MaxDepth: u32 = 100; pub const MaxValueSize: u32 = 16_384; + pub const PutCodeBaseWeight: Weight = 10_000; + pub const PutCodePerByteWeight: Weight = 4; } impl Trait for Test { type Currency = Balances; @@ -173,7 +185,9 @@ impl Trait for Test { type InstantiateBaseFee = InstantiateBaseFee; type MaxDepth = MaxDepth; type MaxValueSize = MaxValueSize; - type BlockGasLimit = BlockGasLimit; + type PutCodeBaseWeight = PutCodeBaseWeight; + type PutCodePerByteWeight = PutCodePerByteWeight; + type WeightToFee = (); } type Balances = pallet_balances::Module; @@ -223,8 +237,6 @@ const DJANGO: u64 = 4; pub struct ExtBuilder { existential_deposit: u64, - gas_price: u64, - block_gas_limit: u64, transfer_fee: u64, instantiation_fee: u64, } @@ -232,8 +244,6 @@ impl Default for ExtBuilder { fn default() -> Self { Self { existential_deposit: 1, - gas_price: 2, - block_gas_limit: 100_000_000, transfer_fee: 0, instantiation_fee: 0, } @@ -244,14 +254,6 @@ impl ExtBuilder { self.existential_deposit = existential_deposit; self } - pub fn gas_price(mut self, gas_price: u64) -> Self { - self.gas_price = gas_price; - self - } - pub fn block_gas_limit(mut self, block_gas_limit: u64) -> Self { - self.block_gas_limit = block_gas_limit; - self - } pub fn transfer_fee(mut self, transfer_fee: u64) -> Self { self.transfer_fee = transfer_fee; self @@ -264,46 +266,50 @@ impl ExtBuilder { EXISTENTIAL_DEPOSIT.with(|v| *v.borrow_mut() = self.existential_deposit); TRANSFER_FEE.with(|v| *v.borrow_mut() = self.transfer_fee); INSTANTIATION_FEE.with(|v| *v.borrow_mut() = self.instantiation_fee); - BLOCK_GAS_LIMIT.with(|v| *v.borrow_mut() = self.block_gas_limit); } pub fn build(self) -> sp_io::TestExternalities { self.set_associated_consts(); let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); - pallet_balances::GenesisConfig:: { - balances: vec![], - }.assimilate_storage(&mut t).unwrap(); - GenesisConfig:: { + pallet_balances::GenesisConfig:: { balances: vec![] } + .assimilate_storage(&mut t) + .unwrap(); + GenesisConfig { current_schedule: Schedule { enable_println: true, ..Default::default() }, - gas_price: self.gas_price, - }.assimilate_storage(&mut t).unwrap(); + } + .assimilate_storage(&mut t) + .unwrap(); sp_io::TestExternalities::new(t) } } /// Generate Wasm binary and code hash from wabt source. -fn compile_module(wabt_module: &str) - -> Result<(Vec, ::Output), wabt::Error> - where T: frame_system::Trait +fn compile_module( + wabt_module: &str, +) -> Result<(Vec, ::Output), wabt::Error> +where + T: frame_system::Trait, { let wasm = wabt::wat2wasm(wabt_module)?; let code_hash = T::Hashing::hash(&wasm); Ok((wasm, code_hash)) } -// Perform a simple transfer to a non-existent account supplying way more gas than needed. -// Then we check that the all unused gas is refunded. +/// The payment for gas is handled outside of contract execution (e.g `call`) functions. #[test] -fn refunds_unused_gas() { - ExtBuilder::default().gas_price(2).build().execute_with(|| { +fn call_doesnt_pay_for_gas() { + ExtBuilder::default().build().execute_with(|| { Balances::deposit_creating(&ALICE, 100_000_000); - - assert_ok!(Contracts::call(Origin::signed(ALICE), BOB, 0, 100_000, Vec::new())); - - // 2 * 135 - gas price multiplied by the call base fee. - assert_eq!(Balances::free_balance(ALICE), 100_000_000 - (2 * 135)); + assert_ok!(Contracts::call( + Origin::signed(ALICE), + BOB, + 0, + 100_000, + Vec::new() + )); + assert_eq!(Balances::free_balance(&ALICE), 100_000_000); }); } @@ -409,7 +415,7 @@ fn instantiate_and_call_and_deposit_event() { ExtBuilder::default().existential_deposit(100).build().execute_with(|| { Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm)); + assert_ok!(Contracts::put_code(Origin::signed(ALICE), wasm)); // Check at the end to get hash on error easily let creation = Contracts::instantiate( @@ -499,7 +505,7 @@ fn dispatch_call() { ExtBuilder::default().existential_deposit(50).build().execute_with(|| { Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm)); + assert_ok!(Contracts::put_code(Origin::signed(ALICE), wasm)); // Let's keep this assert even though it's redundant. If you ever need to update the // wasm source this test will fail and will show you the actual hash. @@ -637,7 +643,7 @@ fn dispatch_call_not_dispatched_after_top_level_transaction_failure() { ExtBuilder::default().existential_deposit(50).build().execute_with(|| { Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm)); + assert_ok!(Contracts::put_code(Origin::signed(ALICE), wasm)); // Let's keep this assert even though it's redundant. If you ever need to update the // wasm source this test will fail and will show you the actual hash. @@ -742,7 +748,7 @@ fn run_out_of_gas() { .execute_with(|| { Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm)); + assert_ok!(Contracts::put_code(Origin::signed(ALICE), wasm)); assert_ok!(Contracts::instantiate( Origin::signed(ALICE), @@ -894,9 +900,12 @@ fn test_set_rent_code_and_hash() { let (wasm, code_hash) = compile_module::(CODE_SET_RENT).unwrap(); - ExtBuilder::default().existential_deposit(50).build().execute_with(|| { - Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm)); + ExtBuilder::default() + .existential_deposit(50) + .build() + .execute_with(|| { + Balances::deposit_creating(&ALICE, 1_000_000); + assert_ok!(Contracts::put_code(Origin::signed(ALICE), wasm)); // If you ever need to update the wasm source this test will fail // and will show you the actual hash. @@ -928,7 +937,7 @@ fn storage_size() { ExtBuilder::default().existential_deposit(50).build().execute_with(|| { // Create Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm)); + assert_ok!(Contracts::put_code(Origin::signed(ALICE), wasm)); assert_ok!(Contracts::instantiate( Origin::signed(ALICE), 30_000, @@ -965,7 +974,7 @@ fn deduct_blocks() { ExtBuilder::default().existential_deposit(50).build().execute_with(|| { // Create Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm)); + assert_ok!(Contracts::put_code(Origin::signed(ALICE), wasm)); assert_ok!(Contracts::instantiate( Origin::signed(ALICE), 30_000, @@ -1059,7 +1068,7 @@ fn claim_surcharge(blocks: u64, trigger_call: impl Fn() -> bool, removes: bool) ExtBuilder::default().existential_deposit(50).build().execute_with(|| { // Create Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm)); + assert_ok!(Contracts::put_code(Origin::signed(ALICE), wasm)); assert_ok!(Contracts::instantiate( Origin::signed(ALICE), 100, @@ -1092,7 +1101,7 @@ fn removals(trigger_call: impl Fn() -> bool) { ExtBuilder::default().existential_deposit(50).build().execute_with(|| { // Create Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm.clone())); + assert_ok!(Contracts::put_code(Origin::signed(ALICE), wasm.clone())); assert_ok!(Contracts::instantiate( Origin::signed(ALICE), 100, @@ -1128,7 +1137,7 @@ fn removals(trigger_call: impl Fn() -> bool) { ExtBuilder::default().existential_deposit(50).build().execute_with(|| { // Create Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm.clone())); + assert_ok!(Contracts::put_code(Origin::signed(ALICE), wasm.clone())); assert_ok!(Contracts::instantiate( Origin::signed(ALICE), 1_000, @@ -1163,7 +1172,7 @@ fn removals(trigger_call: impl Fn() -> bool) { ExtBuilder::default().existential_deposit(50).build().execute_with(|| { // Create Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm.clone())); + assert_ok!(Contracts::put_code(Origin::signed(ALICE), wasm.clone())); assert_ok!(Contracts::instantiate( Origin::signed(ALICE), 50+Balances::minimum_balance(), @@ -1207,7 +1216,7 @@ fn call_removed_contract() { ExtBuilder::default().existential_deposit(50).build().execute_with(|| { // Create Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm.clone())); + assert_ok!(Contracts::put_code(Origin::signed(ALICE), wasm.clone())); assert_ok!(Contracts::instantiate( Origin::signed(ALICE), 100, @@ -1300,7 +1309,7 @@ fn default_rent_allowance_on_instantiate() { ExtBuilder::default().existential_deposit(50).build().execute_with(|| { // Create Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm)); + assert_ok!(Contracts::put_code(Origin::signed(ALICE), wasm)); assert_ok!(Contracts::instantiate( Origin::signed(ALICE), 30_000, @@ -1411,10 +1420,13 @@ fn restoration(test_different_storage: bool, test_restore_to_with_dirty_storage: let (restoration_wasm, restoration_code_hash) = compile_module::(CODE_RESTORATION).unwrap(); - ExtBuilder::default().existential_deposit(50).build().execute_with(|| { - Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, restoration_wasm)); - assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, set_rent_wasm)); + ExtBuilder::default() + .existential_deposit(50) + .build() + .execute_with(|| { + Balances::deposit_creating(&ALICE, 1_000_000); + assert_ok!(Contracts::put_code(Origin::signed(ALICE), restoration_wasm)); + assert_ok!(Contracts::put_code(Origin::signed(ALICE), set_rent_wasm)); // If you ever need to update the wasm source this test will fail // and will show you the actual hash. @@ -1692,7 +1704,7 @@ fn storage_max_value_limit() { ExtBuilder::default().existential_deposit(50).build().execute_with(|| { // Create Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm)); + assert_ok!(Contracts::put_code(Origin::signed(ALICE), wasm)); assert_ok!(Contracts::instantiate( Origin::signed(ALICE), 30_000, @@ -2056,8 +2068,8 @@ fn deploy_and_call_other_contract() { ExtBuilder::default().existential_deposit(50).build().execute_with(|| { // Create Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, callee_wasm)); - assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, caller_wasm)); + assert_ok!(Contracts::put_code(Origin::signed(ALICE), callee_wasm)); + assert_ok!(Contracts::put_code(Origin::signed(ALICE), caller_wasm)); assert_ok!(Contracts::instantiate( Origin::signed(ALICE), @@ -2181,11 +2193,14 @@ const CODE_SELF_DESTRUCT: &str = r#" #[test] fn self_destruct_by_draining_balance() { let (wasm, code_hash) = compile_module::(CODE_SELF_DESTRUCT).unwrap(); - ExtBuilder::default().existential_deposit(50).build().execute_with(|| { - Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm)); + ExtBuilder::default() + .existential_deposit(50) + .build() + .execute_with(|| { + Balances::deposit_creating(&ALICE, 1_000_000); + assert_ok!(Contracts::put_code(Origin::signed(ALICE), wasm)); - // Instantiate the BOB contract. + // Instantiate the BOB contract. assert_ok!(Contracts::instantiate( Origin::signed(ALICE), 100_000, @@ -2217,11 +2232,14 @@ fn self_destruct_by_draining_balance() { #[test] fn cannot_self_destruct_while_live() { let (wasm, code_hash) = compile_module::(CODE_SELF_DESTRUCT).unwrap(); - ExtBuilder::default().existential_deposit(50).build().execute_with(|| { - Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm)); + ExtBuilder::default() + .existential_deposit(50) + .build() + .execute_with(|| { + Balances::deposit_creating(&ALICE, 1_000_000); + assert_ok!(Contracts::put_code(Origin::signed(ALICE), wasm)); - // Instantiate the BOB contract. + // Instantiate the BOB contract. assert_ok!(Contracts::instantiate( Origin::signed(ALICE), 100_000, @@ -2419,8 +2437,8 @@ fn destroy_contract_and_transfer_funds() { ExtBuilder::default().existential_deposit(50).build().execute_with(|| { // Create Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, callee_wasm)); - assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, caller_wasm)); + assert_ok!(Contracts::put_code(Origin::signed(ALICE), callee_wasm)); + assert_ok!(Contracts::put_code(Origin::signed(ALICE), caller_wasm)); // This deploys the BOB contract, which in turn deploys the CHARLIE contract during // construction. @@ -2512,9 +2530,12 @@ const CODE_SELF_DESTRUCTING_CONSTRUCTOR: &str = r#" #[test] fn cannot_self_destruct_in_constructor() { let (wasm, code_hash) = compile_module::(CODE_SELF_DESTRUCTING_CONSTRUCTOR).unwrap(); - ExtBuilder::default().existential_deposit(50).build().execute_with(|| { - Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm)); + ExtBuilder::default() + .existential_deposit(50) + .build() + .execute_with(|| { + Balances::deposit_creating(&ALICE, 1_000_000); + assert_ok!(Contracts::put_code(Origin::signed(ALICE), wasm)); // Fail to instantiate the BOB contract since its final balance is below existential // deposit. @@ -2533,17 +2554,25 @@ fn cannot_self_destruct_in_constructor() { #[test] fn check_block_gas_limit_works() { - ExtBuilder::default().block_gas_limit(50).build().execute_with(|| { - let info = DispatchInfo { weight: 100, class: DispatchClass::Normal, pays_fee: true }; - let check = CheckBlockGasLimit::(Default::default()); - let call: Call = crate::Call::put_code(1000, vec![]).into(); - - assert_eq!( - check.validate(&0, &call, info, 0), InvalidTransaction::ExhaustsResources.into(), - ); + ExtBuilder::default().build().execute_with(|| { + let info = DispatchInfo { + weight: 100, + class: DispatchClass::Normal, + pays_fee: true, + }; + let check = GasWeightConversion::::new(); + let call: Call = crate::Call::put_code(vec![]).into(); + match check.pre_dispatch(&0, &call, info, 0) { + Ok(None) => {} + _ => panic!("put_code is not dynamic and should pass validation"), + } - let call: Call = crate::Call::update_schedule(Default::default()).into(); - assert_eq!(check.validate(&0, &call, info, 0), Ok(Default::default())); + let check = GasWeightConversion::::new(); + let call: Call = crate::Call::call(Default::default(), 0, 100, vec![]).into(); + match check.pre_dispatch(&0, &call, info, 0) { + Ok(Some(_)) => {} + _ => panic!(), + } }); } @@ -2635,7 +2664,7 @@ fn get_runtime_storage() { 0x14144020u32.to_le_bytes().to_vec().as_ref() ); - assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm)); + assert_ok!(Contracts::put_code(Origin::signed(ALICE), wasm)); assert_ok!(Contracts::instantiate( Origin::signed(ALICE), 100, diff --git a/frame/contracts/src/wasm/code_cache.rs b/frame/contracts/src/wasm/code_cache.rs index cb942a25892cd..6850701afa9cf 100644 --- a/frame/contracts/src/wasm/code_cache.rs +++ b/frame/contracts/src/wasm/code_cache.rs @@ -26,30 +26,11 @@ //! this guarantees that every instrumented contract code in cache cannot have the version equal to the current one. //! Thus, before executing a contract it should be reinstrument with new schedule. -use crate::gas::{Gas, GasMeter, Token}; use crate::wasm::{prepare, runtime::Env, PrefabWasmModule}; use crate::{CodeHash, CodeStorage, PristineCode, Schedule, Trait}; -use sp_std::prelude::*; -use sp_runtime::traits::{Hash, Bounded}; use frame_support::StorageMap; - -/// Gas metering token that used for charging storing code into the code storage. -/// -/// Specifies the code length in bytes. -#[cfg_attr(test, derive(Debug, PartialEq, Eq))] -#[derive(Copy, Clone)] -pub struct PutCodeToken(u32); - -impl Token for PutCodeToken { - type Metadata = Schedule; - - fn calculate_amount(&self, metadata: &Schedule) -> Gas { - metadata - .put_code_per_byte_cost - .checked_mul(self.0.into()) - .unwrap_or_else(|| Bounded::max_value()) - } -} +use sp_runtime::traits::Hash; +use sp_std::prelude::*; /// Put code in the storage. The hash of code is used as a key and is returned /// as a result of this function. @@ -57,18 +38,8 @@ impl Token for PutCodeToken { /// This function instruments the given code and caches it in the storage. pub fn save( original_code: Vec, - gas_meter: &mut GasMeter, schedule: &Schedule, ) -> Result, &'static str> { - // The first time instrumentation is on the user. However, consequent reinstrumentation - // due to the schedule changes is on governance system. - if gas_meter - .charge(schedule, PutCodeToken(original_code.len() as u32)) - .is_out_of_gas() - { - return Err("there is not enough gas for storing the code"); - } - let prefab_module = prepare::prepare_contract::(&original_code, schedule)?; let code_hash = T::Hashing::hash(&original_code); diff --git a/frame/system/src/lib.rs b/frame/system/src/lib.rs index 57be7b157cb33..95bc957748454 100644 --- a/frame/system/src/lib.rs +++ b/frame/system/src/lib.rs @@ -697,7 +697,7 @@ impl Module { ExtrinsicCount::get().unwrap_or_default() } - /// Gets a total weight of all executed extrinsics. + /// Gets a total weight of all executed extrinsics so far. pub fn all_extrinsics_weight() -> Weight { AllExtrinsicsWeight::get().unwrap_or_default() } @@ -706,6 +706,13 @@ impl Module { AllExtrinsicsLen::get().unwrap_or_default() } + /// Returns how much unreserved weight is left for the current block. + /// + /// This is the opposite of `all_extrinsics_weight` provided for convenience. + pub fn remaining_weight() -> Weight { + T::MaximumBlockWeight::get().saturating_sub(Self::all_extrinsics_weight()) + } + /// Inform the system module of some additional weight that should be accounted for, in the /// current block. ///