Integrate pallet_contracts gas with the weight system (#5712)

Co-Authored-By: Tomasz Drwięga <tomusdrw@users.noreply.github.com>
Co-Authored-By: Sergei Pepyakin <sergei@parity.io>
This commit is contained in:
Alexander Theißen
2020-04-24 10:48:13 +02:00
committed by GitHub
parent e731817e24
commit 000c924b62
20 changed files with 386 additions and 680 deletions
+1
View File
@@ -4077,6 +4077,7 @@ dependencies = [
"pallet-contracts-primitives",
"pallet-randomness-collective-flip",
"pallet-timestamp",
"pallet-transaction-payment",
"parity-scale-codec",
"parity-wasm 0.41.0",
"pwasm-utils",
-1
View File
@@ -292,7 +292,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,
+1 -2
View File
@@ -57,7 +57,6 @@ impl<Number> FactoryState<Number> {
frame_system::CheckNonce::from(index),
frame_system::CheckWeight::new(),
pallet_transaction_payment::ChargeTransactionPayment::from(0),
Default::default(),
)
}
}
@@ -122,7 +121,7 @@ impl RuntimeAdapter for FactoryState<Number> {
(*amount).into()
)
)
}, key, (version, genesis_hash.clone(), prior_block_hash.clone(), (), (), (), ()))
}, key, (version, genesis_hash.clone(), prior_block_hash.clone(), (), (), ()))
}
fn inherent_extrinsics(&self) -> InherentData {
+1 -2
View File
@@ -633,12 +633,11 @@ mod tests {
check_nonce,
check_weight,
payment,
Default::default(),
);
let raw_payload = SignedPayload::from_raw(
function,
extra,
(version, genesis_hash, genesis_hash, (), (), (), ())
(version, genesis_hash, genesis_hash, (), (), ())
);
let signature = raw_payload.using_encoded(|payload| {
signer.sign(payload)
+8 -3
View File
@@ -606,13 +606,18 @@ fn deploying_wasm_contract_should_work() {
CheckedExtrinsic {
signed: Some((charlie(), signed_extra(0, 0))),
function: Call::Contracts(
pallet_contracts::Call::put_code::<Runtime>(10_000, transfer_code)
pallet_contracts::Call::put_code::<Runtime>(transfer_code)
),
},
CheckedExtrinsic {
signed: Some((charlie(), signed_extra(1, 0))),
function: Call::Contracts(
pallet_contracts::Call::instantiate::<Runtime>(1 * DOLLARS, 10_000, transfer_ch, Vec::new())
pallet_contracts::Call::instantiate::<Runtime>(
1 * DOLLARS,
500_000_000,
transfer_ch,
Vec::new()
)
),
},
CheckedExtrinsic {
@@ -621,7 +626,7 @@ fn deploying_wasm_contract_should_work() {
pallet_contracts::Call::call::<Runtime>(
pallet_indices::address::Address::Id(addr.clone()),
10,
10_000,
500_000_000,
vec![0x00, 0x01, 0x02, 0x03]
)
),
+1 -16
View File
@@ -73,7 +73,6 @@ use constants::{time::*, currency::*};
#[cfg(feature = "std")]
include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));
/// Runtime version.
pub const VERSION: RuntimeVersion = RuntimeVersion {
spec_name: create_runtime_str!("node"),
@@ -460,9 +459,6 @@ impl pallet_treasury::Trait for Runtime {
}
parameter_types! {
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;
@@ -470,15 +466,12 @@ parameter_types! {
}
impl pallet_contracts::Trait for Runtime {
type Currency = Balances;
type Time = Timestamp;
type Randomness = RandomnessCollectiveFlip;
type Call = Call;
type Event = Event;
type DetermineContractAddress = pallet_contracts::SimpleAddressDeterminer<Runtime>;
type ComputeDispatchFee = pallet_contracts::DefaultDispatchFeeComputor<Runtime>;
type TrieIdGenerator = pallet_contracts::TrieIdFromParentCounter<Runtime>;
type GasPayment = ();
type RentPayment = ();
type SignedClaimHandicap = pallet_contracts::DefaultSignedClaimHandicap;
type TombstoneDeposit = TombstoneDeposit;
@@ -486,14 +479,8 @@ impl pallet_contracts::Trait for Runtime {
type RentByteFee = RentByteFee;
type RentDepositOffset = RentDepositOffset;
type SurchargeReward = SurchargeReward;
type TransactionBaseFee = ContractTransactionBaseFee;
type TransactionByteFee = ContractTransactionByteFee;
type ContractFee = ContractFee;
type CallBaseFee = pallet_contracts::DefaultCallBaseFee;
type InstantiateBaseFee = pallet_contracts::DefaultInstantiateBaseFee;
type MaxDepth = pallet_contracts::DefaultMaxDepth;
type MaxValueSize = pallet_contracts::DefaultMaxValueSize;
type BlockGasLimit = pallet_contracts::DefaultBlockGasLimit;
}
impl pallet_sudo::Trait for Runtime {
@@ -536,7 +523,6 @@ impl<LocalCall> frame_system::offchain::CreateSignedTransaction<LocalCall> for R
frame_system::CheckNonce::<Runtime>::from(nonce),
frame_system::CheckWeight::<Runtime>::new(),
pallet_transaction_payment::ChargeTransactionPayment::<Runtime>::from(tip),
Default::default(),
);
let raw_payload = SignedPayload::new(call, extra).map_err(|e| {
debug::warn!("Unable to create signed payload: {:?}", e);
@@ -692,7 +678,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<T>},
Contracts: pallet_contracts::{Module, Call, Config<T>, Storage, Event<T>},
Contracts: pallet_contracts::{Module, Call, Config, Storage, Event<T>},
Sudo: pallet_sudo::{Module, Call, Config<T>, Storage, Event<T>},
ImOnline: pallet_im_online::{Module, Call, Storage, Event<T>, ValidateUnsigned, Config<T>},
AuthorityDiscovery: pallet_authority_discovery::{Module, Call, Config},
@@ -724,7 +710,6 @@ pub type SignedExtra = (
frame_system::CheckNonce<Runtime>,
frame_system::CheckWeight<Runtime>,
pallet_transaction_payment::ChargeTransactionPayment<Runtime>,
pallet_contracts::CheckBlockGasLimit<Runtime>,
);
/// Unchecked extrinsic type as expected by this runtime.
pub type UncheckedExtrinsic = generic::UncheckedExtrinsic<Address, Call, Signature, SignedExtra>;
@@ -97,7 +97,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 {
@@ -74,7 +74,6 @@ pub fn signed_extra(nonce: Index, extra_fee: Balance) -> SignedExtra {
frame_system::CheckNonce::from(nonce),
frame_system::CheckWeight::new(),
pallet_transaction_payment::ChargeTransactionPayment::from(extra_fee),
Default::default(),
)
}
-2
View File
@@ -708,7 +708,6 @@ fn create_extrinsic<C: Crypto>(
frame_system::CheckNonce::<Runtime>::from(i),
frame_system::CheckWeight::<Runtime>::new(),
pallet_transaction_payment::ChargeTransactionPayment::<Runtime>::from(f),
Default::default(),
)
};
let raw_payload = SignedPayload::from_raw(
@@ -721,7 +720,6 @@ fn create_extrinsic<C: Crypto>(
(),
(),
(),
(),
),
);
let signature = raw_payload.using_encoded(|payload| signer.sign(payload)).into_runtime();
+1
View File
@@ -25,6 +25,7 @@ sp-sandbox = { version = "0.8.0-dev", default-features = false, path = "../../pr
frame-support = { version = "2.0.0-dev", default-features = false, path = "../support" }
frame-system = { version = "2.0.0-dev", default-features = false, path = "../system" }
pallet-contracts-primitives = { version = "2.0.0-dev", default-features = false, path = "common" }
pallet-transaction-payment = { version = "2.0.0-dev", default-features = false, path = "../transaction-payment" }
[dev-dependencies]
wabt = "0.9.2"
+4 -3
View File
@@ -46,9 +46,10 @@ const CONTRACT_IS_A_TOMBSTONE: i64 = 3;
/// This value is used to set the upper bound for maximal contract calls to
/// prevent blocking the RPC for too long.
///
/// Based on W3F research spreadsheet:
/// https://docs.google.com/spreadsheets/d/1h0RqncdqiWI4KgxO0z9JIpZEJESXjX_ZCK6LFX6veDo/view
const GAS_PER_SECOND: u64 = 1_000_000_000;
/// As 1 gas is equal to 1 weight we base this on the conducted benchmarks which
/// determined runtime weights:
/// https://github.com/paritytech/substrate/pull/5446
const GAS_PER_SECOND: u64 = 1_000_000_000_000;
/// A private newtype for converting `ContractAccessError` into an RPC error.
struct ContractAccessError(pallet_contracts_primitives::ContractAccessError);
+43 -36
View File
@@ -17,7 +17,7 @@
use super::{CodeHash, Config, ContractAddressFor, Event, RawEvent, Trait,
TrieId, BalanceOf, ContractInfo};
use crate::account_db::{AccountDb, DirectAccountDb, OverlayAccountDb};
use crate::gas::{Gas, GasMeter, Token, approx_gas_for_balance};
use crate::gas::{Gas, GasMeter, Token};
use crate::rent;
use sp_std::prelude::*;
@@ -203,6 +203,9 @@ pub trait Ext {
///
/// Returns `None` if the value doesn't exist.
fn get_runtime_storage(&self, key: &[u8]) -> Option<Vec<u8>>;
/// Returns the price of one weight unit.
fn get_weight_price(&self) -> BalanceOf<Self::T>;
}
/// Loader is a companion of the `Vm` trait. It loads an appropriate abstract
@@ -605,21 +608,19 @@ pub enum TransferFeeKind {
#[cfg_attr(test, derive(Debug, PartialEq, Eq))]
#[derive(Copy, Clone)]
pub struct TransferFeeToken<Balance> {
pub struct TransferFeeToken {
kind: TransferFeeKind,
gas_price: Balance,
}
impl<T: Trait> Token<T> for TransferFeeToken<BalanceOf<T>> {
impl<T: Trait> Token<T> for TransferFeeToken {
type Metadata = Config<T>;
#[inline]
fn calculate_amount(&self, metadata: &Config<T>) -> Gas {
let balance_fee = match self.kind {
TransferFeeKind::ContractInstantiate => metadata.contract_account_instantiate_fee,
TransferFeeKind::Transfer => return metadata.schedule.transfer_cost,
};
approx_gas_for_balance(self.gas_price, balance_fee)
match self.kind {
TransferFeeKind::ContractInstantiate => metadata.schedule.instantiate_cost,
TransferFeeKind::Transfer => metadata.schedule.transfer_cost,
}
}
}
@@ -668,7 +669,6 @@ fn transfer<'a, T: Trait, V: Vm<T>, L: Loader<T>>(
};
TransferFeeToken {
kind,
gas_price: gas_meter.gas_price(),
}
};
@@ -868,6 +868,13 @@ where
fn get_runtime_storage(&self, key: &[u8]) -> Option<Vec<u8>> {
unhashed::get_raw(&key)
}
fn get_weight_price(&self) -> BalanceOf<Self::T> {
use pallet_transaction_payment::Module as Payment;
use sp_runtime::SaturatedConversion;
let price = Payment::<T>::weight_to_fee_with_adjustment::<u128>(1);
price.saturated_into()
}
}
/// These tests exercise the executive layer.
@@ -889,6 +896,7 @@ mod tests {
use crate::{
account_db::AccountDb, gas::GasMeter, tests::{ExtBuilder, Test},
exec::{ExecReturnValue, ExecError, STATUS_SUCCESS}, CodeHash, Config,
gas::Gas,
};
use std::{cell::RefCell, rc::Rc, collections::HashMap, marker::PhantomData};
use assert_matches::assert_matches;
@@ -898,6 +906,8 @@ mod tests {
const BOB: u64 = 2;
const CHARLIE: u64 = 3;
const GAS_LIMIT: Gas = 10_000_000_000;
impl<'a, T, V, L> ExecutionContext<'a, T, V, L>
where T: crate::Trait
{
@@ -1003,7 +1013,7 @@ mod tests {
#[test]
fn it_works() {
let value = Default::default();
let mut gas_meter = GasMeter::<Test>::with_limit(10000, 1);
let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
let data = vec![];
let vm = MockVm::new();
@@ -1044,7 +1054,7 @@ mod tests {
ctx.overlay.set_balance(&origin, 100);
ctx.overlay.set_balance(&dest, 0);
let mut gas_meter = GasMeter::<Test>::with_limit(1000, 1);
let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
let result = ctx.call(dest, 0, &mut gas_meter, vec![]);
assert_matches!(result, Ok(_));
@@ -1064,7 +1074,7 @@ mod tests {
ctx.overlay.set_balance(&origin, 100);
let mut gas_meter = GasMeter::<Test>::with_limit(1000, 1);
let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
let result = ctx.instantiate(1, &mut gas_meter, &code, vec![]);
assert_matches!(result, Ok(_));
@@ -1093,7 +1103,7 @@ mod tests {
let output = ctx.call(
dest,
55,
&mut GasMeter::<Test>::with_limit(1000, 1),
&mut GasMeter::<Test>::new(GAS_LIMIT),
vec![],
).unwrap();
@@ -1126,7 +1136,7 @@ mod tests {
let output = ctx.call(
dest,
55,
&mut GasMeter::<Test>::with_limit(1000, 1),
&mut GasMeter::<Test>::new(GAS_LIMIT),
vec![],
).unwrap();
@@ -1152,7 +1162,7 @@ mod tests {
ctx.overlay.set_balance(&origin, 100);
ctx.overlay.set_balance(&dest, 0);
let mut gas_meter = GasMeter::<Test>::with_limit(1000, 1);
let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
let result = ctx.call(dest, 50, &mut gas_meter, vec![]);
assert_matches!(result, Ok(_));
@@ -1163,7 +1173,6 @@ mod tests {
ExecFeeToken::Call,
TransferFeeToken {
kind: TransferFeeKind::Transfer,
gas_price: 1u64
},
);
});
@@ -1178,7 +1187,7 @@ mod tests {
ctx.overlay.set_balance(&origin, 100);
ctx.overlay.set_balance(&dest, 15);
let mut gas_meter = GasMeter::<Test>::with_limit(1000, 1);
let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
let result = ctx.call(dest, 50, &mut gas_meter, vec![]);
assert_matches!(result, Ok(_));
@@ -1189,7 +1198,6 @@ mod tests {
ExecFeeToken::Call,
TransferFeeToken {
kind: TransferFeeKind::Transfer,
gas_price: 1u64
},
);
});
@@ -1207,7 +1215,7 @@ mod tests {
ctx.overlay.set_balance(&origin, 100);
ctx.overlay.set_balance(&dest, 15);
let mut gas_meter = GasMeter::<Test>::with_limit(1000, 1);
let mut gas_meter = GasMeter::<Test>::new(GAS_LIMIT);
let result = ctx.instantiate(50, &mut gas_meter, &code, vec![]);
assert_matches!(result, Ok(_));
@@ -1218,7 +1226,6 @@ mod tests {
ExecFeeToken::Instantiate,
TransferFeeToken {
kind: TransferFeeKind::ContractInstantiate,
gas_price: 1u64
},
);
});
@@ -1242,7 +1249,7 @@ mod tests {
let result = ctx.call(
dest,
100,
&mut GasMeter::<Test>::with_limit(1000, 1),
&mut GasMeter::<Test>::new(GAS_LIMIT),
vec![],
);
@@ -1279,7 +1286,7 @@ mod tests {
let result = ctx.call(
dest,
0,
&mut GasMeter::<Test>::with_limit(1000, 1),
&mut GasMeter::<Test>::new(GAS_LIMIT),
vec![],
);
@@ -1310,7 +1317,7 @@ mod tests {
let result = ctx.call(
dest,
0,
&mut GasMeter::<Test>::with_limit(1000, 1),
&mut GasMeter::<Test>::new(GAS_LIMIT),
vec![],
);
@@ -1338,7 +1345,7 @@ mod tests {
let result = ctx.call(
BOB,
0,
&mut GasMeter::<Test>::with_limit(10000, 1),
&mut GasMeter::<Test>::new(GAS_LIMIT),
vec![1, 2, 3, 4],
);
assert_matches!(result, Ok(_));
@@ -1363,7 +1370,7 @@ mod tests {
let result = ctx.instantiate(
1,
&mut GasMeter::<Test>::with_limit(10000, 1),
&mut GasMeter::<Test>::new(GAS_LIMIT),
&input_data_ch,
vec![1, 2, 3, 4],
);
@@ -1413,7 +1420,7 @@ mod tests {
let result = ctx.call(
BOB,
value,
&mut GasMeter::<Test>::with_limit(100000, 1),
&mut GasMeter::<Test>::new(GAS_LIMIT),
vec![],
);
@@ -1459,7 +1466,7 @@ mod tests {
let result = ctx.call(
dest,
0,
&mut GasMeter::<Test>::with_limit(10000, 1),
&mut GasMeter::<Test>::new(GAS_LIMIT),
vec![],
);
@@ -1500,7 +1507,7 @@ mod tests {
let result = ctx.call(
BOB,
0,
&mut GasMeter::<Test>::with_limit(10000, 1),
&mut GasMeter::<Test>::new(GAS_LIMIT),
vec![],
);
@@ -1522,7 +1529,7 @@ mod tests {
assert_matches!(
ctx.instantiate(
0, // <- zero endowment
&mut GasMeter::<Test>::with_limit(10000, 1),
&mut GasMeter::<Test>::new(GAS_LIMIT),
&dummy_ch,
vec![],
),
@@ -1548,7 +1555,7 @@ mod tests {
let instantiated_contract_address = assert_matches!(
ctx.instantiate(
100,
&mut GasMeter::<Test>::with_limit(10000, 1),
&mut GasMeter::<Test>::new(GAS_LIMIT),
&dummy_ch,
vec![],
),
@@ -1588,7 +1595,7 @@ mod tests {
let instantiated_contract_address = assert_matches!(
ctx.instantiate(
100,
&mut GasMeter::<Test>::with_limit(10000, 1),
&mut GasMeter::<Test>::new(GAS_LIMIT),
&dummy_ch,
vec![],
),
@@ -1633,7 +1640,7 @@ mod tests {
ctx.overlay.instantiate_contract(&BOB, instantiator_ch).unwrap();
assert_matches!(
ctx.call(BOB, 20, &mut GasMeter::<Test>::with_limit(1000, 1), vec![]),
ctx.call(BOB, 20, &mut GasMeter::<Test>::new(GAS_LIMIT), vec![]),
Ok(_)
);
@@ -1693,7 +1700,7 @@ mod tests {
ctx.overlay.instantiate_contract(&BOB, instantiator_ch).unwrap();
assert_matches!(
ctx.call(BOB, 20, &mut GasMeter::<Test>::with_limit(1000, 1), vec![]),
ctx.call(BOB, 20, &mut GasMeter::<Test>::new(GAS_LIMIT), vec![]),
Ok(_)
);
@@ -1730,7 +1737,7 @@ mod tests {
assert_matches!(
ctx.instantiate(
100,
&mut GasMeter::<Test>::with_limit(10000, 1),
&mut GasMeter::<Test>::new(GAS_LIMIT),
&terminate_ch,
vec![],
),
@@ -1766,7 +1773,7 @@ mod tests {
let result = ctx.instantiate(
1,
&mut GasMeter::<Test>::with_limit(10000, 1),
&mut GasMeter::<Test>::new(GAS_LIMIT),
&rent_allowance_ch,
vec![],
);
+42 -112
View File
@@ -14,22 +14,18 @@
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
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::Trait;
use sp_std::marker::PhantomData;
use sp_runtime::traits::Zero;
use frame_support::dispatch::{
DispatchError, DispatchResultWithPostInfo, PostDispatchInfo, DispatchErrorWithPostInfo,
};
#[cfg(test)]
use std::{any::Any, fmt::Debug};
// Gas units are chosen to be represented by u64 so that gas metering instructions can operate on
// them efficiently.
pub type Gas = u64;
// Gas is essentially the same as weight. It is a 1 to 1 correspondence.
pub type Gas = frame_support::weights::Weight;
#[must_use]
#[derive(Debug, PartialEq, Eq)]
@@ -88,20 +84,19 @@ pub struct ErasedToken {
}
pub struct GasMeter<T: Trait> {
limit: Gas,
gas_limit: Gas,
/// Amount of gas left from initial gas limit. Can reach zero.
gas_left: Gas,
gas_price: BalanceOf<T>,
_phantom: PhantomData<T>,
#[cfg(test)]
tokens: Vec<ErasedToken>,
}
impl<T: Trait> GasMeter<T> {
pub fn with_limit(gas_limit: Gas, gas_price: BalanceOf<T>) -> GasMeter<T> {
pub fn new(gas_limit: Gas) -> Self {
GasMeter {
limit: gas_limit,
gas_limit,
gas_left: gas_limit,
gas_price,
_phantom: PhantomData,
#[cfg(test)]
tokens: Vec::new(),
}
@@ -147,6 +142,14 @@ impl<T: Trait> GasMeter<T> {
}
}
// Account for not fully used gas.
//
// This can be used after dispatching a runtime call to refund gas that was not
// used by the dispatchable.
pub fn refund(&mut self, gas: Gas) {
self.gas_left = self.gas_left.saturating_add(gas).max(self.gas_limit);
}
/// Allocate some amount of gas and perform some work with
/// a newly created nested gas meter.
///
@@ -165,7 +168,7 @@ impl<T: Trait> GasMeter<T> {
f(None)
} else {
self.gas_left = self.gas_left - amount;
let mut nested = GasMeter::with_limit(amount, self.gas_price);
let mut nested = GasMeter::new(amount);
let r = f(Some(&mut nested));
@@ -175,8 +178,9 @@ impl<T: Trait> GasMeter<T> {
}
}
pub fn gas_price(&self) -> BalanceOf<T> {
self.gas_price
/// Returns how much gas left from the initial budget.
fn gas_spent(&self) -> Gas {
self.gas_limit - self.gas_left
}
/// Returns how much gas left from the initial budget.
@@ -184,9 +188,17 @@ impl<T: Trait> GasMeter<T> {
self.gas_left
}
/// Returns how much gas was spent.
fn spent(&self) -> Gas {
self.limit - self.gas_left
/// Turn this GasMeter into a DispatchResult that contains the actually used gas.
pub fn into_dispatch_result<R, E>(self, result: Result<R, E>) -> DispatchResultWithPostInfo where
E: Into<DispatchError>,
{
let post_info = PostDispatchInfo {
actual_weight: Some(self.gas_spent()),
};
result
.map(|_| post_info)
.map_err(|e| DispatchErrorWithPostInfo { post_info, error: e.into() })
}
#[cfg(test)]
@@ -195,68 +207,6 @@ impl<T: Trait> GasMeter<T> {
}
}
/// 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<T: Trait>(
transactor: &T::AccountId,
gas_limit: Gas,
) -> Result<(GasMeter<T>, NegativeImbalanceOf<T>), DispatchError> {
// Buy the specified amount of gas.
let gas_price = <Module<T>>::gas_price();
let cost = if gas_price.is_zero() {
<BalanceOf<T>>::zero()
} else {
<BalanceOf<T> as TryFrom<Gas>>::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<T: Trait>(
transactor: &T::AccountId,
gas_meter: GasMeter<T>,
imbalance: NegativeImbalanceOf<T>,
) {
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<Balance>(gas_price: Balance, balance: Balance) -> Gas
where Balance: AtLeast32Bit
{
if gas_price.is_zero() {
Zero::zero()
} else {
(balance / gas_price).saturated_into::<Gas>()
}
}
/// A simple utility macro that helps to match against a
/// list of tokens.
#[macro_export]
@@ -298,7 +248,7 @@ macro_rules! match_tokens {
#[cfg(test)]
mod tests {
use super::{GasMeter, Token};
use crate::{tests::Test, gas::approx_gas_for_balance};
use crate::tests::Test;
/// A trivial token that charges the specified number of gas units.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
@@ -326,26 +276,24 @@ mod tests {
#[test]
fn it_works() {
let gas_meter = GasMeter::<Test>::with_limit(50000, 10);
let gas_meter = GasMeter::<Test>::new(50000);
assert_eq!(gas_meter.gas_left(), 50000);
}
#[test]
fn simple() {
let mut gas_meter = GasMeter::<Test>::with_limit(50000, 10);
let mut gas_meter = GasMeter::<Test>::new(50000);
let result = gas_meter
.charge(&MultiplierTokenMetadata { multiplier: 3 }, MultiplierToken(10));
assert!(!result.is_out_of_gas());
assert_eq!(gas_meter.gas_left(), 49_970);
assert_eq!(gas_meter.spent(), 30);
assert_eq!(gas_meter.gas_price(), 10);
}
#[test]
fn tracing() {
let mut gas_meter = GasMeter::<Test>::with_limit(50000, 10);
let mut gas_meter = GasMeter::<Test>::new(50000);
assert!(!gas_meter.charge(&(), SimpleToken(1)).is_out_of_gas());
assert!(!gas_meter
.charge(&MultiplierTokenMetadata { multiplier: 3 }, MultiplierToken(10))
@@ -358,7 +306,7 @@ mod tests {
// This test makes sure that nothing can be executed if there is no gas.
#[test]
fn refuse_to_execute_anything_if_zero() {
let mut gas_meter = GasMeter::<Test>::with_limit(0, 10);
let mut gas_meter = GasMeter::<Test>::new(0);
assert!(gas_meter.charge(&(), SimpleToken(1)).is_out_of_gas());
}
@@ -369,7 +317,7 @@ mod tests {
// if the gas meter runs out of gas. However, this is just a nice property to have.
#[test]
fn overcharge_is_unrecoverable() {
let mut gas_meter = GasMeter::<Test>::with_limit(200, 10);
let mut gas_meter = GasMeter::<Test>::new(200);
// The first charge is should lead to OOG.
assert!(gas_meter.charge(&(), SimpleToken(300)).is_out_of_gas());
@@ -383,25 +331,7 @@ mod tests {
// possible.
#[test]
fn charge_exact_amount() {
let mut gas_meter = GasMeter::<Test>::with_limit(25, 10);
let mut gas_meter = GasMeter::<Test>::new(25);
assert!(!gas_meter.charge(&(), SimpleToken(25)).is_out_of_gas());
}
// A unit test for `fn approx_gas_for_balance()`, and makes
// sure setting gas_price 0 does not cause `div by zero` error.
#[test]
fn approx_gas_for_balance_works() {
let tests = vec![
(approx_gas_for_balance(0_u64, 123), 0),
(approx_gas_for_balance(0_u64, 456), 0),
(approx_gas_for_balance(1_u64, 123), 123),
(approx_gas_for_balance(1_u64, 456), 456),
(approx_gas_for_balance(100_u64, 900), 9),
(approx_gas_for_balance(123_u64, 900), 7),
];
for (lhs, rhs) in tests {
assert_eq!(lhs, rhs);
}
}
}
+80 -225
View File
@@ -64,15 +64,6 @@
//! initialize the contract.
//! * `call` - Makes a call to an account, optionally transferring some balance.
//!
//! ### Signed Extensions
//!
//! The contracts module defines the following extension:
//!
//! - [`CheckBlockGasLimit`]: 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.
//!
//! ## Usage
//!
//! The Contract module is a work in progress. The following examples show how this Contract module
@@ -114,21 +105,20 @@ use codec::{Codec, Encode, Decode};
use sp_io::hashing::blake2_256;
use sp_runtime::{
traits::{
Hash, StaticLookup, Zero, MaybeSerializeDeserialize, Member, SignedExtension,
DispatchInfoOf,
},
transaction_validity::{
ValidTransaction, InvalidTransaction, TransactionValidity, TransactionValidityError,
Hash, StaticLookup, Zero, MaybeSerializeDeserialize, Member,
},
RuntimeDebug,
};
use frame_support::dispatch::{DispatchResult, Dispatchable};
use frame_support::dispatch::{
PostDispatchInfo, DispatchResult, Dispatchable, DispatchResultWithPostInfo
};
use frame_support::weights::MINIMUM_WEIGHT;
use frame_support::{
Parameter, decl_module, decl_event, decl_storage, decl_error,
parameter_types, IsSubType, storage::child::{self, ChildInfo},
};
use frame_support::traits::{OnUnbalanced, Currency, Get, Time, Randomness};
use frame_support::weights::{FunctionOf, DispatchClass, Weight, GetDispatchInfo, Pays};
use frame_system::{self as system, ensure_signed, RawOrigin, ensure_root};
use pallet_contracts_primitives::{RentProjection, ContractAccessError};
@@ -295,9 +285,9 @@ where
}
}
pub type BalanceOf<T> = <<T as Trait>::Currency as Currency<<T as frame_system::Trait>::AccountId>>::Balance;
pub type BalanceOf<T> = <<T as pallet_transaction_payment::Trait>::Currency as Currency<<T as frame_system::Trait>::AccountId>>::Balance;
pub type NegativeImbalanceOf<T> =
<<T as Trait>::Currency as Currency<<T as frame_system::Trait>::AccountId>>::NegativeImbalance;
<<T as pallet_transaction_payment::Trait>::Currency as Currency<<T as frame_system::Trait>::AccountId>>::NegativeImbalance;
parameter_types! {
/// A reasonable default value for [`Trait::SignedClaimedHandicap`].
@@ -312,35 +302,21 @@ parameter_types! {
pub const DefaultRentDepositOffset: u32 = 1000;
/// A reasonable default value for [`Trait::SurchargeReward`].
pub const DefaultSurchargeReward: u32 = 150;
/// A reasonable default value for [`Trait::TransferFee`].
pub const DefaultTransferFee: u32 = 0;
/// A reasonable default value for [`Trait::InstantiationFee`].
pub const DefaultInstantiationFee: u32 = 0;
/// A reasonable default value for [`Trait::TransactionBaseFee`].
pub const DefaultTransactionBaseFee: u32 = 0;
/// A reasonable default value for [`Trait::TransactionByteFee`].
pub const DefaultTransactionByteFee: u32 = 0;
/// A reasonable default value for [`Trait::ContractFee`].
pub const DefaultContractFee: u32 = 21;
/// A reasonable default value for [`Trait::CallBaseFee`].
pub const DefaultCallBaseFee: u32 = 1000;
/// A reasonable default value for [`Trait::InstantiateBaseFee`].
pub const DefaultInstantiateBaseFee: u32 = 1000;
/// A reasonable default value for [`Trait::MaxDepth`].
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 {
type Currency: Currency<Self::AccountId>;
pub trait Trait: frame_system::Trait + pallet_transaction_payment::Trait {
type Time: Time;
type Randomness: Randomness<Self::Hash>;
/// The outer call dispatch type.
type Call: Parameter + Dispatchable<Origin=<Self as frame_system::Trait>::Origin> + IsSubType<Module<Self>, Self>;
type Call:
Parameter +
Dispatchable<PostInfo=PostDispatchInfo, Origin=<Self as frame_system::Trait>::Origin> +
IsSubType<Module<Self>, Self> + GetDispatchInfo;
/// The overarching event type.
type Event: From<Event<Self>> + Into<<Self as frame_system::Trait>::Event>;
@@ -348,18 +324,9 @@ pub trait Trait: frame_system::Trait {
/// A function type to get the contract address given the instantiator.
type DetermineContractAddress: ContractAddressFor<CodeHash<Self>, Self::AccountId>;
/// A function type that computes the fee for dispatching the given `Call`.
///
/// It is recommended (though not required) for this function to return a fee that would be
/// taken by the Executive module for regular dispatch.
type ComputeDispatchFee: ComputeDispatchFee<<Self as Trait>::Call, BalanceOf<Self>>;
/// trie id generator
type TrieIdGenerator: TrieIdGenerator<Self::AccountId>;
/// Handler for the unbalanced reduction when making a gas payment.
type GasPayment: OnUnbalanced<NegativeImbalanceOf<Self>>;
/// Handler for rent payments.
type RentPayment: OnUnbalanced<NegativeImbalanceOf<Self>>;
@@ -392,29 +359,11 @@ pub trait Trait: frame_system::Trait {
/// to removal of a contract.
type SurchargeReward: Get<BalanceOf<Self>>;
/// The fee to be paid for making a transaction; the base.
type TransactionBaseFee: Get<BalanceOf<Self>>;
/// The fee to be paid for making a transaction; the per-byte portion.
type TransactionByteFee: Get<BalanceOf<Self>>;
/// The fee required to instantiate a contract instance.
type ContractFee: Get<BalanceOf<Self>>;
/// The base fee charged for calling into a contract.
type CallBaseFee: Get<Gas>;
/// The base fee charged for instantiating a contract.
type InstantiateBaseFee: Get<Gas>;
/// The maximum nesting level of a call/instantiate stack.
type MaxDepth: Get<u32>;
/// The maximum size of a storage value in bytes.
type MaxValueSize: Get<u32>;
/// The maximum amount of gas that could be expended per block.
type BlockGasLimit: Get<Gas>;
}
/// Simple contract address determiner.
@@ -440,19 +389,6 @@ where
}
}
/// The default dispatch fee computor computes the fee in the same way that
/// the implementation of `ChargeTransactionPayment` for the Balances module does. Note that this only takes a fixed
/// fee based on size. Unlike the balances module, weight-fee is applied.
pub struct DefaultDispatchFeeComputor<T: Trait>(PhantomData<T>);
impl<T: Trait> ComputeDispatchFee<<T as Trait>::Call, BalanceOf<T>> for DefaultDispatchFeeComputor<T> {
fn compute_dispatch_fee(call: &<T as Trait>::Call) -> BalanceOf<T> {
let encoded_len = call.using_encoded(|encoded| encoded.len() as u32);
let base_fee = T::TransactionBaseFee::get();
let byte_fee = T::TransactionByteFee::get();
base_fee + byte_fee * encoded_len.into()
}
}
decl_error! {
/// Error for the contracts module.
pub enum Error for Module<T: Trait> {
@@ -505,24 +441,6 @@ decl_module! {
/// to removal of a contract.
const SurchargeReward: BalanceOf<T> = T::SurchargeReward::get();
/// The fee to be paid for making a transaction; the base.
const TransactionBaseFee: BalanceOf<T> = T::TransactionBaseFee::get();
/// The fee to be paid for making a transaction; the per-byte portion.
const TransactionByteFee: BalanceOf<T> = T::TransactionByteFee::get();
/// The fee required to instantiate a contract instance. A reasonable default value
/// is 21.
const ContractFee: BalanceOf<T> = T::ContractFee::get();
/// The base fee charged for calling into a contract. A reasonable default
/// value is 135.
const CallBaseFee: Gas = T::CallBaseFee::get();
/// The base fee charged for instantiating a contract. A reasonable default value
/// is 175.
const InstantiateBaseFee: Gas = T::InstantiateBaseFee::get();
/// The maximum nesting level of a call/instantiate stack. A reasonable default
/// value is 100.
const MaxDepth: u32 = T::MaxDepth::get();
@@ -530,10 +448,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.
@@ -554,24 +468,21 @@ 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 = MINIMUM_WEIGHT]
#[weight = FunctionOf(
|args: (&Vec<u8>,)| Module::<T>::calc_code_put_costs(args.0) + MINIMUM_WEIGHT,
DispatchClass::Normal,
Pays::Yes
)]
pub fn put_code(
origin,
#[compact] gas_limit: Gas,
code: Vec<u8>
) -> DispatchResult {
let origin = ensure_signed(origin)?;
let (mut gas_meter, imbalance) = gas::buy_gas::<T>(&origin, gas_limit)?;
ensure_signed(origin)?;
let schedule = <Module<T>>::current_schedule();
let result = wasm::save_code::<T>(code, &mut gas_meter, &schedule);
let result = wasm::save_code::<T>(code, &schedule);
if let Ok(code_hash) = result {
Self::deposit_event(RawEvent::CodeStored(code_hash));
}
gas::refund_unused_gas::<T>(&origin, gas_meter, imbalance);
result.map(|_| ()).map_err(Into::into)
}
@@ -582,20 +493,27 @@ decl_module! {
/// * If the account is a regular account, any value will be transferred.
/// * If no account exists and the call value is not less than `existential_deposit`,
/// a regular account will be created and any value will be transferred.
#[weight = MINIMUM_WEIGHT]
#[weight = FunctionOf(
|args: (&<T::Lookup as StaticLookup>::Source, &BalanceOf<T>, &Weight, &Vec<u8>)|
args.2 + MINIMUM_WEIGHT,
DispatchClass::Normal,
Pays::Yes
)]
pub fn call(
origin,
dest: <T::Lookup as StaticLookup>::Source,
#[compact] value: BalanceOf<T>,
#[compact] gas_limit: Gas,
data: Vec<u8>
) -> DispatchResult {
) -> DispatchResultWithPostInfo {
let origin = ensure_signed(origin)?;
let dest = T::Lookup::lookup(dest)?;
let mut gas_meter = GasMeter::new(gas_limit);
Self::bare_call(origin, dest, value, gas_limit, data)
.map(|_| ())
.map_err(|e| e.reason.into())
let result = Self::execute_wasm(origin, &mut gas_meter, |ctx, gas_meter| {
ctx.call(dest, value, gas_meter, data)
});
gas_meter.into_dispatch_result(result.map_err(|e| e.reason))
}
/// Instantiates a new contract from the `codehash` generated by `put_code`, optionally transferring some balance.
@@ -608,22 +526,26 @@ decl_module! {
/// after the execution is saved as the `code` of the account. That code will be invoked
/// upon any call received by this account.
/// - The contract is initialized.
#[weight = MINIMUM_WEIGHT]
#[weight = FunctionOf(
|args: (&BalanceOf<T>, &Weight, &CodeHash<T>, &Vec<u8>)| args.1 + MINIMUM_WEIGHT,
DispatchClass::Normal,
Pays::Yes
)]
pub fn instantiate(
origin,
#[compact] endowment: BalanceOf<T>,
#[compact] gas_limit: Gas,
code_hash: CodeHash<T>,
data: Vec<u8>
) -> DispatchResult {
) -> DispatchResultWithPostInfo {
let origin = ensure_signed(origin)?;
let mut gas_meter = GasMeter::new(gas_limit);
Self::execute_wasm(origin, gas_limit, |ctx, gas_meter| {
let result = Self::execute_wasm(origin, &mut gas_meter, |ctx, gas_meter| {
ctx.instantiate(endowment, gas_meter, &code_hash, data)
.map(|(_address, output)| output)
})
.map(|_| ())
.map_err(|e| e.reason.into())
});
gas_meter.into_dispatch_result(result.map_err(|e| e.reason))
}
/// Allows block producers to claim a small reward for evicting a contract. If a block producer
@@ -658,10 +580,6 @@ decl_module! {
T::Currency::deposit_into_existing(&rewarded, T::SurchargeReward::get())?;
}
}
fn on_finalize() {
GasSpent::kill();
}
}
}
@@ -678,7 +596,8 @@ impl<T: Trait> Module<T> {
gas_limit: Gas,
input_data: Vec<u8>,
) -> ExecResult {
Self::execute_wasm(origin, gas_limit, |ctx, gas_meter| {
let mut gas_meter = GasMeter::new(gas_limit);
Self::execute_wasm(origin, &mut gas_meter, |ctx, gas_meter| {
ctx.call(dest, value, gas_meter, input_data)
})
}
@@ -710,40 +629,27 @@ impl<T: Trait> Module<T> {
}
impl<T: Trait> Module<T> {
fn calc_code_put_costs(code: &Vec<u8>) -> Gas {
<Module<T>>::current_schedule().put_code_per_byte_cost.saturating_mul(code.len() as Gas)
}
fn execute_wasm(
origin: T::AccountId,
gas_limit: Gas,
gas_meter: &mut GasMeter<T>,
func: impl FnOnce(&mut ExecutionContext<T, WasmVm, WasmLoader>, &mut GasMeter<T>) -> 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::<T>(&origin, gas_limit),
// We don't have a spare buffer here in the first place, so create a new empty one.
Vec::new()
);
let cfg = Config::preload();
let vm = WasmVm::new(&cfg.schedule);
let loader = WasmLoader::new(&cfg.schedule);
let mut ctx = ExecutionContext::top_level(origin.clone(), &cfg, &vm, &loader);
let result = func(&mut ctx, &mut gas_meter);
let result = func(&mut ctx, gas_meter);
if result.as_ref().map(|output| output.is_success()).unwrap_or(false) {
// Commit all changes that made it thus far into the persistent storage.
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::<T>(&origin, gas_meter, imbalance);
// Execute deferred actions.
ctx.deferred.into_iter().for_each(|deferred| {
use self::exec::DeferredAction::*;
@@ -759,7 +665,13 @@ impl<T: Trait> Module<T> {
origin: who,
call,
} => {
let info = call.get_dispatch_info();
let result = call.dispatch(RawOrigin::Signed(who.clone()).into());
let post_info = match result {
Ok(post_info) => post_info,
Err(err) => err.post_info,
};
gas_meter.refund(post_info.calc_unspent(&info));
Self::deposit_event(RawEvent::Dispatched(who, result.is_ok()));
}
RestoreTo {
@@ -917,8 +829,6 @@ decl_event! {
decl_storage! {
trait Store for Module<T: Trait> as Contracts {
/// Gas spent so far in this block.
GasSpent get(fn gas_spent): 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.
@@ -929,8 +839,6 @@ decl_storage! {
pub AccountCounter: u64 = 0;
/// The code associated with a given account.
pub ContractInfoOf: map hasher(twox_64_concat) T::AccountId => Option<ContractInfo<T>>;
/// The price of one unit of gas.
GasPrice get(fn gas_price) config(): BalanceOf<T> = 1.into();
}
}
@@ -944,7 +852,6 @@ pub struct Config<T: Trait> {
pub tombstone_deposit: BalanceOf<T>,
pub max_depth: u32,
pub max_value_size: u32,
pub contract_account_instantiate_fee: BalanceOf<T>,
}
impl<T: Trait> Config<T> {
@@ -955,7 +862,6 @@ impl<T: Trait> Config<T> {
tombstone_deposit: T::TombstoneDeposit::get(),
max_depth: T::MaxDepth::get(),
max_value_size: T::MaxValueSize::get(),
contract_account_instantiate_fee: T::ContractFee::get(),
}
}
}
@@ -994,6 +900,9 @@ pub struct Schedule {
/// Base gas cost to instantiate a contract.
pub instantiate_base_cost: Gas,
/// Base gas cost to dispatch a runtime call.
pub dispatch_base_cost: Gas,
/// Gas cost per one byte read from the sandbox memory.
pub sandbox_data_read_cost: Gas,
@@ -1003,6 +912,9 @@ pub struct Schedule {
/// Cost for a simple balance transfer.
pub transfer_cost: Gas,
/// Cost for instantiating a new contract.
pub instantiate_cost: Gas,
/// The maximum number of topics supported by an event.
pub max_event_topics: u32,
@@ -1026,22 +938,29 @@ pub struct Schedule {
pub max_subject_len: u32,
}
// 500 (2 instructions per nano second on 2GHZ) * 1000x slowdown through wasmi
// This is a wild guess and should be viewed as a rough estimation.
// Proper benchmarks are needed before this value and its derivatives can be used in production.
const WASM_INSTRUCTION_COST: Gas = 500_000;
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,
event_data_per_byte_cost: 1,
event_per_topic_cost: 1,
event_base_cost: 1,
call_base_cost: 135,
instantiate_base_cost: 175,
sandbox_data_read_cost: 1,
sandbox_data_write_cost: 1,
transfer_cost: 100,
put_code_per_byte_cost: WASM_INSTRUCTION_COST,
grow_mem_cost: WASM_INSTRUCTION_COST,
regular_op_cost: WASM_INSTRUCTION_COST,
return_data_per_byte_cost: WASM_INSTRUCTION_COST,
event_data_per_byte_cost: WASM_INSTRUCTION_COST,
event_per_topic_cost: WASM_INSTRUCTION_COST,
event_base_cost: WASM_INSTRUCTION_COST,
call_base_cost: 135 * WASM_INSTRUCTION_COST,
dispatch_base_cost: 135 * WASM_INSTRUCTION_COST,
instantiate_base_cost: 175 * WASM_INSTRUCTION_COST,
sandbox_data_read_cost: WASM_INSTRUCTION_COST,
sandbox_data_write_cost: WASM_INSTRUCTION_COST,
transfer_cost: 100 * WASM_INSTRUCTION_COST,
instantiate_cost: 200 * WASM_INSTRUCTION_COST,
max_event_topics: 4,
max_stack_height: 64 * 1024,
max_memory_pages: 16,
@@ -1052,67 +971,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<T: Trait + Send + Sync>(PhantomData<T>);
impl<T: Trait + Send + Sync> Default for CheckBlockGasLimit<T> {
fn default() -> Self {
Self(PhantomData)
}
}
impl<T: Trait + Send + Sync> sp_std::fmt::Debug for CheckBlockGasLimit<T> {
#[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<T: Trait + Send + Sync> SignedExtension for CheckBlockGasLimit<T> {
const IDENTIFIER: &'static str = "CheckBlockGasLimit";
type AccountId = T::AccountId;
type Call = <T as Trait>::Call;
type AdditionalSigned = ();
type Pre = ();
fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> { Ok(()) }
fn validate(
&self,
_: &Self::AccountId,
call: &Self::Call,
_: &DispatchInfoOf<Self::Call>,
_: 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() - <Module<T>>::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"),
}
}
}
+127 -187
View File
@@ -22,20 +22,22 @@
use crate::{
BalanceOf, ComputeDispatchFee, ContractAddressFor, ContractInfo, ContractInfoOf, GenesisConfig,
Module, RawAliveContractInfo, RawEvent, Trait, TrieId, TrieIdFromParentCounter, Schedule,
TrieIdGenerator, CheckBlockGasLimit, account_db::{AccountDb, DirectAccountDb, OverlayAccountDb},
TrieIdGenerator, account_db::{AccountDb, DirectAccountDb, OverlayAccountDb},
gas::Gas,
};
use assert_matches::assert_matches;
use hex_literal::*;
use codec::{Decode, Encode, KeyedVec};
use sp_runtime::{
Perbill, BuildStorage, transaction_validity::{InvalidTransaction, ValidTransaction},
traits::{BlakeTwo256, Hash, IdentityLookup, SignedExtension},
traits::{BlakeTwo256, Hash, IdentityLookup, SignedExtension, Convert},
testing::{Digest, DigestItem, Header, UintAuthorityId, H256},
};
use frame_support::{
assert_ok, assert_err, impl_outer_dispatch, impl_outer_event, impl_outer_origin, parameter_types,
assert_ok, assert_err, assert_err_ignore_postinfo, impl_outer_dispatch, impl_outer_event,
impl_outer_origin, parameter_types,
storage::child, StorageMap, StorageValue, traits::{Currency, Get},
weights::{DispatchInfo, DispatchClass, Weight, Pays},
weights::{DispatchInfo, DispatchClass, Weight, PostDispatchInfo, Pays},
};
use std::{cell::RefCell, sync::atomic::{AtomicUsize, Ordering}};
use sp_core::storage::well_known_keys;
@@ -70,9 +72,6 @@ impl_outer_dispatch! {
thread_local! {
static EXISTENTIAL_DEPOSIT: RefCell<u64> = RefCell::new(0);
static TRANSFER_FEE: RefCell<u64> = RefCell::new(0);
static INSTANTIATION_FEE: RefCell<u64> = RefCell::new(0);
static BLOCK_GAS_LIMIT: RefCell<u64> = RefCell::new(0);
}
pub struct ExistentialDeposit;
@@ -80,16 +79,6 @@ impl Get<u64> for ExistentialDeposit {
fn get() -> u64 { EXISTENTIAL_DEPOSIT.with(|v| *v.borrow()) }
}
pub struct TransferFee;
impl Get<u64> for TransferFee {
fn get() -> u64 { TRANSFER_FEE.with(|v| *v.borrow()) }
}
pub struct BlockGasLimit;
impl Get<u64> for BlockGasLimit {
fn get() -> u64 { BLOCK_GAS_LIMIT.with(|v| *v.borrow()) }
}
#[derive(Clone, Eq, PartialEq, Debug)]
pub struct Test;
parameter_types! {
@@ -142,24 +131,37 @@ parameter_types! {
pub const RentByteFee: u64 = 4;
pub const RentDepositOffset: u64 = 10_000;
pub const SurchargeReward: u64 = 150;
pub const TransactionBaseFee: u64 = 2;
pub const TransactionByteFee: u64 = 6;
pub const ContractFee: u64 = 21;
pub const CallBaseFee: u64 = 135;
pub const InstantiateBaseFee: u64 = 175;
pub const MaxDepth: u32 = 100;
pub const MaxValueSize: u32 = 16_384;
}
impl Trait for Test {
parameter_types! {
pub const TransactionBaseFee: u64 = 0;
pub const TransactionByteFee: u64 = 0;
}
impl Convert<Weight, BalanceOf<Self>> for Test {
fn convert(w: Weight) -> BalanceOf<Self> {
w
}
}
impl pallet_transaction_payment::Trait for Test {
type Currency = Balances;
type OnTransactionPayment = ();
type TransactionBaseFee = TransactionBaseFee;
type TransactionByteFee = TransactionByteFee;
type WeightToFee = Test;
type FeeMultiplierUpdate = ();
}
impl Trait for Test {
type Time = Timestamp;
type Randomness = Randomness;
type Call = Call;
type DetermineContractAddress = DummyContractAddressFor;
type Event = MetaEvent;
type ComputeDispatchFee = DummyComputeDispatchFee;
type TrieIdGenerator = DummyTrieIdGenerator;
type GasPayment = ();
type RentPayment = ();
type SignedClaimHandicap = SignedClaimHandicap;
type TombstoneDeposit = TombstoneDeposit;
@@ -167,14 +169,8 @@ impl Trait for Test {
type RentByteFee = RentByteFee;
type RentDepositOffset = RentDepositOffset;
type SurchargeReward = SurchargeReward;
type TransactionBaseFee = TransactionBaseFee;
type TransactionByteFee = TransactionByteFee;
type ContractFee = ContractFee;
type CallBaseFee = CallBaseFee;
type InstantiateBaseFee = InstantiateBaseFee;
type MaxDepth = MaxDepth;
type MaxValueSize = MaxValueSize;
type BlockGasLimit = BlockGasLimit;
}
type Balances = pallet_balances::Module<Test>;
@@ -219,21 +215,15 @@ const BOB: u64 = 2;
const CHARLIE: u64 = 3;
const DJANGO: u64 = 4;
const GAS_LIMIT: Gas = 10_000_000_000;
pub struct ExtBuilder {
existential_deposit: u64,
gas_price: u64,
block_gas_limit: u64,
transfer_fee: u64,
instantiation_fee: u64,
}
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,
}
}
}
@@ -242,27 +232,8 @@ 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
}
pub fn instantiation_fee(mut self, instantiation_fee: u64) -> Self {
self.instantiation_fee = instantiation_fee;
self
}
pub fn set_associated_consts(&self) {
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();
@@ -270,12 +241,11 @@ impl ExtBuilder {
pallet_balances::GenesisConfig::<Test> {
balances: vec![],
}.assimilate_storage(&mut t).unwrap();
GenesisConfig::<Test> {
GenesisConfig {
current_schedule: Schedule {
enable_println: true,
..Default::default()
},
gas_price: self.gas_price,
}.assimilate_storage(&mut t).unwrap();
let mut ext = sp_io::TestExternalities::new(t);
ext.execute_with(|| System::set_block_number(1));
@@ -293,17 +263,21 @@ fn compile_module<T>(wabt_module: &str)
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.
// Perform a simple transfer to a non-existent account.
// Then we check that only the base costs are returned as actual costs.
#[test]
fn refunds_unused_gas() {
ExtBuilder::default().gas_price(2).build().execute_with(|| {
fn returns_base_call_cost() {
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_eq!(
Contracts::call(Origin::signed(ALICE), BOB, 0, GAS_LIMIT, Vec::new()),
Ok(
PostDispatchInfo {
actual_weight: Some(67500000),
}
)
);
});
}
@@ -388,13 +362,13 @@ 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(
Origin::signed(ALICE),
100,
100_000,
GAS_LIMIT,
code_hash.into(),
vec![],
);
@@ -462,7 +436,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.
@@ -487,7 +461,7 @@ fn dispatch_call() {
assert_ok!(Contracts::instantiate(
Origin::signed(ALICE),
100,
100_000,
GAS_LIMIT,
code_hash.into(),
vec![],
));
@@ -496,7 +470,7 @@ fn dispatch_call() {
Origin::signed(ALICE),
BOB, // newly created account
0,
100_000,
GAS_LIMIT,
vec![],
));
@@ -583,7 +557,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.
@@ -608,19 +582,19 @@ fn dispatch_call_not_dispatched_after_top_level_transaction_failure() {
assert_ok!(Contracts::instantiate(
Origin::signed(ALICE),
100,
100_000,
GAS_LIMIT,
code_hash.into(),
vec![],
));
// Call the newly instantiated contract. The contract is expected to dispatch a call
// and then trap.
assert_err!(
assert_err_ignore_postinfo!(
Contracts::call(
Origin::signed(ALICE),
BOB, // newly created account
0,
100_000,
GAS_LIMIT,
vec![],
),
"contract trapped during execution"
@@ -679,24 +653,24 @@ 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),
100,
100_000,
GAS_LIMIT,
code_hash.into(),
vec![],
));
// Call the contract with a fixed gas limit. It must run out of gas because it just
// loops forever.
assert_err!(
assert_err_ignore_postinfo!(
Contracts::call(
Origin::signed(ALICE),
BOB, // newly created account
0,
1000,
67_500_000,
vec![],
),
"ran out of gas during contract execution"
@@ -725,7 +699,7 @@ fn test_set_rent_code_and_hash() {
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));
// If you ever need to update the wasm source this test will fail
// and will show you the actual hash.
@@ -757,21 +731,21 @@ 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,
100_000, code_hash.into(),
GAS_LIMIT, code_hash.into(),
<Test as pallet_balances::Trait>::Balance::from(1_000u32).encode() // rent allowance
));
let bob_contract = ContractInfoOf::<Test>::get(BOB).unwrap().get_alive().unwrap();
assert_eq!(bob_contract.storage_size, <Test as Trait>::StorageSizeOffset::get() + 4);
assert_ok!(Contracts::call(Origin::signed(ALICE), BOB, 0, 100_000, call::set_storage_4_byte()));
assert_ok!(Contracts::call(Origin::signed(ALICE), BOB, 0, GAS_LIMIT, call::set_storage_4_byte()));
let bob_contract = ContractInfoOf::<Test>::get(BOB).unwrap().get_alive().unwrap();
assert_eq!(bob_contract.storage_size, <Test as Trait>::StorageSizeOffset::get() + 4 + 4);
assert_ok!(Contracts::call(Origin::signed(ALICE), BOB, 0, 100_000, call::remove_storage_4_byte()));
assert_ok!(Contracts::call(Origin::signed(ALICE), BOB, 0, GAS_LIMIT, call::remove_storage_4_byte()));
let bob_contract = ContractInfoOf::<Test>::get(BOB).unwrap().get_alive().unwrap();
assert_eq!(bob_contract.storage_size, <Test as Trait>::StorageSizeOffset::get() + 4);
});
@@ -794,11 +768,11 @@ 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,
100_000, code_hash.into(),
GAS_LIMIT, code_hash.into(),
<Test as pallet_balances::Trait>::Balance::from(1_000u32).encode() // rent allowance
));
@@ -810,7 +784,7 @@ fn deduct_blocks() {
initialize_block(5);
// Trigger rent through call
assert_ok!(Contracts::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null()));
assert_ok!(Contracts::call(Origin::signed(ALICE), BOB, 0, GAS_LIMIT, call::null()));
// Check result
let rent = (8 + 4 - 3) // storage size = size_offset + deploy_set_storage - deposit_offset
@@ -825,7 +799,7 @@ fn deduct_blocks() {
initialize_block(12);
// Trigger rent through call
assert_ok!(Contracts::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null()));
assert_ok!(Contracts::call(Origin::signed(ALICE), BOB, 0, GAS_LIMIT, call::null()));
// Check result
let rent_2 = (8 + 4 - 2) // storage size = size_offset + deploy_set_storage - deposit_offset
@@ -837,7 +811,7 @@ fn deduct_blocks() {
assert_eq!(Balances::free_balance(BOB), 30_000 - rent - rent_2);
// Second call on same block should have no effect on rent
assert_ok!(Contracts::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null()));
assert_ok!(Contracts::call(Origin::signed(ALICE), BOB, 0, GAS_LIMIT, call::null()));
let bob_contract = ContractInfoOf::<Test>::get(BOB).unwrap().get_alive().unwrap();
assert_eq!(bob_contract.rent_allowance, 1_000 - rent - rent_2);
@@ -850,7 +824,7 @@ fn deduct_blocks() {
fn call_contract_removals() {
removals(|| {
// Call on already-removed account might fail, and this is fine.
Contracts::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null());
Contracts::call(Origin::signed(ALICE), BOB, 0, GAS_LIMIT, call::null());
true
});
}
@@ -888,11 +862,11 @@ 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,
100_000, code_hash.into(),
GAS_LIMIT, code_hash.into(),
<Test as pallet_balances::Trait>::Balance::from(1_000u32).encode() // rent allowance
));
@@ -921,11 +895,11 @@ 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,
100_000, code_hash.into(),
GAS_LIMIT, code_hash.into(),
<Test as pallet_balances::Trait>::Balance::from(1_000u32).encode() // rent allowance
));
@@ -957,11 +931,11 @@ 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,
100_000, code_hash.into(),
GAS_LIMIT, code_hash.into(),
<Test as pallet_balances::Trait>::Balance::from(100u32).encode() // rent allowance
));
@@ -992,11 +966,11 @@ 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(),
100_000, code_hash.into(),
GAS_LIMIT, code_hash.into(),
<Test as pallet_balances::Trait>::Balance::from(1_000u32).encode() // rent allowance
));
@@ -1006,7 +980,7 @@ fn removals(trigger_call: impl Fn() -> bool) {
assert_eq!(Balances::free_balance(BOB), 50 + Balances::minimum_balance());
// Transfer funds
assert_ok!(Contracts::call(Origin::signed(ALICE), BOB, 0, 100_000, call::transfer()));
assert_ok!(Contracts::call(Origin::signed(ALICE), BOB, 0, GAS_LIMIT, call::transfer()));
assert_eq!(ContractInfoOf::<Test>::get(BOB).unwrap().get_alive().unwrap().rent_allowance, 1_000);
assert_eq!(Balances::free_balance(BOB), Balances::minimum_balance());
@@ -1036,23 +1010,23 @@ 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,
100_000, code_hash.into(),
GAS_LIMIT, code_hash.into(),
<Test as pallet_balances::Trait>::Balance::from(1_000u32).encode() // rent allowance
));
// Calling contract should succeed.
assert_ok!(Contracts::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null()));
assert_ok!(Contracts::call(Origin::signed(ALICE), BOB, 0, GAS_LIMIT, call::null()));
// Advance blocks
initialize_block(10);
// Calling contract should remove contract and fail.
assert_err!(
Contracts::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null()),
assert_err_ignore_postinfo!(
Contracts::call(Origin::signed(ALICE), BOB, 0, GAS_LIMIT, call::null()),
"contract has been evicted"
);
// Calling a contract that is about to evict shall emit an event.
@@ -1065,8 +1039,8 @@ fn call_removed_contract() {
]);
// Subsequent contract calls should also fail.
assert_err!(
Contracts::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null()),
assert_err_ignore_postinfo!(
Contracts::call(Origin::signed(ALICE), BOB, 0, GAS_LIMIT, call::null()),
"contract has been evicted"
);
})
@@ -1080,11 +1054,11 @@ 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,
100_000,
GAS_LIMIT,
code_hash.into(),
vec![],
));
@@ -1097,7 +1071,7 @@ fn default_rent_allowance_on_instantiate() {
initialize_block(5);
// Trigger rent through call
assert_ok!(Contracts::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null()));
assert_ok!(Contracts::call(Origin::signed(ALICE), BOB, 0, GAS_LIMIT, call::null()));
// Check contract is still alive
let bob_contract = ContractInfoOf::<Test>::get(BOB).unwrap().get_alive();
@@ -1133,8 +1107,8 @@ fn restoration(test_different_storage: bool, test_restore_to_with_dirty_storage:
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));
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.
@@ -1166,7 +1140,7 @@ fn restoration(test_different_storage: bool, test_restore_to_with_dirty_storage:
assert_ok!(Contracts::instantiate(
Origin::signed(ALICE),
30_000,
100_000,
GAS_LIMIT,
set_rent_code_hash.into(),
<Test as pallet_balances::Trait>::Balance::from(0u32).encode()
));
@@ -1179,7 +1153,7 @@ fn restoration(test_different_storage: bool, test_restore_to_with_dirty_storage:
if test_different_storage {
assert_ok!(Contracts::call(
Origin::signed(ALICE),
BOB, 0, 100_000,
BOB, 0, GAS_LIMIT,
call::set_storage_4_byte())
);
}
@@ -1192,8 +1166,8 @@ fn restoration(test_different_storage: bool, test_restore_to_with_dirty_storage:
.get_alive().unwrap().code_hash;
// Call `BOB`, which makes it pay rent. Since the rent allowance is set to 0
// we expect that it will get removed leaving tombstone.
assert_err!(
Contracts::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null()),
assert_err_ignore_postinfo!(
Contracts::call(Origin::signed(ALICE), BOB, 0, GAS_LIMIT, call::null()),
"contract has been evicted"
);
assert!(ContractInfoOf::<Test>::get(BOB).unwrap().get_tombstone().is_some());
@@ -1215,7 +1189,7 @@ fn restoration(test_different_storage: bool, test_restore_to_with_dirty_storage:
assert_ok!(Contracts::instantiate(
Origin::signed(CHARLIE),
30_000,
100_000,
GAS_LIMIT,
restoration_code_hash.into(),
<Test as pallet_balances::Trait>::Balance::from(0u32).encode()
));
@@ -1235,7 +1209,7 @@ fn restoration(test_different_storage: bool, test_restore_to_with_dirty_storage:
Origin::signed(ALICE),
DJANGO,
0,
100_000,
GAS_LIMIT,
vec![],
));
@@ -1349,11 +1323,11 @@ 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,
100_000,
GAS_LIMIT,
code_hash.into(),
vec![],
));
@@ -1367,17 +1341,17 @@ fn storage_max_value_limit() {
Origin::signed(ALICE),
BOB,
0,
100_000,
GAS_LIMIT,
Encode::encode(&self::MaxValueSize::get()),
));
// Call contract with too large a storage value.
assert_err!(
assert_err_ignore_postinfo!(
Contracts::call(
Origin::signed(ALICE),
BOB,
0,
100_000,
GAS_LIMIT,
Encode::encode(&(self::MaxValueSize::get() + 1)),
),
"contract trapped during execution"
@@ -1395,13 +1369,13 @@ 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),
100_000,
100_000,
GAS_LIMIT,
caller_code_hash.into(),
vec![],
));
@@ -1412,41 +1386,24 @@ fn deploy_and_call_other_contract() {
Origin::signed(ALICE),
BOB,
0,
200_000,
GAS_LIMIT,
callee_code_hash.as_ref().to_vec(),
));
});
}
#[test]
fn deploy_works_without_gas_price() {
let (wasm, code_hash) = compile_module::<Test>(&load_wasm("get_runtime_storage.wat"))
.unwrap();
ExtBuilder::default().existential_deposit(50).gas_price(0).build().execute_with(|| {
Balances::deposit_creating(&ALICE, 1_000_000);
assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm));
assert_ok!(Contracts::instantiate(
Origin::signed(ALICE),
100,
100_000,
code_hash.into(),
vec![],
));
});
}
#[test]
fn cannot_self_destruct_through_draning() {
let (wasm, code_hash) = compile_module::<Test>(&load_wasm("drain.wat")).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));
assert_ok!(Contracts::put_code(Origin::signed(ALICE), wasm));
// Instantiate the BOB contract.
assert_ok!(Contracts::instantiate(
Origin::signed(ALICE),
100_000,
100_000,
GAS_LIMIT,
code_hash.into(),
vec![],
));
@@ -1459,12 +1416,12 @@ fn cannot_self_destruct_through_draning() {
// Call BOB with no input data, forcing it to run until out-of-balance
// and eventually trapping because below existential deposit.
assert_err!(
assert_err_ignore_postinfo!(
Contracts::call(
Origin::signed(ALICE),
BOB,
0,
100_000,
GAS_LIMIT,
vec![],
),
"contract trapped during execution"
@@ -1478,13 +1435,13 @@ fn cannot_self_destruct_while_live() {
.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));
assert_ok!(Contracts::put_code(Origin::signed(ALICE), wasm));
// Instantiate the BOB contract.
assert_ok!(Contracts::instantiate(
Origin::signed(ALICE),
100_000,
100_000,
GAS_LIMIT,
code_hash.into(),
vec![],
));
@@ -1497,12 +1454,12 @@ fn cannot_self_destruct_while_live() {
// Call BOB with input data, forcing it make a recursive call to itself to
// self-destruct, resulting in a trap.
assert_err!(
assert_err_ignore_postinfo!(
Contracts::call(
Origin::signed(ALICE),
BOB,
0,
100_000,
GAS_LIMIT,
vec![0],
),
"contract trapped during execution"
@@ -1522,13 +1479,13 @@ fn self_destruct_works() {
.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));
assert_ok!(Contracts::put_code(Origin::signed(ALICE), wasm));
// Instantiate the BOB contract.
assert_ok!(Contracts::instantiate(
Origin::signed(ALICE),
100_000,
100_000,
GAS_LIMIT,
code_hash.into(),
vec![],
));
@@ -1545,10 +1502,10 @@ fn self_destruct_works() {
Origin::signed(ALICE),
BOB,
0,
100_000,
GAS_LIMIT,
vec![],
),
Ok(())
Ok(_)
);
// Check that account is gone
@@ -1571,15 +1528,15 @@ 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.
assert_ok!(Contracts::instantiate(
Origin::signed(ALICE),
200_000,
100_000,
GAS_LIMIT,
caller_code_hash.into(),
callee_code_hash.as_ref().to_vec(),
));
@@ -1595,7 +1552,7 @@ fn destroy_contract_and_transfer_funds() {
Origin::signed(ALICE),
BOB,
0,
100_000,
GAS_LIMIT,
CHARLIE.encode(),
));
@@ -1610,15 +1567,15 @@ fn cannot_self_destruct_in_constructor() {
compile_module::<Test>(&load_wasm("self_destructing_constructor.wat")).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));
assert_ok!(Contracts::put_code(Origin::signed(ALICE), wasm));
// Fail to instantiate the BOB because the call that is issued in the deploy
// function exhausts all balances which puts it below the existential deposit.
assert_err!(
assert_err_ignore_postinfo!(
Contracts::instantiate(
Origin::signed(ALICE),
100_000,
100_000,
GAS_LIMIT,
code_hash.into(),
vec![],
),
@@ -1627,23 +1584,6 @@ 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: Pays::Yes };
let check = CheckBlockGasLimit::<Test>(Default::default());
let call: Call = crate::Call::put_code(1000, vec![]).into();
assert_eq!(
check.validate(&0, &call, &info, 0), InvalidTransaction::ExhaustsResources.into(),
);
let call: Call = crate::Call::update_schedule(Default::default()).into();
assert_eq!(check.validate(&0, &call, &info, 0), Ok(Default::default()));
});
}
#[test]
fn get_runtime_storage() {
let (wasm, code_hash) = compile_module::<Test>(&load_wasm("get_runtime_storage.wat"))
.unwrap();
@@ -1655,11 +1595,11 @@ 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,
100_000,
GAS_LIMIT,
code_hash.into(),
vec![],
));
@@ -1667,7 +1607,7 @@ fn get_runtime_storage() {
Origin::signed(ALICE),
BOB,
0,
100_000,
GAS_LIMIT,
vec![],
));
});
@@ -1679,13 +1619,13 @@ fn crypto_hashes() {
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));
// Instantiate the CRYPTO_HASHES contract.
assert_ok!(Contracts::instantiate(
Origin::signed(ALICE),
100_000,
100_000,
GAS_LIMIT,
code_hash.into(),
vec![],
));
@@ -1714,7 +1654,7 @@ fn crypto_hashes() {
ALICE,
BOB,
0,
100_000,
GAS_LIMIT,
params,
).unwrap();
assert_eq!(result.status, 0);
@@ -26,49 +26,20 @@
//! 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 sp_runtime::traits::Hash;
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<T: Trait> Token<T> 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())
}
}
/// Put code in the storage. The hash of code is used as a key and is returned
/// as a result of this function.
///
/// This function instruments the given code and caches it in the storage.
pub fn save<T: Trait>(
original_code: Vec<u8>,
gas_meter: &mut GasMeter<T>,
schedule: &Schedule,
) -> Result<CodeHash<T>, &'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::<Env>(&original_code, schedule)?;
let code_hash = T::Hashing::hash(&original_code);
+42 -34
View File
@@ -157,12 +157,14 @@ mod tests {
use crate::gas::{Gas, GasMeter};
use crate::tests::{Test, Call};
use crate::wasm::prepare::prepare_contract;
use crate::CodeHash;
use crate::{CodeHash, BalanceOf};
use wabt;
use hex_literal::hex;
use assert_matches::assert_matches;
use sp_runtime::DispatchError;
const GAS_LIMIT: Gas = 10_000_000_000;
#[derive(Debug, PartialEq, Eq)]
struct DispatchEntry(Call);
@@ -373,6 +375,9 @@ mod tests {
)
)
}
fn get_weight_price(&self) -> BalanceOf<Self::T> {
1312_u32.into()
}
}
impl Ext for &mut MockExt {
@@ -478,6 +483,9 @@ mod tests {
fn get_runtime_storage(&self, key: &[u8]) -> Option<Vec<u8>> {
(**self).get_runtime_storage(key)
}
fn get_weight_price(&self) -> BalanceOf<Self::T> {
(**self).get_weight_price()
}
}
fn execute<E: Ext>(
@@ -544,7 +552,7 @@ mod tests {
CODE_TRANSFER,
vec![],
&mut mock_ext,
&mut GasMeter::with_limit(50_000, 1),
&mut GasMeter::new(GAS_LIMIT),
).unwrap();
assert_eq!(
@@ -553,7 +561,7 @@ mod tests {
to: 7,
value: 153,
data: Vec::new(),
gas_left: 49978,
gas_left: 9989000000,
}]
);
}
@@ -604,7 +612,7 @@ mod tests {
CODE_CALL,
vec![],
&mut mock_ext,
&mut GasMeter::with_limit(50_000, 1),
&mut GasMeter::new(GAS_LIMIT),
).unwrap();
assert_eq!(
@@ -613,7 +621,7 @@ mod tests {
to: 9,
value: 6,
data: vec![1, 2, 3, 4],
gas_left: 49971,
gas_left: 9985500000,
}]
);
}
@@ -666,7 +674,7 @@ mod tests {
CODE_INSTANTIATE,
vec![],
&mut mock_ext,
&mut GasMeter::with_limit(50_000, 1),
&mut GasMeter::new(GAS_LIMIT),
).unwrap();
assert_eq!(
@@ -675,7 +683,7 @@ mod tests {
code_hash: [0x11; 32].into(),
endowment: 3,
data: vec![1, 2, 3, 4],
gas_left: 49947,
gas_left: 9973500000,
}]
);
}
@@ -709,14 +717,14 @@ mod tests {
CODE_TERMINATE,
vec![],
&mut mock_ext,
&mut GasMeter::with_limit(50_000, 1),
&mut GasMeter::new(GAS_LIMIT),
).unwrap();
assert_eq!(
&mock_ext.terminations,
&[TerminationEntry {
beneficiary: 0x09,
gas_left: 49989,
gas_left: 9994500000,
}]
);
}
@@ -767,7 +775,7 @@ mod tests {
&CODE_TRANSFER_LIMITED_GAS,
vec![],
&mut mock_ext,
&mut GasMeter::with_limit(50_000, 1),
&mut GasMeter::new(GAS_LIMIT),
).unwrap();
assert_eq!(
@@ -860,7 +868,7 @@ mod tests {
CODE_GET_STORAGE,
vec![],
mock_ext,
&mut GasMeter::with_limit(50_000, 1),
&mut GasMeter::new(GAS_LIMIT),
).unwrap();
assert_eq!(output, ExecReturnValue { status: STATUS_SUCCESS, data: [0x22; 32].to_vec() });
@@ -924,7 +932,7 @@ mod tests {
CODE_CALLER,
vec![],
MockExt::default(),
&mut GasMeter::with_limit(50_000, 1),
&mut GasMeter::new(GAS_LIMIT),
).unwrap();
}
@@ -986,7 +994,7 @@ mod tests {
CODE_ADDRESS,
vec![],
MockExt::default(),
&mut GasMeter::with_limit(50_000, 1),
&mut GasMeter::new(GAS_LIMIT),
).unwrap();
}
@@ -1041,7 +1049,7 @@ mod tests {
#[test]
fn balance() {
let mut gas_meter = GasMeter::with_limit(50_000, 1);
let mut gas_meter = GasMeter::new(GAS_LIMIT);
let _ = execute(
CODE_BALANCE,
vec![],
@@ -1101,7 +1109,7 @@ mod tests {
#[test]
fn gas_price() {
let mut gas_meter = GasMeter::with_limit(50_000, 1312);
let mut gas_meter = GasMeter::new(GAS_LIMIT);
let _ = execute(
CODE_GAS_PRICE,
vec![],
@@ -1159,7 +1167,7 @@ mod tests {
#[test]
fn gas_left() {
let mut gas_meter = GasMeter::with_limit(50_000, 1312);
let mut gas_meter = GasMeter::new(GAS_LIMIT);
let output = execute(
CODE_GAS_LEFT,
@@ -1169,7 +1177,7 @@ mod tests {
).unwrap();
let gas_left = Gas::decode(&mut output.data.as_slice()).unwrap();
assert!(gas_left < 50_000, "gas_left must be less than initial");
assert!(gas_left < GAS_LIMIT, "gas_left must be less than initial");
assert!(gas_left > gas_meter.gas_left(), "gas_left must be greater than final");
}
@@ -1224,7 +1232,7 @@ mod tests {
#[test]
fn value_transferred() {
let mut gas_meter = GasMeter::with_limit(50_000, 1);
let mut gas_meter = GasMeter::new(GAS_LIMIT);
let _ = execute(
CODE_VALUE_TRANSFERRED,
vec![],
@@ -1260,7 +1268,7 @@ mod tests {
CODE_DISPATCH_CALL,
vec![],
&mut mock_ext,
&mut GasMeter::with_limit(50_000, 1),
&mut GasMeter::new(GAS_LIMIT),
).unwrap();
assert_eq!(
@@ -1300,7 +1308,7 @@ mod tests {
CODE_RETURN_FROM_START_FN,
vec![],
MockExt::default(),
&mut GasMeter::with_limit(50_000, 1),
&mut GasMeter::new(GAS_LIMIT),
).unwrap();
assert_eq!(output, ExecReturnValue { status: STATUS_SUCCESS, data: vec![1, 2, 3, 4] });
@@ -1357,7 +1365,7 @@ mod tests {
#[test]
fn now() {
let mut gas_meter = GasMeter::with_limit(50_000, 1);
let mut gas_meter = GasMeter::new(GAS_LIMIT);
let _ = execute(
CODE_TIMESTAMP_NOW,
vec![],
@@ -1416,7 +1424,7 @@ mod tests {
#[test]
fn minimum_balance() {
let mut gas_meter = GasMeter::with_limit(50_000, 1);
let mut gas_meter = GasMeter::new(GAS_LIMIT);
let _ = execute(
CODE_MINIMUM_BALANCE,
vec![],
@@ -1475,7 +1483,7 @@ mod tests {
#[test]
fn tombstone_deposit() {
let mut gas_meter = GasMeter::with_limit(50_000, 1);
let mut gas_meter = GasMeter::new(GAS_LIMIT);
let _ = execute(
CODE_TOMBSTONE_DEPOSIT,
vec![],
@@ -1543,7 +1551,7 @@ mod tests {
#[test]
fn random() {
let mut gas_meter = GasMeter::with_limit(50_000, 1);
let mut gas_meter = GasMeter::new(GAS_LIMIT);
let output = execute(
CODE_RANDOM,
@@ -1588,7 +1596,7 @@ mod tests {
#[test]
fn deposit_event() {
let mut mock_ext = MockExt::default();
let mut gas_meter = GasMeter::with_limit(50_000, 1);
let mut gas_meter = GasMeter::new(GAS_LIMIT);
let _ = execute(
CODE_DEPOSIT_EVENT,
vec![],
@@ -1601,7 +1609,7 @@ mod tests {
vec![0x00, 0x01, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe5, 0x14, 0x00])
]);
assert_eq!(gas_meter.gas_left(), 49934);
assert_eq!(gas_meter.gas_left(), 9967000000);
}
const CODE_DEPOSIT_EVENT_MAX_TOPICS: &str = r#"
@@ -1634,7 +1642,7 @@ mod tests {
#[test]
fn deposit_event_max_topics() {
// Checks that the runtime traps if there are more than `max_topic_events` topics.
let mut gas_meter = GasMeter::with_limit(50_000, 1);
let mut gas_meter = GasMeter::new(GAS_LIMIT);
assert_matches!(
execute(
@@ -1678,7 +1686,7 @@ mod tests {
#[test]
fn deposit_event_duplicates() {
// Checks that the runtime traps if there are duplicates.
let mut gas_meter = GasMeter::with_limit(50_000, 1);
let mut gas_meter = GasMeter::new(GAS_LIMIT);
assert_matches!(
execute(
@@ -1749,7 +1757,7 @@ mod tests {
CODE_BLOCK_NUMBER,
vec![],
MockExt::default(),
&mut GasMeter::with_limit(50_000, 1),
&mut GasMeter::new(GAS_LIMIT),
).unwrap();
}
@@ -1789,7 +1797,7 @@ mod tests {
CODE_SIMPLE_ASSERT,
input_data,
MockExt::default(),
&mut GasMeter::with_limit(50_000, 1),
&mut GasMeter::new(GAS_LIMIT),
).unwrap();
assert_eq!(output.data.len(), 0);
@@ -1805,7 +1813,7 @@ mod tests {
CODE_SIMPLE_ASSERT,
input_data,
MockExt::default(),
&mut GasMeter::with_limit(50_000, 1),
&mut GasMeter::new(GAS_LIMIT),
).err().unwrap();
assert_eq!(error.buffer.capacity(), 1_234);
@@ -1859,7 +1867,7 @@ mod tests {
CODE_RETURN_WITH_DATA,
hex!("00112233445566778899").to_vec(),
MockExt::default(),
&mut GasMeter::with_limit(50_000, 1),
&mut GasMeter::new(GAS_LIMIT),
).unwrap();
assert_eq!(output, ExecReturnValue { status: 0, data: hex!("445566778899").to_vec() });
@@ -1872,7 +1880,7 @@ mod tests {
CODE_RETURN_WITH_DATA,
hex!("112233445566778899").to_vec(),
MockExt::default(),
&mut GasMeter::with_limit(50_000, 1),
&mut GasMeter::new(GAS_LIMIT),
).unwrap();
assert_eq!(output, ExecReturnValue { status: 17, data: hex!("5566778899").to_vec() });
@@ -1958,7 +1966,7 @@ mod tests {
#[test]
fn get_runtime_storage() {
let mut gas_meter = GasMeter::with_limit(50_000, 1);
let mut gas_meter = GasMeter::new(GAS_LIMIT);
let mock_ext = MockExt::default();
// "\01\02\03\04" - Some(0x14144020)
+11 -12
View File
@@ -16,11 +16,11 @@
//! Environment definition of the wasm smart-contract runtime.
use crate::{Schedule, Trait, CodeHash, ComputeDispatchFee, BalanceOf};
use crate::{Schedule, Trait, CodeHash, BalanceOf};
use crate::exec::{
Ext, ExecResult, ExecError, ExecReturnValue, StorageKey, TopicOf, STATUS_SUCCESS,
};
use crate::gas::{Gas, GasMeter, Token, GasMeterResult, approx_gas_for_balance};
use crate::gas::{Gas, GasMeter, Token, GasMeterResult};
use sp_sandbox;
use frame_system;
use sp_std::{prelude::*, mem, convert::TryInto};
@@ -32,6 +32,7 @@ use sp_io::hashing::{
blake2_128,
sha2_256,
};
use frame_support::weights::GetDispatchInfo;
/// The value returned from ext_call and ext_instantiate contract external functions if the call or
/// instantiation traps. This value is chosen as if the execution does not trap, the return value
@@ -153,8 +154,8 @@ pub enum RuntimeToken {
/// The given number of bytes is read from the sandbox memory and
/// is returned as the return data buffer of the call.
ReturnData(u32),
/// Dispatch fee calculated by `T::ComputeDispatchFee`.
ComputedDispatchFee(Gas),
/// Dispatched a call with the given weight.
DispatchWithWeight(Gas),
/// (topic_count, data_bytes): A buffer of the given size is posted as an event indexed with the
/// given number of topics.
DepositEvent(u32, u32),
@@ -195,7 +196,7 @@ impl<T: Trait> Token<T> for RuntimeToken {
data_and_topics_cost.checked_add(metadata.event_base_cost)
)
},
ComputedDispatchFee(gas) => Some(gas),
DispatchWithWeight(gas) => gas.checked_add(metadata.dispatch_base_cost),
};
value.unwrap_or_else(|| Bounded::max_value())
@@ -692,7 +693,7 @@ define_env!(Env, <E: Ext>,
// The data is encoded as T::Balance. The current contents of the scratch buffer are overwritten.
ext_gas_price(ctx) => {
ctx.scratch_buf.clear();
ctx.gas_meter.gas_price().encode_to(&mut ctx.scratch_buf);
ctx.ext.get_weight_price().encode_to(&mut ctx.scratch_buf);
Ok(())
},
@@ -783,16 +784,14 @@ define_env!(Env, <E: Ext>,
let call: <<E as Ext>::T as Trait>::Call =
read_sandbox_memory_as(ctx, call_ptr, call_len)?;
// Charge gas for dispatching this call.
let fee = {
let balance_fee = <<E as Ext>::T as Trait>::ComputeDispatchFee::compute_dispatch_fee(&call);
approx_gas_for_balance(ctx.gas_meter.gas_price(), balance_fee)
};
// We already deducted the len costs when reading from the sandbox.
// Bill on the actual weight of the dispatched call.
let info = call.get_dispatch_info();
charge_gas(
&mut ctx.gas_meter,
ctx.schedule,
&mut ctx.special_trap,
RuntimeToken::ComputedDispatchFee(fee)
RuntimeToken::DispatchWithWeight(info.weight)
)?;
ctx.ext.note_dispatch_call(call);
+12 -4
View File
@@ -193,10 +193,6 @@ macro_rules! assert_noop {
}
}
/// Panic if an expression doesn't evaluate to an `Err`.
///
/// Used as `assert_err!(expression_to_assert, expected_err_expression)`.
/// Assert an expression returns an error specified.
///
/// Used as `assert_err!(expression_to_assert, expected_error_expression)`
@@ -208,6 +204,18 @@ macro_rules! assert_err {
}
}
/// Assert an expression returns an error specified.
///
/// This can be used on`DispatchResultWithPostInfo` when the post info should
/// be ignored.
#[macro_export]
#[cfg(feature = "std")]
macro_rules! assert_err_ignore_postinfo {
( $x:expr , $y:expr $(,)? ) => {
assert_err!($x.map(|_| ()).map_err(|e| e.error), $y);
}
}
/// Panic if an expression doesn't evaluate to `Ok`.
///
/// Used as `assert_ok!(expression_to_assert, expected_ok_expression)`,
+11 -9
View File
@@ -47,7 +47,7 @@ use sp_runtime::{
},
traits::{
Zero, Saturating, SignedExtension, SaturatedConversion, Convert, Dispatchable,
DispatchInfoOf, PostDispatchInfoOf,
DispatchInfoOf, PostDispatchInfoOf, UniqueSaturatedFrom, UniqueSaturatedInto,
},
};
use pallet_transaction_payment_rpc_runtime_api::RuntimeDispatchInfo;
@@ -116,9 +116,7 @@ decl_module! {
}
}
impl<T: Trait> Module<T> where
T::Call: Dispatchable<Info=DispatchInfo, PostInfo=PostDispatchInfo>,
{
impl<T: Trait> Module<T> {
/// Query the data that we know about the fee of a given `call`.
///
/// As this module is not and cannot be aware of the internals of a signed extension, it only
@@ -133,6 +131,7 @@ impl<T: Trait> Module<T> where
where
T: Send + Sync,
BalanceOf<T>: Send + Sync,
T::Call: Dispatchable<Info=DispatchInfo>,
{
// NOTE: we can actually make it understand `ChargeTransactionPayment`, but would be some
// hassle for sure. We have to make it aware of the index of `ChargeTransactionPayment` in
@@ -165,7 +164,9 @@ impl<T: Trait> Module<T> where
len: u32,
info: &DispatchInfoOf<T::Call>,
tip: BalanceOf<T>,
) -> BalanceOf<T> {
) -> BalanceOf<T> where
T::Call: Dispatchable<Info=DispatchInfo>,
{
if info.pays_fee == Pays::Yes {
let len = <BalanceOf<T>>::from(len);
let per_byte = T::TransactionByteFee::get();
@@ -188,11 +189,12 @@ impl<T: Trait> Module<T> where
///
/// This fee is already adjusted by the per block fee adjustment factor and is therefore
/// the share that the weight contributes to the overall fee of a transaction.
pub fn weight_to_fee_with_adjustment(weight: Weight) -> BalanceOf<T> where
BalanceOf<T>: From<u64>
pub fn weight_to_fee_with_adjustment<Balance>(weight: Weight) -> Balance where
Balance: UniqueSaturatedFrom<u128>
{
NextFeeMultiplier::get().saturated_multiply_accumulate(
Self::weight_to_fee(weight)
let fee = UniqueSaturatedInto::<u128>::unique_saturated_into(Self::weight_to_fee(weight));
UniqueSaturatedFrom::unique_saturated_from(
NextFeeMultiplier::get().saturated_multiply_accumulate(fee)
)
}